Whamcloud - gitweb
LU-12826 mdt: limit root to change project state by default 56/37056/2
authorWang Shilong <wshilong@ddn.com>
Tue, 22 Oct 2019 06:15:02 +0000 (14:15 +0800)
committerOleg Drokin <green@whamcloud.com>
Fri, 3 Jan 2020 23:43:39 +0000 (23:43 +0000)
The current project quota implementation allows users to
change the Project ID of files for which they have write
permission to any value. This is not useful if the project
quota is intended to be enforced instead of only being used
for quota accouting.

Change it so that by default only root can change the projid
of a file. Setting "mdt.*.enable_chprojid_gid" will allow
users with the specified numeric Group ID (eg. 1 = "admin") to
also change the projid of a file. Use "-1" to return the previous
behavior where all users can change the projid of their files.

Lustre-change: https://review.whamcloud.com/36544
Lustre-commit: 8fad70c0872ba13133024e4abf53a0bbee7ba1e9

Change-Id: I91c138d29f4d0b9bc607528d86893451904c9892
Signed-off-by: Wang Shilong <wshilong@ddn.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Li Xi <lixi@ddn.com>
Reviewed-by: Stephan Thiell <sthiell@stanford.edu>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Signed-off-by: Wang Shilong <wshilong@ddn.com>
Reviewed-on: https://review.whamcloud.com/37056
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
lustre/include/md_object.h
lustre/mdd/mdd_object.c
lustre/mdt/mdt_handler.c
lustre/mdt/mdt_internal.h
lustre/mdt/mdt_lproc.c
lustre/mdt/mdt_reint.c
lustre/tests/sanity-quota.sh

index fb8c67f..a5f994e 100644 (file)
@@ -134,6 +134,7 @@ struct md_attr {
        int                      ma_lmm_size;
        int                      ma_lmv_size;
        int                      ma_acl_size;
+       int                      ma_enable_chprojid_gid;
 };
 
 /** Additional parameters for create */
index 45d49fb..6343817 100644 (file)
@@ -637,6 +637,22 @@ int mdd_update_time(const struct lu_env *env, struct mdd_object *obj,
        RETURN(rc);
 }
 
+
+static bool is_project_state_change(const struct lu_attr *oattr,
+                                   struct lu_attr *la)
+{
+       if (la->la_valid & LA_PROJID &&
+           oattr->la_projid != la->la_projid)
+               return true;
+
+       if ((la->la_valid & LA_FLAGS) &&
+           (la->la_flags & LUSTRE_PROJINHERIT_FL) !=
+           (oattr->la_flags & LUSTRE_PROJINHERIT_FL))
+               return true;
+
+       return false;
+}
+
 /*
  * This gives the same functionality as the code between
  * sys_chmod and inode_setattr
@@ -646,10 +662,12 @@ int mdd_update_time(const struct lu_env *env, struct mdd_object *obj,
  */
 static int mdd_fix_attr(const struct lu_env *env, struct mdd_object *obj,
                        const struct lu_attr *oattr, struct lu_attr *la,
-                       const unsigned long flags)
+                       const struct md_attr *ma)
 {
        struct lu_ucred  *uc;
        int               rc = 0;
+       const unsigned long flags = ma->ma_attr_flags;
+
        ENTRY;
 
        if (!la->la_valid)
@@ -669,6 +687,14 @@ static int mdd_fix_attr(const struct lu_env *env, struct mdd_object *obj,
        if (uc == NULL)
                RETURN(0);
 
+       if (is_project_state_change(oattr, la)) {
+               if (!md_capable(uc, CFS_CAP_SYS_RESOURCE) &&
+                   !lustre_in_group_p(uc, ma->ma_enable_chprojid_gid) &&
+                   !(ma->ma_enable_chprojid_gid == -1 &&
+                     mdd_permission_internal(env, obj, oattr, MAY_WRITE)))
+                       RETURN(-EPERM);
+       }
+
        if (la->la_valid == LA_CTIME) {
                if (!(flags & MDS_PERM_BYPASS))
                        /* This is only for set ctime when rename's source is
@@ -1175,7 +1201,7 @@ int mdd_attr_set(const struct lu_env *env, struct md_object *obj,
                RETURN(rc);
 
        *la_copy = ma->ma_attr;
-       rc = mdd_fix_attr(env, mdd_obj, attr, la_copy, ma->ma_attr_flags);
+       rc = mdd_fix_attr(env, mdd_obj, attr, la_copy, ma);
        if (rc)
                RETURN(rc);
 
index 3693d5b..85f1ce2 100644 (file)
@@ -5325,6 +5325,7 @@ static int mdt_init0(const struct lu_env *env, struct mdt_device *m,
        m->mdt_enable_striped_dir = 1;
        m->mdt_enable_dir_migration = 1;
        m->mdt_enable_remote_dir_gid = 0;
+       m->mdt_enable_chprojid_gid = 0;
        m->mdt_enable_remote_rename = 1;
 
        atomic_set(&m->mdt_mds_mds_conns, 0);
index 4ac9674..862f136 100644 (file)
@@ -257,6 +257,8 @@ struct mdt_device {
                                   /* user with gid can create remote/striped
                                    * dir, and set default dir stripe */
        gid_t                      mdt_enable_remote_dir_gid;
+                                  /* user with this gid can change projid */
+       gid_t                      mdt_enable_chprojid_gid;
 
        /* lock for osfs and md_root */
        spinlock_t                 mdt_lock;
index acd4fd8..102b041 100644 (file)
@@ -982,6 +982,36 @@ mdt_enable_remote_rename_seq_write(struct file *file, const char __user *buffer,
 }
 LPROC_SEQ_FOPS(mdt_enable_remote_rename);
 
+static int
+mdt_enable_chprojid_gid_seq_show(struct seq_file *m, void *data)
+{
+       struct obd_device *obd = m->private;
+       struct mdt_device *mdt = mdt_dev(obd->obd_lu_dev);
+
+       seq_printf(m, "%u\n",  mdt->mdt_enable_chprojid_gid);
+
+       return 0;
+}
+
+static ssize_t
+mdt_enable_chprojid_gid_seq_write(struct file *file, const char __user *buffer,
+                                 size_t count, loff_t *off)
+{
+       struct seq_file *m = file->private_data;
+       struct obd_device *obd = m->private;
+       struct mdt_device *mdt = mdt_dev(obd->obd_lu_dev);
+       int val;
+       int rc;
+
+       rc = kstrtoint_from_user(buffer, count, 0, &val);
+       if (rc)
+               return rc;
+
+       mdt->mdt_enable_chprojid_gid = val;
+       return count;
+}
+LPROC_SEQ_FOPS(mdt_enable_chprojid_gid);
+
 LPROC_SEQ_FOPS_RO_TYPE(mdt, recovery_status);
 LPROC_SEQ_FOPS_RO_TYPE(mdt, num_exports);
 LPROC_SEQ_FOPS_RO_TYPE(mdt, target_instance);
@@ -1053,6 +1083,8 @@ static struct lprocfs_vars lprocfs_mdt_obd_vars[] = {
          .fops =       &mdt_enable_dir_migration_fops          },
        { .name =       "enable_remote_rename",
          .fops =       &mdt_enable_remote_rename_fops          },
+       { .name =       "enable_chprojid_gid",
+         .fops =       &mdt_enable_chprojid_gid_fops           },
        { .name =       "hsm_control",
          .fops =       &mdt_hsm_cdt_control_fops               },
        { .name =       "recovery_time_hard",
index 77f4a9c..f50be3d 100644 (file)
@@ -652,6 +652,7 @@ static int mdt_reint_setattr(struct mdt_thread_info *info,
        if (mdt_object_remote(mo))
                GOTO(out_put, rc = -EREMOTE);
 
+       ma->ma_enable_chprojid_gid = mdt->mdt_enable_chprojid_gid;
        /* revoke lease lock if size is going to be changed */
        if (unlikely(ma->ma_attr.la_valid & LA_SIZE &&
                     !(ma->ma_attr_flags & MDS_TRUNC_KEEP_LEASE) &&
index 50a88b3..8599387 100755 (executable)
@@ -3523,6 +3523,50 @@ test_64() {
 }
 run_test 64 "lfs project on symlink files should fail"
 
+test_66() {
+       ! is_project_quota_supported &&
+               skip "Project quota is not supported"
+       setup_quota_test || error "setup quota failed with $?"
+       stack_trap cleanup_quota_test EXIT
+       local old=$(do_facet mds1 $LCTL get_param -n \
+                   mdt.*.enable_chprojid_gid | head -1)
+       local testdir=$DIR/$tdir/foo
+
+       do_facet mds1 $LCTL set_param mdt.*.enable_chprojid_gid=0
+       stack_trap "do_facet mds1 $LCTL set_param mdt.*.enable_chprojid_gid=0" \
+               EXIT
+
+       test_mkdir -i 0 -c 1 $testdir || error "failed to mkdir"
+       chown -R $TSTID:$TSTID $testdir
+       change_project -sp $TSTPRJID $testdir
+       $RUNAS mkdir $testdir/foo || error "failed to mkdir foo"
+
+       $RUNAS lfs project -p 0 $testdir/foo &&
+               error "nonroot user should fail to set projid"
+
+       $RUNAS lfs project -C $testdir/foo &&
+               error "nonroot user should fail to clear projid"
+
+       change_project -C $testdir/foo || error "failed to clear project"
+
+       do_facet mds1 $LCTL set_param mdt.*.enable_chprojid_gid=-1
+       $RUNAS lfs project -p $TSTPRJID $testdir/foo || error \
+       "failed to set projid with normal user when enable_chprojid_gid=-1"
+
+       $RUNAS lfs project -rC $testdir/ || error \
+"failed to clear project state with normal user when enable_chprojid_gid=-1"
+
+       touch $testdir/bar || error "failed touch $testdir/bar"
+       $RUNAS lfs project -p $TSTPRJID $testdir/bar && error \
+       "normal user should not be able to set projid on root owned file"
+
+       change_project -p $TSTPRJID $testdir/bar || error \
+               "root should be able to change its own file's projid"
+
+       cleanup_quota_test
+}
+run_test 66 "nonroot user can not change project state in default"
+
 quota_fini()
 {
        do_nodes $(comma_list $(nodes_list)) "lctl set_param debug=-quota"