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>
int ma_lmm_size;
int ma_lmv_size;
int ma_acl_size;
+ int ma_enable_chprojid_gid;
};
/** Additional parameters for create */
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
*/
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)
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
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);
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);
/* 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;
}
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);
.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",
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) &&
}
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"