Test for kernel and filesystem support for conenctable file handles. With -N flag, encode connectable file handles and fail the test if the kernel or filesystem do not support conenctable file handles. Verify that decoding connectable file handles always results in a non empty path of the fd. Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- common/rc | 16 ++++++++++--- src/open_by_handle.c | 53 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/common/rc b/common/rc index 6592c835..6407b744 100644 --- a/common/rc +++ b/common/rc @@ -3829,8 +3829,14 @@ _require_freeze() } # Does NFS export work on this fs? -_require_exportfs() +_require_open_by_handle() { + local what="NFS export" + local opts="$1" + if [ "$1" == "-N" ]; then + what="connectable file handles" + fi + _require_test_program "open_by_handle" # virtiofs doesn't support open_by_handle_at(2) yet, though the syscall @@ -3841,10 +3847,14 @@ _require_exportfs() _notrun "$FSTYP doesn't support open_by_handle_at(2)" mkdir -p "$TEST_DIR"/exportfs_test - $here/src/open_by_handle -c "$TEST_DIR"/exportfs_test 2>&1 \ - || _notrun "$FSTYP does not support NFS export" + $here/src/open_by_handle $opts -c "$TEST_DIR"/exportfs_test 2>&1 \ + || _notrun "$FSTYP does not support $what" } +_require_exportfs() +{ + _require_open_by_handle +} # Does shutdown work on this fs? _require_scratch_shutdown() diff --git a/src/open_by_handle.c b/src/open_by_handle.c index a99cce4b..7b664201 100644 --- a/src/open_by_handle.c +++ b/src/open_by_handle.c @@ -96,6 +96,9 @@ Examples: #ifndef AT_HANDLE_MNT_ID_UNIQUE # define AT_HANDLE_MNT_ID_UNIQUE 0x001 #endif +#ifndef AT_HANDLE_CONNECTABLE +# define AT_HANDLE_CONNECTABLE 0x002 +#endif #define MAXFILES 1024 @@ -121,6 +124,7 @@ void usage(void) fprintf(stderr, "open_by_handle -d <test_dir> [N] - unlink test files and hardlinks, drop caches and try to open by handle\n"); fprintf(stderr, "open_by_handle -m <test_dir> [N] - rename test files, drop caches and try to open by handle\n"); fprintf(stderr, "open_by_handle -M <test_dir> [N] - do not silently skip the mount ID verifications\n"); + fprintf(stderr, "open_by_handle -N <test_dir> [N] - encode connectable file handles\n"); fprintf(stderr, "open_by_handle -p <test_dir> - create/delete and try to open by handle also test_dir itself\n"); fprintf(stderr, "open_by_handle -i <handles_file> <test_dir> [N] - read test files handles from file and try to open by handle\n"); fprintf(stderr, "open_by_handle -o <handles_file> <test_dir> [N] - get file handles of test files and write handles to file\n"); @@ -130,14 +134,16 @@ void usage(void) fprintf(stderr, "open_by_handle -C <feature> - check if <feature> is supported by the kernel.\n"); fprintf(stderr, " <feature> can be any of the following values:\n"); fprintf(stderr, " - AT_HANDLE_MNT_ID_UNIQUE\n"); + fprintf(stderr, " - AT_HANDLE_CONNECTABLE\n"); exit(EXIT_FAILURE); } -static int do_name_to_handle_at(const char *fname, struct file_handle *fh, - int bufsz, bool force_check_mountid) +static int do_name_to_handle_at(const char *fname, struct file_handle *fh, int bufsz, + bool force_check_mountid, bool connectable) { int ret; int mntid_short; + int at_flags; static bool skip_mntid, skip_mntid_unique; @@ -181,18 +187,24 @@ static int do_name_to_handle_at(const char *fname, struct file_handle *fh, } } + at_flags = connectable ? AT_HANDLE_CONNECTABLE : 0; fh->handle_bytes = bufsz; - ret = name_to_handle_at(AT_FDCWD, fname, fh, &mntid_short, 0); + ret = name_to_handle_at(AT_FDCWD, fname, fh, &mntid_short, at_flags); if (bufsz < fh->handle_bytes) { /* Query the filesystem required bufsz and the file handle */ if (ret != -1 || errno != EOVERFLOW) { fprintf(stderr, "%s: unexpected result from name_to_handle_at: %d (%m)\n", fname, ret); return EXIT_FAILURE; } - ret = name_to_handle_at(AT_FDCWD, fname, fh, &mntid_short, 0); + ret = name_to_handle_at(AT_FDCWD, fname, fh, &mntid_short, at_flags); } if (ret < 0) { - fprintf(stderr, "%s: name_to_handle: %m\n", fname); + /* No filesystem support for encoding connectable file handles (e.g. overlayfs)? */ + if (connectable) + fprintf(stderr, "%s: name_to_handle_at(AT_HANDLE_CONNECTABLE) not supported by %s\n", + fname, errno == EINVAL ? "kernel" : "filesystem"); + else + fprintf(stderr, "%s: name_to_handle: %m\n", fname); return EXIT_FAILURE; } @@ -245,8 +257,17 @@ static int check_feature(const char *feature) return EXIT_FAILURE; } return 0; + } else if (!strcmp(feature, "AT_HANDLE_CONNECTABLE")) { + int ret = name_to_handle_at(AT_FDCWD, ".", NULL, NULL, AT_HANDLE_CONNECTABLE); + /* If AT_HANDLE_CONNECTABLE is supported, we get EFAULT. */ + if (ret < 0 && errno == EINVAL) { + fprintf(stderr, "name_to_handle_at(AT_HANDLE_CONNECTABLE) not supported by running kernel\n"); + return EXIT_FAILURE; + } + return 0; } + fprintf(stderr, "unknown feature name '%s'\n", feature); return EXIT_FAILURE; } @@ -270,13 +291,13 @@ int main(int argc, char **argv) int create = 0, delete = 0, nlink = 1, move = 0; int rd = 0, wr = 0, wrafter = 0, parent = 0; int keepopen = 0, drop_caches = 1, sleep_loop = 0; - int force_check_mountid = 0; + bool force_check_mountid = 0, connectable = 0; int bufsz = MAX_HANDLE_SZ; if (argc < 2) usage(); - while ((c = getopt(argc, argv, "cC:ludmMrwapknhi:o:sz")) != -1) { + while ((c = getopt(argc, argv, "cC:ludmMNrwapknhi:o:sz")) != -1) { switch (c) { case 'c': create = 1; @@ -313,6 +334,9 @@ int main(int argc, char **argv) case 'M': force_check_mountid = 1; break; + case 'N': + connectable = 1; + break; case 'p': parent = 1; break; @@ -445,7 +469,8 @@ int main(int argc, char **argv) return EXIT_FAILURE; } } else { - ret = do_name_to_handle_at(fname, &handle[i].fh, bufsz, force_check_mountid); + ret = do_name_to_handle_at(fname, &handle[i].fh, bufsz, + force_check_mountid, connectable); if (ret) return EXIT_FAILURE; } @@ -475,7 +500,8 @@ int main(int argc, char **argv) return EXIT_FAILURE; } } else { - ret = do_name_to_handle_at(test_dir, &dir_handle.fh, bufsz, force_check_mountid); + ret = do_name_to_handle_at(test_dir, &dir_handle.fh, bufsz, + force_check_mountid, connectable); if (ret) return EXIT_FAILURE; } @@ -589,6 +615,15 @@ int main(int argc, char **argv) errno = 0; fd = open_by_handle_at(mount_fd, &handle[i].fh, wrafter ? O_RDWR : O_RDONLY); if ((nlink || keepopen) && fd >= 0) { + char linkname[PATH_MAX]; + char procname[64]; + sprintf(procname, "/proc/self/fd/%i", fd); + int n = readlink(procname, linkname, PATH_MAX); + + /* check that fd is "connected" - that is has a non empty path */ + if (connectable && n <= 1) { + printf("open_by_handle(%s) returned a disconnected fd!\n", fname); + } if (rd) { char buf[4] = {0}; int size = read(fd, buf, 4); -- 2.34.1