2 * open_by_handle.c - attempt to create a file handle and open it
3 * with open_by_handle_at() syscall
5 * Copyright (C) 2017 CTERA Networks. All Rights Reserved.
6 * Author: Amir Goldstein <amir73il@gmail.com>
11 * Copyright (C) 2010 Red Hat, Inc. All Rights reserved.
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.
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.
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.
30 usage: open_by_handle [-cludmrwapk] [<-i|-o> <handles_file>] <test_dir> [num_files]
34 1. Create test set of of test_dir with N files and try to get their NFS handles:
36 open_by_handle -cp <test_dir> [N]
38 This is used by new helper _require_exportfs() to check
39 if filesystem supports exportfs
41 2. Get file handles for existing test set, drop caches and try to
42 open all files by handle:
44 open_by_handle -p <test_dir> [N]
46 3. Get file handles for existing test set and write them to a file.
47 Read file handles from file and open files by handle:
49 open_by_handle -p -o <handles_file> <test_dir> [N]
50 open_by_handle -p -i <handles_file> <test_dir> [N]
52 4. Get file handles for existing test set, write data to files,
53 drop caches, open all files by handle, read and verify written
54 data, write new data to file:
56 open_by_handle -rwa <test_dir> [N]
58 5. Get file handles for existing test set, unlink all test files,
59 remove test_dir, drop caches, try to open all files by handle
62 open_by_handle -dp <test_dir> [N]
64 6. Get file handles for existing test set, keep open file handles for all
65 test files, unlink all test files, drop caches and try to open all files
66 by handle (should work):
68 open_by_handle -dk <test_dir> [N]
70 7. Get file handles for existing test set, rename all test files,
71 drop caches, try to open all files by handle (should work):
73 open_by_handle -m <test_dir> [N]
75 8. Get file handles for existing test set, hardlink all test files,
76 then unlink the original files, drop caches and try to open all
77 files by handle (should work):
79 open_by_handle -l <test_dir> [N]
80 open_by_handle -u <test_dir> [N]
82 This test is done with 2 invocations of the program, first to
83 hardlink (-l) and then to unlink the originals (-u), because
84 we would like to be able to perform the hardlinks on overlay
85 lower layer and unlink on upper layer.
87 NOTE that open_by_handle -u doesn't check if the files are
88 hardlinked, it just assumes that they are. If they are not
89 then the test will fail, because file handles would be stale.
98 #include <sys/types.h>
100 #include <linux/limits.h>
103 #define MAXFILES 1024
106 struct file_handle fh;
107 unsigned char fid[MAX_HANDLE_SZ];
108 } handle[MAXFILES], dir_handle;
112 fprintf(stderr, "usage: open_by_handle [-cludmrwapk] [<-i|-o> <handles_file>] <test_dir> [num_files]\n");
113 fprintf(stderr, "\n");
114 fprintf(stderr, "open_by_handle -c <test_dir> [N] - create N test files under test_dir, try to get file handles and exit\n");
115 fprintf(stderr, "open_by_handle <test_dir> [N] - get file handles of test files, drop caches and try to open by handle\n");
116 fprintf(stderr, "open_by_handle -k <test_dir> [N] - get file handles of files that are kept open, drop caches and try to open by handle\n");
117 fprintf(stderr, "open_by_handle -w <test_dir> [N] - write data to test files before open by handle\n");
118 fprintf(stderr, "open_by_handle -r <test_dir> [N] - read data from test files after open by handle and verify written data\n");
119 fprintf(stderr, "open_by_handle -a <test_dir> [N] - write data to test files after open by handle\n");
120 fprintf(stderr, "open_by_handle -l <test_dir> [N] - create hardlinks to test files, drop caches and try to open by handle\n");
121 fprintf(stderr, "open_by_handle -u <test_dir> [N] - unlink (hardlinked) test files, drop caches and try to open by handle\n");
122 fprintf(stderr, "open_by_handle -d <test_dir> [N] - unlink test files and hardlinks, drop caches and try to open by handle\n");
123 fprintf(stderr, "open_by_handle -m <test_dir> [N] - rename test files, drop caches and try to open by handle\n");
124 fprintf(stderr, "open_by_handle -p <test_dir> - create/delete and try to open by handle also test_dir itself\n");
125 fprintf(stderr, "open_by_handle -i <handles_file> <test_dir> [N] - read test files handles from file and try to open by handle\n");
126 fprintf(stderr, "open_by_handle -o <handles_file> <test_dir> [N] - get file handles of test files and write handles to file\n");
130 int main(int argc, char **argv)
136 char dname[PATH_MAX];
137 char fname[PATH_MAX];
138 char fname2[PATH_MAX];
141 int mount_fd, mount_id;
142 char *infile = NULL, *outfile = NULL;
143 int in_fd = 0, out_fd = 0;
145 int create = 0, delete = 0, nlink = 1, move = 0;
146 int rd = 0, wr = 0, wrafter = 0, parent = 0;
152 while ((c = getopt(argc, argv, "cludmrwapki:o:")) != -1) {
158 /* Write data before open_by_handle_at() */
162 /* Read data after open_by_handle_at() */
166 /* Write data after open_by_handle_at() */
191 in_fd = open(infile, O_RDONLY);
199 out_fd = creat(outfile, 0644);
206 fprintf(stderr, "illegal option '%s'\n", argv[optind]);
213 test_dir = argv[optind++];
215 numfiles = atoi(argv[optind]);
216 if (!numfiles || numfiles > MAXFILES) {
217 fprintf(stderr, "illegal value '%s' for num_files\n", argv[optind]);
222 * The way we determine the mount_dir to be used for mount_fd argument
223 * for open_by_handle_at() depends on other command line arguments:
225 * -p flag usually (see -i below) implies that test_dir is NOT a mount
226 * point, but a directory inside a mount point that we will create
227 * and/or encode/decode during the test, so we use test_dir's parent
228 * for mount_fd. Even when not creatig test_dir, if we would use
229 * test_dir as mount_fd, then drop_caches will not drop the test_dir
232 * If -p is not specified, we don't have a hint whether test_dir is a
233 * mount point or not, so we assume the worst case, that it is a
234 * mount point and therefore, we cannnot use parent as mount_fd,
235 * because parent may be on a differnt file system.
237 * -i flag, even with -p flag, implies that test_dir IS a mount point,
238 * because we are testing open by handle of dir, which may have been
239 * deleted or renamed and we are not creating nor encoding the
240 * directory file handle. -i flag is meant to be used for tests
241 * after encoding file handles and mount cycle the file system. If
242 * we would require the test to pass in with -ip the test_dir we
243 * want to decode and not the mount point, that would have populated
244 * the dentry cache and the use of -ip flag combination would not
245 * allow testing decode of dir file handle in cold dcache scenario.
247 if (parent && !in_fd) {
248 strcpy(dname, test_dir);
249 mount_dir = dirname(dname);
251 ret = mkdir(test_dir, 0700);
252 if (ret < 0 && errno != EEXIST) {
253 strcat(dname, ": mkdir");
258 mount_dir = test_dir;
261 mount_fd = open(mount_dir, O_RDONLY|O_DIRECTORY);
268 * Create the test files and remove leftover hardlinks from previous run
270 for (i=0; create && i < numfiles; i++) {
271 sprintf(fname, "%s/file%06d", test_dir, i);
272 sprintf(fname2, "%s/link%06d", test_dir, i);
273 fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
275 strcat(fname, ": open(O_CREAT)");
280 /* blow up leftovers hardlinks if they exist */
281 ret = unlink(fname2);
282 if (ret < 0 && errno != ENOENT) {
283 strcat(fname2, ": unlink");
289 /* sync to get the new inodes to hit the disk */
293 * encode the file handles or read them from file (-i) and maybe store
294 * them to a file (-o).
296 for (i=0; i < numfiles; i++) {
297 sprintf(fname, "%s/file%06d", test_dir, i);
299 ret = read(in_fd, (char *)&handle[i], sizeof(*handle));
300 if (ret < sizeof(*handle)) {
301 fprintf(stderr, "failed reading file handle #%d from '%s'\n", i, infile);
305 handle[i].fh.handle_bytes = MAX_HANDLE_SZ;
306 ret = name_to_handle_at(AT_FDCWD, fname, &handle[i].fh, &mount_id, 0);
308 strcat(fname, ": name_to_handle");
314 /* Open without close to keep unlinked files around */
315 fd = open(fname, O_RDONLY);
317 strcat(fname, ": open(O_RDONLY)");
323 ret = write(out_fd, (char *)&handle[i], sizeof(*handle));
324 if (ret < sizeof(*handle)) {
325 fprintf(stderr, "failed writing file handle #%d to '%s'\n", i, outfile);
333 ret = read(in_fd, (char *)&dir_handle, sizeof(*handle));
334 if (ret < sizeof(*handle)) {
335 fprintf(stderr, "failed reading dir file handle from '%s'\n", infile);
339 dir_handle.fh.handle_bytes = MAX_HANDLE_SZ;
340 ret = name_to_handle_at(AT_FDCWD, test_dir, &dir_handle.fh, &mount_id, 0);
342 strcat(dname, ": name_to_handle");
348 ret = write(out_fd, (char *)&dir_handle, sizeof(*handle));
349 if (ret < sizeof(*handle)) {
350 fprintf(stderr, "failed writing dir file handle to '%s'\n", outfile);
356 /* write data to files */
357 for (i=0; wr && i < numfiles; i++) {
358 sprintf(fname, "%s/file%06d", test_dir, i);
359 fd = open(fname, O_WRONLY, 0644);
361 strcat(fname, ": open");
365 if (write(fd, "aaaa", 4) != 4) {
366 strcat(fname, ": write before");
373 /* If creating test set or saving files handles, we are done */
374 if (create || out_fd)
377 /* hardlink the files */
378 for (i=0; nlink > 1 && i < numfiles; i++) {
379 sprintf(fname, "%s/file%06d", test_dir, i);
380 sprintf(fname2, "%s/link%06d", test_dir, i);
381 ret = link(fname, fname2);
383 strcat(fname2, ": link");
389 /* rename the files */
390 for (i=0; move && i < numfiles; i++) {
391 sprintf(fname, "%s/file%06d", test_dir, i);
392 sprintf(fname2, "%s/link%06d", test_dir, i);
393 ret = rename(fname, fname2);
395 strcat(fname2, ": rename");
401 /* unlink the files */
402 for (i=0; delete && i < numfiles; i++) {
403 sprintf(fname, "%s/file%06d", test_dir, i);
404 sprintf(fname2, "%s/link%06d", test_dir, i);
407 strcat(fname, ": unlink");
411 /* with -d flag, delete the hardlink if it exists */
413 ret = unlink(fname2);
414 if (ret < 0 && errno != ENOENT) {
415 strcat(fname2, ": unlink");
421 if (parent && delete && !nlink) {
422 ret = rmdir(test_dir);
424 strcat(dname, ": rmdir");
430 /* sync to get log forced for unlink transactions to hit the disk */
433 /* sync once more FTW */
437 * now drop the caches so that unlinked inodes are reclaimed and
438 * buftarg page cache is emptied so that the inode cluster has to be
439 * fetched from disk again for the open_by_handle() call.
441 ret = system("echo 3 > /proc/sys/vm/drop_caches");
443 perror("drop_caches");
448 * now try to open the files by the stored handles. Expecting ESTALE
449 * if all files and their hardlinks have been unlinked.
451 for (i=0; i < numfiles; i++) {
453 fd = open_by_handle_at(mount_fd, &handle[i].fh, wrafter ? O_RDWR : O_RDONLY);
454 if ((nlink || keepopen) && fd >= 0) {
457 int size = read(fd, buf, 4);
459 strcat(fname, ": read");
463 if (size < 4 || memcmp(buf, "aaaa", 4)) {
464 printf("open_by_handle(%s) returned stale data '%.*s'!\n", fname, size, buf);
467 if (wrafter && write(fd, "aaaa", 4) != 4) {
468 strcat(fname, ": write after");
474 } else if (!nlink && !keepopen && fd < 0 && (errno == ENOENT || errno == ESTALE)) {
477 sprintf(fname, "%s/file%06d", test_dir, i);
479 printf("open_by_handle(%s) opened an unlinked file!\n", fname);
482 printf("open_by_handle(%s) returned %d incorrectly on %s%s file!\n",
484 nlink ? "a linked" : "an unlinked",
485 keepopen ? " open" : "");
491 fd = open_by_handle_at(mount_fd, &dir_handle.fh, O_RDONLY|O_DIRECTORY);
494 printf("open_by_handle(%s) opened an unlinked dir!\n", dname);
498 * Sanity check dir fd - expect to access orig file IFF
499 * it was not unlinked nor renamed.
501 strcpy(fname, "file000000");
502 ret = faccessat(fd, fname, F_OK, 0);
503 if ((ret == 0) != (!delete && !move) ||
504 ((ret < 0) && errno != ENOENT)) {
505 strcat(fname, ": unexpected result from faccessat");
510 * Expect to access link file if ran test with -l flag
511 * (nlink > 1), -m flag (orig moved to link name) or
512 * -u flag (which implied previous -l run).
514 strcpy(fname2, "link000000");
515 ret = faccessat(fd, fname2, F_OK, 0);
516 if (ret < 0 && (nlink > 1 || delete || move ||
518 strcat(fname2, ": unexpected result from faccessat");
524 } else if (nlink || !(errno == ENOENT || errno == ESTALE)) {
525 printf("open_by_handle(%s) returned %d incorrectly on %s dir!\n",
527 nlink ? "a linked" : "an unlinked");