*/
__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
{
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;
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])
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;
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;
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);
} else {
repbody->mbo_fid1 = mdt->mdt_md_root_fid;
}
+ exp->exp_root_fid = repbody->mbo_fid1;
repbody->mbo_valid |= OBD_MD_FLID;
EXIT;
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);
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)) {
}
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