Whamcloud - gitweb
LU-18240 sec: enforce per-nodemap project quota for root 36/56436/11
authorSebastien Buisson <sbuisson@ddn.com>
Fri, 20 Sep 2024 06:41:32 +0000 (08:41 +0200)
committerOleg Drokin <green@whamcloud.com>
Sun, 2 Feb 2025 06:25:44 +0000 (06:25 +0000)
Add a new 'ignore_root_prjquota' rbac role on nodemaps, enabled by
default. When not present, project quota for root is enforced for
clients part of this nodemap.

The tricky part is to find a way to pass this nodemap-related
information to the quota enforcement code. On the data path, this is
put in the transaction handle in a field 'th_ignore_root_proj_quota'
when ofd_trans_create() is called. Then the OSD layer is able to put
it in a field 'lqi_ignore_root_proj_quota' of struct lquota_id_info,
so that it can be checked by the QSD handler.
On the metadata path, it needs one more intermediate step as the MDD
layer does not have access to the client export to retrieve the
nodemap. So a new field 'uc_rbac_ignore_root_prjquota' is added to
struct lu_ucred. It is set in ucred_set_rbac_roles(), and used in
mdd_trans_create() to put the value in the transaction handle.

Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
Change-Id: I71292fb76cbc6476b8ead83bf7a686fd1b03611e
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/56436
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Marc Vef <mvef@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
23 files changed:
lustre/doc/lctl-nodemap-modify.8
lustre/include/dt_object.h
lustre/include/lustre_nodemap.h
lustre/include/lustre_quota.h
lustre/include/md_object.h
lustre/include/uapi/linux/lustre/lustre_idl.h
lustre/lod/lod_sub_object.c
lustre/mdd/mdd_trans.c
lustre/mdt/mdt_coordinator.c
lustre/mdt/mdt_handler.c
lustre/mdt/mdt_lib.c
lustre/mdt/mdt_restripe.c
lustre/obdecho/echo_client.c
lustre/ofd/ofd_trans.c
lustre/osd-ldiskfs/osd_handler.c
lustre/osd-ldiskfs/osd_quota.c
lustre/osd-zfs/osd_object.c
lustre/osd-zfs/osd_quota.c
lustre/ptlrpc/wiretest.c
lustre/quota/qsd_handler.c
lustre/tests/sanity-quota.sh
lustre/utils/wirecheck.c
lustre/utils/wiretest.c

index 912998e..e382adc 100644 (file)
@@ -99,6 +99,8 @@ Defaults to all, which means all roles are allowed. Other possible values
 it is still possible to lock or unlock encrypted directories,
 as these operations only need read access to fscrypt metadata.
 .br
+- ignore_root_prjquota, so that project quota is not enforced for the root user.
+.br
 - quota_ops, to allow quota modifications.
 .br
 - server_upcall, to define which identity upcall to use. If set, identity upcall
index 9f7d8cd..7414c0a 100644 (file)
@@ -2034,7 +2034,9 @@ struct thandle {
        /* whether ignore quota */
                                th_ignore_quota:1,
        /* whether restart transaction */
-                               th_restart_tran:1;
+                               th_restart_tran:1,
+       /* enforce project quota for root */
+                               th_ignore_root_proj_quota:1;
 };
 
 /*
index 087019e..d6f65b4 100644 (file)
@@ -33,6 +33,7 @@ static const struct nodemap_rbac_name {
        { NODEMAP_RBAC_CHLG_OPS,        "chlg_ops"      },
        { NODEMAP_RBAC_FSCRYPT_ADMIN,   "fscrypt_admin" },
        { NODEMAP_RBAC_SERVER_UPCALL,   "server_upcall" },
+       { NODEMAP_RBAC_IGN_ROOT_PRJQUOTA,       "ignore_root_prjquota"  },
 };
 
 struct nodemap_pde {
index d112989..c69415a 100644 (file)
@@ -238,6 +238,8 @@ struct lquota_id_info {
 
        /* whether we are reporting blocks or inodes */
        bool                     lqi_is_blk;
+       /* enforce project quota for root */
+       bool                     lqi_ignore_root_proj_quota;
 };
 
 /* With the DoM, both inode quota in meta pool and block quota in data pool
index 672ee72..0800308 100644 (file)
@@ -701,6 +701,7 @@ struct lu_ucred {
        int                      uc_rbac_chlg_ops:1;
        int                      uc_rbac_fscrypt_admin:1;
        int                      uc_rbac_server_upcall:1;
+       int                      uc_rbac_ignore_root_prjquota:1;
 };
 
 struct lu_ucred *lu_ucred(const struct lu_env *env);
index 4aa3e50..d73ec61 100644 (file)
@@ -3825,13 +3825,15 @@ enum nodemap_rbac_roles {
        NODEMAP_RBAC_CHLG_OPS           = 0x00000010,
        NODEMAP_RBAC_FSCRYPT_ADMIN      = 0x00000020,
        NODEMAP_RBAC_SERVER_UPCALL      = 0x00000040,
+       NODEMAP_RBAC_IGN_ROOT_PRJQUOTA  = 0x00000080,
        NODEMAP_RBAC_NONE       = (__u32)~(NODEMAP_RBAC_FILE_PERMS      |
                                           NODEMAP_RBAC_DNE_OPS |
                                           NODEMAP_RBAC_QUOTA_OPS       |
                                           NODEMAP_RBAC_BYFID_OPS       |
                                           NODEMAP_RBAC_CHLG_OPS        |
                                           NODEMAP_RBAC_FSCRYPT_ADMIN   |
-                                          NODEMAP_RBAC_SERVER_UPCALL),
+                                          NODEMAP_RBAC_SERVER_UPCALL   |
+                                          NODEMAP_RBAC_IGN_ROOT_PRJQUOTA),
        NODEMAP_RBAC_ALL        = 0xFFFFFFFF, /* future caps ON by default */
 };
 
index 0e60e87..5abae8c 100644 (file)
@@ -57,6 +57,8 @@ struct thandle *lod_sub_get_thandle(const struct lu_env *env,
 
        tth = container_of(th, struct top_thandle, tt_super);
        tth->tt_master_sub_thandle->th_ignore_quota = th->th_ignore_quota;
+       tth->tt_master_sub_thandle->th_ignore_root_proj_quota =
+               th->th_ignore_root_proj_quota;
 
        /* local object must be mdt object, Note: during ost object
         * creation, FID is not assigned until osp_create(),
@@ -87,6 +89,7 @@ struct thandle *lod_sub_get_thandle(const struct lu_env *env,
        if (IS_ERR(sub_th))
                RETURN(sub_th);
        sub_th->th_ignore_quota = th->th_ignore_quota;
+       sub_th->th_ignore_root_proj_quota = th->th_ignore_root_proj_quota;
 
        if (tth->tt_multiple_thandle != NULL && record_update != NULL &&
            th->th_result == 0)
index 9c5e888..87c3294 100644 (file)
@@ -40,12 +40,18 @@ struct thandle *mdd_trans_create(const struct lu_env *env,
                return ERR_PTR(-EINPROGRESS);
 
        th = mdd_child_ops(mdd)->dt_trans_create(env, mdd->mdd_child);
-       if (!IS_ERR(th) && uc)
+       if (IS_ERR(th)) {
+               barrier_exit(mdd->mdd_bottom);
+               goto out;
+       }
+
+       if (uc)
                th->th_ignore_quota = !!cap_raised(uc->uc_cap, CAP_SYS_RESOURCE);
 
-       if (IS_ERR(th))
-               barrier_exit(mdd->mdd_bottom);
+       th->th_ignore_root_proj_quota =
+               uc ? uc->uc_rbac_ignore_root_prjquota : 1;
 
+out:
        return th;
 }
 
index 9102690..e9a465c 100644 (file)
@@ -1082,6 +1082,7 @@ int hsm_init_ucred(struct lu_ucred *uc)
        uc->uc_rbac_chlg_ops = 1;
        uc->uc_rbac_fscrypt_admin = 1;
        uc->uc_rbac_server_upcall = 1;
+       uc->uc_rbac_ignore_root_prjquota = 1;
 
        RETURN(0);
 }
index f3807cb..cebe87f 100644 (file)
@@ -7147,6 +7147,7 @@ static int mdt_ctxt_add_dirty_flag(struct lu_env *env,
        mdt_ucred(info)->uc_rbac_chlg_ops = 1;
        mdt_ucred(info)->uc_rbac_fscrypt_admin = 1;
        mdt_ucred(info)->uc_rbac_server_upcall = 1;
+       mdt_ucred(info)->uc_rbac_ignore_root_prjquota = 1;
        rc = mdt_add_dirty_flag(info, mfd->mfd_object, &info->mti_attr);
 
        lu_context_exit(&ses);
index 3953821..e8791f9 100644 (file)
@@ -185,6 +185,8 @@ static void ucred_set_rbac_roles(struct mdt_thread_info *info,
        uc->uc_rbac_chlg_ops = !!(rbac & NODEMAP_RBAC_CHLG_OPS);
        uc->uc_rbac_fscrypt_admin = !!(rbac & NODEMAP_RBAC_FSCRYPT_ADMIN);
        uc->uc_rbac_server_upcall = !!(rbac & NODEMAP_RBAC_SERVER_UPCALL);
+       uc->uc_rbac_ignore_root_prjquota =
+               !!(rbac & NODEMAP_RBAC_IGN_ROOT_PRJQUOTA);
 }
 
 static int new_init_ucred(struct mdt_thread_info *info, ucred_init_type_t type,
index 3c5e0f4..f1a2b4e 100644 (file)
@@ -908,6 +908,7 @@ int mdt_restriper_start(struct mdt_device *mdt)
        uc->uc_rbac_chlg_ops = 1;
        uc->uc_rbac_fscrypt_admin = 1;
        uc->uc_rbac_server_upcall = 1;
+       uc->uc_rbac_ignore_root_prjquota = 1;
 
        task = kthread_create(mdt_restriper_main, info, "mdt_restriper_%03d",
                              mdt_seq_site(mdt)->ss_node_id);
index 6bffb95..2286fd7 100644 (file)
@@ -1822,6 +1822,7 @@ static void echo_ucred_init(struct lu_env *env)
        ucred->uc_rbac_chlg_ops = 1;
        ucred->uc_rbac_fscrypt_admin = 1;
        ucred->uc_rbac_server_upcall = 1;
+       ucred->uc_rbac_ignore_root_prjquota = 1;
 }
 
 static void echo_ucred_fini(struct lu_env *env)
index b96c720..20b14a3 100644 (file)
@@ -19,6 +19,8 @@
 
 #define DEBUG_SUBSYSTEM S_FILTER
 
+#include <obd_class.h>
+#include <lustre_nodemap.h>
 #include "ofd_internal.h"
 
 /**
@@ -51,9 +53,22 @@ struct thandle *ofd_trans_create(const struct lu_env *env,
        if (IS_ERR(th))
                return th;
 
-       /* export can require sync operations */
-       if (info->fti_exp != NULL)
+       if (info->fti_exp != NULL) {
+               struct lu_nodemap *nodemap;
+
+               /* export can require sync operations */
                th->th_sync |= info->fti_exp->exp_need_sync;
+
+               nodemap = nodemap_get_from_exp(info->fti_exp);
+               if (!IS_ERR_OR_NULL(nodemap)) {
+                       th->th_ignore_root_proj_quota = !!(nodemap->nmf_rbac &
+                                               NODEMAP_RBAC_IGN_ROOT_PRJQUOTA);
+                       nodemap_putref(nodemap);
+               } else {
+                       th->th_ignore_root_proj_quota = 1;
+               }
+       }
+
        return th;
 }
 
index 1d4ff86..71281d4 100644 (file)
@@ -2946,10 +2946,12 @@ static int osd_declare_attr_qid(const struct lu_env *env,
        int rc;
        struct osd_thread_info *info = osd_oti_get(env);
        struct lquota_id_info  *qi = &info->oti_qi;
+       struct thandle *th = &oh->ot_super;
 
        qi->lqi_type = type;
        /* inode accounting */
        qi->lqi_is_blk = false;
+       qi->lqi_ignore_root_proj_quota = th->th_ignore_root_proj_quota;
 
        /* one more inode for the new id ... */
        qi->lqi_id.qid_uid = new_id;
index 31264ee..80b07eb 100644 (file)
@@ -685,6 +685,7 @@ int osd_declare_inode_qid(const struct lu_env *env, qid_t uid, qid_t gid,
 
        /* and now project quota */
        qi->lqi_id.qid_projid = projid;
+       qi->lqi_ignore_root_proj_quota = th->th_ignore_root_proj_quota;
        qi->lqi_type = PRJQUOTA;
        rcp = osd_declare_qid(env, oh, qi, obj, true, local_flags);
 
index b21e4ea..77a9f5c 100644 (file)
@@ -1181,6 +1181,8 @@ static int osd_declare_attr_set(const struct lu_env *env,
                if (!zpl_is_valid_projid(attr->la_projid))
                        GOTO(out, rc = -EINVAL);
 
+               info->oti_qi.lqi_ignore_root_proj_quota =
+                       handle->th_ignore_root_proj_quota;
                rc = qsd_transfer(env, osd_def_qsd(osd),
                                  &oh->ot_quota_trans, PRJQUOTA,
                                  obj->oo_attr.la_projid,
index c9e4645..64d9487 100644 (file)
@@ -570,6 +570,7 @@ int osd_declare_quota(const struct lu_env *env, struct osd_device *osd,
        /* for project quota */
        if (osd->od_projectused_dn) {
                qi->lqi_id.qid_projid = projid;
+               qi->lqi_ignore_root_proj_quota = th->th_ignore_root_proj_quota;
                qi->lqi_type = PRJQUOTA;
                rcp = qsd_op_begin(env, qsd, &oh->ot_quota_trans, qi,
                                   local_flags);
index f925bf9..dbff368 100644 (file)
@@ -6523,8 +6523,10 @@ void lustre_assert_wire_constants(void)
                (unsigned)NODEMAP_RBAC_FSCRYPT_ADMIN);
        LASSERTF(NODEMAP_RBAC_SERVER_UPCALL == 0x00000040UL, "found 0x%.8xUL\n",
                 (unsigned)NODEMAP_RBAC_SERVER_UPCALL);
-       LASSERTF(NODEMAP_RBAC_NONE == 0xffffff80UL, "found 0x%.8xUL\n",
-               (unsigned)NODEMAP_RBAC_NONE);
+       LASSERTF(NODEMAP_RBAC_IGN_ROOT_PRJQUOTA == 0x00000080UL, "found 0x%.8xUL\n",
+                (unsigned)NODEMAP_RBAC_IGN_ROOT_PRJQUOTA);
+       LASSERTF(NODEMAP_RBAC_NONE == 0xffffff00UL, "found 0x%.8xUL\n",
+                (unsigned)NODEMAP_RBAC_NONE);
        LASSERTF(NODEMAP_RBAC_ALL == 0xffffffffUL, "found 0x%.8xUL\n",
                (unsigned)NODEMAP_RBAC_ALL);
 
index e6c5088..3d7f82a 100644 (file)
@@ -889,7 +889,8 @@ int qsd_op_begin(const struct lu_env *env, struct qsd_instance *qsd,
            (qsd->qsd_type_array[qi->lqi_type])->qqi_acct_failed)
                RETURN(0);
 
-       if (local_flags && qi->lqi_id.qid_projid && qsd->qsd_root_prj_enable)
+       if (local_flags && qi->lqi_id.qid_projid &&
+           (qsd->qsd_root_prj_enable || !qi->lqi_ignore_root_proj_quota))
                *local_flags |= QUOTA_FL_ROOT_PRJQUOTA;
 
        LASSERTF(trans->lqt_id_cnt <= QUOTA_MAX_TRANSIDS, "id_cnt=%d\n",
index d4078fe..5079252 100755 (executable)
@@ -1226,6 +1226,9 @@ test_1j() {
        local testf="$DIR/$tdir/$tfile-0"
        local testf1="$DIR/$tdir/$tfile-1"
        local testf2="$DIR/$tdir/$tfile-2"
+       local client_ip=$(host_nids_address $HOSTNAME $NETTYPE)
+       local client_nid=$(h2nettype $client_ip)
+       local nm=test_1j
 
        (( $OST1_VERSION >= $(version_code 2.14.0.74) )) ||
                skip "need OST at least 2.14.0.74"
@@ -1292,6 +1295,39 @@ test_1j() {
        do_facet ost2 $LCTL set_param $procf=0 ||
                error "disable root quotas for project failed"
 
+       if (( $OST1_VERSION >= $(version_code 2.16.50) )); then
+               do_facet mgs $LCTL nodemap_modify --name default \
+                       --property admin --value 1
+               do_facet mgs $LCTL nodemap_modify --name default \
+                       --property trusted --value 1
+               do_facet mgs $LCTL nodemap_add $nm
+               stack_trap "do_facet mgs $LCTL nodemap_del $nm || true"
+               do_facet mgs $LCTL nodemap_add_range    \
+                       --name $nm --range $client_nid
+               do_facet mgs $LCTL nodemap_modify --name $nm \
+                       --property admin --value 1
+               do_facet mgs $LCTL nodemap_modify --name $nm \
+                       --property trusted --value 1
+               # do not set ignore_root_prjquota rbac role
+               do_facet mgs $LCTL nodemap_modify --name $nm --property rbac \
+                       --value file_perms,dne_ops,quota_ops,byfid_ops,chlg_ops
+               do_facet mgs $LCTL nodemap_activate 1
+               stack_trap "do_facet mgs $LCTL nodemap_activate 0"
+               wait_nm_sync active
+               wait_nm_sync default admin_nodemap
+               wait_nm_sync default trusted_nodemap
+               wait_nm_sync $nm admin_nodemap
+               wait_nm_sync $nm trusted_nodemap
+               wait_nm_sync $nm rbac
+
+               runas -u 0 -g 0 $DD of=$testf count=$((limit/2)) \
+                       seek=$limit oflag=direct &&
+                       quota_error "project" $TSTPRJID "root write should fail"
+
+               do_facet mgs $LCTL nodemap_activate 0
+               wait_nm_sync active
+       fi
+
        runas -u 0 -g 0 $DD of=$testf count=$limit seek=$limit oflag=direct ||
                quota_error "project" $TSTPRJID "root write to project failed"
 
index ccb1baa..4c7e572 100644 (file)
@@ -3083,6 +3083,7 @@ static void check_nodemap_key(void)
        CHECK_VALUE_X(NODEMAP_RBAC_CHLG_OPS);
        CHECK_VALUE_X(NODEMAP_RBAC_FSCRYPT_ADMIN);
        CHECK_VALUE_X(NODEMAP_RBAC_SERVER_UPCALL);
+       CHECK_VALUE_X(NODEMAP_RBAC_IGN_ROOT_PRJQUOTA);
        CHECK_VALUE_X(NODEMAP_RBAC_NONE);
        CHECK_VALUE_X(NODEMAP_RBAC_ALL);
 }
index d68ddd2..eb3e146 100644 (file)
@@ -6566,8 +6566,10 @@ void lustre_assert_wire_constants(void)
                (unsigned)NODEMAP_RBAC_FSCRYPT_ADMIN);
        LASSERTF(NODEMAP_RBAC_SERVER_UPCALL == 0x00000040UL, "found 0x%.8xUL\n",
                 (unsigned)NODEMAP_RBAC_SERVER_UPCALL);
-       LASSERTF(NODEMAP_RBAC_NONE == 0xffffff80UL, "found 0x%.8xUL\n",
-               (unsigned)NODEMAP_RBAC_NONE);
+       LASSERTF(NODEMAP_RBAC_IGN_ROOT_PRJQUOTA == 0x00000080UL, "found 0x%.8xUL\n",
+                (unsigned)NODEMAP_RBAC_IGN_ROOT_PRJQUOTA);
+       LASSERTF(NODEMAP_RBAC_NONE == 0xffffff00UL, "found 0x%.8xUL\n",
+                (unsigned)NODEMAP_RBAC_NONE);
        LASSERTF(NODEMAP_RBAC_ALL == 0xffffffffUL, "found 0x%.8xUL\n",
                (unsigned)NODEMAP_RBAC_ALL);