Whamcloud - gitweb
LU-16605 lfs: Add -n option to fid2path 26/51626/12
authorArshad Hussain <arshad.hussain@aeoncomputing.com>
Tue, 11 Jul 2023 05:55:36 +0000 (11:25 +0530)
committerOleg Drokin <green@whamcloud.com>
Thu, 31 Aug 2023 06:33:07 +0000 (06:33 +0000)
Add '-n' option to fid2path to allow printing
only the filename of the file instead of the
whole parent pathname.

Test-case sanity/226d added.

Test-Parameters: trivial testlist=sanity
Signed-off-by: Arshad Hussain <arshad.hussain@aeoncomputing.com>
Change-Id: Ieebd39a1655b4e3ad20cdbb4941dbb44882845f4
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/51626
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Patrick Farrell <pfarrell@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/doc/lfs-fid2path.1
lustre/tests/sanity.sh
lustre/utils/lfs.c

index aa2a5ab..dc1ea72 100644 (file)
@@ -30,6 +30,9 @@ If a file has multiple hard links, then print only the specified LINK,
 starting at link 0.  If multiple FIDs are given, but only one
 pathname is needed for each file, use
 .BR "--link=0" .
+.TP
+\fB\-n\fR, \fB\-\-name\fR
+Print only the filename instead of whole pathname
 .SH EXAMPLES
 .TP
 .B $ lfs fid2path /mnt/testfs [0x200000403:0x11f:0x0]
@@ -41,7 +44,9 @@ pathname is needed for each file, use
  file /mnt/lustre/Link_
 .br
  file
-
+.TP
+.B $ lfs fid2path -n /mnt/testfs [0x200000403:0x11f:0x0]
+hosts
 .SH SEE ALSO
 .BR lfs (1),
 .BR lfs-getstripe (1),
index 3c73faf..a486121 100755 (executable)
@@ -21071,6 +21071,77 @@ test_226c () {
 }
 run_test 226c "call path2fid and fid2path under remote dir with subdir mount"
 
+test_226d () {
+       (( $CLIENT_VERSION >= $(version_code 2.15.57) )) ||
+               skip "Need client at least version 2.15.57"
+
+       # Define First test dataset
+       local testdirs_01=$DIR/$tdir
+       local testdata_01=$testdirs_01/${tdir}_01
+       local testresult_01=${tdir}_01
+       # Define Second test dataset
+       local testdirs_02=$DIR/$tdir/$tdir
+       local testdata_02=$testdirs_02/${tdir}_02
+       local testresult_02=${tdir}_02
+       # Define third test dataset (top level)
+       local testdata_03=$DIR/${tdir}_03
+       local testresult_03=${tdir}_03
+
+       # Create first test dataset
+       mkdir -p $testdirs_01 || error "cannot create dir $testdirs_01"
+       touch $testdata_01 || error "cannot create file $testdata_01"
+
+       # Create second test dataset
+       mkdir -p $testdirs_02 || error "cannot create dir $testdirs_02"
+       touch $testdata_02 || error "cannot create file $testdata_02"
+
+       # Create third test dataset
+       touch $testdata_03 || error "cannot create file $testdata_03"
+
+       local fid01=$($LFS getstripe -F "$testdata_01") ||
+               error "getstripe failed on $testdata_01"
+       local fid02=$($LFS getstripe -F "$testdata_02") ||
+               error "getstripe failed on $testdata_01"
+       local fid03=$($LFS getstripe -F "$testdata_03") ||
+               error "getstripe failed on $testdata_03"
+
+       # Verify only -n option
+       local out1=$($LFS fid2path -n $DIR $fid01) ||
+               error "fid2path failed on $fid01"
+       local out2=$($LFS fid2path -n $DIR $fid02) ||
+               error "fid2path failed on $fid02"
+       local out3=$($LFS fid2path -n $DIR $fid03) ||
+               error "fid2path failed on $fid03"
+
+       [[ "$out1" == "$testresult_01" ]] ||
+               error "fid2path failed: Expected $testresult_01 got $out1"
+       [[ "$out2" == "$testresult_02" ]] ||
+               error "fid2path failed: Expected $testresult_02 got $out2"
+       [[ "$out3" == "$testresult_03" ]] ||
+               error "fid2path failed: Expected $testresult_03 got $out3"
+
+       # Verify with option -fn together
+       out1=$($LFS fid2path -fn $DIR $fid01) ||
+               error "fid2path -fn failed on $fid01"
+       out2=$($LFS fid2path -fn $DIR $fid02) ||
+               error "fid2path -fn failed on $fid02"
+       out3=$($LFS fid2path -fn $DIR $fid03) ||
+               error "fid2path -fn failed on $fid03"
+
+       local tmpout=$(echo $out1 | cut -d" " -f2)
+       [[ "$tmpout" == "$testresult_01" ]] ||
+               error "fid2path -fn failed: Expected $testresult_01 got $out1"
+
+       tmpout=$(echo $out2 | cut -d" " -f2)
+       [[ "$tmpout" == "$testresult_02" ]] ||
+               error "fid2path -fn failed: Expected $testresult_02 got $out2"
+
+       tmpout=$(echo $out3 | cut -d" " -f2)
+       [[ "$tmpout" == "$testresult_03" ]] ||
+               error "fid2path -fn failed: Expected $testresult_03 got $out3"
+}
+run_test 226d "verify fid2path with -n and -fn option"
+
 test_226e () {
        (( $CLIENT_VERSION >= $(version_code 2.15.56) )) ||
                skip "Need client at least version 2.15.56"
index bf4ba78..5b41dcd 100644 (file)
@@ -77,6 +77,7 @@
 #include <linux/lnet/nidstr.h>
 #include <lnetconfig/cyaml.h>
 #include "lstddef.h"
+#include <uapi/linux/lustre/lustre_idl.h>
 
 #ifndef NSEC_PER_SEC
 # define NSEC_PER_SEC 1000000000UL
@@ -482,8 +483,8 @@ command_t cmdlist[] = {
        {"fid2path", lfs_fid2path, 0,
         "Resolve the full path(s) for given FID(s). For a specific hardlink "
         "specify link number <linkno>.\n"
-        "usage: fid2path [--print-fid|-f] [--print-link|-c] [--link|-l <linkno>] "
-        "[--print0|-0] <fsname|root> <fid>..."},
+        "usage: fid2path [--print0|-0] [--print-fid|-f] [--print-link|-c] "
+        "[--link|-l <linkno>] [--name|-n] <fsname|root> <fid>..."},
        {"path2fid", lfs_path2fid, 0, "Display the fid(s) for a given path(s).\n"
         "usage: path2fid [--parents] <path> ..."},
        {"rmfid", lfs_rmfid, 0, "Remove file(s) by FID(s)\n"
@@ -9706,6 +9707,72 @@ static void rstripc(char *str, int c)
                end[-1] = '\0';
 }
 
+/* Helper function to lfs_fid2path. To print out only the file names and
+ * not the full path. Do not call OBD_IOC_FID2PATH for every file. Instead
+ * read the trusted.link xattr and loop over all the records to get all the
+ * file names.
+ */
+static int lfs_fid2path_prn_name(char *mnt_dir, char *path_buf,
+                                bool print_linkno, bool print_fid, char *ptr,
+                                const char *fid_str, int linktmp)
+{
+       char buf[65536]; /* BUFFER_SIZE 65536 */
+       char full_path[PATH_MAX * 2 + 2];
+       struct link_ea_header *leh;
+       struct link_ea_entry *lee;
+       ssize_t size;
+       int reclen, i, rc = 0;
+
+       /* Generate full_path */
+       snprintf(full_path, sizeof(full_path) - 1, "%s/%s", mnt_dir, path_buf);
+
+       size = getxattr(full_path, "trusted.link", buf, sizeof(buf));
+       if (size < 0) {
+               fprintf(stderr, "%s: failed to read %s xattr: %s\n", path_buf,
+                       "trusted.link", strerror(errno));
+               rc = -errno;
+               goto fail;
+       }
+
+       leh = (struct link_ea_header *)buf;
+
+       if (leh->leh_magic == __swab32(LINK_EA_MAGIC))
+               leh->leh_reccount = __swab32(leh->leh_reccount);
+
+       lee = (struct link_ea_entry *)(leh + 1);
+
+       for (i = 0; i < leh->leh_reccount; i++) {
+               reclen = (lee->lee_reclen[0] << 8) | lee->lee_reclen[1];
+
+               /* handle -n -l case */
+               if (print_linkno) {
+                       ptr = strrchr(path_buf, '/');
+                       if (!ptr)
+                               ptr = path_buf;
+                       else
+                               ptr = ptr + 1;
+
+                       if (strcmp(ptr, lee->lee_name) == 0) {
+                               if (print_fid)
+                                       printf("%s ", fid_str);
+
+                               printf("%d ", linktmp);
+                               printf("%s\n", lee->lee_name);
+                               break;
+                       }
+               } else {
+                       if (print_fid)
+                               printf("%s ", fid_str);
+                       printf("%s\n", lee->lee_name);
+               }
+
+               /* Get next record */
+               lee = (struct link_ea_entry *)((char *)lee + reclen);
+       }
+fail:
+       return rc;
+}
+
 static int lfs_fid2path(int argc, char **argv)
 {
        struct option long_opts[] = {
@@ -9715,8 +9782,11 @@ static int lfs_fid2path(int argc, char **argv)
                { .val = 'c',   .name = "print-link",   .has_arg = no_argument },
                { .val = 'f',   .name = "print-fid",    .has_arg = no_argument },
                { .val = 'l',   .name = "link", .has_arg = required_argument },
+               { .val = 'n',   .name = "name", .has_arg = no_argument },
                { .name = NULL } };
-       char short_opts[] = "0cfl:pr:";
+       char short_opts[] = "0cfl:pr:n";
+       bool print_only_fname = false;
+       bool print_linkno = false;
        bool print_link = false;
        bool print_fid = false;
        bool print_mnt_dir;
@@ -9731,7 +9801,8 @@ static int lfs_fid2path(int argc, char **argv)
        int c;
        int i;
 
-       while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, short_opts,long_opts, NULL)) !=
+               -1) {
                switch (c) {
                case '0':
                        link_separator = '\0';
@@ -9751,6 +9822,12 @@ static int lfs_fid2path(int argc, char **argv)
                                        progname, optarg);
                                return CMD_HELP;
                        }
+                       print_linkno = true;
+                       break;
+               case 'n':
+                       /* Bypass the full parent path if true
+                        * only print the final filename */
+                       print_only_fname = true;
                        break;
                case 'r':
                        /* recno is something to do with changelogs
@@ -9812,6 +9889,7 @@ static int lfs_fid2path(int argc, char **argv)
        for (i = optind + 1; i < argc; i++) {
                const char *fid_str = argv[i];
                struct lu_fid fid;
+               char *ptr = NULL;
                int rc2;
 
                rc2 = llapi_fid_parse(fid_str, &fid, NULL);
@@ -9826,13 +9904,15 @@ static int lfs_fid2path(int argc, char **argv)
                }
 
                int linktmp = (linkno >= 0) ? linkno : 0;
+
                while (1) {
                        int oldtmp = linktmp;
                        long long rectmp = recno;
                        char path_buf[PATH_MAX];
 
-                       rc2 = llapi_fid2path_at(mnt_fd, &fid,
-                               path_buf, sizeof(path_buf), &rectmp, &linktmp);
+                       rc2 = llapi_fid2path_at(mnt_fd, &fid, path_buf,
+                                               sizeof(path_buf), &rectmp,
+                                               &linktmp);
                        if (rc2 < 0) {
                                fprintf(stderr,
                                        "%s fid2path: cannot find %s %s: %s\n",
@@ -9843,6 +9923,25 @@ static int lfs_fid2path(int argc, char **argv)
                                break;
                        }
 
+                       if (print_only_fname && !print_link) {
+                               /* '-n' is passed as option here.
+                                * For all other cases of -c fall back
+                                * to default(else) path as to get the link
+                                * count associated with the file name call
+                                * to OBD_IOC_FID2PATH is required
+                                */
+                               rc = lfs_fid2path_prn_name(mnt_dir,
+                                                          path_buf,
+                                                          print_linkno,
+                                                          print_fid, ptr,
+                                                          fid_str, linktmp);
+                               /* llapi_fid2path_at() is already called once
+                                * in this case. No need to call it again.
+                                * Break out as we have all the filenames.
+                                */
+                               break;
+                       }
+
                        if (print_fid)
                                printf("%s ", fid_str);
 
@@ -9859,11 +9958,18 @@ static int lfs_fid2path(int argc, char **argv)
                         *
                         * Note that llapi_fid2path() returns "" for the root
                         * FID. */
-
-                       printf("%s%s%s%c",
-                              print_mnt_dir ? mnt_dir : "",
-                              (print_mnt_dir || *path_buf == '\0') ?
-                              "/" : "", path_buf, link_separator);
+                       if (!print_only_fname) {
+                               printf("%s%s%s%c",
+                                      print_mnt_dir ? mnt_dir : "",
+                                      (print_mnt_dir || *path_buf == '\0') ?
+                                      "/" : "", path_buf, link_separator);
+                       } else {
+                               ptr = strrchr(path_buf, '/');
+                               if (!ptr)
+                                       printf("%s\n", path_buf);
+                               else
+                                       printf("%s\n", ptr + 1);
+                       }
 
                        if (linkno >= 0)
                                /* specified linkno */
@@ -13293,8 +13399,3 @@ int main(int argc, char **argv)
 
        return rc < 0 ? -rc : rc;
 }
-
-#ifdef _LUSTRE_IDL_H_
-/* Everything we need here should be included by lustreapi.h. */
-# error "lfs should not depend on lustre_idl.h"
-#endif /* _LUSTRE_IDL_H_ */