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 [-cludmrwap] <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, rename all test files,
59 drop caches, try to open all files by handle (should work):
61 open_by_handle -m <test_dir> [N]
63 6. Get file handles for existing test set, hardlink all test files,
64 then unlink the original files, drop caches and try to open all
65 files by handle (should work):
67 open_by_handle -l <test_dir> [N]
68 open_by_handle -u <test_dir> [N]
70 This test is done with 2 invocations of the program, first to
71 hardlink (-l) and then to unlink the originals (-u), because
72 we would like to be able to perform the hardlinks on overlay
73 lower layer and unlink on upper layer.
75 NOTE that open_by_handle -u doesn't check if the files are
76 hardlinked, it just assumes that they are. If they are not
77 then the test will fail, because file handles would be stale.
86 #include <sys/types.h>
88 #include <linux/limits.h>
94 struct file_handle fh;
95 unsigned char fid[MAX_HANDLE_SZ];
96 } handle[MAXFILES], dir_handle;
100 fprintf(stderr, "usage: open_by_handle [-cludmrwap] <test_dir> [num_files]\n");
101 fprintf(stderr, "\n");
102 fprintf(stderr, "open_by_handle -c <test_dir> [N] - create N test files under test_dir, try to get file handles and exit\n");
103 fprintf(stderr, "open_by_handle <test_dir> [N] - get file handles of test files, drop caches and try to open by handle\n");
104 fprintf(stderr, "open_by_handle -w <test_dir> [N] - write data to test files before open by handle\n");
105 fprintf(stderr, "open_by_handle -r <test_dir> [N] - read data from test files after open by handle and verify written data\n");
106 fprintf(stderr, "open_by_handle -a <test_dir> [N] - write data to test files after open by handle\n");
107 fprintf(stderr, "open_by_handle -l <test_dir> [N] - create hardlinks to test files, drop caches and try to open by handle\n");
108 fprintf(stderr, "open_by_handle -u <test_dir> [N] - unlink (hardlinked) test files, drop caches and try to open by handle\n");
109 fprintf(stderr, "open_by_handle -d <test_dir> [N] - unlink test files and hardlinks, drop caches and try to open by handle\n");
110 fprintf(stderr, "open_by_handle -m <test_dir> [N] - rename test files, drop caches and try to open by handle\n");
111 fprintf(stderr, "open_by_handle -p <test_dir> - create/delete and try to open by handle also test_dir itself\n");
115 int main(int argc, char **argv)
121 char dname[PATH_MAX];
122 char fname[PATH_MAX];
123 char fname2[PATH_MAX];
126 int mount_fd, mount_id;
128 int create = 0, delete = 0, nlink = 1, move = 0;
129 int rd = 0, wr = 0, wrafter = 0, parent = 0;
131 if (argc < 2 || argc > 4)
134 while ((c = getopt(argc, argv, "cludmrwap")) != -1) {
140 /* Write data before open_by_handle_at() */
144 /* Read data after open_by_handle_at() */
148 /* Write data after open_by_handle_at() */
169 fprintf(stderr, "illegal option '%s'\n", argv[optind]);
174 if (optind == argc || optind > 2)
176 test_dir = argv[optind++];
178 numfiles = atoi(argv[optind]);
179 if (!numfiles || numfiles > MAXFILES) {
180 fprintf(stderr, "illegal value '%s' for num_files\n", argv[optind]);
185 strcpy(dname, test_dir);
187 * -p flag implies that test_dir is NOT a mount point,
188 * so its parent can be used as mount_fd for open_by_handle_at.
190 mount_dir = dirname(dname);
192 ret = mkdir(test_dir, 0700);
193 if (ret < 0 && errno != EEXIST) {
194 strcat(dname, ": mkdir");
199 mount_dir = test_dir;
202 mount_fd = open(mount_dir, O_RDONLY|O_DIRECTORY);
209 * Create the test files and remove leftover hardlinks from previous run
211 for (i=0; create && i < numfiles; i++) {
212 sprintf(fname, "%s/file%06d", test_dir, i);
213 sprintf(fname2, "%s/link%06d", test_dir, i);
214 fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
216 strcat(fname, ": open");
221 /* blow up leftovers hardlinks if they exist */
222 ret = unlink(fname2);
223 if (ret < 0 && errno != ENOENT) {
224 strcat(fname2, ": unlink");
230 /* sync to get the new inodes to hit the disk */
233 /* create the handles */
234 for (i=0; i < numfiles; i++) {
235 sprintf(fname, "%s/file%06d", test_dir, i);
236 handle[i].fh.handle_bytes = MAX_HANDLE_SZ;
237 ret = name_to_handle_at(AT_FDCWD, fname, &handle[i].fh, &mount_id, 0);
239 strcat(fname, ": name_to_handle");
246 dir_handle.fh.handle_bytes = MAX_HANDLE_SZ;
247 ret = name_to_handle_at(AT_FDCWD, test_dir, &dir_handle.fh, &mount_id, 0);
249 strcat(dname, ": name_to_handle");
255 /* write data to files */
256 for (i=0; wr && i < numfiles; i++) {
257 sprintf(fname, "%s/file%06d", test_dir, i);
258 fd = open(fname, O_WRONLY, 0644);
260 strcat(fname, ": open");
264 if (write(fd, "aaaa", 4) != 4) {
265 strcat(fname, ": write before");
272 /* after creating test set only check that fs supports exportfs */
276 /* hardlink the files */
277 for (i=0; nlink > 1 && i < numfiles; i++) {
278 sprintf(fname, "%s/file%06d", test_dir, i);
279 sprintf(fname2, "%s/link%06d", test_dir, i);
280 ret = link(fname, fname2);
282 strcat(fname2, ": link");
288 /* rename the files */
289 for (i=0; move && i < numfiles; i++) {
290 sprintf(fname, "%s/file%06d", test_dir, i);
291 sprintf(fname2, "%s/link%06d", test_dir, i);
292 ret = rename(fname, fname2);
294 strcat(fname2, ": rename");
300 /* unlink the files */
301 for (i=0; delete && i < numfiles; i++) {
302 sprintf(fname, "%s/file%06d", test_dir, i);
303 sprintf(fname2, "%s/link%06d", test_dir, i);
306 strcat(fname, ": unlink");
310 /* with -d flag, delete the hardlink if it exists */
312 ret = unlink(fname2);
313 if (ret < 0 && errno != ENOENT) {
314 strcat(fname2, ": unlink");
320 if (parent && delete && !nlink) {
321 ret = rmdir(test_dir);
323 strcat(dname, ": rmdir");
329 /* sync to get log forced for unlink transactions to hit the disk */
332 /* sync once more FTW */
336 * now drop the caches so that unlinked inodes are reclaimed and
337 * buftarg page cache is emptied so that the inode cluster has to be
338 * fetched from disk again for the open_by_handle() call.
340 ret = system("echo 3 > /proc/sys/vm/drop_caches");
342 perror("drop_caches");
347 * now try to open the files by the stored handles. Expecting ESTALE
348 * if all files and their hardlinks have been unlinked.
350 for (i=0; i < numfiles; i++) {
352 fd = open_by_handle_at(mount_fd, &handle[i].fh, wrafter ? O_RDWR : O_RDONLY);
353 if (nlink && fd >= 0) {
356 int size = read(fd, buf, 4);
358 strcat(fname, ": read");
362 if (size < 4 || memcmp(buf, "aaaa", 4)) {
363 printf("open_by_handle(%s) returned stale data '%.*s'!\n", fname, size, buf);
366 if (wrafter && write(fd, "aaaa", 4) != 4) {
367 strcat(fname, ": write after");
373 } else if (!nlink && fd < 0 && (errno == ENOENT || errno == ESTALE)) {
376 sprintf(fname, "%s/file%06d", test_dir, i);
378 printf("open_by_handle(%s) opened an unlinked file!\n", fname);
381 printf("open_by_handle(%s) returned %d incorrectly on %s file!\n",
383 nlink ? "a linked" : "an unlinked");
389 fd = open_by_handle_at(mount_fd, &dir_handle.fh, O_RDONLY|O_DIRECTORY);
392 printf("open_by_handle(%s) opened an unlinked dir!\n", dname);
396 * Sanity check dir fd - expect to access orig file IFF
397 * it was not unlinked nor renamed.
399 strcpy(fname, "file000000");
400 ret = faccessat(fd, fname, F_OK, 0);
401 if ((ret == 0) != (!delete && !move) ||
402 ((ret < 0) && errno != ENOENT)) {
403 strcat(fname, ": unexpected result from faccessat");
408 * Expect to access link file if ran test with -l flag
409 * (nlink > 1), -m flag (orig moved to link name) or
410 * -u flag (which implied previous -l run).
412 strcpy(fname2, "link000000");
413 ret = faccessat(fd, fname2, F_OK, 0);
414 if (ret < 0 && (nlink > 1 || delete || move ||
416 strcat(fname2, ": unexpected result from faccessat");
422 } else if (nlink || !(errno == ENOENT || errno == ESTALE)) {
423 printf("open_by_handle(%s) returned %d incorrectly on %s dir!\n",
425 nlink ? "a linked" : "an unlinked");