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 <allanjude@freebsd.org>
This commit is contained in:
Allan Jude 2019-05-19 16:25:28 -04:00 committed by Allan Jude
parent 345196be18
commit 72fdcaa68c
2 changed files with 115 additions and 60 deletions

View File

@ -358,18 +358,18 @@ get_usage(zfs_help_t idx)
"\tunallow [-r] -s @setname [<perm|@setname>[,...]] "
"<filesystem|volume>\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[,...]] "
"<filesystem|snapshot|path>\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[,...]] "
"<filesystem|snapshot|path>\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] ... <filesystem|snapshot|path>\n"));
case HELP_PROJECT:
return (gettext("\tproject [-d|-r] <directory|file ...>\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)

View File

@ -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