Message ID | 20240904175639.2269694-2-cyphar@cyphar.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [xfstests,v3,1/2] open_by_handle: verify u32 and u64 mount IDs | expand |
On Wed, Sep 4, 2024 at 7:57 PM Aleksa Sarai <cyphar@cyphar.com> wrote: > > In order to make sure we are actually testing AT_HANDLE_MNT_ID_UNIQUE, > add a test (based on generic/426) which runs the open_by_handle in a > mode where it will error out if there is a problem with getting mount > IDs. The test is skipped if the kernel doesn't support the necessary > features. > > Suggested-by: Amir Goldstein <amir73il@gmail.com> > Signed-off-by: Aleksa Sarai <cyphar@cyphar.com> Apart from one minor nits below, you may add: Reviewed-by: Amir Goldstein <amir73il@gmail.com> > --- > common/rc | 24 ++++++++++++++++ > src/open_by_handle.c | 63 ++++++++++++++++++++++++++++++++++------- > tests/generic/756 | 65 +++++++++++++++++++++++++++++++++++++++++++ > tests/generic/756.out | 5 ++++ > 4 files changed, 147 insertions(+), 10 deletions(-) > create mode 100755 tests/generic/756 > create mode 100644 tests/generic/756.out > > diff --git a/common/rc b/common/rc > index 9da9fe188297..0beaf2ff1126 100644 > --- a/common/rc > +++ b/common/rc > @@ -5178,6 +5178,30 @@ _require_fibmap() > rm -f $file > } > > +_require_statx_unique_mountid() > +{ > + # statx(STATX_MNT_ID=0x1000) was added in Linux 5.8. > + # statx(STATX_MNT_ID_UNIQUE=0x4000) was added in Linux 6.9. > + # We only need to check the latter. > + > + export STATX_MNT_ID_UNIQUE=0x4000 > + local statx_mask=$( > + ${XFS_IO_PROG} -c "statx -m $STATX_MNT_ID_UNIQUE -r" "$TEST_DIR" | > + sed -En 's/stat\.mask = (0x[0-9a-f]+)/\1/p' > + ) > + > + [[ $(( statx_mask & STATX_MNT_ID_UNIQUE )) == $((STATX_MNT_ID_UNIQUE)) ]] || > + _notrun "statx does not support STATX_MNT_ID_UNIQUE on this kernel" > +} > + > +_require_open_by_handle_unique_mountid() > +{ > + _require_test_program "open_by_handle" > + > + $here/src/open_by_handle -C AT_HANDLE_MNT_ID_UNIQUE 2>&1 \ > + || _notrun "name_to_handle_at does not support AT_HANDLE_MNT_ID_UNIQUE" > +} > + > _try_wipe_scratch_devs() > { > test -x "$WIPEFS_PROG" || return 0 > diff --git a/src/open_by_handle.c b/src/open_by_handle.c > index 920ec7d9170b..b5c1a30abbbc 100644 > --- a/src/open_by_handle.c > +++ b/src/open_by_handle.c > @@ -106,9 +106,11 @@ struct handle { > > void usage(void) > { > - fprintf(stderr, "usage: open_by_handle [-cludmrwapknhs] [<-i|-o> <handles_file>] <test_dir> [num_files]\n"); > + fprintf(stderr, "usage: open_by_handle [-cludmMrwapknhs] [<-i|-o> <handles_file>] <test_dir> [num_files]\n"); > + fprintf(stderr, " open_by_handle -C <feature>\n"); > fprintf(stderr, "\n"); > fprintf(stderr, "open_by_handle -c <test_dir> [N] - create N test files under test_dir, try to get file handles and exit\n"); > + fprintf(stderr, "open_by_handle -c <test_dir> [N] - create N test files under test_dir, try to get file handles and exit\n"); > fprintf(stderr, "open_by_handle <test_dir> [N] - get file handles of test files, drop caches and try to open by handle\n"); > fprintf(stderr, "open_by_handle -n <test_dir> [N] - get file handles of test files and try to open by handle without drop caches\n"); > 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,19 +119,23 @@ void usage(void) > fprintf(stderr, "open_by_handle -a <test_dir> [N] - write data to test files after open by handle\n"); > fprintf(stderr, "open_by_handle -l <test_dir> [N] - create hardlinks to test files, drop caches and try to open by handle\n"); > fprintf(stderr, "open_by_handle -u <test_dir> [N] - unlink (hardlinked) test files, drop caches and try to open by handle\n"); > - fprintf(stderr, "open_by_handle -U <test_dir> [N] - verify the mount ID returned with AT_HANDLE_MNT_ID_UNIQUE is correct\n"); I guess this was not supposed to be in the first patch > 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 -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"); > fprintf(stderr, "open_by_handle -s <test_dir> [N] - wait in sleep loop after opening files by handle to keep them open\n"); > fprintf(stderr, "open_by_handle -z <test_dir> [N] - query filesystem required buffer size\n"); > + fprintf(stderr, "\n"); > + 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"); > exit(EXIT_FAILURE); > } > > static int do_name_to_handle_at(const char *fname, struct file_handle *fh, > - int bufsz) > + int bufsz, bool force_check_mountid) > { > int ret; > int mntid_short; > @@ -145,10 +151,15 @@ static int do_name_to_handle_at(const char *fname, struct file_handle *fh, > fprintf(stderr, "%s: statx(STATX_MNT_ID): %m\n", fname); > return EXIT_FAILURE; > } > - if (!(statxbuf.stx_mask & STATX_MNT_ID)) > + if (!(statxbuf.stx_mask & STATX_MNT_ID)) { > + if (force_check_mountid) { > + fprintf(stderr, "%s: statx(STATX_MNT_ID) not supported by running kernel\n", fname); > + return EXIT_FAILURE; > + } > skip_mntid = true; > - else > + } else { > statx_mntid_short = statxbuf.stx_mnt_id; > + } > } > > if (!skip_mntid_unique) { > @@ -160,10 +171,15 @@ static int do_name_to_handle_at(const char *fname, struct file_handle *fh, > * STATX_MNT_ID_UNIQUE was added fairly recently in Linux 6.8, so if the > * kernel doesn't give us a unique mount ID just skip it. > */ > - if (!(statxbuf.stx_mask & STATX_MNT_ID_UNIQUE)) > + if (!(statxbuf.stx_mask & STATX_MNT_ID_UNIQUE)) { > + if (force_check_mountid) { > + fprintf(stderr, "%s: statx(STATX_MNT_ID_UNIQUE) not supported by running kernel\n", fname); > + return EXIT_FAILURE; > + } > skip_mntid_unique = true; > - else > + } else { > statx_mntid_unique = statxbuf.stx_mnt_id; > + } > } > > fh->handle_bytes = bufsz; > @@ -204,6 +220,10 @@ static int do_name_to_handle_at(const char *fname, struct file_handle *fh, > return EXIT_FAILURE; > } > /* EINVAL means AT_HANDLE_MNT_ID_UNIQUE is not supported */ > + if (force_check_mountid) { > + fprintf(stderr, "%s: name_to_handle_at(AT_HANDLE_MNT_ID_UNIQUE) not supported by running kernel\n", fname); > + return EXIT_FAILURE; > + } > skip_mntid_unique = true; > } else { > if (mntid_unique != statx_mntid_unique) { > @@ -216,6 +236,22 @@ static int do_name_to_handle_at(const char *fname, struct file_handle *fh, > return 0; > } > > +static int check_feature(const char *feature) > +{ > + if (!strcmp(feature, "AT_HANDLE_MNT_ID_UNIQUE")) { > + int ret = name_to_handle_at(AT_FDCWD, ".", NULL, NULL, AT_HANDLE_MNT_ID_UNIQUE); > + /* If AT_HANDLE_MNT_ID_UNIQUE is supported, we get EFAULT. */ > + if (ret < 0 && errno == EINVAL) { > + fprintf(stderr, "name_to_handle_at(AT_HANDLE_MNT_ID_UNIQUE) not supported by running kernel\n"); > + return EXIT_FAILURE; > + } > + return 0; > + } > + > + fprintf(stderr, "unknown feature name '%s'\n", feature); > + return EXIT_FAILURE; > +} > + > int main(int argc, char **argv) > { > int i, c; > @@ -235,16 +271,20 @@ 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; > int bufsz = MAX_HANDLE_SZ; > > if (argc < 2) > usage(); > > - while ((c = getopt(argc, argv, "cludmrwapknhi:o:sz")) != -1) { > + while ((c = getopt(argc, argv, "cC:ludmMrwapknhi:o:sz")) != -1) { > switch (c) { > case 'c': > create = 1; > break; > + case 'C': > + /* Check kernel feature support. */ > + return check_feature(optarg); > case 'w': > /* Write data before open_by_handle_at() */ > wr = 1; > @@ -271,6 +311,9 @@ int main(int argc, char **argv) > case 'm': > move = 1; > break; > + case 'M': > + force_check_mountid = 1; > + break; > case 'p': > parent = 1; > break; > @@ -403,7 +446,7 @@ int main(int argc, char **argv) > return EXIT_FAILURE; > } > } else { > - ret = do_name_to_handle_at(fname, &handle[i].fh, bufsz); > + ret = do_name_to_handle_at(fname, &handle[i].fh, bufsz, force_check_mountid); > if (ret) > return EXIT_FAILURE; > } > @@ -433,7 +476,7 @@ int main(int argc, char **argv) > return EXIT_FAILURE; > } > } else { > - ret = do_name_to_handle_at(test_dir, &dir_handle.fh, bufsz); > + ret = do_name_to_handle_at(test_dir, &dir_handle.fh, bufsz, force_check_mountid); > if (ret) > return EXIT_FAILURE; > } > diff --git a/tests/generic/756 b/tests/generic/756 > new file mode 100755 > index 000000000000..c7a82cfd25f4 > --- /dev/null > +++ b/tests/generic/756 > @@ -0,0 +1,65 @@ > +#! /bin/bash > +# SPDX-License-Identifier: GPL-2.0 > +# Copyright (C) 2017 CTERA Networks. All Rights Reserved. > +# Copyright (C) 2024 Aleksa Sarai <cyphar@cyphar.com> > +# > +# FS QA Test No. 756 > +# > +# Check stale handles pointing to unlinked files and non-stale handles pointing > +# to linked files while verifying that u64 mount IDs are correctly returned. > +# > +. ./common/preamble > +_begin_fstest auto quick exportfs > + > +# Import common functions. > +. ./common/filter > + > + > +# Modify as appropriate. > +_require_test > +# _require_exportfs and already requires open_by_handle, but let's not count on it > +_require_test_program "open_by_handle" > +_require_exportfs > +# We need both STATX_MNT_ID_UNIQUE and AT_HANDLE_MNT_ID_UNIQUE. > +_require_statx_unique_mountid > +_require_open_by_handle_unique_mountid > + > +NUMFILES=1024 > +testdir=$TEST_DIR/$seq-dir > +mkdir -p $testdir > + > +# Create empty test files in test dir > +create_test_files() > +{ > + local dir=$1 > + > + mkdir -p $dir > + rm -f $dir/* > + $here/src/open_by_handle -c $dir $NUMFILES > +} > + > +# Test encode/decode file handles > +test_file_handles() > +{ > + local dir=$1 > + local opt=$2 > + > + echo test_file_handles $* | _filter_test_dir > + $here/src/open_by_handle $opt $dir $NUMFILES > +} > + > +# Check stale handles to deleted files > +create_test_files $testdir > +test_file_handles $testdir -Md > + > +# Check non-stale handles to linked files > +create_test_files $testdir > +test_file_handles $testdir -M > + > +# Check non-stale handles to files that were hardlinked and original deleted > +create_test_files $testdir > +test_file_handles $testdir -Ml > +test_file_handles $testdir -Mu > + > +status=0 > +exit > diff --git a/tests/generic/756.out b/tests/generic/756.out > new file mode 100644 > index 000000000000..48aed88d87b9 > --- /dev/null > +++ b/tests/generic/756.out > @@ -0,0 +1,5 @@ > +QA output created by 756 > +test_file_handles TEST_DIR/756-dir -Md > +test_file_handles TEST_DIR/756-dir -M > +test_file_handles TEST_DIR/756-dir -Ml > +test_file_handles TEST_DIR/756-dir -Mu > -- > 2.46.0 >
On 2024-09-04, Amir Goldstein <amir73il@gmail.com> wrote: > On Wed, Sep 4, 2024 at 7:57 PM Aleksa Sarai <cyphar@cyphar.com> wrote: > > > > In order to make sure we are actually testing AT_HANDLE_MNT_ID_UNIQUE, > > add a test (based on generic/426) which runs the open_by_handle in a > > mode where it will error out if there is a problem with getting mount > > IDs. The test is skipped if the kernel doesn't support the necessary > > features. > > > > Suggested-by: Amir Goldstein <amir73il@gmail.com> > > Signed-off-by: Aleksa Sarai <cyphar@cyphar.com> > > Apart from one minor nits below, you may add: > > Reviewed-by: Amir Goldstein <amir73il@gmail.com> > > > --- > > common/rc | 24 ++++++++++++++++ > > src/open_by_handle.c | 63 ++++++++++++++++++++++++++++++++++------- > > tests/generic/756 | 65 +++++++++++++++++++++++++++++++++++++++++++ > > tests/generic/756.out | 5 ++++ > > 4 files changed, 147 insertions(+), 10 deletions(-) > > create mode 100755 tests/generic/756 > > create mode 100644 tests/generic/756.out > > > > diff --git a/common/rc b/common/rc > > index 9da9fe188297..0beaf2ff1126 100644 > > --- a/common/rc > > +++ b/common/rc > > @@ -5178,6 +5178,30 @@ _require_fibmap() > > rm -f $file > > } > > > > +_require_statx_unique_mountid() > > +{ > > + # statx(STATX_MNT_ID=0x1000) was added in Linux 5.8. > > + # statx(STATX_MNT_ID_UNIQUE=0x4000) was added in Linux 6.9. > > + # We only need to check the latter. > > + > > + export STATX_MNT_ID_UNIQUE=0x4000 > > + local statx_mask=$( > > + ${XFS_IO_PROG} -c "statx -m $STATX_MNT_ID_UNIQUE -r" "$TEST_DIR" | > > + sed -En 's/stat\.mask = (0x[0-9a-f]+)/\1/p' > > + ) > > + > > + [[ $(( statx_mask & STATX_MNT_ID_UNIQUE )) == $((STATX_MNT_ID_UNIQUE)) ]] || > > + _notrun "statx does not support STATX_MNT_ID_UNIQUE on this kernel" > > +} > > + > > +_require_open_by_handle_unique_mountid() > > +{ > > + _require_test_program "open_by_handle" > > + > > + $here/src/open_by_handle -C AT_HANDLE_MNT_ID_UNIQUE 2>&1 \ > > + || _notrun "name_to_handle_at does not support AT_HANDLE_MNT_ID_UNIQUE" > > +} > > + > > _try_wipe_scratch_devs() > > { > > test -x "$WIPEFS_PROG" || return 0 > > diff --git a/src/open_by_handle.c b/src/open_by_handle.c > > index 920ec7d9170b..b5c1a30abbbc 100644 > > --- a/src/open_by_handle.c > > +++ b/src/open_by_handle.c > > @@ -106,9 +106,11 @@ struct handle { > > > > void usage(void) > > { > > - fprintf(stderr, "usage: open_by_handle [-cludmrwapknhs] [<-i|-o> <handles_file>] <test_dir> [num_files]\n"); > > + fprintf(stderr, "usage: open_by_handle [-cludmMrwapknhs] [<-i|-o> <handles_file>] <test_dir> [num_files]\n"); > > + fprintf(stderr, " open_by_handle -C <feature>\n"); > > fprintf(stderr, "\n"); > > fprintf(stderr, "open_by_handle -c <test_dir> [N] - create N test files under test_dir, try to get file handles and exit\n"); > > + fprintf(stderr, "open_by_handle -c <test_dir> [N] - create N test files under test_dir, try to get file handles and exit\n"); > > fprintf(stderr, "open_by_handle <test_dir> [N] - get file handles of test files, drop caches and try to open by handle\n"); > > fprintf(stderr, "open_by_handle -n <test_dir> [N] - get file handles of test files and try to open by handle without drop caches\n"); > > 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,19 +119,23 @@ void usage(void) > > fprintf(stderr, "open_by_handle -a <test_dir> [N] - write data to test files after open by handle\n"); > > fprintf(stderr, "open_by_handle -l <test_dir> [N] - create hardlinks to test files, drop caches and try to open by handle\n"); > > fprintf(stderr, "open_by_handle -u <test_dir> [N] - unlink (hardlinked) test files, drop caches and try to open by handle\n"); > > - fprintf(stderr, "open_by_handle -U <test_dir> [N] - verify the mount ID returned with AT_HANDLE_MNT_ID_UNIQUE is correct\n"); > > I guess this was not supposed to be in the first patch Yeah, it got mixed up when splitting the patch. I'll fix it up. > > 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 -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"); > > fprintf(stderr, "open_by_handle -s <test_dir> [N] - wait in sleep loop after opening files by handle to keep them open\n"); > > fprintf(stderr, "open_by_handle -z <test_dir> [N] - query filesystem required buffer size\n"); > > + fprintf(stderr, "\n"); > > + 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"); > > exit(EXIT_FAILURE); > > } > > > > static int do_name_to_handle_at(const char *fname, struct file_handle *fh, > > - int bufsz) > > + int bufsz, bool force_check_mountid) > > { > > int ret; > > int mntid_short; > > @@ -145,10 +151,15 @@ static int do_name_to_handle_at(const char *fname, struct file_handle *fh, > > fprintf(stderr, "%s: statx(STATX_MNT_ID): %m\n", fname); > > return EXIT_FAILURE; > > } > > - if (!(statxbuf.stx_mask & STATX_MNT_ID)) > > + if (!(statxbuf.stx_mask & STATX_MNT_ID)) { > > + if (force_check_mountid) { > > + fprintf(stderr, "%s: statx(STATX_MNT_ID) not supported by running kernel\n", fname); > > + return EXIT_FAILURE; > > + } > > skip_mntid = true; > > - else > > + } else { > > statx_mntid_short = statxbuf.stx_mnt_id; > > + } > > } > > > > if (!skip_mntid_unique) { > > @@ -160,10 +171,15 @@ static int do_name_to_handle_at(const char *fname, struct file_handle *fh, > > * STATX_MNT_ID_UNIQUE was added fairly recently in Linux 6.8, so if the > > * kernel doesn't give us a unique mount ID just skip it. > > */ > > - if (!(statxbuf.stx_mask & STATX_MNT_ID_UNIQUE)) > > + if (!(statxbuf.stx_mask & STATX_MNT_ID_UNIQUE)) { > > + if (force_check_mountid) { > > + fprintf(stderr, "%s: statx(STATX_MNT_ID_UNIQUE) not supported by running kernel\n", fname); > > + return EXIT_FAILURE; > > + } > > skip_mntid_unique = true; > > - else > > + } else { > > statx_mntid_unique = statxbuf.stx_mnt_id; > > + } > > } > > > > fh->handle_bytes = bufsz; > > @@ -204,6 +220,10 @@ static int do_name_to_handle_at(const char *fname, struct file_handle *fh, > > return EXIT_FAILURE; > > } > > /* EINVAL means AT_HANDLE_MNT_ID_UNIQUE is not supported */ > > + if (force_check_mountid) { > > + fprintf(stderr, "%s: name_to_handle_at(AT_HANDLE_MNT_ID_UNIQUE) not supported by running kernel\n", fname); > > + return EXIT_FAILURE; > > + } > > skip_mntid_unique = true; > > } else { > > if (mntid_unique != statx_mntid_unique) { > > @@ -216,6 +236,22 @@ static int do_name_to_handle_at(const char *fname, struct file_handle *fh, > > return 0; > > } > > > > +static int check_feature(const char *feature) > > +{ > > + if (!strcmp(feature, "AT_HANDLE_MNT_ID_UNIQUE")) { > > + int ret = name_to_handle_at(AT_FDCWD, ".", NULL, NULL, AT_HANDLE_MNT_ID_UNIQUE); > > + /* If AT_HANDLE_MNT_ID_UNIQUE is supported, we get EFAULT. */ > > + if (ret < 0 && errno == EINVAL) { > > + fprintf(stderr, "name_to_handle_at(AT_HANDLE_MNT_ID_UNIQUE) not supported by running kernel\n"); > > + return EXIT_FAILURE; > > + } > > + return 0; > > + } > > + > > + fprintf(stderr, "unknown feature name '%s'\n", feature); > > + return EXIT_FAILURE; > > +} > > + > > int main(int argc, char **argv) > > { > > int i, c; > > @@ -235,16 +271,20 @@ 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; > > int bufsz = MAX_HANDLE_SZ; > > > > if (argc < 2) > > usage(); > > > > - while ((c = getopt(argc, argv, "cludmrwapknhi:o:sz")) != -1) { > > + while ((c = getopt(argc, argv, "cC:ludmMrwapknhi:o:sz")) != -1) { > > switch (c) { > > case 'c': > > create = 1; > > break; > > + case 'C': > > + /* Check kernel feature support. */ > > + return check_feature(optarg); > > case 'w': > > /* Write data before open_by_handle_at() */ > > wr = 1; > > @@ -271,6 +311,9 @@ int main(int argc, char **argv) > > case 'm': > > move = 1; > > break; > > + case 'M': > > + force_check_mountid = 1; > > + break; > > case 'p': > > parent = 1; > > break; > > @@ -403,7 +446,7 @@ int main(int argc, char **argv) > > return EXIT_FAILURE; > > } > > } else { > > - ret = do_name_to_handle_at(fname, &handle[i].fh, bufsz); > > + ret = do_name_to_handle_at(fname, &handle[i].fh, bufsz, force_check_mountid); > > if (ret) > > return EXIT_FAILURE; > > } > > @@ -433,7 +476,7 @@ int main(int argc, char **argv) > > return EXIT_FAILURE; > > } > > } else { > > - ret = do_name_to_handle_at(test_dir, &dir_handle.fh, bufsz); > > + ret = do_name_to_handle_at(test_dir, &dir_handle.fh, bufsz, force_check_mountid); > > if (ret) > > return EXIT_FAILURE; > > } > > diff --git a/tests/generic/756 b/tests/generic/756 > > new file mode 100755 > > index 000000000000..c7a82cfd25f4 > > --- /dev/null > > +++ b/tests/generic/756 > > @@ -0,0 +1,65 @@ > > +#! /bin/bash > > +# SPDX-License-Identifier: GPL-2.0 > > +# Copyright (C) 2017 CTERA Networks. All Rights Reserved. > > +# Copyright (C) 2024 Aleksa Sarai <cyphar@cyphar.com> > > +# > > +# FS QA Test No. 756 > > +# > > +# Check stale handles pointing to unlinked files and non-stale handles pointing > > +# to linked files while verifying that u64 mount IDs are correctly returned. > > +# > > +. ./common/preamble > > +_begin_fstest auto quick exportfs > > + > > +# Import common functions. > > +. ./common/filter > > + > > + > > +# Modify as appropriate. > > +_require_test > > +# _require_exportfs and already requires open_by_handle, but let's not count on it > > +_require_test_program "open_by_handle" > > +_require_exportfs > > +# We need both STATX_MNT_ID_UNIQUE and AT_HANDLE_MNT_ID_UNIQUE. > > +_require_statx_unique_mountid > > +_require_open_by_handle_unique_mountid > > + > > +NUMFILES=1024 > > +testdir=$TEST_DIR/$seq-dir > > +mkdir -p $testdir > > + > > +# Create empty test files in test dir > > +create_test_files() > > +{ > > + local dir=$1 > > + > > + mkdir -p $dir > > + rm -f $dir/* > > + $here/src/open_by_handle -c $dir $NUMFILES > > +} > > + > > +# Test encode/decode file handles > > +test_file_handles() > > +{ > > + local dir=$1 > > + local opt=$2 > > + > > + echo test_file_handles $* | _filter_test_dir > > + $here/src/open_by_handle $opt $dir $NUMFILES > > +} > > + > > +# Check stale handles to deleted files > > +create_test_files $testdir > > +test_file_handles $testdir -Md > > + > > +# Check non-stale handles to linked files > > +create_test_files $testdir > > +test_file_handles $testdir -M > > + > > +# Check non-stale handles to files that were hardlinked and original deleted > > +create_test_files $testdir > > +test_file_handles $testdir -Ml > > +test_file_handles $testdir -Mu > > + > > +status=0 > > +exit > > diff --git a/tests/generic/756.out b/tests/generic/756.out > > new file mode 100644 > > index 000000000000..48aed88d87b9 > > --- /dev/null > > +++ b/tests/generic/756.out > > @@ -0,0 +1,5 @@ > > +QA output created by 756 > > +test_file_handles TEST_DIR/756-dir -Md > > +test_file_handles TEST_DIR/756-dir -M > > +test_file_handles TEST_DIR/756-dir -Ml > > +test_file_handles TEST_DIR/756-dir -Mu > > -- > > 2.46.0 > >
diff --git a/common/rc b/common/rc index 9da9fe188297..0beaf2ff1126 100644 --- a/common/rc +++ b/common/rc @@ -5178,6 +5178,30 @@ _require_fibmap() rm -f $file } +_require_statx_unique_mountid() +{ + # statx(STATX_MNT_ID=0x1000) was added in Linux 5.8. + # statx(STATX_MNT_ID_UNIQUE=0x4000) was added in Linux 6.9. + # We only need to check the latter. + + export STATX_MNT_ID_UNIQUE=0x4000 + local statx_mask=$( + ${XFS_IO_PROG} -c "statx -m $STATX_MNT_ID_UNIQUE -r" "$TEST_DIR" | + sed -En 's/stat\.mask = (0x[0-9a-f]+)/\1/p' + ) + + [[ $(( statx_mask & STATX_MNT_ID_UNIQUE )) == $((STATX_MNT_ID_UNIQUE)) ]] || + _notrun "statx does not support STATX_MNT_ID_UNIQUE on this kernel" +} + +_require_open_by_handle_unique_mountid() +{ + _require_test_program "open_by_handle" + + $here/src/open_by_handle -C AT_HANDLE_MNT_ID_UNIQUE 2>&1 \ + || _notrun "name_to_handle_at does not support AT_HANDLE_MNT_ID_UNIQUE" +} + _try_wipe_scratch_devs() { test -x "$WIPEFS_PROG" || return 0 diff --git a/src/open_by_handle.c b/src/open_by_handle.c index 920ec7d9170b..b5c1a30abbbc 100644 --- a/src/open_by_handle.c +++ b/src/open_by_handle.c @@ -106,9 +106,11 @@ struct handle { void usage(void) { - fprintf(stderr, "usage: open_by_handle [-cludmrwapknhs] [<-i|-o> <handles_file>] <test_dir> [num_files]\n"); + fprintf(stderr, "usage: open_by_handle [-cludmMrwapknhs] [<-i|-o> <handles_file>] <test_dir> [num_files]\n"); + fprintf(stderr, " open_by_handle -C <feature>\n"); fprintf(stderr, "\n"); fprintf(stderr, "open_by_handle -c <test_dir> [N] - create N test files under test_dir, try to get file handles and exit\n"); + fprintf(stderr, "open_by_handle -c <test_dir> [N] - create N test files under test_dir, try to get file handles and exit\n"); fprintf(stderr, "open_by_handle <test_dir> [N] - get file handles of test files, drop caches and try to open by handle\n"); fprintf(stderr, "open_by_handle -n <test_dir> [N] - get file handles of test files and try to open by handle without drop caches\n"); 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,19 +119,23 @@ void usage(void) fprintf(stderr, "open_by_handle -a <test_dir> [N] - write data to test files after open by handle\n"); fprintf(stderr, "open_by_handle -l <test_dir> [N] - create hardlinks to test files, drop caches and try to open by handle\n"); fprintf(stderr, "open_by_handle -u <test_dir> [N] - unlink (hardlinked) test files, drop caches and try to open by handle\n"); - fprintf(stderr, "open_by_handle -U <test_dir> [N] - verify the mount ID returned with AT_HANDLE_MNT_ID_UNIQUE is correct\n"); 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 -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"); fprintf(stderr, "open_by_handle -s <test_dir> [N] - wait in sleep loop after opening files by handle to keep them open\n"); fprintf(stderr, "open_by_handle -z <test_dir> [N] - query filesystem required buffer size\n"); + fprintf(stderr, "\n"); + 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"); exit(EXIT_FAILURE); } static int do_name_to_handle_at(const char *fname, struct file_handle *fh, - int bufsz) + int bufsz, bool force_check_mountid) { int ret; int mntid_short; @@ -145,10 +151,15 @@ static int do_name_to_handle_at(const char *fname, struct file_handle *fh, fprintf(stderr, "%s: statx(STATX_MNT_ID): %m\n", fname); return EXIT_FAILURE; } - if (!(statxbuf.stx_mask & STATX_MNT_ID)) + if (!(statxbuf.stx_mask & STATX_MNT_ID)) { + if (force_check_mountid) { + fprintf(stderr, "%s: statx(STATX_MNT_ID) not supported by running kernel\n", fname); + return EXIT_FAILURE; + } skip_mntid = true; - else + } else { statx_mntid_short = statxbuf.stx_mnt_id; + } } if (!skip_mntid_unique) { @@ -160,10 +171,15 @@ static int do_name_to_handle_at(const char *fname, struct file_handle *fh, * STATX_MNT_ID_UNIQUE was added fairly recently in Linux 6.8, so if the * kernel doesn't give us a unique mount ID just skip it. */ - if (!(statxbuf.stx_mask & STATX_MNT_ID_UNIQUE)) + if (!(statxbuf.stx_mask & STATX_MNT_ID_UNIQUE)) { + if (force_check_mountid) { + fprintf(stderr, "%s: statx(STATX_MNT_ID_UNIQUE) not supported by running kernel\n", fname); + return EXIT_FAILURE; + } skip_mntid_unique = true; - else + } else { statx_mntid_unique = statxbuf.stx_mnt_id; + } } fh->handle_bytes = bufsz; @@ -204,6 +220,10 @@ static int do_name_to_handle_at(const char *fname, struct file_handle *fh, return EXIT_FAILURE; } /* EINVAL means AT_HANDLE_MNT_ID_UNIQUE is not supported */ + if (force_check_mountid) { + fprintf(stderr, "%s: name_to_handle_at(AT_HANDLE_MNT_ID_UNIQUE) not supported by running kernel\n", fname); + return EXIT_FAILURE; + } skip_mntid_unique = true; } else { if (mntid_unique != statx_mntid_unique) { @@ -216,6 +236,22 @@ static int do_name_to_handle_at(const char *fname, struct file_handle *fh, return 0; } +static int check_feature(const char *feature) +{ + if (!strcmp(feature, "AT_HANDLE_MNT_ID_UNIQUE")) { + int ret = name_to_handle_at(AT_FDCWD, ".", NULL, NULL, AT_HANDLE_MNT_ID_UNIQUE); + /* If AT_HANDLE_MNT_ID_UNIQUE is supported, we get EFAULT. */ + if (ret < 0 && errno == EINVAL) { + fprintf(stderr, "name_to_handle_at(AT_HANDLE_MNT_ID_UNIQUE) not supported by running kernel\n"); + return EXIT_FAILURE; + } + return 0; + } + + fprintf(stderr, "unknown feature name '%s'\n", feature); + return EXIT_FAILURE; +} + int main(int argc, char **argv) { int i, c; @@ -235,16 +271,20 @@ 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; int bufsz = MAX_HANDLE_SZ; if (argc < 2) usage(); - while ((c = getopt(argc, argv, "cludmrwapknhi:o:sz")) != -1) { + while ((c = getopt(argc, argv, "cC:ludmMrwapknhi:o:sz")) != -1) { switch (c) { case 'c': create = 1; break; + case 'C': + /* Check kernel feature support. */ + return check_feature(optarg); case 'w': /* Write data before open_by_handle_at() */ wr = 1; @@ -271,6 +311,9 @@ int main(int argc, char **argv) case 'm': move = 1; break; + case 'M': + force_check_mountid = 1; + break; case 'p': parent = 1; break; @@ -403,7 +446,7 @@ int main(int argc, char **argv) return EXIT_FAILURE; } } else { - ret = do_name_to_handle_at(fname, &handle[i].fh, bufsz); + ret = do_name_to_handle_at(fname, &handle[i].fh, bufsz, force_check_mountid); if (ret) return EXIT_FAILURE; } @@ -433,7 +476,7 @@ int main(int argc, char **argv) return EXIT_FAILURE; } } else { - ret = do_name_to_handle_at(test_dir, &dir_handle.fh, bufsz); + ret = do_name_to_handle_at(test_dir, &dir_handle.fh, bufsz, force_check_mountid); if (ret) return EXIT_FAILURE; } diff --git a/tests/generic/756 b/tests/generic/756 new file mode 100755 index 000000000000..c7a82cfd25f4 --- /dev/null +++ b/tests/generic/756 @@ -0,0 +1,65 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2017 CTERA Networks. All Rights Reserved. +# Copyright (C) 2024 Aleksa Sarai <cyphar@cyphar.com> +# +# FS QA Test No. 756 +# +# Check stale handles pointing to unlinked files and non-stale handles pointing +# to linked files while verifying that u64 mount IDs are correctly returned. +# +. ./common/preamble +_begin_fstest auto quick exportfs + +# Import common functions. +. ./common/filter + + +# Modify as appropriate. +_require_test +# _require_exportfs and already requires open_by_handle, but let's not count on it +_require_test_program "open_by_handle" +_require_exportfs +# We need both STATX_MNT_ID_UNIQUE and AT_HANDLE_MNT_ID_UNIQUE. +_require_statx_unique_mountid +_require_open_by_handle_unique_mountid + +NUMFILES=1024 +testdir=$TEST_DIR/$seq-dir +mkdir -p $testdir + +# Create empty test files in test dir +create_test_files() +{ + local dir=$1 + + mkdir -p $dir + rm -f $dir/* + $here/src/open_by_handle -c $dir $NUMFILES +} + +# Test encode/decode file handles +test_file_handles() +{ + local dir=$1 + local opt=$2 + + echo test_file_handles $* | _filter_test_dir + $here/src/open_by_handle $opt $dir $NUMFILES +} + +# Check stale handles to deleted files +create_test_files $testdir +test_file_handles $testdir -Md + +# Check non-stale handles to linked files +create_test_files $testdir +test_file_handles $testdir -M + +# Check non-stale handles to files that were hardlinked and original deleted +create_test_files $testdir +test_file_handles $testdir -Ml +test_file_handles $testdir -Mu + +status=0 +exit diff --git a/tests/generic/756.out b/tests/generic/756.out new file mode 100644 index 000000000000..48aed88d87b9 --- /dev/null +++ b/tests/generic/756.out @@ -0,0 +1,5 @@ +QA output created by 756 +test_file_handles TEST_DIR/756-dir -Md +test_file_handles TEST_DIR/756-dir -M +test_file_handles TEST_DIR/756-dir -Ml +test_file_handles TEST_DIR/756-dir -Mu
In order to make sure we are actually testing AT_HANDLE_MNT_ID_UNIQUE, add a test (based on generic/426) which runs the open_by_handle in a mode where it will error out if there is a problem with getting mount IDs. The test is skipped if the kernel doesn't support the necessary features. Suggested-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Aleksa Sarai <cyphar@cyphar.com> --- common/rc | 24 ++++++++++++++++ src/open_by_handle.c | 63 ++++++++++++++++++++++++++++++++++------- tests/generic/756 | 65 +++++++++++++++++++++++++++++++++++++++++++ tests/generic/756.out | 5 ++++ 4 files changed, 147 insertions(+), 10 deletions(-) create mode 100755 tests/generic/756 create mode 100644 tests/generic/756.out