Whamcloud - gitweb
LU-16494 fileset: check fileset for operations by fid 96/49696/8
authorSebastien Buisson <sbuisson@ddn.com>
Thu, 19 Jan 2023 17:07:27 +0000 (18:07 +0100)
committerOleg Drokin <green@whamcloud.com>
Tue, 14 Feb 2023 06:05:00 +0000 (06:05 +0000)
Some operations by FID, such as lfs rmfid, must be aware of
subdirectory mount (fileset) so that they do not operate on files
that are outside of the namespace currently mounted by the client.

For lfs rmfid, we first proceed to a fid2path resolution. As fid2path
is already fileset aware, it fails if a file or a link to a file is
outside of the subdirectory mount. So we carry on with rmfid only
for FIDs for which the file and all links do appear under the
current fileset.

This new behavior is enabled as soon as we detect a subdirectory mount
is done (either directly or imposed by a nodemap fileset). This means
the new behavior does not impact normal, whole-namespace client mount.

sanity test_421h is added to exercise this new capability.

Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
Change-Id: I47136ac0a3324b9afdd01b0f902abc37938bd361
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/49696
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: jsimmons <jsimmons@infradead.org>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/include/lustre_export.h
lustre/llite/dir.c
lustre/llite/file.c
lustre/llite/llite_internal.h
lustre/mdt/mdt_handler.c
lustre/obdclass/genops.c
lustre/tests/sanity.sh

index d3c0d3f..4887865 100644 (file)
@@ -316,6 +316,7 @@ struct obd_export {
         */
        __u64                   exp_last_xid;
        long                    *exp_used_slots;
+       struct lu_fid           exp_root_fid; /* subdir mount fid */
 };
 
 #define exp_target_data u.eu_target_data
index d749989..47762ba 100644 (file)
@@ -1339,6 +1339,7 @@ int ll_rmfid(struct file *file, void __user *arg)
 {
        const struct fid_array __user *ufa = arg;
        struct inode *inode = file_inode(file);
+       struct ll_sb_info *sbi = ll_i2sbi(inode);
        struct fid_array *lfa = NULL;
        size_t size;
        unsigned nr;
@@ -1366,8 +1367,84 @@ int ll_rmfid(struct file *file, void __user *arg)
        if (copy_from_user(lfa, arg, size))
                GOTO(free_rcs, rc = -EFAULT);
 
+       /* In case of subdirectory mount, we need to make sure all the files
+        * for which we want to remove FID are visible in the namespace.
+        */
+       if (!fid_is_root(&sbi->ll_root_fid)) {
+               struct fid_array *lfa_new = NULL;
+               int path_len = PATH_MAX, linkno;
+               struct getinfo_fid2path *gf;
+               int idx, last_idx = nr - 1;
+
+               OBD_ALLOC(lfa_new, size);
+               if (!lfa_new)
+                       GOTO(free_rcs, rc = -ENOMEM);
+               lfa_new->fa_nr = 0;
+
+               gf = kmalloc(sizeof(*gf) + path_len + 1, GFP_NOFS);
+               if (!gf)
+                       GOTO(free_rcs, rc = -ENOMEM);
+
+               for (idx = 0; idx < nr; idx++) {
+                       linkno = 0;
+                       while (1) {
+                               memset(gf, 0, sizeof(*gf) + path_len + 1);
+                               gf->gf_fid = lfa->fa_fids[idx];
+                               gf->gf_pathlen = path_len;
+                               gf->gf_linkno = linkno;
+                               rc = __ll_fid2path(inode, gf,
+                                                  sizeof(*gf) + gf->gf_pathlen,
+                                                  gf->gf_pathlen);
+                               if (rc == -ENAMETOOLONG) {
+                                       struct getinfo_fid2path *tmpgf;
+
+                                       path_len += PATH_MAX;
+                                       tmpgf = krealloc(gf,
+                                                    sizeof(*gf) + path_len + 1,
+                                                    GFP_NOFS);
+                                       if (!tmpgf) {
+                                               kfree(gf);
+                                               OBD_FREE(lfa_new, size);
+                                               GOTO(free_rcs, rc = -ENOMEM);
+                                       }
+                                       gf = tmpgf;
+                                       continue;
+                               }
+                               if (rc)
+                                       break;
+                               if (gf->gf_linkno == linkno)
+                                       break;
+                               linkno = gf->gf_linkno;
+                       }
+
+                       if (!rc) {
+                               /* All the links for this fid are visible in the
+                                * mounted subdir. So add it to the list of fids
+                                * to remove.
+                                */
+                               lfa_new->fa_fids[lfa_new->fa_nr++] =
+                                       lfa->fa_fids[idx];
+                       } else {
+                               /* At least one link for this fid is not visible
+                                * in the mounted subdir. So add it at the end
+                                * of the list that will be hidden to lower
+                                * layers, and set -ENOENT as ret code.
+                                */
+                               lfa_new->fa_fids[last_idx] = lfa->fa_fids[idx];
+                               rcs[last_idx--] = rc;
+                       }
+               }
+               kfree(gf);
+               OBD_FREE(lfa, size);
+               lfa = lfa_new;
+       }
+
+       if (lfa->fa_nr == 0)
+               GOTO(free_rcs, rc = rcs[nr - 1]);
+
        /* Call mdc_iocontrol */
        rc = md_rmfid(ll_i2mdexp(file_inode(file)), lfa, rcs, NULL);
+       lfa->fa_nr = nr;
        if (!rc) {
                for (i = 0; i < nr; i++)
                        if (rcs[i])
index 692e3e5..e25809e 100644 (file)
@@ -3041,9 +3041,37 @@ lookup:
        return rc;
 }
 
-int ll_fid2path(struct inode *inode, void __user *arg)
+int __ll_fid2path(struct inode *inode, struct getinfo_fid2path *gfout,
+                 size_t outsize, __u32 pathlen_orig)
 {
        struct obd_export *exp = ll_i2mdexp(inode);
+       int rc;
+
+       /* Append root FID after gfout to let MDT know the root FID so that
+        * it can lookup the correct path, this is mainly for fileset.
+        * old server without fileset mount support will ignore this.
+        */
+       *gfout->gf_u.gf_root_fid = *ll_inode2fid(inode);
+
+       /* Call mdc_iocontrol */
+       rc = obd_iocontrol(OBD_IOC_FID2PATH, exp, outsize, gfout, NULL);
+
+       if (!rc && gfout->gf_pathlen && gfout->gf_u.gf_path[0] == '/') {
+               /* by convention, server side (mdt_path_current()) puts
+                * a leading '/' to tell client that we are dealing with
+                * an encrypted file
+                */
+               rc = fid2path_for_enc_file(inode, gfout->gf_u.gf_path,
+                                          gfout->gf_pathlen);
+               if (!rc && strlen(gfout->gf_u.gf_path) > pathlen_orig)
+                       rc = -EOVERFLOW;
+       }
+
+       return rc;
+}
+
+int ll_fid2path(struct inode *inode, void __user *arg)
+{
        const struct getinfo_fid2path __user *gfin = arg;
        __u32 pathlen, pathlen_orig;
        struct getinfo_fid2path *gfout;
@@ -3072,30 +3100,12 @@ gf_alloc:
 
        if (copy_from_user(gfout, arg, sizeof(*gfout)))
                GOTO(gf_free, rc = -EFAULT);
-       /* append root FID after gfout to let MDT know the root FID so that it
-        * can lookup the correct path, this is mainly for fileset.
-        * old server without fileset mount support will ignore this. */
-       *gfout->gf_u.gf_root_fid = *ll_inode2fid(inode);
-       gfout->gf_pathlen = pathlen;
 
-       /* Call mdc_iocontrol */
-       rc = obd_iocontrol(OBD_IOC_FID2PATH, exp, outsize, gfout, NULL);
-       if (rc != 0)
+       gfout->gf_pathlen = pathlen;
+       rc = __ll_fid2path(inode, gfout, outsize, pathlen_orig);
+       if (rc)
                GOTO(gf_free, rc);
 
-       if (gfout->gf_pathlen && gfout->gf_u.gf_path[0] == '/') {
-               /* by convention, server side (mdt_path_current()) puts
-                * a leading '/' to tell client that we are dealing with
-                * an encrypted file
-                */
-               rc = fid2path_for_enc_file(inode, gfout->gf_u.gf_path,
-                                          gfout->gf_pathlen);
-               if (rc)
-                       GOTO(gf_free, rc);
-               if (strlen(gfout->gf_u.gf_path) > gfin->gf_pathlen)
-                       GOTO(gf_free, rc = -EOVERFLOW);
-       }
-
        if (copy_to_user(arg, gfout, sizeof(*gfout) + pathlen_orig))
                rc = -EFAULT;
 
index 60ac650..9ed03ca 100644 (file)
@@ -1252,6 +1252,8 @@ int ll_dir_getstripe(struct inode *inode, void **plmm, int *plmm_size,
 int ll_fsync(struct file *file, loff_t start, loff_t end, int data);
 int ll_merge_attr(const struct lu_env *env, struct inode *inode);
 int ll_fid2path(struct inode *inode, void __user *arg);
+int __ll_fid2path(struct inode *inode, struct getinfo_fid2path *gfout,
+                 size_t outsize, __u32 pathlen_orig);
 int ll_data_version(struct inode *inode, __u64 *data_version, int flags);
 int ll_hsm_release(struct inode *inode);
 int ll_hsm_state_set(struct inode *inode, struct hsm_state_set *hss);
index 644441e..01a3610 100644 (file)
@@ -458,6 +458,7 @@ static int mdt_get_root(struct tgt_session_info *tsi)
        } else {
                repbody->mbo_fid1 = mdt->mdt_md_root_fid;
        }
+       exp->exp_root_fid = repbody->mbo_fid1;
        repbody->mbo_valid |= OBD_MD_FLID;
 
        EXIT;
@@ -7313,6 +7314,18 @@ static int mdt_fid2path(struct mdt_thread_info *info,
                RETURN(-EINVAL);
        }
 
+       /* return error if client-provided root fid
+        * is not the one stored in the export
+        */
+       if (root_fid && !fid_is_zero(&info->mti_exp->exp_root_fid) &&
+           !lu_fid_eq(root_fid, &info->mti_exp->exp_root_fid)) {
+               CDEBUG(D_INFO,
+                      "%s: root fid from client "DFID" but "DFID" stored in export\n",
+                      mdt_obd_name(mdt), PFID(root_fid),
+                      PFID(&info->mti_exp->exp_root_fid));
+               RETURN(-EXDEV);
+       }
+
        obj = mdt_object_find(info->mti_env, mdt, &fp->gf_fid);
        if (IS_ERR(obj)) {
                rc = PTR_ERR(obj);
index abb6d3d..fb719bb 100644 (file)
@@ -1067,6 +1067,9 @@ struct obd_export *__class_new_export(struct obd_device *obd,
        obd_init_export(export);
 
        at_init(&export->exp_bl_lock_at, obd_timeout, 0);
+       export->exp_root_fid.f_seq = 0;
+       export->exp_root_fid.f_oid = 0;
+       export->exp_root_fid.f_ver = 0;
 
        spin_lock(&obd->obd_dev_lock);
        if (!obd_uuid_equals(cluuid, &obd->obd_uuid)) {
index 7f55e16..c514c24 100755 (executable)
@@ -27369,6 +27369,71 @@ test_421g() {
 }
 run_test 421g "rmfid to return errors properly"
 
+test_421h() {
+       local mount_other
+       local mount_ret
+       local rmfid_ret
+       local old_fid
+       local fidA
+       local fidB
+       local fidC
+       local fidD
+
+       (( MDS1_VERSION >= $(version_code 2.15.53) )) ||
+               skip "Need MDS version at least 2.15.53"
+
+       test_mkdir $DIR/$tdir
+       test_mkdir $DIR/$tdir/subdir
+       touch $DIR/$tdir/subdir/file0
+       old_fid=$(lfs path2fid $DIR/$tdir/subdir/file0 | sed "s/[/][^:]*://g")
+       echo File $DIR/$tdir/subdir/file0 FID $old_fid
+       rm -f $DIR/$tdir/subdir/file0
+       touch $DIR/$tdir/subdir/fileA
+       fidA=$(lfs path2fid $DIR/$tdir/subdir/fileA | sed "s/[/][^:]*://g")
+       echo File $DIR/$tdir/subdir/fileA FID $fidA
+       touch $DIR/$tdir/subdir/fileB
+       fidB=$(lfs path2fid $DIR/$tdir/subdir/fileB | sed "s/[/][^:]*://g")
+       echo File $DIR/$tdir/subdir/fileB FID $fidB
+       ln $DIR/$tdir/subdir/fileB $DIR/$tdir/subdir/fileB_hl
+       touch $DIR/$tdir/subdir/fileC
+       fidC=$(lfs path2fid $DIR/$tdir/subdir/fileC | sed "s/[/][^:]*://g")
+       echo File $DIR/$tdir/subdir/fileC FID $fidC
+       ln $DIR/$tdir/subdir/fileC $DIR/$tdir/fileC
+       touch $DIR/$tdir/fileD
+       fidD=$(lfs path2fid $DIR/$tdir/fileD | sed "s/[/][^:]*://g")
+       echo File $DIR/$tdir/fileD FID $fidD
+
+       # mount another client mount point with subdirectory mount
+       export FILESET=/$tdir/subdir
+       mount_other=${MOUNT}_other
+       mount_client $mount_other ${MOUNT_OPTS}
+       mount_ret=$?
+       export FILESET=""
+       (( mount_ret == 0 )) || error "mount $mount_other failed"
+
+       echo Removing FIDs:
+       echo $LFS rmfid $mount_other $old_fid $fidA $fidD $fidB $fidC
+       $LFS rmfid $mount_other $old_fid $fidA $fidD $fidB $fidC
+       rmfid_ret=$?
+
+       umount_client $mount_other || error "umount $mount_other failed"
+
+       (( rmfid_ret != 0 )) || error "rmfid should have failed"
+
+       # fileA should have been deleted
+       stat $DIR/$tdir/subdir/fileA && error "fileA not deleted"
+
+       # fileB should have been deleted
+       stat $DIR/$tdir/subdir/fileB && error "fileB not deleted"
+
+       # fileC should not have been deleted, fid also exists outside of fileset
+       stat $DIR/$tdir/subdir/fileC || error "fileC deleted"
+
+       # fileD should not have been deleted, it exists outside of fileset
+       stat $DIR/$tdir/fileD || error "fileD deleted"
+}
+run_test 421h "rmfid with fileset mount"
+
 test_422() {
        test_mkdir -i 0 -c 1 -p $DIR/$tdir/d1
        test_mkdir -i 0 -c 1 -p $DIR/$tdir/d2