From d5e405d1168dc48504d031facd5a43b036e8776e Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Wed, 30 Jun 2021 18:30:57 +0200 Subject: [PATCH] LU-14797 nodemap: map project id Add calls to nodemap_map_id() in order to map project IDs from client ID to server ID and conversely. Also extend nodemap_can_setquota() to allow setquota on project only if ID is not squashed or deny_unknown is not set. Update sanity-sec test_27a to exercise the feature. Lustre-change: https://review.whamcloud.com/44119 Lustre-commit: 0daeebcbdc4e89d59221299f2687cfd3c4f00b5b Signed-off-by: Sebastien Buisson Change-Id: Id66458550d312404b1993ead8940c3d12eaadccd Reviewed-by: Patrick Farrell Reviewed-by: Andreas Dilger Reviewed-on: https://review.whamcloud.com/45487 Tested-by: jenkins Tested-by: Maloo --- lustre/include/lustre_nodemap.h | 2 +- lustre/mdt/mdt_handler.c | 14 ++++++++------ lustre/mdt/mdt_io.c | 6 +++++- lustre/mdt/mdt_lib.c | 11 ++++++----- lustre/ofd/ofd_dev.c | 4 ++++ lustre/ofd/ofd_io.c | 6 +++++- lustre/ptlrpc/nodemap_handler.c | 30 +++++++++++++++++++++++++---- lustre/target/tgt_handler.c | 3 +++ lustre/tests/sanity-sec.sh | 42 +++++++++++++++++++++++++++++++++++++++++ lustre/utils/lfs.c | 6 +++++- 10 files changed, 105 insertions(+), 19 deletions(-) diff --git a/lustre/include/lustre_nodemap.h b/lustre/include/lustre_nodemap.h index 15ed270..17306a5 100644 --- a/lustre/include/lustre_nodemap.h +++ b/lustre/include/lustre_nodemap.h @@ -155,7 +155,7 @@ int nodemap_set_squash_gid(const char *name, gid_t gid); int nodemap_set_squash_projid(const char *name, projid_t projid); int nodemap_set_audit_mode(const char *name, bool enable_audit); int nodemap_set_forbid_encryption(const char *name, bool forbid_encryption); -bool nodemap_can_setquota(const struct lu_nodemap *nodemap); +bool nodemap_can_setquota(struct lu_nodemap *nodemap, __u32 qc_type, __u32 id); int nodemap_add_idmap(const char *name, enum nodemap_id_type id_type, const __u32 map[2]); int nodemap_del_idmap(const char *name, enum nodemap_id_type id_type, diff --git a/lustre/mdt/mdt_handler.c b/lustre/mdt/mdt_handler.c index 9f5097c..f1491e2 100644 --- a/lustre/mdt/mdt_handler.c +++ b/lustre/mdt/mdt_handler.c @@ -821,7 +821,7 @@ void mdt_pack_attr2body(struct mdt_thread_info *info, struct mdt_body *b, b->mbo_nlink = attr->la_nlink; b->mbo_valid |= OBD_MD_FLNLINK; } - if (attr->la_valid & (LA_UID|LA_GID)) { + if (attr->la_valid & (LA_UID|LA_GID|LA_PROJID)) { nodemap = nodemap_get_from_exp(exp); if (IS_ERR(nodemap)) goto out; @@ -840,8 +840,9 @@ void mdt_pack_attr2body(struct mdt_thread_info *info, struct mdt_body *b, } if (attr->la_valid & LA_PROJID) { - /* TODO, nodemap for project id */ - b->mbo_projid = attr->la_projid; + b->mbo_projid = nodemap_map_id(nodemap, NODEMAP_PROJID, + NODEMAP_FS_TO_CLIENT, + attr->la_projid); b->mbo_valid |= OBD_MD_FLPROJID; } @@ -3050,7 +3051,8 @@ static int mdt_quotactl(struct tgt_session_info *tsi) case LUSTRE_Q_SETQUOTAPOOL: case LUSTRE_Q_SETINFOPOOL: case LUSTRE_Q_SETDEFAULT_POOL: - if (!nodemap_can_setquota(nodemap)) + if (!nodemap_can_setquota(nodemap, oqctl->qc_type, + oqctl->qc_id)) GOTO(out_nodemap, rc = -EPERM); /* fallthrough */ case Q_GETINFO: @@ -3084,8 +3086,8 @@ static int mdt_quotactl(struct tgt_session_info *tsi) NODEMAP_CLIENT_TO_FS, id); break; case PRJQUOTA: - /* todo: check/map project id */ - id = oqctl->qc_id; + id = nodemap_map_id(nodemap, NODEMAP_PROJID, + NODEMAP_CLIENT_TO_FS, id); break; default: GOTO(out_nodemap, rc = -EOPNOTSUPP); diff --git a/lustre/mdt/mdt_io.c b/lustre/mdt/mdt_io.c index e46443c..6296ce0 100644 --- a/lustre/mdt/mdt_io.c +++ b/lustre/mdt/mdt_io.c @@ -748,7 +748,7 @@ int mdt_obd_commitrw(const struct lu_env *env, int cmd, struct obd_export *exp, if (cmd == OBD_BRW_WRITE) { struct lu_nodemap *nodemap; - __u32 mapped_uid, mapped_gid; + __u32 mapped_uid, mapped_gid, mapped_projid; nodemap = nodemap_get_from_exp(exp); if (IS_ERR(nodemap)) @@ -759,6 +759,9 @@ int mdt_obd_commitrw(const struct lu_env *env, int cmd, struct obd_export *exp, mapped_gid = nodemap_map_id(nodemap, NODEMAP_GID, NODEMAP_FS_TO_CLIENT, oa->o_gid); + mapped_projid = nodemap_map_id(nodemap, NODEMAP_PROJID, + NODEMAP_FS_TO_CLIENT, + oa->o_projid); if (!IS_ERR_OR_NULL(nodemap)) { /* do not bypass quota enforcement if squashed uid */ if (unlikely(mapped_uid == nodemap->nm_squash_uid)) { @@ -831,6 +834,7 @@ int mdt_obd_commitrw(const struct lu_env *env, int cmd, struct obd_export *exp, */ oa->o_uid = mapped_uid; oa->o_gid = mapped_gid; + oa->o_projid = mapped_projid; } else if (cmd == OBD_BRW_READ) { /* If oa != NULL then mdt_preprw_read updated the inode * atime and we should update the lvb so that other glimpses diff --git a/lustre/mdt/mdt_lib.c b/lustre/mdt/mdt_lib.c index 2374d4f..ed98825 100644 --- a/lustre/mdt/mdt_lib.c +++ b/lustre/mdt/mdt_lib.c @@ -1129,11 +1129,12 @@ static int mdt_setattr_unpack_rec(struct mdt_thread_info *info) if (IS_ERR(nodemap)) RETURN(PTR_ERR(nodemap)); - la->la_uid = nodemap_map_id(nodemap, NODEMAP_UID, - NODEMAP_CLIENT_TO_FS, rec->sa_uid); - la->la_gid = nodemap_map_id(nodemap, NODEMAP_GID, - NODEMAP_CLIENT_TO_FS, rec->sa_gid); - la->la_projid = rec->sa_projid; + la->la_uid = nodemap_map_id(nodemap, NODEMAP_UID, + NODEMAP_CLIENT_TO_FS, rec->sa_uid); + la->la_gid = nodemap_map_id(nodemap, NODEMAP_GID, + NODEMAP_CLIENT_TO_FS, rec->sa_gid); + la->la_projid = nodemap_map_id(nodemap, NODEMAP_PROJID, + NODEMAP_CLIENT_TO_FS, rec->sa_projid); nodemap_putref(nodemap); la->la_size = rec->sa_size; diff --git a/lustre/ofd/ofd_dev.c b/lustre/ofd/ofd_dev.c index 0f9eaa9..fa26e05 100644 --- a/lustre/ofd/ofd_dev.c +++ b/lustre/ofd/ofd_dev.c @@ -2381,6 +2381,10 @@ static int ofd_quotactl(struct tgt_session_info *tsi) id = nodemap_map_id(nodemap, NODEMAP_GID, NODEMAP_CLIENT_TO_FS, repoqc->qc_id); + else if (oqctl->qc_type == PRJQUOTA) + id = nodemap_map_id(nodemap, NODEMAP_PROJID, + NODEMAP_CLIENT_TO_FS, + repoqc->qc_id); nodemap_putref(nodemap); diff --git a/lustre/ofd/ofd_io.c b/lustre/ofd/ofd_io.c index a62970d..cf06a34 100644 --- a/lustre/ofd/ofd_io.c +++ b/lustre/ofd/ofd_io.c @@ -1457,7 +1457,7 @@ int ofd_commitrw(const struct lu_env *env, int cmd, struct obd_export *exp, if (cmd == OBD_BRW_WRITE) { struct lu_nodemap *nodemap; - __u32 mapped_uid, mapped_gid; + __u32 mapped_uid, mapped_gid, mapped_projid; nodemap = nodemap_get_from_exp(exp); if (IS_ERR(nodemap)) @@ -1468,6 +1468,9 @@ int ofd_commitrw(const struct lu_env *env, int cmd, struct obd_export *exp, mapped_gid = nodemap_map_id(nodemap, NODEMAP_GID, NODEMAP_FS_TO_CLIENT, oa->o_gid); + mapped_projid = nodemap_map_id(nodemap, NODEMAP_PROJID, + NODEMAP_FS_TO_CLIENT, + oa->o_projid); if (!IS_ERR_OR_NULL(nodemap)) { /* do not bypass quota enforcement if squashed uid */ @@ -1550,6 +1553,7 @@ int ofd_commitrw(const struct lu_env *env, int cmd, struct obd_export *exp, * server ID will be returned back to client in that case. */ oa->o_uid = mapped_uid; oa->o_gid = mapped_gid; + oa->o_projid = mapped_projid; } else if (cmd == OBD_BRW_READ) { rc = ofd_commitrw_read(env, ofd, fid, objcount, npages, lnb); diff --git a/lustre/ptlrpc/nodemap_handler.c b/lustre/ptlrpc/nodemap_handler.c index 97a7622..3383a0c 100644 --- a/lustre/ptlrpc/nodemap_handler.c +++ b/lustre/ptlrpc/nodemap_handler.c @@ -1424,14 +1424,36 @@ out: EXPORT_SYMBOL(nodemap_set_squash_projid); /** - * Returns true if this nodemap has root user access. Always returns true if - * nodemaps are not active. + * Check if nodemap allows setting quota. + * + * If nodemap is not active, always allow. + * For user and group quota, allow if the nodemap allows root access. + * For project quota, allow if project id is not squashed or deny_unknown + * is not set. * * \param nodemap nodemap to check access for + * \param qc_type quota type + * \param id client id to map + * \retval true is setquota is allowed, false otherwise */ -bool nodemap_can_setquota(const struct lu_nodemap *nodemap) +bool nodemap_can_setquota(struct lu_nodemap *nodemap, __u32 qc_type, __u32 id) { - return !nodemap_active || (nodemap && nodemap->nmf_allow_root_access); + if (!nodemap_active) + return true; + + if (!nodemap || !nodemap->nmf_allow_root_access) + return false; + + if (qc_type == PRJQUOTA) { + id = nodemap_map_id(nodemap, NODEMAP_PROJID, + NODEMAP_CLIENT_TO_FS, id); + + if (id == nodemap->nm_squash_projid && + nodemap->nmf_deny_unknown) + return false; + } + + return true; } EXPORT_SYMBOL(nodemap_can_setquota); diff --git a/lustre/target/tgt_handler.c b/lustre/target/tgt_handler.c index 071fcae..d0e1a5b 100644 --- a/lustre/target/tgt_handler.c +++ b/lustre/target/tgt_handler.c @@ -265,6 +265,9 @@ static int tgt_ost_body_unpack(struct tgt_session_info *tsi, __u32 flags) body->oa.o_gid = nodemap_map_id(nodemap, NODEMAP_GID, NODEMAP_CLIENT_TO_FS, body->oa.o_gid); + body->oa.o_projid = nodemap_map_id(nodemap, NODEMAP_PROJID, + NODEMAP_CLIENT_TO_FS, + body->oa.o_projid); nodemap_putref(nodemap); tsi->tsi_ost_body = body; diff --git a/lustre/tests/sanity-sec.sh b/lustre/tests/sanity-sec.sh index 5213d02..3c23370 100755 --- a/lustre/tests/sanity-sec.sh +++ b/lustre/tests/sanity-sec.sh @@ -949,6 +949,20 @@ test_13() { rc=$? [[ $rc != 0 ]] && error "nodemap_add failed with $rc" && return 1 + for (( i = 0; i < NODEMAP_COUNT; i++ )); do + local csum=${HOSTNAME_CHECKSUM}_${i} + + if ! do_facet mgs $LCTL nodemap_modify --name $csum \ + --property admin --value 0; then + rc=$((rc + 1)) + fi + if ! do_facet mgs $LCTL nodemap_modify --name $csum \ + --property trusted --value 0; then + rc=$((rc + 1)) + fi + done + [[ $rc != 0 ]] && error "nodemap_modify failed with $rc" && return 1 + rc=0 for ((i = 0; i < NODEMAP_COUNT; i++)); do if ! add_range ${HOSTNAME_CHECKSUM}_${i} $i; then @@ -1939,11 +1953,15 @@ run_test 26 "test transferring very large nodemap" nodemap_exercise_fileset() { local nm="$1" local loop=0 + local check_proj=true + + (( $MDS1_VERSION >= $(version_code 2.14.0) )) || check_proj=false # setup if [ "$nm" == "default" ]; then do_facet mgs $LCTL nodemap_activate 1 wait_nm_sync active + check_proj=false else nodemap_test_setup fi @@ -1964,6 +1982,23 @@ nodemap_exercise_fileset() { error "unable to add fileset info to $nm nodemap for servers" wait_nm_sync $nm fileset "nodemap.${nm}.fileset=/$subdir" + if $check_proj; then + do_facet mgs $LCTL nodemap_modify --name $nm \ + --property admin --value 1 + wait_nm_sync $nm admin_nodemap + do_facet mgs $LCTL nodemap_modify --name $nm \ + --property trusted --value 0 + wait_nm_sync $nm trusted_nodemap + do_facet mgs $LCTL nodemap_modify --name $nm \ + --property map_mode --value projid + wait_nm_sync $nm map_mode + do_facet mgs $LCTL nodemap_add_idmap --name $nm \ + --idtype projid --idmap 1:1 + do_facet mgs $LCTL nodemap_modify --name $nm \ + --property deny_unknown --value 1 + wait_nm_sync $nm deny_unknown + fi + # re-mount client zconf_umount_clients ${clients_arr[0]} $MOUNT || error "unable to umount client ${clients_arr[0]}" @@ -1977,6 +2012,13 @@ nodemap_exercise_fileset() { do_node ${clients_arr[0]} test -f $MOUNT/this_is_$subdir || error "fileset not taken into account" + if $check_proj; then + $LFS setquota -p 1 -b 10000 -B 11000 -i 0 -I 0 $MOUNT || + error "setquota -p 1 failed" + $LFS setquota -p 2 -b 10000 -B 11000 -i 0 -I 0 $MOUNT && + error "setquota -p 2 should have failed" + fi + # re-mount client with sub-subdir zconf_umount_clients ${clients_arr[0]} $MOUNT || error "unable to umount client ${clients_arr[0]}" diff --git a/lustre/utils/lfs.c b/lustre/utils/lfs.c index 19d2b0d..1369c71 100644 --- a/lustre/utils/lfs.c +++ b/lustre/utils/lfs.c @@ -7471,9 +7471,13 @@ quota_type_def: if (rc) { if (*obd_type) fprintf(stderr, - "%s setquota: cannot quotactl '%s' '%s': %s", + "%s setquota: cannot quotactl '%s' '%s': %s\n", progname, obd_type, obd_uuid2str(&qctl->obd_uuid), strerror(-rc)); + else + fprintf(stderr, + "%s setquota: quotactl failed: %s\n", + progname, strerror(-rc)); } out: free(qctl); -- 1.8.3.1