From 72fdcaa68c14dd526637f77d0355e596f79fbaaf Mon Sep 17 00:00:00 2001 From: Allan Jude Date: Sun, 19 May 2019 16:25:28 -0400 Subject: [PATCH] zfs userspace: support recursing through child datasets Recurse through child datasets and return the sum of space used by each user or group. use ZFS_ITER_ARGS_CAN_BE_PATHS so a dataset can be specified by its name or mountpoint Signed-off-by: Allan Jude --- cmd/zfs/zfs_main.c | 150 +++++++++++++++++++++++++-------------- man/man8/zfs-userspace.8 | 25 +++++-- 2 files changed, 115 insertions(+), 60 deletions(-) diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index d05cb29c69..27c1cc4535 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -358,18 +358,18 @@ get_usage(zfs_help_t idx) "\tunallow [-r] -s @setname [[,...]] " "\n")); case HELP_USERSPACE: - return (gettext("\tuserspace [-Hinp] [-o field[,...]] " - "[-s field] ...\n" + return (gettext("\tuserspace [-Hinpr] [-d depth] " + "[-o field[,...]] [-s field] ...\n" "\t [-S field] ... [-t type[,...]] " "\n")); case HELP_GROUPSPACE: - return (gettext("\tgroupspace [-Hinp] [-o field[,...]] " - "[-s field] ...\n" + return (gettext("\tgroupspace [-Hinpr] [-d depth] " + "[-o field[,...]] [-s field] ...\n" "\t [-S field] ... [-t type[,...]] " "\n")); case HELP_PROJECTSPACE: - return (gettext("\tprojectspace [-Hp] [-o field[,...]] " - "[-s field] ... \n" + return (gettext("\tprojectspace [-Hpr] [-d depth] " + "[-o field[,...]] [-s field] ... \n" "\t [-S field] ... \n")); case HELP_PROJECT: return (gettext("\tproject [-d|-r] \n" @@ -2625,13 +2625,13 @@ zfs_do_upgrade(int argc, char **argv) } /* - * zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...] + * zfs userspace [-Hinpr]] [-d depth] [-o field[,...]] [-s field [-s field]...] * [-S field [-S field]...] [-t type[,...]] * filesystem | snapshot | path - * zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...] + * zfs groupspace [-Hinpr]] [-d depth] [-o field[,...]] [-s field [-s field]...] * [-S field [-S field]...] [-t type[,...]] * filesystem | snapshot | path - * zfs projectspace [-Hp] [-o field[,...]] [-s field [-s field]...] + * zfs projectspace [-Hpr]] [-d depth] [-o field[,...]] [-s field [-s field]...] * [-S field [-S field]...] filesystem | snapshot | path * * -H Scripted mode; elide headers and separate columns by tabs. @@ -2639,6 +2639,8 @@ zfs_do_upgrade(int argc, char **argv) * -n Print numeric ID instead of user/group name. * -o Control which fields to display. * -p Use exact (parsable) numeric output. + * -r Recursive over children of the dataset as well. + * -d Limit depth of recursion * -s Specify sort columns, descending order. * -S Specify sort columns, ascending order. * -t Control which object types to display. @@ -2699,6 +2701,12 @@ typedef struct us_cbdata { size_t cb_width[USFIELD_LAST]; } us_cbdata_t; +typedef struct us_cb_recurse { + int types; + zfs_userspace_cb_t zfs_us_cb; + us_cbdata_t *cb; +} us_cb_recurse_t; + static boolean_t us_populated = B_FALSE; typedef struct { @@ -2850,6 +2858,36 @@ us_type2str(unsigned field_type) } } +static int +userspace_recurse_cb(zfs_handle_t *zhp, void *arg) +{ + us_cb_recurse_t *usrcb = (us_cb_recurse_t *)arg; + zfs_userquota_prop_t p; + int ret; + + if (zfs_get_underlying_type(zhp) != ZFS_TYPE_FILESYSTEM) { + (void) fprintf(stderr, gettext("operation is only applicable " + "to filesystems and their snapshots\n")); + return (1); + } + + for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) { + if ((zfs_prop_is_user(p) && + !(usrcb->types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) || + (zfs_prop_is_group(p) && + !(usrcb->types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))) || + (zfs_prop_is_project(p) && usrcb->types != USTYPE_PROJ)) + continue; + + usrcb->cb->cb_prop = p; + if ((ret = zfs_userspace(zhp, p, usrcb->zfs_us_cb, + usrcb->cb)) != 0) + return (ret); + } + + return (0); +} + static int userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space) { @@ -2975,34 +3013,6 @@ userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space) if (nameidx >= 0 && namelen > cb->cb_width[nameidx]) cb->cb_width[nameidx] = namelen; - /* - * Check if this type/name combination is in the list and update it; - * otherwise add new node to the list. - */ - if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) { - uu_avl_insert(avl, node, idx); - } else { - nvlist_free(props); - free(node); - node = n; - props = node->usn_nvl; - } - - /* Calculate/update width of USED/QUOTA fields */ - if (cb->cb_nicenum) { - if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED || - prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA || - prop == ZFS_PROP_PROJECTUSED || - prop == ZFS_PROP_PROJECTQUOTA) { - zfs_nicebytes(space, sizebuf, sizeof (sizebuf)); - } else { - zfs_nicenum(space, sizebuf, sizeof (sizebuf)); - } - } else { - (void) snprintf(sizebuf, sizeof (sizebuf), "%llu", - (u_longlong_t)space); - } - sizelen = strlen(sizebuf); if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_PROJECTUSED) { propname = "used"; @@ -3027,6 +3037,39 @@ userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space) } else { return (-1); } + + /* + * Check if this type/name combination is in the list and update it; + * otherwise add new node to the list. + */ + if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) { + uu_avl_insert(avl, node, idx); + } else { + uint64_t oldspace = 0; + + if (nvlist_lookup_uint64(n->usn_nvl, propname, &oldspace) == 0) + space += oldspace; + nvlist_free(props); + free(node); + node = n; + props = node->usn_nvl; + } + + /* Calculate/update width of USED/QUOTA fields */ + if (cb->cb_nicenum) { + if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED || + prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA || + prop == ZFS_PROP_PROJECTUSED || + prop == ZFS_PROP_PROJECTQUOTA) { + zfs_nicebytes(space, sizebuf, sizeof (sizebuf)); + } else { + zfs_nicenum(space, sizebuf, sizeof (sizebuf)); + } + } else { + (void) snprintf(sizebuf, sizeof (sizebuf), "%llu", + (u_longlong_t)space); + } + sizelen = strlen(sizebuf); sizeidx = us_field_index(propname); if (sizeidx >= 0 && sizelen > cb->cb_width[sizeidx]) cb->cb_width[sizeidx] = sizelen; @@ -3189,7 +3232,6 @@ static int zfs_do_userspace(int argc, char **argv) { zfs_handle_t *zhp; - zfs_userquota_prop_t p; uu_avl_pool_t *avl_pool; uu_avl_t *avl_tree; uu_avl_walk_t *walk; @@ -3199,6 +3241,8 @@ zfs_do_userspace(int argc, char **argv) char *tfield = NULL; int cfield = 0; int fields[256]; + int flags = ZFS_ITER_ARGS_CAN_BE_PATHS; + int limit = 0; int i; boolean_t scripted = B_FALSE; boolean_t prtnum = B_FALSE; @@ -3209,6 +3253,7 @@ zfs_do_userspace(int argc, char **argv) zfs_sort_column_t *sortcol = NULL; int types = USTYPE_PSX_USR | USTYPE_SMB_USR; us_cbdata_t cb; + us_cb_recurse_t usrcb; us_node_t *node; us_node_t *rmnode; uu_list_pool_t *listpool; @@ -3227,8 +3272,11 @@ zfs_do_userspace(int argc, char **argv) prtnum = B_TRUE; } - while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) { + while ((c = getopt(argc, argv, "d:nHpo:rs:S:t:i")) != -1) { switch (c) { + case 'd': + limit = parse_depth(optarg, &flags); + break; case 'n': if (types == USTYPE_PROJ) { (void) fprintf(stderr, @@ -3246,6 +3294,9 @@ zfs_do_userspace(int argc, char **argv) case 'o': ofield = optarg; break; + case 'r': + flags |= ZFS_ITER_RECURSE; + break; case 's': case 'S': if (zfs_add_sort_column(&sortcol, optarg, @@ -3358,6 +3409,10 @@ zfs_do_userspace(int argc, char **argv) (void) zfs_add_sort_column(&sortcol, "type", B_FALSE); (void) zfs_add_sort_column(&sortcol, "name", B_FALSE); + usrcb.types = types; + usrcb.zfs_us_cb = userspace_cb; + usrcb.cb = &cb; + cb.cb_sortcol = sortcol; cb.cb_numname = prtnum; cb.cb_nicenum = !parsable; @@ -3368,21 +3423,8 @@ zfs_do_userspace(int argc, char **argv) for (i = 0; i < USFIELD_LAST; i++) cb.cb_width[i] = strlen(gettext(us_field_hdr[i])); - for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) { - if ((zfs_prop_is_user(p) && - !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) || - (zfs_prop_is_group(p) && - !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))) || - (zfs_prop_is_project(p) && types != USTYPE_PROJ)) - continue; - - cb.cb_prop = p; - if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0) { - zfs_close(zhp); - return (ret); - } - } - zfs_close(zhp); + ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM, NULL, NULL, + limit, userspace_recurse_cb, &usrcb); /* Sort the list */ if ((node = uu_avl_first(avl_tree)) == NULL) diff --git a/man/man8/zfs-userspace.8 b/man/man8/zfs-userspace.8 index b7bd61b570..e23bfb6c81 100644 --- a/man/man8/zfs-userspace.8 +++ b/man/man8/zfs-userspace.8 @@ -39,7 +39,8 @@ .Sh SYNOPSIS .Nm zfs .Cm userspace -.Op Fl Hinp +.Op Fl Hinpr +.Op Fl d Ar depth .Oo Fl o Ar field Ns Oo , Ns Ar field Oc Ns … Oc .Oo Fl s Ar field Oc Ns … .Oo Fl S Ar field Oc Ns … @@ -47,7 +48,8 @@ .Ar filesystem Ns | Ns Ar snapshot Ns | Ns Ar path .Nm zfs .Cm groupspace -.Op Fl Hinp +.Op Fl Hinpr +.Op Fl d Ar depth .Oo Fl o Ar field Ns Oo , Ns Ar field Oc Ns … Oc .Oo Fl s Ar field Oc Ns … .Oo Fl S Ar field Oc Ns … @@ -55,7 +57,8 @@ .Ar filesystem Ns | Ns Ar snapshot Ns | Ns Ar path .Nm zfs .Cm projectspace -.Op Fl Hp +.Op Fl Hpr +.Op Fl d Ar depth .Oo Fl o Ar field Ns Oo , Ns Ar field Oc Ns … Oc .Oo Fl s Ar field Oc Ns … .Oo Fl S Ar field Oc Ns … @@ -66,7 +69,8 @@ .It Xo .Nm zfs .Cm userspace -.Op Fl Hinp +.Op Fl Hinpr +.Op Fl d Ar depth .Oo Fl o Ar field Ns Oo , Ns Ar field Oc Ns … Oc .Oo Fl s Ar field Oc Ns … .Oo Fl S Ar field Oc Ns … @@ -90,6 +94,10 @@ Do not print headers, use tab-delimited output. Sort by this field in reverse order. See .Fl s . +.It Fl d Ar depth +Calculate the sum of usage for the specified filesystem, snapshot, +or path and any of its children, limiting the recursion to +.Ar depth . .It Fl i Translate SID to POSIX ID. The POSIX ID may be ephemeral if no mapping exists. @@ -122,6 +130,9 @@ The default is to display all fields. Use exact .Pq parsable numeric output. +.It Fl r +Recursively calculate the sum of usage for the specified +filesystem, snapshot, or path and any of its children. .It Fl s Ar field Sort output by this field. The @@ -146,7 +157,8 @@ The default can be changed to include group types. .It Xo .Nm zfs .Cm groupspace -.Op Fl Hinp +.Op Fl Hinpr +.Op Fl d Ar depth .Oo Fl o Ar field Ns Oo , Ns Ar field Oc Ns … Oc .Oo Fl s Ar field Oc Ns … .Oo Fl S Ar field Oc Ns … @@ -162,7 +174,8 @@ except that the default types to display are .It Xo .Nm zfs .Cm projectspace -.Op Fl Hp +.Op Fl Hpr +.Op Fl d Ar depth .Oo Fl o Ar field Ns Oo , Ns Ar field Oc Ns … Oc .Oo Fl s Ar field Oc Ns … .Oo Fl S Ar field Oc Ns …