src/open_by_handle: program to exercise open_by_handle_at() syscall
[xfstests-dev.git] / src / open_by_handle.c
1 /*
2  * open_by_handle.c - attempt to create a file handle and open it
3  *                    with open_by_handle_at() syscall
4  *
5  * Copyright (C) 2017 CTERA Networks. All Rights Reserved.
6  * Author: Amir Goldstein <amir73il@gmail.com>
7  *
8  * from:
9  *  stale_handle.c
10  *
11  *  Copyright (C) 2010 Red Hat, Inc. All Rights reserved.
12  *
13  *  This program is free software; you can redistribute it and/or modify
14  *  it under the terms of the GNU General Public License as published by
15  *  the Free Software Foundation; either version 2 of the License, or
16  *  (at your option) any later version.
17  *
18  *  This program is distributed in the hope that it will be useful,
19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *  GNU General Public License for more details.
22  *
23  *  You should have received a copy of the GNU General Public License
24  *  along with this program; if not, write to the Free Software
25  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <errno.h>
36 #include <linux/limits.h>
37
38 #define NUMFILES 1024
39
40 struct handle {
41         struct file_handle fh;
42         unsigned char fid[MAX_HANDLE_SZ];
43 } handle[NUMFILES];
44
45 int main(int argc, char **argv)
46 {
47         int     i;
48         int     fd;
49         int     ret;
50         int     failed = 0;
51         char    fname[PATH_MAX];
52         char    *test_dir;
53         int     mount_fd, mount_id;
54
55         if (argc != 2) {
56                 fprintf(stderr, "usage: open_by_handle <test_dir>\n");
57                 return EXIT_FAILURE;
58         }
59
60         test_dir = argv[1];
61         mount_fd = open(test_dir, O_RDONLY|O_DIRECTORY);
62         if (mount_fd < 0) {
63                 perror("open test_dir");
64                 return EXIT_FAILURE;
65         }
66
67         /*
68          * create a large number of files to force allocation of new inode
69          * chunks on disk.
70          */
71         for (i=0; i < NUMFILES; i++) {
72                 sprintf(fname, "%s/file%06d", test_dir, i);
73                 fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
74                 if (fd < 0) {
75                         printf("Warning (%s,%d), open(%s) failed.\n", __FILE__, __LINE__, fname);
76                         perror(fname);
77                         return EXIT_FAILURE;
78                 }
79                 close(fd);
80         }
81
82         /* sync to get the new inodes to hit the disk */
83         sync();
84
85         /* create the handles */
86         for (i=0; i < NUMFILES; i++) {
87                 sprintf(fname, "%s/file%06d", test_dir, i);
88                 handle[i].fh.handle_bytes = MAX_HANDLE_SZ;
89                 ret = name_to_handle_at(AT_FDCWD, fname, &handle[i].fh, &mount_id, 0);
90                 if (ret < 0) {
91                         perror("name_to_handle");
92                         return EXIT_FAILURE;
93                 }
94         }
95
96         /* unlink the files */
97         for (i=0; i < NUMFILES; i++) {
98                 sprintf(fname, "%s/file%06d", test_dir, i);
99                 ret = unlink(fname);
100                 if (ret < 0) {
101                         perror("unlink");
102                         return EXIT_FAILURE;
103                 }
104         }
105
106         /* sync to get log forced for unlink transactions to hit the disk */
107         sync();
108
109         /* sync once more FTW */
110         sync();
111
112         /*
113          * now drop the caches so that unlinked inodes are reclaimed and
114          * buftarg page cache is emptied so that the inode cluster has to be
115          * fetched from disk again for the open_by_handle() call.
116          */
117         ret = system("echo 3 > /proc/sys/vm/drop_caches");
118         if (ret < 0) {
119                 perror("drop_caches");
120                 return EXIT_FAILURE;
121         }
122
123         /*
124          * now try to open the files by the stored handles. Expecting ENOENT
125          * for all of them.
126          */
127         for (i=0; i < NUMFILES; i++) {
128                 errno = 0;
129                 fd = open_by_handle_at(mount_fd, &handle[i].fh, O_RDWR);
130                 if (fd < 0 && (errno == ENOENT || errno == ESTALE)) {
131                         continue;
132                 }
133                 if (fd >= 0) {
134                         printf("open_by_handle(%d) opened an unlinked file!\n", i);
135                         close(fd);
136                 } else
137                         printf("open_by_handle(%d) returned %d incorrectly on an unlinked file!\n", i, errno);
138                 failed++;
139         }
140         if (failed)
141                 return EXIT_FAILURE;
142         return EXIT_SUCCESS;
143 }