Whamcloud - gitweb
LU-15218 quota: delete unused quota ID 48/45548/14
authorHongchao Zhang <hongchao@whamcloud.com>
Fri, 21 Jan 2022 00:43:56 +0000 (08:43 +0800)
committerOleg Drokin <green@whamcloud.com>
Mon, 31 Jan 2022 01:38:20 +0000 (01:38 +0000)
Add lfs option '--delete' to delete unused quota ID.

Signed-off-by: Hongchao Zhang <hongchao@whamcloud.com>
Change-Id: I0d8e6b61dc23c7b22b6054bcced087b8dc94a277
Reviewed-on: https://review.whamcloud.com/45548
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Li Dongyang <dongyangli@ddn.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/doc/lfs-setquota.1
lustre/include/uapi/linux/lustre/lustre_user.h
lustre/llite/dir.c
lustre/mdt/mdt_handler.c
lustre/quota/lquota_disk.c
lustre/quota/lquota_internal.h
lustre/quota/qmt_handler.c
lustre/quota/qmt_lock.c
lustre/quota/qsd_writeback.c
lustre/tests/sanity-quota.sh
lustre/utils/lfs.c

index fb9eeb6..5b07f63 100644 (file)
@@ -15,7 +15,7 @@ lfs setquota \- set quota limits or grace time for users, groups or projects.
        [\fB--inode-grace\fR|\fB-i\fR \fIINODE_GRACE_TIME\fR] <\fIfilesystem\fR>
 .TP
 .BR "lfs setquota " { -u | --user | -g | --group | -p | --projid "} " \fIUID\fR|\fIGID\fR|\fIPROJID\fR
-       [\fB--default|-d\fR] <\fIfilesystem\fR>
+       [\fB--default|-D\fR] <\fIfilesystem\fR>
 .TP
 .BR "lfs setquota " { -U | --default-usr | -G | --default-grp | -P | --default-prj }
        [\fB--block-softlimit\fR|\fB-b\fR \fIBLOCK_SOFTLIMIT\fR[\fBkMGTPE\fR]]
@@ -23,6 +23,9 @@ lfs setquota \- set quota limits or grace time for users, groups or projects.
        [\fB--inode-softlimit\fR|\fB-i\fR \fIINODE_SOFTLIMIT\fR[\fBkMGTPE\fR]]
        [\fB--inode-hardlimit\fR|\fB-I\fR \fIINODE_HARDLIMIT\fR[\fBkMGTPE\fR]] <\fIfilesystem\fR>
 .TP
+.BR "lfs setquota " { -u | --user | -g | --group | -p | --projid "} " \fIUID\fR|\fIGID\fR|\fIPROJID\fR
+       [\fB--delete\fR] <\fIfilesystem\fR>
+.TP
 .SH DESCRIPTION
 .TP
 .BR "lfs setquota " {\fB-u|-g|-p\fR}
@@ -41,7 +44,10 @@ Specify block softlimit, zero means unlimited.
 Specify block hardlimit, zero means unlimited. The block hardlimit should be
 greater than block softlimit when it's being specified.
 .TP
-.BR -d|--default
+.BR --delete
+Delete the unused UID|GID|PROJID.
+.TP
+.BR -D|--default
 Set user/group/project to use the default quota limits.
 .TP
 .BR -g | --group \fIGROUPNAME\fR|\fIGID
@@ -127,7 +133,7 @@ Set default project quota limit.
 .B $ lfs setquota -u bob --block-softlimit 2G --block-hardlimit 1G /mnt/lustre
 Set quotas of user `bob': 1GB block hardlimit and 2 GB block softlimit
 .TP
-.B $ lfs setquota -u bob -d /mnt/lustre
+.B $ lfs setquota -u bob -D /mnt/lustre
 Set quotas of user `bob' to use default quota setting
 .TP
 .B $ lfs setquota -U --block-softlimit 1G --block-hardlimit 2G /mnt/lustre
@@ -142,6 +148,15 @@ Set hard block limit 1G for user 'ivan' per pool 'flash_pool'
 .TP
 .B $ lfs setquota -t -u --block-grace 1000 --pool flash_pool /mnt/lustre
 Set grace time 1000 seconds for block quotas per pool 'flash_pool'
+.TP
+.BR "lfs setquota " { -u | -g | -p "} " \fIUID\fR|\fIGID\fR|\fIPROJID\fR " " [\fB--delete\fR] " " <\fIfilesystem\fR>
+Command deletes the unused UID|GID|PROJID from Quota settings.
+.TP
+.PP
+.SH EXAMPLES
+.TP
+.B $ lfs setquota -u bob --delete /mnt/lustre
+Delete unused user 'bob'.
 .SH SEE ALSO
 .BR lfs (1),
 .BR lfs-quota(1)
index 4e3b720..b7cccc8 100644 (file)
@@ -1291,6 +1291,7 @@ static inline __u64 toqb(__kernel_size_t space)
 #define LUSTRE_Q_SETINFOPOOL   0x800012  /* set pool quota info */
 #define LUSTRE_Q_GETDEFAULT_POOL       0x800013  /* get default pool quota*/
 #define LUSTRE_Q_SETDEFAULT_POOL       0x800014  /* set default pool quota */
+#define LUSTRE_Q_DELETEQID     0x800015  /* delete quota ID */
 /* In the current Lustre implementation, the grace time is either the time
  * or the timestamp to be used after some quota ID exceeds the soft limt,
  * 48 bits should be enough, its high 16 bits can be used as quota flags.
@@ -1315,6 +1316,7 @@ static inline __u64 toqb(__kernel_size_t space)
  * and high 16 bits will contain this flag (see above comment).
  * */
 #define LQUOTA_FLAG_DEFAULT    0x0001
+#define LQUOTA_FLAG_DELETED    0x0002
 
 #define LUSTRE_Q_CMD_IS_POOL(cmd)              \
        (cmd == LUSTRE_Q_GETQUOTAPOOL ||        \
index 7aaa059..0a3b4b6 100644 (file)
@@ -1188,6 +1188,7 @@ int quotactl_ioctl(struct super_block *sb, struct if_quotactl *qctl)
        case LUSTRE_Q_SETQUOTAPOOL:
        case LUSTRE_Q_SETINFOPOOL:
        case LUSTRE_Q_SETDEFAULT_POOL:
+       case LUSTRE_Q_DELETEQID:
                if (!capable(CAP_SYS_ADMIN))
                        RETURN(-EPERM);
 
index b6de915..b2db7c2 100644 (file)
@@ -3219,6 +3219,7 @@ static int mdt_quotactl(struct tgt_session_info *tsi)
        case LUSTRE_Q_SETQUOTAPOOL:
        case LUSTRE_Q_SETINFOPOOL:
        case LUSTRE_Q_SETDEFAULT_POOL:
+       case LUSTRE_Q_DELETEQID:
                if (!nodemap_can_setquota(nodemap, oqctl->qc_type,
                                          oqctl->qc_id))
                        GOTO(out_nodemap, rc = -EPERM);
@@ -3289,6 +3290,7 @@ static int mdt_quotactl(struct tgt_session_info *tsi)
        case LUSTRE_Q_GETINFOPOOL:
        case LUSTRE_Q_SETDEFAULT_POOL:
        case LUSTRE_Q_GETDEFAULT_POOL:
+       case LUSTRE_Q_DELETEQID:
                /* forward quotactl request to QMT */
                rc = qmt_hdls.qmth_quotactl(tsi->tsi_env, qmt, oqctl);
                break;
index 4353f1a..bc71833 100644 (file)
@@ -701,6 +701,36 @@ out:
        return rc;
 }
 
+int lquota_disk_delete(const struct lu_env *env, struct thandle *th,
+                      struct dt_object *obj, __u64 qid, __u64 *ver)
+{
+       struct lquota_thread_info       *qti = lquota_info(env);
+       struct dt_key                   *key = (struct dt_key *)&qid;
+       int                              rc;
+
+       ENTRY;
+
+       LASSERT(dt_object_exists(obj));
+       LASSERT(obj->do_index_ops != NULL);
+
+       /* lock index */
+       dt_write_lock(env, obj, 0);
+
+       /* check whether there is already an existing record for this ID */
+       rc = dt_lookup(env, obj, (struct dt_rec *)&qti->qti_rec, key);
+       if (rc == 0) {
+               rc = dt_delete(env, obj, key, th);
+               if (rc == 0 && ver != NULL) {
+                       *ver = dt_version_get(env, obj);
+                       (*ver)++;
+                       dt_version_set(env, obj, *ver, th);
+               }
+       }
+
+       dt_write_unlock(env, obj);
+       RETURN(rc);
+}
+
 /*
  * Update version of an index file
  *
index c8d69d8..f10ed72 100644 (file)
@@ -186,7 +186,8 @@ struct lquota_entry {
                        lqe_gl:1,         /* glimpse is in progress */
                        lqe_nopreacq:1,   /* pre-acquire disabled */
                        lqe_is_default:1, /* the default quota is used */
-                       lqe_is_global:1;  /* lqe belongs to global pool "0x0"*/
+                       lqe_is_global:1,  /* lqe belongs to global pool "0x0"*/
+                       lqe_is_deleted:1; /* lqe will be deleted soon */
 
        struct lqe_glbl_data    *lqe_glbl_data;
 };
@@ -456,6 +457,18 @@ struct lquota_entry *lqe_locate_find(const struct lu_env *,
                                     struct lquota_site *,
                                     union lquota_id *, bool);
 
+static inline void lqe_set_deleted(struct lquota_entry *lqe)
+{
+       lqe->lqe_enforced = 0;
+       lqe->lqe_edquot = 0;
+       lqe->lqe_is_default = 0;
+       lqe->lqe_hardlimit = 0;
+       lqe->lqe_softlimit = 0;
+       lqe->lqe_gracetime = 0;
+
+       lqe->lqe_is_deleted = 1;
+}
+
 /* lquota_disk.c */
 struct dt_object *lquota_disk_dir_find_create(const struct lu_env *,
                                              struct dt_device *,
@@ -484,6 +497,8 @@ int lquota_disk_declare_write(const struct lu_env *, struct thandle *,
 int lquota_disk_write(const struct lu_env *, struct thandle *,
                      struct dt_object *, union lquota_id *, struct dt_rec *,
                      __u32, __u64 *);
+int lquota_disk_delete(const struct lu_env *env, struct thandle *th,
+                      struct dt_object *obj, __u64 qid, __u64 *ver);
 int lquota_disk_update_ver(const struct lu_env *, struct dt_device *,
                           struct dt_object *, __u64);
 int lquota_disk_write_glb(const struct lu_env *, struct dt_object *, __u64,
index c355da2..40679e0 100644 (file)
@@ -335,9 +335,74 @@ static int qmt_set(const struct lu_env *env, struct qmt_device *qmt,
        if (IS_ERR(lqe))
                        RETURN(PTR_ERR(lqe));
 
+       lqe->lqe_is_deleted = 0;
        rc = qmt_set_with_lqe(env, qmt, lqe, hard, soft, time, valid,
                              is_default, is_updated);
+       if (rc == 0)
+               lqe->lqe_is_deleted = 0;
+
+       lqe_putref(lqe);
+       RETURN(rc);
+}
+
+/*
+ * Delete the quota setting of the specified quota ID
+ *
+ * \param env        - is the environment passed by the caller
+ * \param qmt        - is the quota master target
+ * \param restype    - is the pool type, either block (i.e. LQUOTA_RES_DT) or
+ *                     inode (i.e. LQUOTA_RES_MD)
+ * \param qtype      - is the quota type
+ * \param qid        - is the quota indentifier for which we want to delete its
+ *                     quota settings.
+ */
+static int qmt_delete_qid(const struct lu_env *env, struct qmt_device *qmt,
+                         __u8 restype, __u8 qtype, __u64 qid)
+{
+       struct qmt_thread_info *qti = qmt_info(env);
+       union lquota_id *quota_id = &qti->qti_id;
+       struct thandle *th = NULL;
+       struct qmt_pool_info *qpi = NULL;
+       struct lquota_entry *lqe = NULL;
+       __u64 ver = 0;
+       int rc;
+
+       ENTRY;
+
+       quota_id->qid_uid = qid;
+       lqe = qmt_pool_lqe_lookup(env, qmt, restype, qtype, quota_id, NULL);
+       if (IS_ERR(lqe))
+               RETURN(PTR_ERR(lqe));
+
+       lqe_write_lock(lqe);
+
+       qpi = qmt_pool_lookup_glb(env, qmt, restype);
+       if (IS_ERR(qpi))
+               GOTO(out, rc = -ENOMEM);
+
+       th = qmt_trans_start(env, lqe);
+       if (IS_ERR(th))
+               GOTO(out, rc = PTR_ERR(th));
+
+       rc = lquota_disk_delete(env, th,
+                               qpi->qpi_glb_obj[qtype], qid, &ver);
+
+       dt_trans_stop(env, qmt->qmt_child, th);
+
+       if (rc == 0) {
+               lqe_set_deleted(lqe);
+               qmt_glb_lock_notify(env, lqe, ver);
+       } else if (rc == -ENOENT) {
+               rc = 0;
+       }
+
+out:
+       if (!IS_ERR_OR_NULL(qpi))
+               qpi_putref(env, qpi);
+
+       lqe_write_unlock(lqe);
        lqe_putref(lqe);
+
        RETURN(rc);
 }
 
@@ -482,6 +547,16 @@ static int qmt_quotactl(const struct lu_env *env, struct lu_device *ld,
                                     false, poolname);
                break;
 
+       case LUSTRE_Q_DELETEQID:
+               rc = qmt_delete_qid(env, qmt, LQUOTA_RES_MD, oqctl->qc_type,
+                                   oqctl->qc_id);
+               if (rc)
+                       break;
+
+               rc = qmt_delete_qid(env, qmt, LQUOTA_RES_DT, oqctl->qc_type,
+                                   oqctl->qc_id);
+               break;
+
        default:
                CERROR("%s: unsupported quotactl command: %d\n",
                       qmt->qmt_svname, oqctl->qc_cmd);
index 171d42d..38bde58 100644 (file)
@@ -793,6 +793,11 @@ void qmt_glb_lock_notify(const struct lu_env *env, struct lquota_entry *lqe,
                qti->qti_gl_desc.lquota_desc.gl_time = LQUOTA_GRACE_FLAG(0,
                                                        LQUOTA_FLAG_DEFAULT);
 
+       } else if (lqe->lqe_is_deleted) {
+               qti->qti_gl_desc.lquota_desc.gl_hardlimit = 0;
+               qti->qti_gl_desc.lquota_desc.gl_softlimit = 0;
+               qti->qti_gl_desc.lquota_desc.gl_time = LQUOTA_GRACE_FLAG(0,
+                                                       LQUOTA_FLAG_DELETED);
        } else {
                qti->qti_gl_desc.lquota_desc.gl_hardlimit = lqe->lqe_hardlimit;
                qti->qti_gl_desc.lquota_desc.gl_softlimit = lqe->lqe_softlimit;
index e32045e..58f0629 100644 (file)
@@ -292,12 +292,48 @@ static int qsd_process_upd(const struct lu_env *env, struct qsd_upd_rec *upd)
                        return 1;
        }
 
+       if (upd->qur_global &&
+           (LQUOTA_FLAG(upd->qur_rec.lqr_glb_rec.qbr_time) &
+                                                       LQUOTA_FLAG_DELETED)) {
+               struct thandle          *th = NULL;
+               struct dt_object        *obj;
+
+               obj = qqi->qqi_glb_obj;
+
+               th = dt_trans_create(env, qqi->qqi_qsd->qsd_dev);
+               if (IS_ERR(th))
+                       RETURN(PTR_ERR(th));
+
+               rc = lquota_disk_declare_write(env, th, obj, &upd->qur_qid);
+               if (rc)
+                       GOTO(out_del, rc);
+
+               rc = dt_trans_start_local(env, qqi->qqi_qsd->qsd_dev, th);
+               if (rc)
+                       GOTO(out_del, rc);
+
+               rc = lquota_disk_delete(env, th, obj, upd->qur_qid.qid_uid,
+                                       NULL);
+               if (rc == -ENOENT)
+                       rc = 0;
+
+out_del:
+               dt_trans_stop(env, qqi->qqi_qsd->qsd_dev, th);
+               if (lqe != NULL)
+                       lqe_set_deleted(lqe);
+
+               qsd_bump_version(qqi, upd->qur_ver, true);
+               RETURN(rc);
+       }
+
        if (lqe == NULL) {
                lqe = lqe_locate(env, qqi->qqi_site, &upd->qur_qid);
                if (IS_ERR(lqe))
                        GOTO(out, rc = PTR_ERR(lqe));
        }
 
+       lqe->lqe_is_deleted = 0;
+
        /* The in-memory lqe update for slave index copy isn't deferred,
         * we shouldn't touch it here. */
        if (upd->qur_global) {
index 89ba55e..80be73d 100755 (executable)
@@ -3590,6 +3590,68 @@ test_41() {
 }
 run_test 41 "df should return projid-specific values"
 
+test_delete_qid()
+{
+       local qslv_file=$1
+       local qtype_file=$2
+       local qtype=$3
+       local qid=$4
+       local osd="osd-ldiskfs"
+
+       [ "$ost1_FSTYPE" = zfs ] && osd="osd-zfs"
+
+       rm -f $DIR/$tdir/$tfile
+       $LFS setstripe -i 0 -c 1 $DIR/$tdir/$tfile
+       chmod a+rw $DIR/$tdir/$tfile
+
+       $LFS setquota $qtype $qid -B 300M $MOUNT
+       $RUNAS dd if=/dev/zero of=$DIR/$tdir/$tfile bs=1M count=1 ||
+               error "failed to dd"
+
+       do_facet $SINGLEMDS \
+               "cat /proc/fs/lustre/qmt/$FSNAME-QMT0000/dt-0x0/$qtype_file |
+                grep -E 'id: *$qid'" || error "QMT: no qid $qid is found"
+       echo $osd
+       do_facet ost1 \
+               "cat /proc/fs/lustre/$osd/$FSNAME-OST0000/$qslv_file |
+                grep -E 'id: *$qid'" || error "QSD: no qid $qid is found"
+
+       $LFS setquota $qtype $qid --delete $MOUNT
+       do_facet $SINGLEMDS \
+               "cat /proc/fs/lustre/qmt/$FSNAME-QMT0000/dt-0x0/$qtype_file |
+                grep -E 'id: *$qid'" && error "QMT: qid $qid is not deleted"
+       sleep 5
+       do_facet ost1 \
+               "cat /proc/fs/lustre/$osd/$FSNAME-OST0000/$qslv_file |
+                grep -E 'id: *$qid'" && error "QSD: qid $qid is not deleted"
+
+       $LFS setquota $qtype $qid -B 500M $MOUNT
+       $RUNAS dd if=/dev/zero of=$DIR/$tdir/$tfile bs=1M count=1 ||
+               error "failed to dd"
+       do_facet $SINGLEMDS \
+               "cat /proc/fs/lustre/qmt/$FSNAME-QMT0000/dt-0x0/$qtype_file |
+                grep -E 'id: *$qid'" || error "QMT: qid $pid is not recreated"
+       cat /proc/fs/lustre/$osd/$FSNAME-OST0000/$qslv_file
+       do_facet ost1 \
+               "cat /proc/fs/lustre/$osd/$FSNAME-OST0000/$qslv_file |
+                grep -E 'id: *$qid'" || error "QSD: qid $qid is not recreated"
+}
+
+test_48()
+{
+       setup_quota_test || error "setup quota failed with $?"
+       set_ost_qtype $QTYPE || error "enable ost quota failed"
+       quota_init
+
+       test_delete_qid "quota_slave/limit_user" "glb-usr" "-u" $TSTID
+       test_delete_qid "quota_slave/limit_group" "glb-grp" "-g" $TSTID
+       is_project_quota_supported &&
+           test_delete_qid "quota_slave/limit_project" "glb-prj" "-p" "10000"
+
+       cleanup_quota_test
+}
+run_test 48 "lfs quota --delete should delete quota project ID"
+
 test_50() {
        ! is_project_quota_supported &&
                skip "Project quota is not supported"
index b6261f6..3a1511f 100644 (file)
@@ -444,7 +444,8 @@ command_t cmdlist[] = {
         "Usage: getname [--help|-h] [--instance|-i] [--fsname|-n] [path ...]"},
 #ifdef HAVE_SYS_QUOTA_H
        {"setquota", lfs_setquota, 0, "Set filesystem quotas.\n"
-        "usage: setquota [-t][-d] {-u|-U|-g|-G|-p|-P} {-b|-B|-i|-I LIMIT} [--pool POOL] FILESYSTEM"},
+        "usage: setquota [-t][-D] {-u|-U|-g|-G|-p|-P} {-b|-B|-i|-I LIMIT} [--pool POOL] FILESYSTEM\n"
+        "       setquota {-u|-g|-p} --delete FILESYSTEM\n"},
        {"quota", lfs_quota, 0, "Display disk usage and limits.\n"
         "usage: quota [-q] [-v] [-h] [-o OBD_UUID|-i MDT_IDX|-I OST_IDX]\n"
         "             [{-u|-g|-p} UNAME|UID|GNAME|GID|PROJID]\n"
@@ -3330,6 +3331,7 @@ static void lfs_mirror_list_free(struct mirror_args *mirror_list)
 }
 
 enum {
+       LFS_SETQUOTA_DELETE = 1,
        LFS_POOL_OPT = 3,
        LFS_COMP_COUNT_OPT,
        LFS_COMP_START_OPT,
@@ -7678,6 +7680,8 @@ int lfs_setquota(int argc, char **argv)
        { .val = 'B',   .name = "block-hardlimit",
                                                .has_arg = required_argument },
        { .val = 'd',   .name = "default",      .has_arg = no_argument },
+       { .val = LFS_SETQUOTA_DELETE,
+                       .name = "delete",       .has_arg = no_argument },
        { .val = 'g',   .name = "group",        .has_arg = required_argument },
        { .val = 'G',   .name = "default-grp",  .has_arg = no_argument },
        { .val = 'h',   .name = "help",         .has_arg = no_argument },
@@ -7715,8 +7719,7 @@ int lfs_setquota(int argc, char **argv)
                                   * so it can be used as a marker that qc_type
                                   * isn't reinitialized from command line
                                   */
-
-       while ((c = getopt_long(argc, argv, "b:B:dg:Ghi:I:p:Pu:U",
+       while ((c = getopt_long(argc, argv, "b:B:dDg:Ghi:I:p:Pu:U",
                long_opts, NULL)) != -1) {
                switch (c) {
                case 'U':
@@ -7774,9 +7777,19 @@ quota_type_def:
                        }
                        qctl->qc_type = qtype;
                        break;
+#if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(3, 0, 53, 0)
                case 'd':
-                       qctl->qc_cmd = LUSTRE_Q_SETDEFAULT;
+#if LUSTRE_VERSION_CODE > OBD_OCD_VERSION(2, 15, 53, 0)
+                       fprintf(stderr, "'-d' deprecatd, use '-D' or '--default'\n");
+#endif
+                       /* falltrrough */
+#endif
+               case 'D':
                        use_default = true;
+                       qctl->qc_cmd = LUSTRE_Q_SETDEFAULT;
+                       break;
+               case LFS_SETQUOTA_DELETE:
+                       qctl->qc_cmd = LUSTRE_Q_DELETEQID;
                        break;
                case 'b':
                        ARG2ULL(dqb->dqb_bsoftlimit, optarg, 1024);
@@ -7854,7 +7867,8 @@ quota_type_def:
                goto out;
        }
 
-       if (!use_default && limit_mask == 0) {
+       if (!use_default && qctl->qc_cmd != LUSTRE_Q_DELETEQID &&
+           limit_mask == 0) {
                fprintf(stderr,
                        "%s setquota: at least one limit must be specified\n",
                        progname);
@@ -7862,9 +7876,10 @@ quota_type_def:
                goto out;
        }
 
-       if (use_default && limit_mask != 0) {
+       if ((use_default || qctl->qc_cmd == LUSTRE_Q_DELETEQID)  &&
+           limit_mask != 0) {
                fprintf(stderr,
-                       "%s setquota: limits should not be specified when using default quota\n",
+                       "%s setquota: limits should not be specified when using default quota or deleting quota ID\n",
                        progname);
                rc = CMD_HELP;
                goto out;
@@ -7878,6 +7893,14 @@ quota_type_def:
                goto out;
        }
 
+       if (qctl->qc_cmd == LUSTRE_Q_DELETEQID  && qctl->qc_id == 0) {
+               fprintf(stderr,
+                       "%s setquota: can not delete root user/group/project\n",
+                       progname);
+               rc = CMD_HELP;
+               goto out;
+       }
+
        if (optind != argc - 1) {
                fprintf(stderr,
                        "%s setquota: filesystem not specified or unexpected argument '%s'\n",