diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index 75c0e40b61..54063cbbf7 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -374,18 +374,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" @@ -2612,13 +2612,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. @@ -2626,6 +2626,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. @@ -2686,6 +2688,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 { @@ -2835,6 +2843,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) { @@ -2960,34 +2998,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"; @@ -3012,6 +3022,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; @@ -3173,7 +3216,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; @@ -3183,6 +3225,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; @@ -3193,6 +3237,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; @@ -3211,8 +3256,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, @@ -3230,6 +3278,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, @@ -3342,6 +3393,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; @@ -3352,21 +3407,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 4bd7bb7cbe..eef1224f8f 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 … @@ -91,6 +95,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. @@ -123,6 +131,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 @@ -147,7 +158,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 … @@ -163,7 +175,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 …