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] <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, write data to files,
47 drop caches, open all files by handle, read and verify written
48 data, write new data to file:
50 open_by_handle -rwa <test_dir> [N]
52 4. Get file handles for existing test set, unlink all test files,
53 remove test_dir, drop caches, try to open all files by handle
56 open_by_handle -dp <test_dir> [N]
58 5. Get file handles for existing test set, keep open file handles for all
59 test files, unlink all test files, drop caches and try to open all files
60 by handle (should work):
62 open_by_handle -dk <test_dir> [N]
64 6. Get file handles for existing test set, rename all test files,
65 drop caches, try to open all files by handle (should work):
67 open_by_handle -m <test_dir> [N]
69 7. Get file handles for existing test set, hardlink all test files,
70 then unlink the original files, drop caches and try to open all
71 files by handle (should work):
73 open_by_handle -l <test_dir> [N]
74 open_by_handle -u <test_dir> [N]
76 This test is done with 2 invocations of the program, first to
77 hardlink (-l) and then to unlink the originals (-u), because
78 we would like to be able to perform the hardlinks on overlay
79 lower layer and unlink on upper layer.
81 NOTE that open_by_handle -u doesn't check if the files are
82 hardlinked, it just assumes that they are. If they are not
83 then the test will fail, because file handles would be stale.
92 #include <sys/types.h>
94 #include <linux/limits.h>
100 struct file_handle fh;
101 unsigned char fid[MAX_HANDLE_SZ];
102 } handle[MAXFILES], dir_handle;
106 fprintf(stderr, "usage: open_by_handle [-cludmrwapk] <test_dir> [num_files]\n");
107 fprintf(stderr, "\n");
108 fprintf(stderr, "open_by_handle -c <test_dir> [N] - create N test files under test_dir, try to get file handles and exit\n");
109 fprintf(stderr, "open_by_handle <test_dir> [N] - get file handles of test files, drop caches and try to open by handle\n");
110 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");
111 fprintf(stderr, "open_by_handle -w <test_dir> [N] - write data to test files before open by handle\n");
112 fprintf(stderr, "open_by_handle -r <test_dir> [N] - read data from test files after open by handle and verify written data\n");
113 fprintf(stderr, "open_by_handle -a <test_dir> [N] - write data to test files after open by handle\n");
114 fprintf(stderr, "open_by_handle -l <test_dir> [N] - create hardlinks to test files, drop caches and try to open by handle\n");
115 fprintf(stderr, "open_by_handle -u <test_dir> [N] - unlink (hardlinked) test files, drop caches and try to open by handle\n");
116 fprintf(stderr, "open_by_handle -d <test_dir> [N] - unlink test files and hardlinks, drop caches and try to open by handle\n");
117 fprintf(stderr, "open_by_handle -m <test_dir> [N] - rename test files, drop caches and try to open by handle\n");
118 fprintf(stderr, "open_by_handle -p <test_dir> - create/delete and try to open by handle also test_dir itself\n");
122 int main(int argc, char **argv)
128 char dname[PATH_MAX];
129 char fname[PATH_MAX];
130 char fname2[PATH_MAX];
133 int mount_fd, mount_id;
135 int create = 0, delete = 0, nlink = 1, move = 0;
136 int rd = 0, wr = 0, wrafter = 0, parent = 0;
139 if (argc < 2 || argc > 4)
142 while ((c = getopt(argc, argv, "cludmrwapk")) != -1) {
148 /* Write data before open_by_handle_at() */
152 /* Read data after open_by_handle_at() */
156 /* Write data after open_by_handle_at() */
180 fprintf(stderr, "illegal option '%s'\n", argv[optind]);
185 if (optind == argc || optind > 2)
187 test_dir = argv[optind++];
189 numfiles = atoi(argv[optind]);
190 if (!numfiles || numfiles > MAXFILES) {
191 fprintf(stderr, "illegal value '%s' for num_files\n", argv[optind]);
196 strcpy(dname, test_dir);
198 * -p flag implies that test_dir is NOT a mount point,
199 * so its parent can be used as mount_fd for open_by_handle_at.
201 mount_dir = dirname(dname);
203 ret = mkdir(test_dir, 0700);
204 if (ret < 0 && errno != EEXIST) {
205 strcat(dname, ": mkdir");
210 mount_dir = test_dir;
213 mount_fd = open(mount_dir, O_RDONLY|O_DIRECTORY);
220 * Create the test files and remove leftover hardlinks from previous run
222 for (i=0; create && i < numfiles; i++) {
223 sprintf(fname, "%s/file%06d", test_dir, i);
224 sprintf(fname2, "%s/link%06d", test_dir, i);
225 fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
227 strcat(fname, ": open(O_CREAT)");
232 /* blow up leftovers hardlinks if they exist */
233 ret = unlink(fname2);
234 if (ret < 0 && errno != ENOENT) {
235 strcat(fname2, ": unlink");
241 /* sync to get the new inodes to hit the disk */
244 /* create the handles */
245 for (i=0; i < numfiles; i++) {
246 sprintf(fname, "%s/file%06d", test_dir, i);
247 handle[i].fh.handle_bytes = MAX_HANDLE_SZ;
248 ret = name_to_handle_at(AT_FDCWD, fname, &handle[i].fh, &mount_id, 0);
250 strcat(fname, ": name_to_handle");
255 /* Open without close to keep unlinked files around */
256 fd = open(fname, O_RDONLY);
258 strcat(fname, ": open(O_RDONLY)");
266 dir_handle.fh.handle_bytes = MAX_HANDLE_SZ;
267 ret = name_to_handle_at(AT_FDCWD, test_dir, &dir_handle.fh, &mount_id, 0);
269 strcat(dname, ": name_to_handle");
275 /* write data to files */
276 for (i=0; wr && i < numfiles; i++) {
277 sprintf(fname, "%s/file%06d", test_dir, i);
278 fd = open(fname, O_WRONLY, 0644);
280 strcat(fname, ": open");
284 if (write(fd, "aaaa", 4) != 4) {
285 strcat(fname, ": write before");
292 /* after creating test set only check that fs supports exportfs */
296 /* hardlink the files */
297 for (i=0; nlink > 1 && i < numfiles; i++) {
298 sprintf(fname, "%s/file%06d", test_dir, i);
299 sprintf(fname2, "%s/link%06d", test_dir, i);
300 ret = link(fname, fname2);
302 strcat(fname2, ": link");
308 /* rename the files */
309 for (i=0; move && i < numfiles; i++) {
310 sprintf(fname, "%s/file%06d", test_dir, i);
311 sprintf(fname2, "%s/link%06d", test_dir, i);
312 ret = rename(fname, fname2);
314 strcat(fname2, ": rename");
320 /* unlink the files */
321 for (i=0; delete && i < numfiles; i++) {
322 sprintf(fname, "%s/file%06d", test_dir, i);
323 sprintf(fname2, "%s/link%06d", test_dir, i);
326 strcat(fname, ": unlink");
330 /* with -d flag, delete the hardlink if it exists */
332 ret = unlink(fname2);
333 if (ret < 0 && errno != ENOENT) {
334 strcat(fname2, ": unlink");
340 if (parent && delete && !nlink) {
341 ret = rmdir(test_dir);
343 strcat(dname, ": rmdir");
349 /* sync to get log forced for unlink transactions to hit the disk */
352 /* sync once more FTW */
356 * now drop the caches so that unlinked inodes are reclaimed and
357 * buftarg page cache is emptied so that the inode cluster has to be
358 * fetched from disk again for the open_by_handle() call.
360 ret = system("echo 3 > /proc/sys/vm/drop_caches");
362 perror("drop_caches");
367 * now try to open the files by the stored handles. Expecting ESTALE
368 * if all files and their hardlinks have been unlinked.
370 for (i=0; i < numfiles; i++) {
372 fd = open_by_handle_at(mount_fd, &handle[i].fh, wrafter ? O_RDWR : O_RDONLY);
373 if ((nlink || keepopen) && fd >= 0) {
376 int size = read(fd, buf, 4);
378 strcat(fname, ": read");
382 if (size < 4 || memcmp(buf, "aaaa", 4)) {
383 printf("open_by_handle(%s) returned stale data '%.*s'!\n", fname, size, buf);
386 if (wrafter && write(fd, "aaaa", 4) != 4) {
387 strcat(fname, ": write after");
393 } else if (!nlink && !keepopen && fd < 0 && (errno == ENOENT || errno == ESTALE)) {
396 sprintf(fname, "%s/file%06d", test_dir, i);
398 printf("open_by_handle(%s) opened an unlinked file!\n", fname);
401 printf("open_by_handle(%s) returned %d incorrectly on %s%s file!\n",
403 nlink ? "a linked" : "an unlinked",
404 keepopen ? " open" : "");
410 fd = open_by_handle_at(mount_fd, &dir_handle.fh, O_RDONLY|O_DIRECTORY);
413 printf("open_by_handle(%s) opened an unlinked dir!\n", dname);
417 * Sanity check dir fd - expect to access orig file IFF
418 * it was not unlinked nor renamed.
420 strcpy(fname, "file000000");
421 ret = faccessat(fd, fname, F_OK, 0);
422 if ((ret == 0) != (!delete && !move) ||
423 ((ret < 0) && errno != ENOENT)) {
424 strcat(fname, ": unexpected result from faccessat");
429 * Expect to access link file if ran test with -l flag
430 * (nlink > 1), -m flag (orig moved to link name) or
431 * -u flag (which implied previous -l run).
433 strcpy(fname2, "link000000");
434 ret = faccessat(fd, fname2, F_OK, 0);
435 if (ret < 0 && (nlink > 1 || delete || move ||
437 strcat(fname2, ": unexpected result from faccessat");
443 } else if (nlink || !(errno == ENOENT || errno == ESTALE)) {
444 printf("open_by_handle(%s) returned %d incorrectly on %s dir!\n",
446 nlink ? "a linked" : "an unlinked");