diff options
author | Matthew Ahrens <Matthew.Ahrens@Sun.COM> | 2009-04-18 13:41:47 -0700 |
---|---|---|
committer | Matthew Ahrens <Matthew.Ahrens@Sun.COM> | 2009-04-18 13:41:47 -0700 |
commit | 148434217c040ea38dc844384f6ba68d9b325906 (patch) | |
tree | ed04bcf57eac8c6bd1eacab471a79499bac86504 /usr/src | |
parent | eead73cfdc384282a25862c27aed73c597fc10a9 (diff) | |
download | illumos-gate-148434217c040ea38dc844384f6ba68d9b325906.tar.gz |
PSARC/2009/204 ZFS user/group quotas & space accounting
6501037 want user/group quotas on ZFS
6830813 zfs list -t all fails assertion
6827260 assertion failed in arc_read(): hdr == pbuf->b_hdr
6815592 panic: No such hold X on refcount Y from zfs_znode_move
6759986 zfs list shows temporary %clone when doing online zfs recv
Diffstat (limited to 'usr/src')
79 files changed, 4166 insertions, 1913 deletions
diff --git a/usr/src/Makefile.master b/usr/src/Makefile.master index d4840df9aa..f6e08e13b3 100644 --- a/usr/src/Makefile.master +++ b/usr/src/Makefile.master @@ -516,6 +516,9 @@ CLOBBERFILES += $(POFILE) $(POFILES) COMPILE.cpp= $(CC) -E -C $(CFLAGS) $(CPPFLAGS) XGETTEXT= /usr/bin/xgettext XGETFLAGS= -c TRANSLATION_NOTE +GNUXGETTEXT= /usr/gnu/bin/xgettext +GNUXGETFLAGS= --add-comments=TRANSLATION_NOTE --keyword=_ \ + --strict --no-location --omit-header BUILD.po= $(XGETTEXT) $(XGETFLAGS) -d $(<F) $<.i ;\ $(RM) $@ ;\ $(SED) "/^domain/d" < $(<F).po > $@ ;\ @@ -1009,8 +1012,6 @@ PKGARCHIVE=$(SRC)/../../packages/$(MACH)/nightly$(PKGARCHIVESUFFIX) # # Python and Perl executable and message catalog build rules. -# Note that Python i18n isn't supported by this rule set yet, -# as it requires a special build tool (pygettext.py). # .SUFFIXES: .pl .pm .py .pyc @@ -1027,6 +1028,9 @@ PKGARCHIVE=$(SRC)/../../packages/$(MACH)/nightly$(PKGARCHIVESUFFIX) $(PYTHON) -mpy_compile $< @[ $(<)c = $@ ] || $(MV) $(<)c $@ +.py.po: + $(GNUXGETTEXT) $(GNUXGETFLAGS) -d $(<F:%.py=%) $< ; + .pl.po .pm.po: $(XGETTEXT) $(XGETFLAGS) -d $(<F) $< ; $(RM) $@ ; diff --git a/usr/src/Targetdirs b/usr/src/Targetdirs index 80e999ca7f..2af52fc549 100644 --- a/usr/src/Targetdirs +++ b/usr/src/Targetdirs @@ -297,6 +297,9 @@ ROOT.BIN= \ /usr/lib/localedef/src \ /usr/lib/lwp \ /usr/lib/pool \ + /usr/lib/python2.4 \ + /usr/lib/python2.4/vendor-packages \ + /usr/lib/python2.4/vendor-packages/zfs \ /usr/lib/rcap \ /usr/lib/rcap/$(MACH32) \ /usr/lib/saf \ diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index 8013aa6ce4..707a25fcc8 100644 --- a/usr/src/cmd/Makefile +++ b/usr/src/cmd/Makefile @@ -327,6 +327,7 @@ COMMON_SUBDIRS= \ pwck \ pwconv \ pwd \ + pyzfs \ raidctl \ ramdiskadm \ rcap \ @@ -667,6 +668,7 @@ MSGSUBDIRS= \ ptools \ pwconv \ pwd \ + pyzfs \ raidctl \ ramdiskadm \ rcap \ diff --git a/usr/src/cmd/pyzfs/Makefile b/usr/src/cmd/pyzfs/Makefile new file mode 100644 index 0000000000..062a97b019 --- /dev/null +++ b/usr/src/cmd/pyzfs/Makefile @@ -0,0 +1,49 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.cmd + +ROOTCMDDIR= $(ROOTLIB)/zfs + +PYSRCS= pyzfs.py +PYOBJS= $(PYSRCS:%.py=%.pyc) +PYFILES= $(PYSRCS) $(PYOBJS) +POFILE= pyzfs.po + +ROOTLIBZFSFILES= $(PYFILES:%=$(ROOTLIB)/zfs/%) + +.KEEP_STATE: + +all: $(PYOBJS) + +install: all $(ROOTLIBZFSFILES) + +clean: + $(RM) $(PYOBJS) + +$(ROOTLIB)/zfs/%: % + $(INS.pyfile) + +include ../Makefile.targ diff --git a/usr/src/cmd/pyzfs/pyzfs.py b/usr/src/cmd/pyzfs/pyzfs.py new file mode 100644 index 0000000000..3867d91ccd --- /dev/null +++ b/usr/src/cmd/pyzfs/pyzfs.py @@ -0,0 +1,79 @@ +#! /usr/bin/python2.4 -S +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# Note, we want SIGINT (control-c) to exit the process quietly, to mimic +# the standard behavior of C programs. The best we can do with pure +# Python is to run with -S (to disable "import site"), and start our +# program with a "try" statement. Hopefully nobody hits ^C before our +# try statement is executed. + +try: + import site + import gettext + import zfs.util + import zfs.ioctl + import sys + import errno + + """This is the main script for doing zfs subcommands. It doesn't know + what subcommands there are, it just looks for a module zfs.<subcommand> + that implements that subcommand.""" + + _ = gettext.translation("SUNW_OST_OSCMD", "/usr/lib/locale", + fallback=True).gettext + + if len(sys.argv) < 2: + sys.exit(_("missing subcommand argument")) + + zfs.ioctl.set_cmdstr(" ".join(["zfs"] + sys.argv[1:])) + + try: + # import zfs.<subcommand> + # subfunc = zfs.<subcommand>.do_<subcommand> + + subcmd = sys.argv[1] + __import__("zfs." + subcmd) + submod = getattr(zfs, subcmd) + subfunc = getattr(submod, "do_" + subcmd) + except (ImportError, AttributeError): + sys.exit(_("invalid subcommand")) + + try: + subfunc() + except zfs.util.ZFSError, e: + print(e) + sys.exit(1) + +except IOError, e: + import errno + import sys + + if e.errno == errno.EPIPE: + sys.exit(1) + raise +except KeyboardInterrupt: + import sys + + sys.exit(1) diff --git a/usr/src/cmd/truss/codes.c b/usr/src/cmd/truss/codes.c index d63248f219..580c507218 100644 --- a/usr/src/cmd/truss/codes.c +++ b/usr/src/cmd/truss/codes.c @@ -1083,6 +1083,12 @@ const struct ioc { "zfs_cmd_t" }, { (uint_t)ZFS_IOC_SMB_ACL, "ZFS_IOC_SMB_ACL", "zfs_cmd_t" }, + { (uint_t)ZFS_IOC_USERSPACE_ONE, "ZFS_IOC_USERSPACE_ONE", + "zfs_cmd_t" }, + { (uint_t)ZFS_IOC_USERSPACE_MANY, "ZFS_IOC_USERSPACE_MANY", + "zfs_cmd_t" }, + { (uint_t)ZFS_IOC_USERSPACE_UPGRADE, "ZFS_IOC_USERSPACE_UPGRADE", + "zfs_cmd_t" }, /* kssl ioctls */ { (uint_t)KSSL_ADD_ENTRY, "KSSL_ADD_ENTRY", diff --git a/usr/src/cmd/zdb/zdb.c b/usr/src/cmd/zdb/zdb.c index c4a3747fdf..afbe53bac2 100644 --- a/usr/src/cmd/zdb/zdb.c +++ b/usr/src/cmd/zdb/zdb.c @@ -1026,6 +1026,8 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES] = { dump_packed_nvlist, /* FUID nvlist size */ dump_zap, /* DSL dataset next clones */ dump_zap, /* DSL scrub queue */ + dump_zap, /* ZFS user/group used */ + dump_zap, /* ZFS user/group quota */ }; static void @@ -1089,6 +1091,14 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header) } if (verbosity >= 4) { + (void) printf("\tdnode flags: %s%s\n", + (dn->dn_phys->dn_flags & DNODE_FLAG_USED_BYTES) ? + "USED_BYTES " : "", + (dn->dn_phys->dn_flags & DNODE_FLAG_USERUSED_ACCOUNTED) ? + "USERUSED_ACCOUNTED " : ""); + (void) printf("\tdnode maxblkid: %llu\n", + (longlong_t)dn->dn_phys->dn_maxblkid); + object_viewer[doi.doi_bonus_type](os, object, bonus, bsize); object_viewer[doi.doi_type](os, object, NULL, 0); *print_header = 1; @@ -1143,7 +1153,7 @@ dump_dir(objset_t *os) uint64_t object, object_count; uint64_t refdbytes, usedobjs, scratch; char numbuf[8]; - char blkbuf[BP_SPRINTF_LEN]; + char blkbuf[BP_SPRINTF_LEN + 20]; char osname[MAXNAMELEN]; char *type = "UNKNOWN"; int verbosity = dump_opt['d']; @@ -1169,8 +1179,8 @@ dump_dir(objset_t *os) nicenum(refdbytes, numbuf); if (verbosity >= 4) { - (void) strcpy(blkbuf, ", rootbp "); - sprintf_blkptr(blkbuf + strlen(blkbuf), + (void) sprintf(blkbuf + strlen(blkbuf), ", rootbp "); + (void) sprintf_blkptr(blkbuf + strlen(blkbuf), BP_SPRINTF_LEN - strlen(blkbuf), os->os->os_rootbp); } else { blkbuf[0] = '\0'; @@ -1205,7 +1215,12 @@ dump_dir(objset_t *os) } dump_object(os, 0, verbosity, &print_header); - object_count = 1; + object_count = 0; + if (os->os->os_userused_dnode && + os->os->os_userused_dnode->dn_type != 0) { + dump_object(os, DMU_USERUSED_OBJECT, verbosity, &print_header); + dump_object(os, DMU_GROUPUSED_OBJECT, verbosity, &print_header); + } object = 0; while ((error = dmu_object_next(os, &object, B_FALSE, 0)) == 0) { diff --git a/usr/src/cmd/zfs/Makefile b/usr/src/cmd/zfs/Makefile index ae681c6561..3bd8a95242 100644 --- a/usr/src/cmd/zfs/Makefile +++ b/usr/src/cmd/zfs/Makefile @@ -19,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# PROG= zfs OBJS= zfs_main.o zfs_iter.o @@ -38,7 +36,7 @@ LINKPROGS= mount umount ROOTETCFSTYPE= $(ROOTETC)/fs/$(FSTYPE) USRLIBFSTYPE= $(ROOTLIB)/fs/$(FSTYPE) -LDLIBS += -lzfs -luutil -lumem -lnvpair -lavl +LDLIBS += -lzfs -luutil -lumem -lnvpair C99MODE= -xc99=%all C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/zfs/zfs_iter.c b/usr/src/cmd/zfs/zfs_iter.c index f0bc01e9de..ca5c2b2327 100644 --- a/usr/src/cmd/zfs/zfs_iter.c +++ b/usr/src/cmd/zfs/zfs_iter.c @@ -368,7 +368,10 @@ zfs_for_each(int argc, char **argv, int flags, zfs_type_t types, * properties other than those listed in cb_proplist/sortcol are * accessed. * - * If cb_proplist is NULL then we retain all the properties. + * If cb_proplist is NULL then we retain all the properties. We + * always retain the zoned property, which some other properties + * need (userquota & friends), and the createtxg property, which + * we need to sort snapshots. */ if (cb.cb_proplist && *cb.cb_proplist) { zprop_list_t *p = *cb.cb_proplist; @@ -388,6 +391,9 @@ zfs_for_each(int argc, char **argv, int flags, zfs_type_t types, } sortcol = sortcol->sc_next; } + + cb.cb_props_table[ZFS_PROP_ZONED] = B_TRUE; + cb.cb_props_table[ZFS_PROP_CREATETXG] = B_TRUE; } else { (void) memset(cb.cb_props_table, B_TRUE, sizeof (cb.cb_props_table)); diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c index 446ad454c7..9dac934a9d 100644 --- a/usr/src/cmd/zfs/zfs_main.c +++ b/usr/src/cmd/zfs/zfs_main.c @@ -39,12 +39,14 @@ #include <unistd.h> #include <fcntl.h> #include <zone.h> +#include <grp.h> +#include <pwd.h> #include <sys/mkdev.h> #include <sys/mntent.h> #include <sys/mnttab.h> #include <sys/mount.h> #include <sys/stat.h> -#include <sys/avl.h> +#include <sys/fs/zfs.h> #include <libzfs.h> #include <libuutil.h> @@ -56,6 +58,7 @@ libzfs_handle_t *g_zfs; static FILE *mnttab_file; static char history_str[HIS_MAX_RECORD_LEN]; +const char *pypath = "/usr/lib/zfs/pyzfs.py"; static int zfs_do_clone(int argc, char **argv); static int zfs_do_create(int argc, char **argv); @@ -75,8 +78,8 @@ static int zfs_do_unshare(int argc, char **argv); static int zfs_do_send(int argc, char **argv); static int zfs_do_receive(int argc, char **argv); static int zfs_do_promote(int argc, char **argv); -static int zfs_do_allow(int argc, char **argv); -static int zfs_do_unallow(int argc, char **argv); +static int zfs_do_userspace(int argc, char **argv); +static int zfs_do_python(int argc, char **argv); /* * Enable a reasonable set of defaults for libumem debugging on DEBUG builds. @@ -116,7 +119,9 @@ typedef enum { HELP_UNMOUNT, HELP_UNSHARE, HELP_ALLOW, - HELP_UNALLOW + HELP_UNALLOW, + HELP_USERSPACE, + HELP_GROUPSPACE } zfs_help_t; typedef struct zfs_command { @@ -150,6 +155,8 @@ static zfs_command_t command_table[] = { { "get", zfs_do_get, HELP_GET }, { "inherit", zfs_do_inherit, HELP_INHERIT }, { "upgrade", zfs_do_upgrade, HELP_UPGRADE }, + { "userspace", zfs_do_userspace, HELP_USERSPACE }, + { "groupspace", zfs_do_userspace, HELP_GROUPSPACE }, { NULL }, { "mount", zfs_do_mount, HELP_MOUNT }, { "unmount", zfs_do_unmount, HELP_UNMOUNT }, @@ -159,9 +166,9 @@ static zfs_command_t command_table[] = { { "send", zfs_do_send, HELP_SEND }, { "receive", zfs_do_receive, HELP_RECEIVE }, { NULL }, - { "allow", zfs_do_allow, HELP_ALLOW }, + { "allow", zfs_do_python, HELP_ALLOW }, { NULL }, - { "unallow", zfs_do_unallow, HELP_UNALLOW }, + { "unallow", zfs_do_python, HELP_UNALLOW }, }; #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) @@ -250,6 +257,14 @@ get_usage(zfs_help_t idx) "<filesystem|volume>\n" "\tunallow [-r] -s @setname [<perm|@setname>[,...]] " "<filesystem|volume>\n")); + case HELP_USERSPACE: + return (gettext("\tuserspace [-hniHp] [-o field[,...]] " + "[-sS field] ... [-t type[,...]]\n" + "\t <filesystem|snapshot>\n")); + case HELP_GROUPSPACE: + return (gettext("\tgroupspace [-hniHpU] [-o field[,...]] " + "[-sS field] ... [-t type[,...]]\n" + "\t <filesystem|snapshot>\n")); } abort(); @@ -311,7 +326,6 @@ usage(boolean_t requested) { int i; boolean_t show_properties = B_FALSE; - boolean_t show_permissions = B_FALSE; FILE *fp = requested ? stdout : stderr; if (current_command == NULL) { @@ -342,13 +356,7 @@ usage(boolean_t requested) strcmp(current_command->name, "list") == 0)) show_properties = B_TRUE; - if (current_command != NULL && - (strcmp(current_command->name, "allow") == 0 || - strcmp(current_command->name, "unallow") == 0)) - show_permissions = B_TRUE; - if (show_properties) { - (void) fprintf(fp, gettext("\nThe following properties are supported:\n")); @@ -359,16 +367,26 @@ usage(boolean_t requested) (void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE, ZFS_TYPE_DATASET); + (void) fprintf(fp, "\t%-15s ", "userused@..."); + (void) fprintf(fp, " NO NO <size>\n"); + (void) fprintf(fp, "\t%-15s ", "groupused@..."); + (void) fprintf(fp, " NO NO <size>\n"); + (void) fprintf(fp, "\t%-15s ", "userquota@..."); + (void) fprintf(fp, "YES NO <size> | none\n"); + (void) fprintf(fp, "\t%-15s ", "groupquota@..."); + (void) fprintf(fp, "YES NO <size> | none\n"); + (void) fprintf(fp, gettext("\nSizes are specified in bytes " "with standard units such as K, M, G, etc.\n")); (void) fprintf(fp, gettext("\nUser-defined properties can " "be specified by using a name containing a colon (:).\n")); - - } else if (show_permissions) { - (void) fprintf(fp, - gettext("\nThe following permissions are supported:\n")); - - zfs_deleg_permissions(); + (void) fprintf(fp, gettext("\nThe {user|group}{used|quota}@ " + "properties must be appended with\n" + "a user or group specifier of one of these forms:\n" + " POSIX name (eg: \"matt\")\n" + " POSIX id (eg: \"126829\")\n" + " SMB name@domain (eg: \"matt@sun\")\n" + " SMB SID (eg: \"S-1-234-567-89\")\n")); } else { (void) fprintf(fp, gettext("\nFor the property list, run: %s\n"), @@ -1084,6 +1102,17 @@ get_callback(zfs_handle_t *zhp, void *data) zprop_print_one_property(zfs_get_name(zhp), cbp, zfs_prop_to_name(pl->pl_prop), buf, sourcetype, source); + } else if (zfs_prop_userquota(pl->pl_user_prop)) { + sourcetype = ZPROP_SRC_LOCAL; + + if (zfs_prop_get_userquota(zhp, pl->pl_user_prop, + buf, sizeof (buf), cbp->cb_literal) != 0) { + sourcetype = ZPROP_SRC_NONE; + (void) strlcpy(buf, "-", sizeof (buf)); + } + + zprop_print_one_property(zfs_get_name(zhp), cbp, + pl->pl_user_prop, buf, sourcetype, source); } else { if (nvlist_lookup_nvlist(userprop, pl->pl_user_prop, &propval) != 0) { @@ -1460,21 +1489,30 @@ upgrade_set_callback(zfs_handle_t *zhp, void *data) { upgrade_cbdata_t *cb = data; int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); - - if (cb->cb_version >= ZPL_VERSION_FUID) { - int spa_version; - - if (zfs_spa_version(zhp, &spa_version) < 0) - return (-1); - - if (spa_version < SPA_VERSION_FUID) { - /* can't upgrade */ - (void) printf(gettext("%s: can not be upgraded; " - "the pool version needs to first be upgraded\nto " - "version %d\n\n"), - zfs_get_name(zhp), SPA_VERSION_FUID); - cb->cb_numfailed++; - return (0); + int i; + static struct { int zplver; int spaver; } table[] = { + {ZPL_VERSION_FUID, SPA_VERSION_FUID}, + {ZPL_VERSION_USERSPACE, SPA_VERSION_USERSPACE}, + {0, 0} + }; + + + for (i = 0; table[i].zplver; i++) { + if (cb->cb_version >= table[i].zplver) { + int spa_version; + + if (zfs_spa_version(zhp, &spa_version) < 0) + return (-1); + + if (spa_version < table[i].spaver) { + /* can't upgrade */ + (void) printf(gettext("%s: can not be " + "upgraded; the pool version needs to first " + "be upgraded\nto version %d\n\n"), + zfs_get_name(zhp), table[i].spaver); + cb->cb_numfailed++; + return (0); + } } } @@ -1575,6 +1613,8 @@ zfs_do_upgrade(int argc, char **argv) (void) printf(gettext(" 2 Enhanced directory entries\n")); (void) printf(gettext(" 3 Case insensitive and File system " "unique identifer (FUID)\n")); + (void) printf(gettext(" 4 userquota, groupquota " + "properties\n")); (void) printf(gettext("\nFor more information on a particular " "version, including supported releases, see:\n\n")); (void) printf("http://www.opensolaris.org/os/community/zfs/" @@ -1623,6 +1663,81 @@ zfs_do_upgrade(int argc, char **argv) } /* + * zfs userspace + */ +static void +userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space) +{ + zfs_userquota_prop_t *typep = arg; + zfs_userquota_prop_t p = *typep; + char *name, *ug, *propname; + char namebuf[32]; + char sizebuf[32]; + + if (domain == NULL || domain[0] == '\0') { + if (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) { + struct group *g = getgrgid(rid); + if (g) + name = g->gr_name; + } else { + struct passwd *p = getpwuid(rid); + if (p) + name = p->pw_name; + } + } + + if (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) + ug = "group"; + else + ug = "user"; + + if (p == ZFS_PROP_USERUSED || p == ZFS_PROP_GROUPUSED) + propname = "used"; + else + propname = "quota"; + + if (!name) { + (void) snprintf(namebuf, sizeof (namebuf), + "%llu", (longlong_t)rid); + name = namebuf; + } + zfs_nicenum(space, sizebuf, sizeof (sizebuf)); + + (void) printf("%s %s %s%c%s %s\n", propname, ug, domain, + domain[0] ? '-' : ' ', name, sizebuf); +} + +static int +zfs_do_userspace(int argc, char **argv) +{ + zfs_handle_t *zhp; + zfs_userquota_prop_t p; + int error; + + /* + * Try the python version. If the execv fails, we'll continue + * and do a simplistic implementation. + */ + (void) execv(pypath, argv-1); + + (void) printf("internal error: %s not found\n" + "falling back on built-in implementation, " + "some features will not work\n", pypath); + + if ((zhp = zfs_open(g_zfs, argv[argc-1], ZFS_TYPE_DATASET)) == NULL) + return (1); + + (void) printf("PROP TYPE NAME VALUE\n"); + + for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) { + error = zfs_userspace(zhp, p, userspace_cb, &p); + if (error) + break; + } + return (error); +} + +/* * list [-r][-d max] [-H] [-o property[,property]...] [-t type[,type]...] * [-s property [-s property]...] [-S property [-S property]...] * <dataset> ... @@ -1711,7 +1826,6 @@ print_dataset(zfs_handle_t *zhp, zprop_list_t *pl, boolean_t scripted) first = B_FALSE; } - right_justify = B_FALSE; if (pl->pl_prop != ZPROP_INVAL) { if (zfs_prop_get(zhp, pl->pl_prop, property, sizeof (property), NULL, NULL, 0, B_FALSE) != 0) @@ -1720,6 +1834,13 @@ print_dataset(zfs_handle_t *zhp, zprop_list_t *pl, boolean_t scripted) propstr = property; right_justify = zfs_prop_align_right(pl->pl_prop); + } else if (zfs_prop_userquota(pl->pl_user_prop)) { + if (zfs_prop_get_userquota(zhp, pl->pl_user_prop, + property, sizeof (property), B_FALSE) != 0) + propstr = "-"; + else + propstr = property; + right_justify = B_TRUE; } else { if (nvlist_lookup_nvlist(userprops, pl->pl_user_prop, &propval) != 0) @@ -1727,6 +1848,7 @@ print_dataset(zfs_handle_t *zhp, zprop_list_t *pl, boolean_t scripted) else verify(nvlist_lookup_string(propval, ZPROP_VALUE, &propstr) == 0); + right_justify = B_FALSE; } width = pl->pl_width; @@ -2525,390 +2647,6 @@ zfs_do_receive(int argc, char **argv) return (err != 0); } -typedef struct allow_cb { - int a_permcnt; - size_t a_treeoffset; -} allow_cb_t; - -static void -zfs_print_perms(avl_tree_t *tree) -{ - zfs_perm_node_t *permnode; - - permnode = avl_first(tree); - while (permnode != NULL) { - (void) printf("%s", permnode->z_pname); - permnode = AVL_NEXT(tree, permnode); - if (permnode) - (void) printf(","); - else - (void) printf("\n"); - } -} - -/* - * Iterate over user/groups/everyone/... and the call perm_iter - * function to print actual permission when tree has >0 nodes. - */ -static void -zfs_iter_perms(avl_tree_t *tree, const char *banner, allow_cb_t *cb) -{ - zfs_allow_node_t *item; - avl_tree_t *ptree; - - item = avl_first(tree); - while (item) { - ptree = (void *)((char *)item + cb->a_treeoffset); - if (avl_numnodes(ptree)) { - if (cb->a_permcnt++ == 0) - (void) printf("%s\n", banner); - (void) printf("\t%s", item->z_key); - /* - * Avoid an extra space being printed - * for "everyone" which is keyed with a null - * string - */ - if (item->z_key[0] != '\0') - (void) printf(" "); - zfs_print_perms(ptree); - } - item = AVL_NEXT(tree, item); - } -} - -#define LINES "-------------------------------------------------------------\n" -static int -zfs_print_allows(char *ds) -{ - zfs_allow_t *curperms, *perms; - zfs_handle_t *zhp; - allow_cb_t allowcb = { 0 }; - char banner[MAXPATHLEN]; - - if (ds[0] == '-') - usage(B_FALSE); - - if (strrchr(ds, '@')) { - (void) fprintf(stderr, gettext("Snapshots don't have 'allow'" - " permissions\n")); - return (1); - } - if ((zhp = zfs_open(g_zfs, ds, ZFS_TYPE_DATASET)) == NULL) - return (1); - - if (zfs_perm_get(zhp, &perms)) { - (void) fprintf(stderr, - gettext("Failed to retrieve 'allows' on %s\n"), ds); - zfs_close(zhp); - return (1); - } - - zfs_close(zhp); - - if (perms != NULL) - (void) printf("%s", LINES); - for (curperms = perms; curperms; curperms = curperms->z_next) { - - (void) snprintf(banner, sizeof (banner), - gettext("Permission sets on (%s)"), curperms->z_setpoint); - allowcb.a_treeoffset = - offsetof(zfs_allow_node_t, z_localdescend); - allowcb.a_permcnt = 0; - zfs_iter_perms(&curperms->z_sets, banner, &allowcb); - - (void) snprintf(banner, sizeof (banner), - gettext("Create time permissions on (%s)"), - curperms->z_setpoint); - allowcb.a_treeoffset = - offsetof(zfs_allow_node_t, z_localdescend); - allowcb.a_permcnt = 0; - zfs_iter_perms(&curperms->z_crperms, banner, &allowcb); - - - (void) snprintf(banner, sizeof (banner), - gettext("Local permissions on (%s)"), curperms->z_setpoint); - allowcb.a_treeoffset = offsetof(zfs_allow_node_t, z_local); - allowcb.a_permcnt = 0; - zfs_iter_perms(&curperms->z_user, banner, &allowcb); - zfs_iter_perms(&curperms->z_group, banner, &allowcb); - zfs_iter_perms(&curperms->z_everyone, banner, &allowcb); - - (void) snprintf(banner, sizeof (banner), - gettext("Descendent permissions on (%s)"), - curperms->z_setpoint); - allowcb.a_treeoffset = offsetof(zfs_allow_node_t, z_descend); - allowcb.a_permcnt = 0; - zfs_iter_perms(&curperms->z_user, banner, &allowcb); - zfs_iter_perms(&curperms->z_group, banner, &allowcb); - zfs_iter_perms(&curperms->z_everyone, banner, &allowcb); - - (void) snprintf(banner, sizeof (banner), - gettext("Local+Descendent permissions on (%s)"), - curperms->z_setpoint); - allowcb.a_treeoffset = - offsetof(zfs_allow_node_t, z_localdescend); - allowcb.a_permcnt = 0; - zfs_iter_perms(&curperms->z_user, banner, &allowcb); - zfs_iter_perms(&curperms->z_group, banner, &allowcb); - zfs_iter_perms(&curperms->z_everyone, banner, &allowcb); - - (void) printf("%s", LINES); - } - zfs_free_allows(perms); - return (0); -} - -#define ALLOWOPTIONS "ldcsu:g:e" -#define UNALLOWOPTIONS "ldcsu:g:er" - -/* - * Validate options, and build necessary datastructure to display/remove/add - * permissions. - * Returns 0 - If permissions should be added/removed - * Returns 1 - If permissions should be displayed. - * Returns -1 - on failure - */ -int -parse_allow_args(int *argc, char **argv[], boolean_t unallow, - char **ds, int *recurse, nvlist_t **zperms) -{ - int c; - char *options = unallow ? UNALLOWOPTIONS : ALLOWOPTIONS; - zfs_deleg_inherit_t deleg_type = ZFS_DELEG_NONE; - zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN; - char *who = NULL; - char *perms = NULL; - zfs_handle_t *zhp; - - while ((c = getopt(*argc, *argv, options)) != -1) { - switch (c) { - case 'l': - if (who_type == ZFS_DELEG_CREATE || - who_type == ZFS_DELEG_NAMED_SET) - usage(B_FALSE); - - deleg_type |= ZFS_DELEG_PERM_LOCAL; - break; - case 'd': - if (who_type == ZFS_DELEG_CREATE || - who_type == ZFS_DELEG_NAMED_SET) - usage(B_FALSE); - - deleg_type |= ZFS_DELEG_PERM_DESCENDENT; - break; - case 'r': - *recurse = B_TRUE; - break; - case 'c': - if (who_type != ZFS_DELEG_WHO_UNKNOWN) - usage(B_FALSE); - if (deleg_type) - usage(B_FALSE); - who_type = ZFS_DELEG_CREATE; - break; - case 's': - if (who_type != ZFS_DELEG_WHO_UNKNOWN) - usage(B_FALSE); - if (deleg_type) - usage(B_FALSE); - who_type = ZFS_DELEG_NAMED_SET; - break; - case 'u': - if (who_type != ZFS_DELEG_WHO_UNKNOWN) - usage(B_FALSE); - who_type = ZFS_DELEG_USER; - who = optarg; - break; - case 'g': - if (who_type != ZFS_DELEG_WHO_UNKNOWN) - usage(B_FALSE); - who_type = ZFS_DELEG_GROUP; - who = optarg; - break; - case 'e': - if (who_type != ZFS_DELEG_WHO_UNKNOWN) - usage(B_FALSE); - who_type = ZFS_DELEG_EVERYONE; - break; - default: - usage(B_FALSE); - break; - } - } - - if (deleg_type == 0) - deleg_type = ZFS_DELEG_PERM_LOCALDESCENDENT; - - *argc -= optind; - *argv += optind; - - if (unallow == B_FALSE && *argc == 1) { - /* - * Only print permissions if no options were processed - */ - if (optind == 1) - return (1); - else - usage(B_FALSE); - } - - /* - * initialize variables for zfs_build_perms based on number - * of arguments. - * 3 arguments ==> zfs [un]allow joe perm,perm,perm <dataset> or - * zfs [un]allow -s @set1 perm,perm <dataset> - * 2 arguments ==> zfs [un]allow -c perm,perm <dataset> or - * zfs [un]allow -u|-g <name> perm <dataset> or - * zfs [un]allow -e perm,perm <dataset> - * zfs unallow joe <dataset> - * zfs unallow -s @set1 <dataset> - * 1 argument ==> zfs [un]allow -e <dataset> or - * zfs [un]allow -c <dataset> - */ - - switch (*argc) { - case 3: - perms = (*argv)[1]; - who = (*argv)[0]; - *ds = (*argv)[2]; - - /* - * advance argc/argv for do_allow cases. - * for do_allow case make sure who have a know who type - * and its not a permission set. - */ - if (unallow == B_TRUE) { - *argc -= 2; - *argv += 2; - } else if (who_type != ZFS_DELEG_WHO_UNKNOWN && - who_type != ZFS_DELEG_NAMED_SET) - usage(B_FALSE); - break; - - case 2: - if (unallow == B_TRUE && (who_type == ZFS_DELEG_EVERYONE || - who_type == ZFS_DELEG_CREATE || who != NULL)) { - perms = (*argv)[0]; - *ds = (*argv)[1]; - } else { - if (unallow == B_FALSE && - (who_type == ZFS_DELEG_WHO_UNKNOWN || - who_type == ZFS_DELEG_NAMED_SET)) - usage(B_FALSE); - else if (who_type == ZFS_DELEG_WHO_UNKNOWN || - who_type == ZFS_DELEG_NAMED_SET) - who = (*argv)[0]; - else if (who_type != ZFS_DELEG_NAMED_SET) - perms = (*argv)[0]; - *ds = (*argv)[1]; - } - if (unallow == B_TRUE) { - (*argc)--; - (*argv)++; - } - break; - - case 1: - if (unallow == B_FALSE) - usage(B_FALSE); - if (who == NULL && who_type != ZFS_DELEG_CREATE && - who_type != ZFS_DELEG_EVERYONE) - usage(B_FALSE); - *ds = (*argv)[0]; - break; - - default: - usage(B_FALSE); - } - - if (strrchr(*ds, '@')) { - (void) fprintf(stderr, - gettext("Can't set or remove 'allow' permissions " - "on snapshots.\n")); - return (-1); - } - - if ((zhp = zfs_open(g_zfs, *ds, ZFS_TYPE_DATASET)) == NULL) - return (-1); - - if ((zfs_build_perms(zhp, who, perms, - who_type, deleg_type, zperms)) != 0) { - zfs_close(zhp); - return (-1); - } - zfs_close(zhp); - return (0); -} - -static int -zfs_do_allow(int argc, char **argv) -{ - char *ds; - nvlist_t *zperms = NULL; - zfs_handle_t *zhp; - int unused; - int ret; - - if ((ret = parse_allow_args(&argc, &argv, B_FALSE, &ds, - &unused, &zperms)) == -1) - return (1); - - if (ret == 1) - return (zfs_print_allows(argv[0])); - - if ((zhp = zfs_open(g_zfs, ds, ZFS_TYPE_DATASET)) == NULL) - return (1); - - if (zfs_perm_set(zhp, zperms)) { - zfs_close(zhp); - nvlist_free(zperms); - return (1); - } - nvlist_free(zperms); - zfs_close(zhp); - - return (0); -} - -static int -unallow_callback(zfs_handle_t *zhp, void *data) -{ - nvlist_t *nvp = (nvlist_t *)data; - int error; - - error = zfs_perm_remove(zhp, nvp); - if (error) { - (void) fprintf(stderr, gettext("Failed to remove permissions " - "on %s\n"), zfs_get_name(zhp)); - } - return (error); -} - -static int -zfs_do_unallow(int argc, char **argv) -{ - int recurse = B_FALSE; - char *ds; - int error; - nvlist_t *zperms = NULL; - int flags = 0; - - if (parse_allow_args(&argc, &argv, B_TRUE, - &ds, &recurse, &zperms) == -1) - return (1); - - if (recurse) - flags |= ZFS_ITER_RECURSE; - error = zfs_for_each(argc, argv, flags, - ZFS_TYPE_FILESYSTEM|ZFS_TYPE_VOLUME, NULL, - NULL, 0, unallow_callback, (void *)zperms); - - if (zperms) - nvlist_free(zperms); - - return (error); -} - typedef struct get_all_cbdata { zfs_handle_t **cb_handles; size_t cb_alloc; @@ -3974,6 +3712,15 @@ zfs_do_unshare(int argc, char **argv) return (unshare_unmount(OP_SHARE, argc, argv)); } +/* ARGSUSED */ +static int +zfs_do_python(int argc, char **argv) +{ + (void) execv(pypath, argv-1); + (void) printf("internal error: %s not found\n", pypath); + return (-1); +} + /* * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. diff --git a/usr/src/cmd/zpool/zpool_main.c b/usr/src/cmd/zpool/zpool_main.c index 623f588b18..5ac80d6ba1 100644 --- a/usr/src/cmd/zpool/zpool_main.c +++ b/usr/src/cmd/zpool/zpool_main.c @@ -376,12 +376,11 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props, } normnm = zpool_prop_to_name(prop); } else { - if ((fprop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { - (void) fprintf(stderr, gettext("property '%s' is " - "not a valid file system property\n"), propname); - return (2); + if ((fprop = zfs_name_to_prop(propname)) != ZPROP_INVAL) { + normnm = zfs_prop_to_name(fprop); + } else { + normnm = propname; } - normnm = zfs_prop_to_name(fprop); } if (nvlist_lookup_string(proplist, normnm, &strval) == 0 && @@ -3527,8 +3526,8 @@ zpool_do_upgrade(int argc, char **argv) (void) printf(gettext(" 11 Improved scrub performance\n")); (void) printf(gettext(" 12 Snapshot properties\n")); (void) printf(gettext(" 13 snapused property\n")); - (void) printf(gettext(" 14 passthrough-x aclinherit " - "support\n")); + (void) printf(gettext(" 14 passthrough-x aclinherit\n")); + (void) printf(gettext(" 15 user/group space accounting\n")); (void) printf(gettext("For more information on a particular " "version, including supported releases, see:\n\n")); (void) printf("http://www.opensolaris.org/os/community/zfs/" diff --git a/usr/src/common/zfs/zfs_deleg.c b/usr/src/common/zfs/zfs_deleg.c index 0fd5800a84..2964cae5db 100644 --- a/usr/src/common/zfs/zfs_deleg.c +++ b/usr/src/common/zfs/zfs_deleg.c @@ -19,13 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - -#pragma ident "%Z%%M% %I% %E% SMI" - #if defined(_KERNEL) #include <sys/systm.h> #include <sys/sunddi.h> @@ -66,6 +63,10 @@ zfs_deleg_perm_tab_t zfs_deleg_perm_tab[] = { {ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE }, {ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_NONE }, {ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP }, + {ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA }, + {ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA }, + {ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED }, + {ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED }, {NULL, ZFS_DELEG_NOTE_NONE } }; diff --git a/usr/src/common/zfs/zfs_deleg.h b/usr/src/common/zfs/zfs_deleg.h index 561b73e63d..cdbbd83de0 100644 --- a/usr/src/common/zfs/zfs_deleg.h +++ b/usr/src/common/zfs/zfs_deleg.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _ZFS_DELEG_H #define _ZFS_DELEG_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/fs/zfs.h> #ifdef __cplusplus @@ -59,6 +57,10 @@ typedef enum { ZFS_DELEG_NOTE_USERPROP, ZFS_DELEG_NOTE_MOUNT, ZFS_DELEG_NOTE_SHARE, + ZFS_DELEG_NOTE_USERQUOTA, + ZFS_DELEG_NOTE_GROUPQUOTA, + ZFS_DELEG_NOTE_USERUSED, + ZFS_DELEG_NOTE_GROUPUSED, ZFS_DELEG_NOTE_NONE } zfs_deleg_note_t; diff --git a/usr/src/common/zfs/zfs_namecheck.c b/usr/src/common/zfs/zfs_namecheck.c index a9d109be20..45730c6fc4 100644 --- a/usr/src/common/zfs/zfs_namecheck.c +++ b/usr/src/common/zfs/zfs_namecheck.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Common name validation routines for ZFS. These routines are shared by the * userland code as well as the ioctl() layer to ensure that we don't @@ -345,19 +343,3 @@ pool_namecheck(const char *pool, namecheck_err_t *why, char *what) return (0); } - -/* - * Check if the dataset name is private for internal usage. - * '$' is reserved for internal dataset names. e.g. "$MOS" - * - * Return 1 if the given name is used internally. - * Return 0 if it is not. - */ -int -dataset_name_hidden(const char *name) -{ - if (strchr(name, '$') != NULL) - return (1); - - return (0); -} diff --git a/usr/src/common/zfs/zfs_namecheck.h b/usr/src/common/zfs/zfs_namecheck.h index ec85e62f72..7711da099b 100644 --- a/usr/src/common/zfs/zfs_namecheck.h +++ b/usr/src/common/zfs/zfs_namecheck.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _ZFS_NAMECHECK_H #define _ZFS_NAMECHECK_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -50,7 +48,6 @@ typedef enum { int pool_namecheck(const char *, namecheck_err_t *, char *); int dataset_namecheck(const char *, namecheck_err_t *, char *); int mountpoint_namecheck(const char *, namecheck_err_t *); -int dataset_name_hidden(const char *); int snapshot_namecheck(const char *, namecheck_err_t *, char *); int permset_namecheck(const char *, namecheck_err_t *, char *); diff --git a/usr/src/common/zfs/zfs_prop.c b/usr/src/common/zfs/zfs_prop.c index effd2dba70..52b0579f30 100644 --- a/usr/src/common/zfs/zfs_prop.c +++ b/usr/src/common/zfs/zfs_prop.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -43,6 +43,14 @@ static zprop_desc_t zfs_prop_table[ZFS_NUM_PROPS]; +/* Note this is indexed by zfs_userquota_prop_t, keep the order the same */ +const char *zfs_userquota_prop_prefixes[] = { + "userused@", + "userquota@", + "groupused@", + "groupquota@" +}; + zprop_desc_t * zfs_prop_get_table(void) { @@ -133,6 +141,7 @@ zfs_prop_init(void) { "1", 1 }, { "2", 2 }, { "3", 3 }, + { "4", 4 }, { "current", ZPL_VERSION }, { NULL } }; @@ -218,7 +227,7 @@ zfs_prop_init(void) /* default index properties */ register_index(ZFS_PROP_VERSION, "version", 0, PROP_DEFAULT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, - "1 | 2 | 3 | current", "VERSION", version_table); + "1 | 2 | 3 | 4 | current", "VERSION", version_table); register_index(ZFS_PROP_CANMOUNT, "canmount", ZFS_CANMOUNT_ON, PROP_DEFAULT, ZFS_TYPE_FILESYSTEM, "on | off | noauto", "CANMOUNT", canmount_table); @@ -307,6 +316,8 @@ zfs_prop_init(void) PROP_INHERIT, ZFS_TYPE_VOLUME, "ISCSIOPTIONS"); register_hidden(ZFS_PROP_GUID, "guid", PROP_TYPE_NUMBER, PROP_READONLY, ZFS_TYPE_DATASET, "GUID"); + register_hidden(ZFS_PROP_USERACCOUNTING, "useraccounting", + PROP_TYPE_NUMBER, PROP_READONLY, ZFS_TYPE_DATASET, NULL); /* oddball properties */ register_impl(ZFS_PROP_CREATION, "creation", PROP_TYPE_NUMBER, 0, NULL, @@ -330,7 +341,6 @@ zfs_name_to_prop(const char *propname) return (zprop_name_to_prop(propname, ZFS_TYPE_DATASET)); } - /* * For user property names, we allow all lowercase alphanumeric characters, plus * a few useful punctuation characters. @@ -368,6 +378,26 @@ zfs_prop_user(const char *name) } /* + * Returns true if this is a valid userspace-type property (one with a '@'). + * Note that after the @, any character is valid (eg, another @, for SID + * user@domain). + */ +boolean_t +zfs_prop_userquota(const char *name) +{ + zfs_userquota_prop_t prop; + + for (prop = 0; prop < ZFS_NUM_USERQUOTA_PROPS; prop++) { + if (strncmp(name, zfs_userquota_prop_prefixes[prop], + strlen(zfs_userquota_prop_prefixes[prop])) == 0) { + return (B_TRUE); + } + } + + return (B_FALSE); +} + +/* * Tables of index types, plus functions to convert between the user view * (strings) and internal representation (uint64_t). */ diff --git a/usr/src/common/zfs/zprop_common.c b/usr/src/common/zfs/zprop_common.c index bd267e2e61..85f55c2762 100644 --- a/usr/src/common/zfs/zprop_common.c +++ b/usr/src/common/zfs/zprop_common.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Common routines used by zfs and zpool property management. */ @@ -205,9 +203,6 @@ propname_match(const char *p, size_t len, zprop_desc_t *prop_entry) #ifndef _KERNEL const char *colname = prop_entry->pd_colname; int c; - - if (colname == NULL) - return (B_FALSE); #endif if (len == strlen(propname) && @@ -215,7 +210,7 @@ propname_match(const char *p, size_t len, zprop_desc_t *prop_entry) return (B_TRUE); #ifndef _KERNEL - if (len != strlen(colname)) + if (colname == NULL || len != strlen(colname)) return (B_FALSE); for (c = 0; c < len; c++) diff --git a/usr/src/grub/capability b/usr/src/grub/capability index fd73791e9c..0f59ec77f5 100644 --- a/usr/src/grub/capability +++ b/usr/src/grub/capability @@ -18,7 +18,7 @@ # # CDDL HEADER END # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # @@ -40,7 +40,7 @@ # This file and the associated version are Solaris specific and are # not a part of the open source distribution of GRUB. # -VERSION=7 +VERSION=8 dboot xVM zfs diff --git a/usr/src/grub/grub-0.97/stage2/zfs-include/dmu_objset.h b/usr/src/grub/grub-0.97/stage2/zfs-include/dmu_objset.h index 6345c13988..8d1cf8ea33 100644 --- a/usr/src/grub/grub-0.97/stage2/zfs-include/dmu_objset.h +++ b/usr/src/grub/grub-0.97/stage2/zfs-include/dmu_objset.h @@ -17,21 +17,22 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_DMU_OBJSET_H #define _SYS_DMU_OBJSET_H -#pragma ident "%Z%%M% %I% %E% SMI" - typedef struct objset_phys { dnode_phys_t os_meta_dnode; zil_header_t os_zil_header; uint64_t os_type; - char os_pad[1024 - sizeof (dnode_phys_t) - sizeof (zil_header_t) - - sizeof (uint64_t)]; + uint64_t os_flags; + char os_pad[2048 - sizeof (dnode_phys_t)*3 - + sizeof (zil_header_t) - sizeof (uint64_t)*2]; + dnode_phys_t os_userused_dnode; + dnode_phys_t os_groupused_dnode; } objset_phys_t; #endif /* _SYS_DMU_OBJSET_H */ diff --git a/usr/src/grub/grub-0.97/stage2/zfs-include/zfs.h b/usr/src/grub/grub-0.97/stage2/zfs-include/zfs.h index 24a72ae594..7612e94cfd 100644 --- a/usr/src/grub/grub-0.97/stage2/zfs-include/zfs.h +++ b/usr/src/grub/grub-0.97/stage2/zfs-include/zfs.h @@ -17,7 +17,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -27,7 +27,7 @@ /* * On-disk version number. */ -#define SPA_VERSION 14ULL +#define SPA_VERSION 15ULL /* * The following are configuration names used in the nvlist describing a pool's diff --git a/usr/src/grub/grub-0.97/stage2/zfs-include/zfs_znode.h b/usr/src/grub/grub-0.97/stage2/zfs-include/zfs_znode.h index 4029a6f187..199b3a605e 100644 --- a/usr/src/grub/grub-0.97/stage2/zfs-include/zfs_znode.h +++ b/usr/src/grub/grub-0.97/stage2/zfs-include/zfs_znode.h @@ -17,20 +17,18 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_FS_ZFS_ZNODE_H #define _SYS_FS_ZFS_ZNODE_H -#pragma ident "%Z%%M% %I% %E% SMI" - #define MASTER_NODE_OBJ 1 #define ZFS_ROOT_OBJ "ROOT" #define ZPL_VERSION_STR "VERSION" -#define ZPL_VERSION 3ULL +#define ZPL_VERSION 4ULL #define ZFS_DIRENT_OBJ(de) BF64_GET(de, 0, 48) diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index 9ca22955f7..fc576c86fb 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -238,6 +238,7 @@ SUBDIRS += \ libzpool \ libzfs \ libzfs_jni \ + pyzfs \ libmapid \ brand \ policykit \ @@ -346,6 +347,7 @@ MSGSUBDIRS= \ mms \ mpss \ pam_modules \ + pyzfs \ rpcsec_gss $(CLOSED_BUILD)MSGSUBDIRS += \ $(CLOSED)/lib/smartcard @@ -601,7 +603,8 @@ librestart: libuutil libscf pkcs11: libcryptoutil print: libldap5 udapl/udapl_tavor: udapl/libdat -libzfs: libdevinfo libdevid libgen libnvpair libuutil libiscsitgt +libzfs: libdevinfo libdevid libgen libnvpair libuutil libiscsitgt \ + libavl libefi libidmap libsec libzfs_jni: libdiskmgt libnvpair libzfs libzpool: libavl libumem libnvpair libsec: libavl libidmap @@ -615,6 +618,7 @@ libvscan: libscf scsi: libnvpair mpapi: libpthread libdevinfo libsysevent libnvpair libgrubmgmt: libdevinfo libzfs libfstyp +pyzfs: libnvpair libsec libidmap libzfs # # The reason this rule checks for the existence of the diff --git a/usr/src/lib/libzfs/Makefile.com b/usr/src/lib/libzfs/Makefile.com index 742bf3610f..6602481348 100644 --- a/usr/src/lib/libzfs/Makefile.com +++ b/usr/src/lib/libzfs/Makefile.com @@ -19,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# LIBRARY= libzfs.a VERS= .1 @@ -50,7 +48,8 @@ INCS += -I../../../common/zfs C99MODE= -xc99=%all C99LMODE= -Xc99=%all -LDLIBS += -lc -lm -ldevinfo -ldevid -lgen -lnvpair -luutil -lavl -lefi +LDLIBS += -lc -lm -ldevinfo -ldevid -lgen -lnvpair -luutil -lavl -lefi \ + -lidmap -lsec CPPFLAGS += $(INCS) -D_REENTRANT SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \ diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h index 8c83d5d946..a41005b255 100644 --- a/usr/src/lib/libzfs/common/libzfs.h +++ b/usr/src/lib/libzfs/common/libzfs.h @@ -370,6 +370,8 @@ extern int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t, zprop_source_t *, char *, size_t, boolean_t); extern int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *, zprop_source_t *, char *, size_t); +extern int zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, + char *propbuf, int proplen, boolean_t literal); extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t); extern int zfs_prop_inherit(zfs_handle_t *, const char *); extern const char *zfs_prop_values(zfs_prop_t); @@ -457,6 +459,12 @@ extern int zfs_send(zfs_handle_t *, const char *, const char *, boolean_t, boolean_t, boolean_t, boolean_t, int); extern int zfs_promote(zfs_handle_t *); +typedef void (*zfs_userspace_cb_t)(void *arg, const char *domain, + uid_t rid, uint64_t space); + +extern int zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, + zfs_userspace_cb_t func, void *arg); + typedef struct recvflags { /* print informational messages (ie, -v was specified) */ int verbose : 1; @@ -495,17 +503,6 @@ extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *, extern int zfs_spa_version(zfs_handle_t *, int *); /* - * dataset permission functions. - */ -extern int zfs_perm_set(zfs_handle_t *, nvlist_t *); -extern int zfs_perm_remove(zfs_handle_t *, nvlist_t *); -extern int zfs_build_perms(zfs_handle_t *, char *, char *, - zfs_deleg_who_type_t, zfs_deleg_inherit_t, nvlist_t **nvlist_t); -extern int zfs_perm_get(zfs_handle_t *, zfs_allow_t **); -extern void zfs_free_allows(zfs_allow_t *); -extern void zfs_deleg_permissions(void); - -/* * Mount support functions. */ extern boolean_t is_mounted(libzfs_handle_t *, const char *special, char **); diff --git a/usr/src/lib/libzfs/common/libzfs_changelist.c b/usr/src/lib/libzfs/common/libzfs_changelist.c index b905bc6cb6..de87a8c692 100644 --- a/usr/src/lib/libzfs/common/libzfs_changelist.c +++ b/usr/src/lib/libzfs/common/libzfs_changelist.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Portions Copyright 2007 Ramprakash Jelari @@ -621,8 +621,6 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags, clp->cl_prop = ZFS_PROP_MOUNTPOINT; } else if (prop == ZFS_PROP_VOLSIZE) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; - } else if (prop == ZFS_PROP_VERSION) { - clp->cl_prop = ZFS_PROP_MOUNTPOINT; } else { clp->cl_prop = prop; } diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c index 41f1734734..ad2a74861b 100644 --- a/usr/src/lib/libzfs/common/libzfs_dataset.c +++ b/usr/src/lib/libzfs/common/libzfs_dataset.c @@ -45,6 +45,8 @@ #include <grp.h> #include <stddef.h> #include <ucred.h> +#include <idmap.h> +#include <aclutils.h> #include <sys/spa.h> #include <sys/zap.h> @@ -56,6 +58,8 @@ #include "zfs_deleg.h" static int zvol_create_link_common(libzfs_handle_t *, const char *, int); +static int userquota_propname_decode(const char *propname, boolean_t zoned, + zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp); /* * Given a single type (not a mask of types), return the type in a human @@ -121,8 +125,8 @@ path_to_str(const char *path, int types) /* * Validate a ZFS path. This is used even before trying to open the dataset, to - * provide a more meaningful error message. We place a more useful message in - * 'buf' detailing exactly why the name was not valid. + * provide a more meaningful error message. We call zfs_error_aux() to + * explain exactly why the name was not valid. */ static int zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type, @@ -346,6 +350,10 @@ put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc) return (-1); } + /* + * XXX Why do we store the user props separately, in addition to + * storing them in zfs_props? + */ if ((userprops = process_user_props(zhp, allprops)) == NULL) { nvlist_free(allprops); return (-1); @@ -772,23 +780,18 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, return (NULL); } + /* + * Make sure this property is valid and applies to this type. + */ + elem = NULL; while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { const char *propname = nvpair_name(elem); - /* - * Make sure this property is valid and applies to this type. - */ - if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { - if (!zfs_prop_user(propname)) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "invalid property '%s'"), propname); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - + prop = zfs_name_to_prop(propname); + if (prop == ZPROP_INVAL && zfs_prop_user(propname)) { /* - * If this is a user property, make sure it's a + * This is a user property: make sure it's a * string, and that it's less than ZAP_MAXNAMELEN. */ if (nvpair_type(elem) != DATA_TYPE_STRING) { @@ -814,6 +817,10 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, continue; } + /* + * Currently, only user properties can be modified on + * snapshots. + */ if (type == ZFS_TYPE_SNAPSHOT) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "this property can not be modified for snapshots")); @@ -821,6 +828,80 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, goto error; } + if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) { + zfs_userquota_prop_t uqtype; + char newpropname[128]; + char domain[128]; + uint64_t rid; + uint64_t valary[3]; + + if (userquota_propname_decode(propname, zoned, + &uqtype, domain, sizeof (domain), &rid) != 0) { + zfs_error_aux(hdl, + dgettext(TEXT_DOMAIN, + "'%s' has an invalid user/group name"), + propname); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } + + if (uqtype != ZFS_PROP_USERQUOTA && + uqtype != ZFS_PROP_GROUPQUOTA) { + zfs_error_aux(hdl, + dgettext(TEXT_DOMAIN, "'%s' is readonly"), + propname); + (void) zfs_error(hdl, EZFS_PROPREADONLY, + errbuf); + goto error; + } + + if (nvpair_type(elem) == DATA_TYPE_STRING) { + (void) nvpair_value_string(elem, &strval); + if (strcmp(strval, "none") == 0) { + intval = 0; + } else if (zfs_nicestrtonum(hdl, + strval, &intval) != 0) { + (void) zfs_error(hdl, + EZFS_BADPROP, errbuf); + goto error; + } + } else if (nvpair_type(elem) == + DATA_TYPE_UINT64) { + (void) nvpair_value_uint64(elem, &intval); + if (intval == 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "use 'none' to disable " + "userquota/groupquota")); + goto error; + } + } else { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' must be a number"), propname); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } + + (void) snprintf(newpropname, sizeof (newpropname), + "%s%s", zfs_userquota_prop_prefixes[uqtype], + domain); + valary[0] = uqtype; + valary[1] = rid; + valary[2] = intval; + if (nvlist_add_uint64_array(ret, newpropname, + valary, 3) != 0) { + (void) no_memory(hdl); + goto error; + } + continue; + } + + if (prop == ZPROP_INVAL) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "invalid property '%s'"), propname); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } + if (!zfs_prop_valid_for_type(prop, type)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' does not " @@ -960,7 +1041,7 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, } else if (getzoneid() != GLOBAL_ZONEID) { /* * If zoned property is 'off', this must be in - * a globle zone. If not, something is wrong. + * a global zone. If not, something is wrong. */ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' cannot be set while dataset " @@ -1144,808 +1225,6 @@ error: return (NULL); } -static int -zfs_get_perm_who(const char *who, zfs_deleg_who_type_t *who_type, - uint64_t *ret_who) -{ - struct passwd *pwd; - struct group *grp; - uid_t id; - - if (*who_type == ZFS_DELEG_EVERYONE || *who_type == ZFS_DELEG_CREATE || - *who_type == ZFS_DELEG_NAMED_SET) { - *ret_who = -1; - return (0); - } - if (who == NULL && !(*who_type == ZFS_DELEG_EVERYONE)) - return (EZFS_BADWHO); - - if (*who_type == ZFS_DELEG_WHO_UNKNOWN && - strcmp(who, "everyone") == 0) { - *ret_who = -1; - *who_type = ZFS_DELEG_EVERYONE; - return (0); - } - - pwd = getpwnam(who); - grp = getgrnam(who); - - if ((*who_type == ZFS_DELEG_USER) && pwd) { - *ret_who = pwd->pw_uid; - } else if ((*who_type == ZFS_DELEG_GROUP) && grp) { - *ret_who = grp->gr_gid; - } else if (pwd) { - *ret_who = pwd->pw_uid; - *who_type = ZFS_DELEG_USER; - } else if (grp) { - *ret_who = grp->gr_gid; - *who_type = ZFS_DELEG_GROUP; - } else { - char *end; - - id = strtol(who, &end, 10); - if (errno != 0 || *end != '\0') { - return (EZFS_BADWHO); - } else { - *ret_who = id; - if (*who_type == ZFS_DELEG_WHO_UNKNOWN) - *who_type = ZFS_DELEG_USER; - } - } - - return (0); -} - -static void -zfs_perms_add_to_nvlist(nvlist_t *who_nvp, char *name, nvlist_t *perms_nvp) -{ - if (perms_nvp != NULL) { - verify(nvlist_add_nvlist(who_nvp, - name, perms_nvp) == 0); - } else { - verify(nvlist_add_boolean(who_nvp, name) == 0); - } -} - -static void -helper(zfs_deleg_who_type_t who_type, uint64_t whoid, char *whostr, - zfs_deleg_inherit_t inherit, nvlist_t *who_nvp, nvlist_t *perms_nvp, - nvlist_t *sets_nvp) -{ - boolean_t do_perms, do_sets; - char name[ZFS_MAX_DELEG_NAME]; - - do_perms = (nvlist_next_nvpair(perms_nvp, NULL) != NULL); - do_sets = (nvlist_next_nvpair(sets_nvp, NULL) != NULL); - - if (!do_perms && !do_sets) - do_perms = do_sets = B_TRUE; - - if (do_perms) { - zfs_deleg_whokey(name, who_type, inherit, - (who_type == ZFS_DELEG_NAMED_SET) ? - whostr : (void *)&whoid); - zfs_perms_add_to_nvlist(who_nvp, name, perms_nvp); - } - if (do_sets) { - zfs_deleg_whokey(name, toupper(who_type), inherit, - (who_type == ZFS_DELEG_NAMED_SET) ? - whostr : (void *)&whoid); - zfs_perms_add_to_nvlist(who_nvp, name, sets_nvp); - } -} - -static void -zfs_perms_add_who_nvlist(nvlist_t *who_nvp, uint64_t whoid, void *whostr, - nvlist_t *perms_nvp, nvlist_t *sets_nvp, - zfs_deleg_who_type_t who_type, zfs_deleg_inherit_t inherit) -{ - if (who_type == ZFS_DELEG_NAMED_SET || who_type == ZFS_DELEG_CREATE) { - helper(who_type, whoid, whostr, 0, - who_nvp, perms_nvp, sets_nvp); - } else { - if (inherit & ZFS_DELEG_PERM_LOCAL) { - helper(who_type, whoid, whostr, ZFS_DELEG_LOCAL, - who_nvp, perms_nvp, sets_nvp); - } - if (inherit & ZFS_DELEG_PERM_DESCENDENT) { - helper(who_type, whoid, whostr, ZFS_DELEG_DESCENDENT, - who_nvp, perms_nvp, sets_nvp); - } - } -} - -/* - * Construct nvlist to pass down to kernel for setting/removing permissions. - * - * The nvlist is constructed as a series of nvpairs with an optional embedded - * nvlist of permissions to remove or set. The topmost nvpairs are the actual - * base attribute named stored in the dsl. - * Arguments: - * - * whostr: is a comma separated list of users, groups, or a single set name. - * whostr may be null for everyone or create perms. - * who_type: is the type of entry in whostr. Typically this will be - * ZFS_DELEG_WHO_UNKNOWN. - * perms: common separated list of permissions. May be null if user - * is requested to remove permissions by who. - * inherit: Specifies the inheritance of the permissions. Will be either - * ZFS_DELEG_PERM_LOCAL and/or ZFS_DELEG_PERM_DESCENDENT. - * nvp The constructed nvlist to pass to zfs_perm_set(). - * The output nvp will look something like this. - * ul$1234 -> {create ; destroy } - * Ul$1234 -> { @myset } - * s-$@myset - { snapshot; checksum; compression } - */ -int -zfs_build_perms(zfs_handle_t *zhp, char *whostr, char *perms, - zfs_deleg_who_type_t who_type, zfs_deleg_inherit_t inherit, nvlist_t **nvp) -{ - nvlist_t *who_nvp; - nvlist_t *perms_nvp = NULL; - nvlist_t *sets_nvp = NULL; - char errbuf[1024]; - char *who_tok, *perm; - int error; - - *nvp = NULL; - - if (perms) { - if ((error = nvlist_alloc(&perms_nvp, - NV_UNIQUE_NAME, 0)) != 0) { - return (1); - } - if ((error = nvlist_alloc(&sets_nvp, - NV_UNIQUE_NAME, 0)) != 0) { - nvlist_free(perms_nvp); - return (1); - } - } - - if ((error = nvlist_alloc(&who_nvp, NV_UNIQUE_NAME, 0)) != 0) { - if (perms_nvp) - nvlist_free(perms_nvp); - if (sets_nvp) - nvlist_free(sets_nvp); - return (1); - } - - if (who_type == ZFS_DELEG_NAMED_SET) { - namecheck_err_t why; - char what; - - if ((error = permset_namecheck(whostr, &why, &what)) != 0) { - nvlist_free(who_nvp); - if (perms_nvp) - nvlist_free(perms_nvp); - if (sets_nvp) - nvlist_free(sets_nvp); - - switch (why) { - case NAME_ERR_NO_AT: - zfs_error_aux(zhp->zfs_hdl, - dgettext(TEXT_DOMAIN, - "set definition must begin with an '@' " - "character")); - } - return (zfs_error(zhp->zfs_hdl, - EZFS_BADPERMSET, whostr)); - } - } - - /* - * Build up nvlist(s) of permissions. Two nvlists are maintained. - * The first nvlist perms_nvp will have normal permissions and the - * other sets_nvp will have only permssion set names in it. - */ - for (perm = strtok(perms, ","); perm; perm = strtok(NULL, ",")) { - const char *perm_canonical = zfs_deleg_canonicalize_perm(perm); - - if (perm_canonical) { - verify(nvlist_add_boolean(perms_nvp, - perm_canonical) == 0); - } else if (perm[0] == '@') { - verify(nvlist_add_boolean(sets_nvp, perm) == 0); - } else { - nvlist_free(who_nvp); - nvlist_free(perms_nvp); - nvlist_free(sets_nvp); - return (zfs_error(zhp->zfs_hdl, EZFS_BADPERM, perm)); - } - } - - if (whostr && who_type != ZFS_DELEG_CREATE) { - who_tok = strtok(whostr, ","); - if (who_tok == NULL) { - nvlist_free(who_nvp); - if (perms_nvp) - nvlist_free(perms_nvp); - if (sets_nvp) - nvlist_free(sets_nvp); - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "Who string is NULL"), - whostr); - return (zfs_error(zhp->zfs_hdl, EZFS_BADWHO, errbuf)); - } - } - - /* - * Now create the nvlist(s) - */ - do { - uint64_t who_id; - - error = zfs_get_perm_who(who_tok, &who_type, - &who_id); - if (error) { - nvlist_free(who_nvp); - if (perms_nvp) - nvlist_free(perms_nvp); - if (sets_nvp) - nvlist_free(sets_nvp); - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, - "Unable to determine uid/gid for " - "%s "), who_tok); - return (zfs_error(zhp->zfs_hdl, EZFS_BADWHO, errbuf)); - } - - /* - * add entries for both local and descendent when required - */ - zfs_perms_add_who_nvlist(who_nvp, who_id, who_tok, - perms_nvp, sets_nvp, who_type, inherit); - - } while (who_tok = strtok(NULL, ",")); - *nvp = who_nvp; - return (0); -} - -static int -zfs_perm_set_common(zfs_handle_t *zhp, nvlist_t *nvp, boolean_t unset) -{ - zfs_cmd_t zc = { 0 }; - int error; - char errbuf[1024]; - - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "Cannot update 'allows' for '%s'"), - zhp->zfs_name); - - if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, nvp)) - return (-1); - - (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - zc.zc_perm_action = unset; - - error = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SET_FSACL, &zc); - if (error && errno == ENOTSUP) { - (void) snprintf(errbuf, sizeof (errbuf), - gettext("Pool must be upgraded to use 'allow/unallow'")); - zcmd_free_nvlists(&zc); - return (zfs_error(zhp->zfs_hdl, EZFS_BADVERSION, errbuf)); - } else if (error) { - return (zfs_standard_error(zhp->zfs_hdl, errno, errbuf)); - } - zcmd_free_nvlists(&zc); - - return (error); -} - -int -zfs_perm_set(zfs_handle_t *zhp, nvlist_t *nvp) -{ - return (zfs_perm_set_common(zhp, nvp, B_FALSE)); -} - -int -zfs_perm_remove(zfs_handle_t *zhp, nvlist_t *perms) -{ - return (zfs_perm_set_common(zhp, perms, B_TRUE)); -} - -static int -perm_compare(const void *arg1, const void *arg2) -{ - const zfs_perm_node_t *node1 = arg1; - const zfs_perm_node_t *node2 = arg2; - int ret; - - ret = strcmp(node1->z_pname, node2->z_pname); - - if (ret > 0) - return (1); - if (ret < 0) - return (-1); - else - return (0); -} - -static void -zfs_destroy_perm_tree(avl_tree_t *tree) -{ - zfs_perm_node_t *permnode; - void *cookie = NULL; - - while ((permnode = avl_destroy_nodes(tree, &cookie)) != NULL) - free(permnode); - avl_destroy(tree); -} - -static void -zfs_destroy_tree(avl_tree_t *tree) -{ - zfs_allow_node_t *allownode; - void *cookie = NULL; - - while ((allownode = avl_destroy_nodes(tree, &cookie)) != NULL) { - zfs_destroy_perm_tree(&allownode->z_localdescend); - zfs_destroy_perm_tree(&allownode->z_local); - zfs_destroy_perm_tree(&allownode->z_descend); - free(allownode); - } - avl_destroy(tree); -} - -void -zfs_free_allows(zfs_allow_t *allow) -{ - zfs_allow_t *allownext; - zfs_allow_t *freeallow; - - allownext = allow; - while (allownext) { - zfs_destroy_tree(&allownext->z_sets); - zfs_destroy_tree(&allownext->z_crperms); - zfs_destroy_tree(&allownext->z_user); - zfs_destroy_tree(&allownext->z_group); - zfs_destroy_tree(&allownext->z_everyone); - freeallow = allownext; - allownext = allownext->z_next; - free(freeallow); - } -} - -static zfs_allow_t * -zfs_alloc_perm_tree(zfs_handle_t *zhp, zfs_allow_t *prev, char *setpoint) -{ - zfs_allow_t *ptree; - - if ((ptree = zfs_alloc(zhp->zfs_hdl, - sizeof (zfs_allow_t))) == NULL) { - return (NULL); - } - - (void) strlcpy(ptree->z_setpoint, setpoint, sizeof (ptree->z_setpoint)); - avl_create(&ptree->z_sets, - perm_compare, sizeof (zfs_allow_node_t), - offsetof(zfs_allow_node_t, z_node)); - avl_create(&ptree->z_crperms, - perm_compare, sizeof (zfs_allow_node_t), - offsetof(zfs_allow_node_t, z_node)); - avl_create(&ptree->z_user, - perm_compare, sizeof (zfs_allow_node_t), - offsetof(zfs_allow_node_t, z_node)); - avl_create(&ptree->z_group, - perm_compare, sizeof (zfs_allow_node_t), - offsetof(zfs_allow_node_t, z_node)); - avl_create(&ptree->z_everyone, - perm_compare, sizeof (zfs_allow_node_t), - offsetof(zfs_allow_node_t, z_node)); - - if (prev) - prev->z_next = ptree; - ptree->z_next = NULL; - return (ptree); -} - -/* - * Add permissions to the appropriate AVL permission tree. - * The appropriate tree may not be the requested tree. - * For example if ld indicates a local permission, but - * same permission also exists as a descendent permission - * then the permission will be removed from the descendent - * tree and add the the local+descendent tree. - */ -static int -zfs_coalesce_perm(zfs_handle_t *zhp, zfs_allow_node_t *allownode, - char *perm, char ld) -{ - zfs_perm_node_t pnode, *permnode, *permnode2; - zfs_perm_node_t *newnode; - avl_index_t where, where2; - avl_tree_t *tree, *altree; - - (void) strlcpy(pnode.z_pname, perm, sizeof (pnode.z_pname)); - - if (ld == ZFS_DELEG_NA) { - tree = &allownode->z_localdescend; - altree = &allownode->z_descend; - } else if (ld == ZFS_DELEG_LOCAL) { - tree = &allownode->z_local; - altree = &allownode->z_descend; - } else { - tree = &allownode->z_descend; - altree = &allownode->z_local; - } - permnode = avl_find(tree, &pnode, &where); - permnode2 = avl_find(altree, &pnode, &where2); - - if (permnode2) { - avl_remove(altree, permnode2); - free(permnode2); - if (permnode == NULL) { - tree = &allownode->z_localdescend; - } - } - - /* - * Now insert new permission in either requested location - * local/descendent or into ld when perm will exist in both. - */ - if (permnode == NULL) { - if ((newnode = zfs_alloc(zhp->zfs_hdl, - sizeof (zfs_perm_node_t))) == NULL) { - return (-1); - } - *newnode = pnode; - avl_add(tree, newnode); - } - return (0); -} - -/* - * Uggh, this is going to be a bit complicated. - * we have an nvlist coming out of the kernel that - * will indicate where the permission is set and then - * it will contain allow of the various "who's", and what - * their permissions are. To further complicate this - * we will then have to coalesce the local,descendent - * and local+descendent permissions where appropriate. - * The kernel only knows about a permission as being local - * or descendent, but not both. - * - * In order to make this easier for zfs_main to deal with - * a series of AVL trees will be used to maintain - * all of this, primarily for sorting purposes as well - * as the ability to quickly locate a specific entry. - * - * What we end up with are tree's for sets, create perms, - * user, groups and everyone. With each of those trees - * we have subtrees for local, descendent and local+descendent - * permissions. - */ -int -zfs_perm_get(zfs_handle_t *zhp, zfs_allow_t **zfs_perms) -{ - zfs_cmd_t zc = { 0 }; - int error; - nvlist_t *nvlist; - nvlist_t *permnv, *sourcenv; - nvpair_t *who_pair, *source_pair; - nvpair_t *perm_pair; - char errbuf[1024]; - zfs_allow_t *zallowp, *newallowp; - char ld; - char *nvpname; - uid_t uid; - gid_t gid; - avl_tree_t *tree; - avl_index_t where; - - (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - - if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) - return (-1); - - while (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) { - if (errno == ENOMEM) { - if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, &zc) != 0) { - zcmd_free_nvlists(&zc); - return (-1); - } - } else if (errno == ENOTSUP) { - zcmd_free_nvlists(&zc); - (void) snprintf(errbuf, sizeof (errbuf), - gettext("Pool must be upgraded to use 'allow'")); - return (zfs_error(zhp->zfs_hdl, - EZFS_BADVERSION, errbuf)); - } else { - zcmd_free_nvlists(&zc); - return (-1); - } - } - - if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &nvlist) != 0) { - zcmd_free_nvlists(&zc); - return (-1); - } - - zcmd_free_nvlists(&zc); - - source_pair = nvlist_next_nvpair(nvlist, NULL); - - if (source_pair == NULL) { - *zfs_perms = NULL; - return (0); - } - - *zfs_perms = zfs_alloc_perm_tree(zhp, NULL, nvpair_name(source_pair)); - if (*zfs_perms == NULL) { - return (0); - } - - zallowp = *zfs_perms; - - for (;;) { - struct passwd *pwd; - struct group *grp; - zfs_allow_node_t *allownode; - zfs_allow_node_t findallownode; - zfs_allow_node_t *newallownode; - - (void) strlcpy(zallowp->z_setpoint, - nvpair_name(source_pair), - sizeof (zallowp->z_setpoint)); - - if ((error = nvpair_value_nvlist(source_pair, &sourcenv)) != 0) - goto abort; - - /* - * Make sure nvlist is composed correctly - */ - if (zfs_deleg_verify_nvlist(sourcenv)) { - goto abort; - } - - who_pair = nvlist_next_nvpair(sourcenv, NULL); - if (who_pair == NULL) { - goto abort; - } - - do { - error = nvpair_value_nvlist(who_pair, &permnv); - if (error) { - goto abort; - } - - /* - * First build up the key to use - * for looking up in the various - * who trees. - */ - ld = nvpair_name(who_pair)[1]; - nvpname = nvpair_name(who_pair); - switch (nvpair_name(who_pair)[0]) { - case ZFS_DELEG_USER: - case ZFS_DELEG_USER_SETS: - tree = &zallowp->z_user; - uid = atol(&nvpname[3]); - pwd = getpwuid(uid); - (void) snprintf(findallownode.z_key, - sizeof (findallownode.z_key), "user %s", - (pwd) ? pwd->pw_name : - &nvpair_name(who_pair)[3]); - break; - case ZFS_DELEG_GROUP: - case ZFS_DELEG_GROUP_SETS: - tree = &zallowp->z_group; - gid = atol(&nvpname[3]); - grp = getgrgid(gid); - (void) snprintf(findallownode.z_key, - sizeof (findallownode.z_key), "group %s", - (grp) ? grp->gr_name : - &nvpair_name(who_pair)[3]); - break; - case ZFS_DELEG_CREATE: - case ZFS_DELEG_CREATE_SETS: - tree = &zallowp->z_crperms; - (void) strlcpy(findallownode.z_key, "", - sizeof (findallownode.z_key)); - break; - case ZFS_DELEG_EVERYONE: - case ZFS_DELEG_EVERYONE_SETS: - (void) snprintf(findallownode.z_key, - sizeof (findallownode.z_key), "everyone"); - tree = &zallowp->z_everyone; - break; - case ZFS_DELEG_NAMED_SET: - case ZFS_DELEG_NAMED_SET_SETS: - (void) snprintf(findallownode.z_key, - sizeof (findallownode.z_key), "%s", - &nvpair_name(who_pair)[3]); - tree = &zallowp->z_sets; - break; - } - - /* - * Place who in tree - */ - allownode = avl_find(tree, &findallownode, &where); - if (allownode == NULL) { - if ((newallownode = zfs_alloc(zhp->zfs_hdl, - sizeof (zfs_allow_node_t))) == NULL) { - goto abort; - } - avl_create(&newallownode->z_localdescend, - perm_compare, - sizeof (zfs_perm_node_t), - offsetof(zfs_perm_node_t, z_node)); - avl_create(&newallownode->z_local, - perm_compare, - sizeof (zfs_perm_node_t), - offsetof(zfs_perm_node_t, z_node)); - avl_create(&newallownode->z_descend, - perm_compare, - sizeof (zfs_perm_node_t), - offsetof(zfs_perm_node_t, z_node)); - (void) strlcpy(newallownode->z_key, - findallownode.z_key, - sizeof (findallownode.z_key)); - avl_insert(tree, newallownode, where); - allownode = newallownode; - } - - /* - * Now iterate over the permissions and - * place them in the appropriate local, - * descendent or local+descendent tree. - * - * The permissions are added to the tree - * via zfs_coalesce_perm(). - */ - perm_pair = nvlist_next_nvpair(permnv, NULL); - if (perm_pair == NULL) - goto abort; - do { - if (zfs_coalesce_perm(zhp, allownode, - nvpair_name(perm_pair), ld) != 0) - goto abort; - } while (perm_pair = nvlist_next_nvpair(permnv, - perm_pair)); - } while (who_pair = nvlist_next_nvpair(sourcenv, who_pair)); - - source_pair = nvlist_next_nvpair(nvlist, source_pair); - if (source_pair == NULL) - break; - - /* - * allocate another node from the link list of - * zfs_allow_t structures - */ - newallowp = zfs_alloc_perm_tree(zhp, zallowp, - nvpair_name(source_pair)); - if (newallowp == NULL) { - goto abort; - } - zallowp = newallowp; - } - nvlist_free(nvlist); - return (0); -abort: - zfs_free_allows(*zfs_perms); - nvlist_free(nvlist); - return (-1); -} - -static char * -zfs_deleg_perm_note(zfs_deleg_note_t note) -{ - /* - * Don't put newlines on end of lines - */ - switch (note) { - case ZFS_DELEG_NOTE_CREATE: - return (dgettext(TEXT_DOMAIN, - "Must also have the 'mount' ability")); - case ZFS_DELEG_NOTE_DESTROY: - return (dgettext(TEXT_DOMAIN, - "Must also have the 'mount' ability")); - case ZFS_DELEG_NOTE_SNAPSHOT: - return (dgettext(TEXT_DOMAIN, - "Must also have the 'mount' ability")); - case ZFS_DELEG_NOTE_ROLLBACK: - return (dgettext(TEXT_DOMAIN, - "Must also have the 'mount' ability")); - case ZFS_DELEG_NOTE_CLONE: - return (dgettext(TEXT_DOMAIN, "Must also have the 'create' " - "ability and 'mount'\n" - "\t\t\t\tability in the origin file system")); - case ZFS_DELEG_NOTE_PROMOTE: - return (dgettext(TEXT_DOMAIN, "Must also have the 'mount'\n" - "\t\t\t\tand 'promote' ability in the origin file system")); - case ZFS_DELEG_NOTE_RENAME: - return (dgettext(TEXT_DOMAIN, "Must also have the 'mount' " - "and 'create' \n\t\t\t\tability in the new parent")); - case ZFS_DELEG_NOTE_RECEIVE: - return (dgettext(TEXT_DOMAIN, "Must also have the 'mount'" - " and 'create' ability")); - case ZFS_DELEG_NOTE_USERPROP: - return (dgettext(TEXT_DOMAIN, - "Allows changing any user property")); - case ZFS_DELEG_NOTE_ALLOW: - return (dgettext(TEXT_DOMAIN, - "Must also have the permission that is being\n" - "\t\t\t\tallowed")); - case ZFS_DELEG_NOTE_MOUNT: - return (dgettext(TEXT_DOMAIN, - "Allows mount/umount of ZFS datasets")); - case ZFS_DELEG_NOTE_SHARE: - return (dgettext(TEXT_DOMAIN, - "Allows sharing file systems over NFS or SMB\n" - "\t\t\t\tprotocols")); - case ZFS_DELEG_NOTE_NONE: - default: - return (dgettext(TEXT_DOMAIN, "")); - } -} - -typedef enum { - ZFS_DELEG_SUBCOMMAND, - ZFS_DELEG_PROP, - ZFS_DELEG_OTHER -} zfs_deleg_perm_type_t; - -/* - * is the permission a subcommand or other? - */ -zfs_deleg_perm_type_t -zfs_deleg_perm_type(const char *perm) -{ - if (strcmp(perm, "userprop") == 0) - return (ZFS_DELEG_OTHER); - else - return (ZFS_DELEG_SUBCOMMAND); -} - -static char * -zfs_deleg_perm_type_str(zfs_deleg_perm_type_t type) -{ - switch (type) { - case ZFS_DELEG_SUBCOMMAND: - return (dgettext(TEXT_DOMAIN, "subcommand")); - case ZFS_DELEG_PROP: - return (dgettext(TEXT_DOMAIN, "property")); - case ZFS_DELEG_OTHER: - return (dgettext(TEXT_DOMAIN, "other")); - } - return (""); -} - -/*ARGSUSED*/ -static int -zfs_deleg_prop_cb(int prop, void *cb) -{ - if (zfs_prop_delegatable(prop)) - (void) fprintf(stderr, "%-15s %-15s\n", zfs_prop_to_name(prop), - zfs_deleg_perm_type_str(ZFS_DELEG_PROP)); - - return (ZPROP_CONT); -} - -void -zfs_deleg_permissions(void) -{ - int i; - - (void) fprintf(stderr, "\n%-15s %-15s\t%s\n\n", "NAME", - "TYPE", "NOTES"); - - /* - * First print out the subcommands - */ - for (i = 0; zfs_deleg_perm_tab[i].z_perm != NULL; i++) { - (void) fprintf(stderr, "%-15s %-15s\t%s\n", - zfs_deleg_perm_tab[i].z_perm, - zfs_deleg_perm_type_str( - zfs_deleg_perm_type(zfs_deleg_perm_tab[i].z_perm)), - zfs_deleg_perm_note(zfs_deleg_perm_tab[i].z_note)); - } - - (void) zprop_iter(zfs_deleg_prop_cb, NULL, B_FALSE, B_TRUE, - ZFS_TYPE_DATASET|ZFS_TYPE_VOLUME); -} - /* * Given a property name and value, set the property for the given dataset. */ @@ -2422,7 +1701,7 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, case PROP_TYPE_INDEX: *val = getprop_uint64(zhp, prop, source); /* - * If we tried to use a defalut value for a + * If we tried to use a default value for a * readonly property, it means that it was not * present; return an error. */ @@ -2716,7 +1995,7 @@ zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val) { char buf[64]; - zfs_nicenum(val, buf, sizeof (buf)); + (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val); return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf)); } @@ -2749,6 +2028,179 @@ zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value, return (0); } +static int +idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser, + char **domainp, idmap_rid_t *ridp) +{ + idmap_handle_t *idmap_hdl = NULL; + idmap_get_handle_t *get_hdl = NULL; + idmap_stat status; + int err = EINVAL; + + if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS) + goto out; + if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS) + goto out; + + if (isuser) { + err = idmap_get_sidbyuid(get_hdl, id, + IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); + } else { + err = idmap_get_sidbygid(get_hdl, id, + IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status); + } + if (err == IDMAP_SUCCESS && + idmap_get_mappings(get_hdl) == IDMAP_SUCCESS && + status == IDMAP_SUCCESS) + err = 0; + else + err = EINVAL; +out: + if (get_hdl) + idmap_get_destroy(get_hdl); + if (idmap_hdl) + (void) idmap_fini(idmap_hdl); + return (err); +} + +/* + * convert the propname into parameters needed by kernel + * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 + * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 + */ +static int +userquota_propname_decode(const char *propname, boolean_t zoned, + zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) +{ + zfs_userquota_prop_t type; + char *cp, *end; + boolean_t isuser; + + domain[0] = '\0'; + + /* Figure out the property type ({user|group}{quota|space}) */ + for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { + if (strncmp(propname, zfs_userquota_prop_prefixes[type], + strlen(zfs_userquota_prop_prefixes[type])) == 0) + break; + } + if (type == ZFS_NUM_USERQUOTA_PROPS) + return (EINVAL); + *typep = type; + + isuser = (type == ZFS_PROP_USERQUOTA || + type == ZFS_PROP_USERUSED); + + cp = strchr(propname, '@') + 1; + + if (strchr(cp, '@')) { + /* + * It's a SID name (eg "user@domain") that needs to be + * turned into S-1-domainID-RID. There should be a + * better way to do this, but for now just translate it + * to the (possibly ephemeral) uid and then back to the + * SID. This is like getsidname(noresolve=TRUE). + */ + uid_t id; + idmap_rid_t rid; + char *mapdomain; + + if (zoned && getzoneid() == GLOBAL_ZONEID) + return (ENOENT); + if (sid_to_id(cp, isuser, &id) != 0) + return (ENOENT); + if (idmap_id_to_numeric_domain_rid(id, isuser, + &mapdomain, &rid) != 0) + return (ENOENT); + (void) strlcpy(domain, mapdomain, domainlen); + *ridp = rid; + } else if (strncmp(cp, "S-1-", 4) == 0) { + /* It's a numeric SID (eg "S-1-234-567-89") */ + (void) strcpy(domain, cp); + cp = strrchr(domain, '-'); + *cp = '\0'; + cp++; + + errno = 0; + *ridp = strtoull(cp, &end, 10); + if (errno == 0 || *end != '\0') + return (EINVAL); + } else if (!isdigit(*cp)) { + /* + * It's a user/group name (eg "user") that needs to be + * turned into a uid/gid + */ + if (zoned && getzoneid() == GLOBAL_ZONEID) + return (ENOENT); + if (isuser) { + struct passwd *pw; + pw = getpwnam(cp); + if (pw == NULL) + return (ENOENT); + *ridp = pw->pw_uid; + } else { + struct group *gr; + gr = getgrnam(cp); + if (gr == NULL) + return (ENOENT); + *ridp = gr->gr_gid; + } + } else { + /* It's a user/group ID (eg "12345"). */ + uid_t id = strtoul(cp, &end, 10); + idmap_rid_t rid; + char *mapdomain; + + if (*end != '\0') + return (EINVAL); + if (id > MAXUID) { + /* It's an ephemeral ID. */ + if (idmap_id_to_numeric_domain_rid(id, isuser, + &mapdomain, &rid) != 0) + return (ENOENT); + (void) strcpy(domain, mapdomain); + *ridp = rid; + } else { + *ridp = id; + } + } + + return (0); +} + +int +zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, + char *propbuf, int proplen, boolean_t literal) +{ + int err; + zfs_cmd_t zc = { 0 }; + zfs_userquota_prop_t type; + + (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); + + err = userquota_propname_decode(propname, + zfs_prop_get_int(zhp, ZFS_PROP_ZONED), + &type, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid); + zc.zc_objset_type = type; + if (err) + return (err); + + err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc); + if (err) + return (err); + + if (literal) { + (void) snprintf(propbuf, proplen, "%llu", + (u_longlong_t)zc.zc_cookie); + } else if (zc.zc_cookie == 0 && + (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { + (void) strlcpy(propbuf, "none", proplen); + } else { + zfs_nicenum(zc.zc_cookie, propbuf, proplen); + } + return (0); +} + /* * Returns the name of the given zfs handle. */ @@ -2826,12 +2278,6 @@ zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, &zc)) == 0) { /* - * Ignore private dataset names. - */ - if (dataset_name_hidden(zc.zc_name)) - continue; - - /* * Silently ignore errors, as the only plausible explanation is * that the pool has since been removed. */ @@ -4488,7 +3934,12 @@ zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props) zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr)); nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr); - if (props[zfs_prop] == B_FALSE) + /* + * We leave user:props in the nvlist, so there will be + * some ZPROP_INVAL. To be extra safe, don't prune + * those. + */ + if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE) (void) nvlist_remove(zhp->zfs_props, nvpair_name(curr), nvpair_type(curr)); curr = next; @@ -4576,3 +4027,36 @@ zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path, return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME, oldname, newname)); } + +int +zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type, + zfs_userspace_cb_t func, void *arg) +{ + zfs_cmd_t zc = { 0 }; + int error; + zfs_useracct_t buf[100]; + + (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); + + zc.zc_objset_type = type; + zc.zc_nvlist_dst = (uintptr_t)buf; + + /* CONSTCOND */ + while (1) { + zfs_useracct_t *zua = buf; + + zc.zc_nvlist_dst_size = sizeof (buf); + error = ioctl(zhp->zfs_hdl->libzfs_fd, + ZFS_IOC_USERSPACE_MANY, &zc); + if (error || zc.zc_nvlist_dst_size == 0) + break; + + while (zc.zc_nvlist_dst_size > 0) { + func(arg, zua->zu_domain, zua->zu_rid, zua->zu_space); + zua++; + zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); + } + } + + return (error); +} diff --git a/usr/src/lib/libzfs/common/libzfs_graph.c b/usr/src/lib/libzfs/common/libzfs_graph.c index e7cbf23860..bc21c51ae2 100644 --- a/usr/src/lib/libzfs/common/libzfs_graph.c +++ b/usr/src/lib/libzfs/common/libzfs_graph.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Iterate over all children of the current object. This includes the normal * dataset hierarchy, but also arbitrary hierarchies due to clones. We want to @@ -399,13 +397,6 @@ iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset) for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0; (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) { - - /* - * Ignore private dataset names. - */ - if (dataset_name_hidden(zc.zc_name)) - continue; - /* * Get statistics for this dataset, to determine the type of the * dataset and clone statistics. If this fails, the dataset has diff --git a/usr/src/lib/libzfs/common/libzfs_sendrecv.c b/usr/src/lib/libzfs/common/libzfs_sendrecv.c index 5a2e2aeb6d..612a099146 100644 --- a/usr/src/lib/libzfs/common/libzfs_sendrecv.c +++ b/usr/src/lib/libzfs/common/libzfs_sendrecv.c @@ -237,6 +237,8 @@ send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv) zfs_prop_t prop = zfs_name_to_prop(propname); nvlist_t *propnv; + assert(zfs_prop_user(propname) || prop != ZPROP_INVAL); + if (!zfs_prop_user(propname) && zfs_prop_readonly(prop)) continue; @@ -594,12 +596,18 @@ dump_filesystem(zfs_handle_t *zhp, void *arg) zhp->zfs_name, sdd->fromsnap); sdd->err = B_TRUE; } else if (!sdd->seento) { - (void) fprintf(stderr, - "WARNING: could not send %s@%s:\n" - "incremental source (%s@%s) " - "is not earlier than it\n", - zhp->zfs_name, sdd->tosnap, - zhp->zfs_name, sdd->fromsnap); + if (sdd->fromsnap) { + (void) fprintf(stderr, + "WARNING: could not send %s@%s:\n" + "incremental source (%s@%s) " + "is not earlier than it\n", + zhp->zfs_name, sdd->tosnap, + zhp->zfs_name, sdd->fromsnap); + } else { + (void) fprintf(stderr, "WARNING: " + "could not send %s@%s: does not exist\n", + zhp->zfs_name, sdd->tosnap); + } sdd->err = B_TRUE; } } else { diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c index 17d8db5c75..e1993ce0f2 100644 --- a/usr/src/lib/libzfs/common/libzfs_util.c +++ b/usr/src/lib/libzfs/common/libzfs_util.c @@ -1221,7 +1221,7 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp, * dataset property, */ if (prop == ZPROP_INVAL && (type == ZFS_TYPE_POOL || - !zfs_prop_user(propname))) { + (!zfs_prop_user(propname) && !zfs_prop_userquota(propname)))) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid property '%s'"), propname); return (zfs_error(hdl, EZFS_BADPROP, diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers index f7d0d2774e..80d7c45bdc 100644 --- a/usr/src/lib/libzfs/common/mapfile-vers +++ b/usr/src/lib/libzfs/common/mapfile-vers @@ -47,18 +47,15 @@ SUNWprivate_1.1 { libzfs_mnttab_cache; libzfs_print_on_error; zfs_allocatable_devs; - zfs_build_perms; zfs_clone; zfs_close; zfs_create; zfs_create_ancestors; zfs_dataset_exists; - zfs_deleg_permissions; zfs_deleg_share_nfs; zfs_destroy; zfs_destroy_snaps; zfs_expand_proplist; - zfs_free_allows; zfs_get_handle; zfs_get_name; zfs_get_pool_handle; @@ -82,9 +79,6 @@ SUNWprivate_1.1 { zfs_nicestrtonum; zfs_open; zfs_path_to_zhandle; - zfs_perm_get; - zfs_perm_remove; - zfs_perm_set; zfs_promote; zfs_prop_align_right; zfs_prop_column_name; @@ -93,14 +87,18 @@ SUNWprivate_1.1 { zfs_prop_get; zfs_prop_get_int; zfs_prop_get_numeric; + zfs_prop_get_table; + zfs_prop_get_userquota; zfs_prop_inherit; zfs_prop_inheritable; + zfs_prop_init; zfs_prop_is_string; zfs_prop_readonly; zfs_prop_set; zfs_prop_string_to_index; zfs_prop_to_name; zfs_prop_user; + zfs_prop_userquota; zfs_prop_valid_for_type; zfs_prop_values; zfs_prune_proplist; @@ -131,6 +129,8 @@ SUNWprivate_1.1 { zfs_unshareall_bypath; zfs_unshareall_nfs; zfs_unshareall_smb; + zfs_userspace; + zfs_userquota_prop_prefixes; zpool_add; zpool_clear; zpool_close; diff --git a/usr/src/lib/pyzfs/Makefile b/usr/src/lib/pyzfs/Makefile new file mode 100644 index 0000000000..c39ef5b9c7 --- /dev/null +++ b/usr/src/lib/pyzfs/Makefile @@ -0,0 +1,54 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.lib + +SUBDIRS= $(MACH) +XGETTEXT= $(GNUXGETTEXT) +XGETFLAGS= $(GNUXGETFLAGS) + +all := TARGET= all +install := TARGET= install +clean := TARGET= clean +clobber := TARGET= clobber +lint := TARGET= lint + +MSGFILES = `$(FIND) . -name '*.py' -o -name '*.c'` +POFILE = pyzfs.po + +.KEEP_STATE: + +all install clean clobber lint: $(SUBDIRS) + +$(POFILE): pofile_MSGFILES + +_msg: $(MSGDOMAINPOFILE) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../../Makefile.msg.targ diff --git a/usr/src/lib/pyzfs/Makefile.com b/usr/src/lib/pyzfs/Makefile.com new file mode 100644 index 0000000000..adfaf729f7 --- /dev/null +++ b/usr/src/lib/pyzfs/Makefile.com @@ -0,0 +1,65 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +LIBRARY = ioctl.a +VERS = +OBJECTS = ioctl.o + +PYSRCS= __init__.py util.py dataset.py \ + allow.py unallow.py \ + userspace.py groupspace.py + + +include ../../Makefile.lib + +LIBLINKS = +SRCDIR = ../common +ROOTLIBDIR= $(ROOT)/usr/lib/python2.4/vendor-packages/zfs +PYOBJS= $(PYSRCS:%.py=$(SRCDIR)/%.pyc) +PYFILES= $(PYSRCS) $(PYSRCS:%.py=%.pyc) +ROOTPYZFSFILES= $(PYFILES:%=$(ROOTLIBDIR)/%) + +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +LIBS = $(DYNLIB) +LDLIBS += -lc -lnvpair -lsec -lidmap -lpython2.4 -lzfs +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -I/usr/include/python2.4 +CPPFLAGS += -I../../../uts/common/fs/zfs +CPPFLAGS += -I../../../common/zfs + +.KEEP_STATE: + +all: $(PYOBJS) $(LIBS) + +install: all $(ROOTPYZFSFILES) + +$(ROOTLIBDIR)/%: % + $(INS.pyfile) + +lint: lintcheck + +include ../../Makefile.targ diff --git a/usr/src/lib/pyzfs/common/__init__.py b/usr/src/lib/pyzfs/common/__init__.py new file mode 100644 index 0000000000..f4b0f53954 --- /dev/null +++ b/usr/src/lib/pyzfs/common/__init__.py @@ -0,0 +1,28 @@ +#! /usr/bin/python2.4 +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +""" +package which provides an administrative interface to ZFS +""" diff --git a/usr/src/lib/pyzfs/common/allow.py b/usr/src/lib/pyzfs/common/allow.py new file mode 100644 index 0000000000..d3a03c7318 --- /dev/null +++ b/usr/src/lib/pyzfs/common/allow.py @@ -0,0 +1,394 @@ +#! /usr/bin/python2.4 +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +"""This module implements the "zfs allow" and "zfs unallow" subcommands. +The only public interface is the zfs.allow.do_allow() function.""" + +import zfs.util +import zfs.dataset +import optparse +import sys +import pwd +import grp +import errno + +_ = zfs.util._ + +class FSPerms(object): + """This class represents all the permissions that are set on a + particular filesystem (not including those inherited).""" + + __slots__ = "create", "sets", "local", "descend", "ld" + __repr__ = zfs.util.default_repr + + def __init__(self, raw): + """Create a FSPerms based on the dict of raw permissions + from zfs.ioctl.get_fsacl().""" + # set of perms + self.create = set() + + # below are { "Ntype name": set(perms) } + # where N is a number that we just use for sorting, + # type is "user", "group", "everyone", or "" (for sets) + # name is a user, group, or set name, or "" (for everyone) + self.sets = dict() + self.local = dict() + self.descend = dict() + self.ld = dict() + + # see the comment in dsl_deleg.c for the definition of whokey + for whokey in raw.keys(): + perms = raw[whokey].keys() + whotypechr = whokey[0].lower() + ws = whokey[3:] + if whotypechr == "c": + self.create.update(perms) + elif whotypechr == "s": + nwho = "1" + ws + self.sets.setdefault(nwho, set()).update(perms) + else: + if whotypechr == "u": + try: + name = pwd.getpwuid(int(ws)).pw_name + except KeyError: + name = ws + nwho = "1user " + name + elif whotypechr == "g": + try: + name = grp.getgrgid(int(ws)).gr_name + except KeyError: + name = ws + nwho = "2group " + name + elif whotypechr == "e": + nwho = "3everyone" + else: + raise ValueError(whotypechr) + + if whokey[1] == "l": + d = self.local + elif whokey[1] == "d": + d = self.descend + else: + raise ValueError(whokey[1]) + + d.setdefault(nwho, set()).update(perms) + + # Find perms that are in both local and descend, and + # move them to ld. + for nwho in self.local: + if nwho not in self.descend: + continue + # note: these are set operations + self.ld[nwho] = self.local[nwho] & self.descend[nwho] + self.local[nwho] -= self.ld[nwho] + self.descend[nwho] -= self.ld[nwho] + + @staticmethod + def __ldstr(d, header): + s = "" + for (nwho, perms) in sorted(d.items()): + # local and descend may have entries where perms + # is an empty set, due to consolidating all + # permissions into ld + if perms: + s += "\t%s %s\n" % \ + (nwho[1:], ",".join(sorted(perms))) + if s: + s = header + s + return s + + def __str__(self): + s = self.__ldstr(self.sets, _("Permission sets:\n")) + + if self.create: + s += _("Create time permissions:\n") + s += "\t%s\n" % ",".join(sorted(self.create)) + + s += self.__ldstr(self.local, _("Local permissions:\n")) + s += self.__ldstr(self.descend, _("Descendent permissions:\n")) + s += self.__ldstr(self.ld, _("Local+Descendent permissions:\n")) + return s.rstrip() + +def args_to_perms(parser, options, who, perms): + """Return a dict of raw perms {"whostr" -> {"perm" -> None}} + based on the command-line input.""" + + # perms is not set if we are doing a "zfs unallow <who> <fs>" to + # remove all of someone's permissions + if perms: + setperms = dict(((p, None) for p in perms if p[0] == "@")) + baseperms = dict(((canonicalized_perm(p), None) + for p in perms if p[0] != "@")) + else: + setperms = None + baseperms = None + + d = dict() + + def storeperm(typechr, inheritchr, arg): + assert typechr in "ugecs" + assert inheritchr in "ld-" + + def mkwhokey(t): + return "%c%c$%s" % (t, inheritchr, arg) + + if baseperms or not perms: + d[mkwhokey(typechr)] = baseperms + if setperms or not perms: + d[mkwhokey(typechr.upper())] = setperms + + def decodeid(w, toidfunc, fmt): + try: + return int(w) + except ValueError: + try: + return toidfunc(w)[2] + except KeyError: + parser.error(fmt % w) + + if options.set: + storeperm("s", "-", who) + elif options.create: + storeperm("c", "-", "") + else: + for w in who: + if options.user: + id = decodeid(w, pwd.getpwnam, + _("invalid user %s")) + typechr = "u" + elif options.group: + id = decodeid(w, grp.getgrnam, + _("invalid group %s")) + typechr = "g" + elif w == "everyone": + id = "" + typechr = "e" + else: + try: + id = pwd.getpwnam(w)[2] + typechr = "u" + except KeyError: + try: + id = grp.getgrnam(w)[2] + typechr = "g" + except KeyError: + parser.error(_("invalid user/group %s") % w) + if options.local: + storeperm(typechr, "l", id) + if options.descend: + storeperm(typechr, "d", id) + return d + +perms_subcmd = dict( + create=_("Must also have the 'mount' ability"), + destroy=_("Must also have the 'mount' ability"), + snapshot=_("Must also have the 'mount' ability"), + rollback=_("Must also have the 'mount' ability"), + clone=_("""Must also have the 'create' ability and 'mount' +\t\t\t\tability in the origin file system"""), + promote=_("""Must also have the 'mount' +\t\t\t\tand 'promote' ability in the origin file system"""), + rename=_("""Must also have the 'mount' and 'create' +\t\t\t\tability in the new parent"""), + receive=_("Must also have the 'mount' and 'create' ability"), + allow=_("Must also have the permission that is being\n\t\t\t\tallowed"), + mount=_("Allows mount/umount of ZFS datasets"), + share=_("Allows sharing file systems over NFS or SMB\n\t\t\t\tprotocols"), + send="", +) + +perms_other = dict( + userprop=_("Allows changing any user property"), + userquota=_("Allows accessing any userquota@... property"), + groupquota=_("Allows accessing any groupquota@... property"), + userused=_("Allows reading any userused@... property"), + groupused=_("Allows reading any groupused@... property"), +) + +def hasset(ds, setname): + """Return True if the given setname (string) is defined for this + ds (Dataset).""" + # It would be nice to cache the result of get_fsacl(). + for raw in ds.get_fsacl().values(): + for whokey in raw.keys(): + if whokey[0].lower() == "s" and whokey[3:] == setname: + return True + return False + +def canonicalized_perm(permname): + """Return the canonical name (string) for this permission (string). + Raises ZFSError if it is not a valid permission.""" + if permname in perms_subcmd.keys() or permname in perms_other.keys(): + return permname + try: + return zfs.dataset.getpropobj(permname).name + except KeyError: + raise zfs.util.ZFSError(errno.EINVAL, permname, + _("invalid permission")) + +def print_perms(): + """Print the set of supported permissions.""" + print(_("\nThe following permissions are supported:\n")) + fmt = "%-16s %-14s\t%s" + print(fmt % (_("NAME"), _("TYPE"), _("NOTES"))) + + for (name, note) in sorted(perms_subcmd.iteritems()): + print(fmt % (name, _("subcommand"), note)) + + for (name, note) in sorted(perms_other.iteritems()): + print(fmt % (name, _("other"), note)) + + for (name, prop) in sorted(zfs.dataset.proptable.iteritems()): + if prop.visible and prop.delegatable(): + print(fmt % (name, _("property"), "")) + +def do_allow(): + """Implementes the "zfs allow" and "zfs unallow" subcommands.""" + un = (sys.argv[1] == "unallow") + + def usage(msg=None): + parser.print_help() + print_perms() + if msg: + print + parser.exit("zfs: error: " + msg) + else: + parser.exit() + + if un: + u = _("""unallow [-rldug] <"everyone"|user|group>[,...] + [<perm|@setname>[,...]] <filesystem|volume> + unallow [-rld] -e [<perm|@setname>[,...]] <filesystem|volume> + unallow [-r] -c [<perm|@setname>[,...]] <filesystem|volume> + unallow [-r] -s @setname [<perm|@setname>[,...]] <filesystem|volume>""") + verb = _("remove") + sstr = _("undefine permission set") + else: + u = _("""allow <filesystem|volume> + allow [-ldug] <"everyone"|user|group>[,...] <perm|@setname>[,...] + <filesystem|volume> + allow [-ld] -e <perm|@setname>[,...] <filesystem|volume> + allow -c <perm|@setname>[,...] <filesystem|volume> + allow -s @setname <perm|@setname>[,...] <filesystem|volume>""") + verb = _("set") + sstr = _("define permission set") + + parser = optparse.OptionParser(usage=u, prog="zfs") + + parser.add_option("-l", action="store_true", dest="local", + help=_("%s permission locally") % verb) + parser.add_option("-d", action="store_true", dest="descend", + help=_("%s permission for descendents") % verb) + parser.add_option("-u", action="store_true", dest="user", + help=_("%s permission for user") % verb) + parser.add_option("-g", action="store_true", dest="group", + help=_("%s permission for group") % verb) + parser.add_option("-e", action="store_true", dest="everyone", + help=_("%s permission for everyone") % verb) + parser.add_option("-c", action="store_true", dest="create", + help=_("%s create time permissions") % verb) + parser.add_option("-s", action="store_true", dest="set", help=sstr) + if un: + parser.add_option("-r", action="store_true", dest="recursive", + help=_("remove permissions recursively")) + + if len(sys.argv) == 3 and not un: + # just print the permissions on this fs + + if sys.argv[2] == "-h": + # hack to make "zfs allow -h" work + usage() + ds = zfs.dataset.Dataset(sys.argv[2]) + + p = dict() + for (fs, raw) in ds.get_fsacl().items(): + p[fs] = FSPerms(raw) + + for fs in sorted(p.keys(), reverse=True): + s = _("---- Permissions on %s ") % fs + print(s + "-" * (70-len(s))) + print(p[fs]) + return + + + (options, args) = parser.parse_args(sys.argv[2:]) + + if sum((bool(options.everyone), bool(options.user), + bool(options.group))) > 1: + parser.error(_("-u, -g, and -e are mutually exclusive")) + + def mungeargs(expected_len): + if un and len(args) == expected_len-1: + return (None, args[expected_len-2]) + elif len(args) == expected_len: + return (args[expected_len-2].split(","), + args[expected_len-1]) + else: + usage(_("wrong number of parameters")) + + if options.set: + if options.local or options.descend or options.user or \ + options.group or options.everyone or options.create: + parser.error(_("invalid option combined with -s")) + if args[0][0] != "@": + parser.error(_("invalid set name: missing '@' prefix")) + + (perms, fsname) = mungeargs(3) + who = args[0] + elif options.create: + if options.local or options.descend or options.user or \ + options.group or options.everyone or options.set: + parser.error(_("invalid option combined with -c")) + + (perms, fsname) = mungeargs(2) + who = None + elif options.everyone: + if options.user or options.group or \ + options.create or options.set: + parser.error(_("invalid option combined with -e")) + + (perms, fsname) = mungeargs(2) + who = ["everyone"] + else: + (perms, fsname) = mungeargs(3) + who = args[0].split(",") + + if not options.local and not options.descend: + options.local = True + options.descend = True + + d = args_to_perms(parser, options, who, perms) + + ds = zfs.dataset.Dataset(fsname, snaps=False) + + if not un and perms: + for p in perms: + if p[0] == "@" and not hasset(ds, p): + parser.error(_("set %s is not defined") % p) + + ds.set_fsacl(un, d) + if un and options.recursive: + for child in ds.descendents(): + child.set_fsacl(un, d) diff --git a/usr/src/lib/pyzfs/common/dataset.py b/usr/src/lib/pyzfs/common/dataset.py new file mode 100644 index 0000000000..b45173e01f --- /dev/null +++ b/usr/src/lib/pyzfs/common/dataset.py @@ -0,0 +1,205 @@ +#! /usr/bin/python2.4 +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +"""Implements the Dataset class, providing methods for manipulating ZFS +datasets. Also implements the Property class, which describes ZFS +properties.""" + +import zfs.ioctl +import zfs.util +import errno + +_ = zfs.util._ + +class Property(object): + """This class represents a ZFS property. It contains + information about the property -- if it's readonly, a number vs + string vs index, etc. Only native properties are represented by + this class -- not user properties (eg "user:prop") or userspace + properties (eg "userquota@joe").""" + + __slots__ = "name", "number", "type", "default", "attr", "validtypes", \ + "values", "colname", "rightalign", "visible", "indextable" + __repr__ = zfs.util.default_repr + + def __init__(self, t): + """t is the tuple of information about this property + from zfs.ioctl.get_proptable, which should match the + members of zprop_desc_t (see zfs_prop.h).""" + + self.name = t[0] + self.number = t[1] + self.type = t[2] + if self.type == "string": + self.default = t[3] + else: + self.default = t[4] + self.attr = t[5] + self.validtypes = t[6] + self.values = t[7] + self.colname = t[8] + self.rightalign = t[9] + self.visible = t[10] + self.indextable = t[11] + + def delegatable(self): + """Return True if this property can be delegated with + "zfs allow".""" + return self.attr != "readonly" + +proptable = dict() +for name, t in zfs.ioctl.get_proptable().iteritems(): + proptable[name] = Property(t) +del name, t + +def getpropobj(name): + """Return the Property object that is identified by the given + name string. It can be the full name, or the column name.""" + try: + return proptable[name] + except KeyError: + for p in proptable.itervalues(): + if p.colname and p.colname.lower() == name: + return p + raise + +class Dataset(object): + """Represents a ZFS dataset (filesystem, snapshot, zvol, clone, etc). + + Generally, this class provides interfaces to the C functions in + zfs.ioctl which actually interface with the kernel to manipulate + datasets. + + Unless otherwise noted, any method can raise a ZFSError to + indicate failure.""" + + __slots__ = "name", "__props" + __repr__ = zfs.util.default_repr + + def __init__(self, name, props=None, + types=("filesystem", "volume"), snaps=True): + """Open the named dataset, checking that it exists and + is of the specified type. + + name is the string name of this dataset. + + props is the property settings dict from zfs.ioctl.next_dataset. + + types is an iterable of strings specifying which types + of datasets are permitted. Accepted strings are + "filesystem" and "volume". Defaults to acceptying all + types. + + snaps is a boolean specifying if snapshots are acceptable. + + Raises a ZFSError if the dataset can't be accessed (eg + doesn't exist) or is not of the specified type. + """ + + self.name = name + + e = zfs.util.ZFSError(errno.EINVAL, + _("cannot open %s") % name, + _("operation not applicable to datasets of this type")) + if "@" in name and not snaps: + raise e + if not props: + props = zfs.ioctl.dataset_props(name) + self.__props = props + if "volume" not in types and self.getprop("type") == 3: + raise e + if "filesystem" not in types and self.getprop("type") == 2: + raise e + + def getprop(self, propname): + """Return the value of the given property for this dataset. + + Currently only works for native properties (those with a + Property object.) + + Raises KeyError if propname does not specify a native property. + Does not raise ZFSError. + """ + + p = getpropobj(propname) + try: + return self.__props[p.name]["value"] + except KeyError: + return p.default + + def parent(self): + """Return a Dataset representing the parent of this one.""" + return Dataset(self.name[:self.name.rindex("/")]) + + def descendents(self): + """A generator function which iterates over all + descendent Datasets (not including snapshots.""" + + cookie = 0 + while True: + # next_dataset raises StopIteration when done + (name, cookie, props) = \ + zfs.ioctl.next_dataset(self.name, False, cookie) + ds = Dataset(name, props) + yield ds + for child in ds.descendents(): + yield child + + def userspace(self, prop): + """A generator function which iterates over a + userspace-type property. + + prop specifies which property ("userused@", + "userquota@", "groupused@", or "groupquota@"). + + returns 3-tuple of domain (string), rid (int), and space (int). + """ + + d = zfs.ioctl.userspace_many(self.name, prop) + for ((domain, rid), space) in d.iteritems(): + yield (domain, rid, space) + + def userspace_upgrade(self): + """Initialize the accounting information for + userused@... and groupused@... properties.""" + return zfs.ioctl.userspace_upgrade(self.name) + + def set_fsacl(self, un, d): + """Add to the "zfs allow"-ed permissions on this Dataset. + + un is True if the specified permissions should be removed. + + d is a dict specifying which permissions to add/remove: + { "whostr" -> None # remove all perms for this entity + "whostr" -> { "perm" -> None} # add/remove these perms + } """ + return zfs.ioctl.set_fsacl(self.name, un, d) + + def get_fsacl(self): + """Get the "zfs allow"-ed permissions on the Dataset. + + Return a dict("whostr": { "perm" -> None }).""" + + return zfs.ioctl.get_fsacl(self.name) diff --git a/usr/src/lib/pyzfs/common/groupspace.py b/usr/src/lib/pyzfs/common/groupspace.py new file mode 100644 index 0000000000..7db4bf3e0c --- /dev/null +++ b/usr/src/lib/pyzfs/common/groupspace.py @@ -0,0 +1,29 @@ +#! /usr/bin/python2.4 +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +import zfs.userspace + +do_groupspace = zfs.userspace.do_userspace + diff --git a/usr/src/lib/pyzfs/common/ioctl.c b/usr/src/lib/pyzfs/common/ioctl.c new file mode 100644 index 0000000000..7a10a4e25e --- /dev/null +++ b/usr/src/lib/pyzfs/common/ioctl.c @@ -0,0 +1,599 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <Python.h> +#include <sys/zfs_ioctl.h> +#include <sys/fs/zfs.h> +#include <strings.h> +#include <unistd.h> +#include <libnvpair.h> +#include <idmap.h> +#include <zone.h> +#include <libintl.h> +#include <libzfs.h> +#include "zfs_prop.h" + +static PyObject *ZFSError; +static int zfsdevfd; + +#ifdef __lint +#define dgettext(x, y) y +#endif + +#define _(s) dgettext(TEXT_DOMAIN, s) + +extern int sid_to_id(char *sid, boolean_t user, uid_t *id); + +/*PRINTFLIKE1*/ +static void +seterr(char *fmt, ...) +{ + char errstr[1024]; + va_list v; + + va_start(v, fmt); + (void) vsnprintf(errstr, sizeof (errstr), fmt, v); + va_end(v); + + PyErr_SetObject(ZFSError, Py_BuildValue("is", errno, errstr)); +} + +static char cmdstr[HIS_MAX_RECORD_LEN]; + +static int +ioctl_with_cmdstr(int ioc, zfs_cmd_t *zc) +{ + int err; + + if (cmdstr[0]) + zc->zc_history = (uint64_t)(uintptr_t)cmdstr; + err = ioctl(zfsdevfd, ioc, zc); + cmdstr[0] = '\0'; + return (err); +} + +static PyObject * +nvl2py(nvlist_t *nvl) +{ + PyObject *pyo; + nvpair_t *nvp; + + pyo = PyDict_New(); + + for (nvp = nvlist_next_nvpair(nvl, NULL); nvp; + nvp = nvlist_next_nvpair(nvl, nvp)) { + PyObject *pyval; + char *sval; + uint64_t ival; + boolean_t bval; + nvlist_t *nval; + + switch (nvpair_type(nvp)) { + case DATA_TYPE_STRING: + (void) nvpair_value_string(nvp, &sval); + pyval = Py_BuildValue("s", sval); + break; + + case DATA_TYPE_UINT64: + (void) nvpair_value_uint64(nvp, &ival); + pyval = Py_BuildValue("K", ival); + break; + + case DATA_TYPE_NVLIST: + (void) nvpair_value_nvlist(nvp, &nval); + pyval = nvl2py(nval); + break; + + case DATA_TYPE_BOOLEAN: + Py_INCREF(Py_None); + pyval = Py_None; + break; + + case DATA_TYPE_BOOLEAN_VALUE: + (void) nvpair_value_boolean_value(nvp, &bval); + pyval = Py_BuildValue("i", bval); + break; + + default: + PyErr_SetNone(PyExc_ValueError); + Py_DECREF(pyo); + return (NULL); + } + + PyDict_SetItemString(pyo, nvpair_name(nvp), pyval); + Py_DECREF(pyval); + } + + return (pyo); +} + +static nvlist_t * +dict2nvl(PyObject *d) +{ + nvlist_t *nvl; + int err; + PyObject *key, *value; + int pos = 0; + + if (!PyDict_Check(d)) { + PyErr_SetObject(PyExc_ValueError, d); + return (NULL); + } + + err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0); + assert(err == 0); + + while (PyDict_Next(d, &pos, &key, &value)) { + char *keystr = PyString_AsString(key); + if (keystr == NULL) { + PyErr_SetObject(PyExc_KeyError, key); + nvlist_free(nvl); + return (NULL); + } + + if (PyDict_Check(value)) { + nvlist_t *valnvl = dict2nvl(value); + err = nvlist_add_nvlist(nvl, keystr, valnvl); + nvlist_free(valnvl); + } else if (value == Py_None) { + err = nvlist_add_boolean(nvl, keystr); + } else if (PyString_Check(value)) { + char *valstr = PyString_AsString(value); + err = nvlist_add_string(nvl, keystr, valstr); + } else if (PyInt_Check(value)) { + uint64_t valint = PyInt_AsUnsignedLongLongMask(value); + err = nvlist_add_uint64(nvl, keystr, valint); + } else if (PyBool_Check(value)) { + boolean_t valbool = value == Py_True ? B_TRUE : B_FALSE; + err = nvlist_add_boolean_value(nvl, keystr, valbool); + } else { + PyErr_SetObject(PyExc_ValueError, value); + nvlist_free(nvl); + return (NULL); + } + assert(err == 0); + } + + return (nvl); +} + +static PyObject * +fakepropval(uint64_t value) +{ + PyObject *d = PyDict_New(); + PyDict_SetItemString(d, "value", Py_BuildValue("K", value)); + return (d); +} + +static void +add_ds_props(zfs_cmd_t *zc, PyObject *nvl) +{ + dmu_objset_stats_t *s = &zc->zc_objset_stats; + PyDict_SetItemString(nvl, "numclones", + fakepropval(s->dds_num_clones)); + PyDict_SetItemString(nvl, "issnap", + fakepropval(s->dds_is_snapshot)); + PyDict_SetItemString(nvl, "inconsistent", + fakepropval(s->dds_inconsistent)); +} + +/* On error, returns NULL but does not set python exception. */ +static PyObject * +ioctl_with_dstnv(int ioc, zfs_cmd_t *zc) +{ + int nvsz = 2048; + void *nvbuf; + PyObject *pynv = NULL; + +again: + nvbuf = malloc(nvsz); + zc->zc_nvlist_dst_size = nvsz; + zc->zc_nvlist_dst = (uintptr_t)nvbuf; + + if (ioctl(zfsdevfd, ioc, zc) == 0) { + nvlist_t *nvl; + + errno = nvlist_unpack(nvbuf, zc->zc_nvlist_dst_size, &nvl, 0); + if (errno == 0) { + pynv = nvl2py(nvl); + nvlist_free(nvl); + } + } else if (errno == ENOMEM) { + free(nvbuf); + nvsz = zc->zc_nvlist_dst_size; + goto again; + } + free(nvbuf); + return (pynv); +} + +static PyObject * +py_next_dataset(PyObject *self, PyObject *args) +{ + int ioc; + uint64_t cookie; + zfs_cmd_t zc = { 0 }; + int snaps; + char *name; + PyObject *nvl; + PyObject *ret = NULL; + + if (!PyArg_ParseTuple(args, "siK", &name, &snaps, &cookie)) + return (NULL); + + (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); + zc.zc_cookie = cookie; + + if (snaps) + ioc = ZFS_IOC_SNAPSHOT_LIST_NEXT; + else + ioc = ZFS_IOC_DATASET_LIST_NEXT; + + nvl = ioctl_with_dstnv(ioc, &zc); + if (nvl) { + add_ds_props(&zc, nvl); + ret = Py_BuildValue("sKO", zc.zc_name, zc.zc_cookie, nvl); + Py_DECREF(nvl); + } else if (errno == ESRCH) { + PyErr_SetNone(PyExc_StopIteration); + } else { + if (snaps) + seterr(_("cannot get snapshots of %s"), name); + else + seterr(_("cannot get child datasets of %s"), name); + } + return (ret); +} + +static PyObject * +py_dataset_props(PyObject *self, PyObject *args) +{ + zfs_cmd_t zc = { 0 }; + int snaps; + char *name; + PyObject *nvl; + + if (!PyArg_ParseTuple(args, "s", &name)) + return (NULL); + + (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); + + nvl = ioctl_with_dstnv(ZFS_IOC_OBJSET_STATS, &zc); + if (nvl) { + add_ds_props(&zc, nvl); + } else { + seterr(_("cannot access dataset %s"), name); + } + return (nvl); +} + +static PyObject * +py_get_fsacl(PyObject *self, PyObject *args) +{ + zfs_cmd_t zc = { 0 }; + char *name; + PyObject *nvl; + + if (!PyArg_ParseTuple(args, "s", &name)) + return (NULL); + + (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); + + nvl = ioctl_with_dstnv(ZFS_IOC_GET_FSACL, &zc); + if (nvl == NULL) + seterr(_("cannot get permissions on %s"), name); + + return (nvl); +} + +static PyObject * +py_set_fsacl(PyObject *self, PyObject *args) +{ + int un; + size_t nvsz; + zfs_cmd_t zc = { 0 }; + char *name, *nvbuf; + PyObject *dict, *file; + nvlist_t *nvl; + int err; + + if (!PyArg_ParseTuple(args, "siO!", &name, &un, + &PyDict_Type, &dict)) + return (NULL); + + nvl = dict2nvl(dict); + if (nvl == NULL) + return (NULL); + + err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE); + assert(err == 0); + nvbuf = malloc(nvsz); + err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0); + assert(err == 0); + + (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); + zc.zc_nvlist_src_size = nvsz; + zc.zc_nvlist_src = (uintptr_t)nvbuf; + zc.zc_perm_action = un; + + err = ioctl_with_cmdstr(ZFS_IOC_SET_FSACL, &zc); + free(nvbuf); + if (err) { + seterr(_("cannot set permissions on %s"), name); + return (NULL); + } + + Py_RETURN_NONE; +} + +static PyObject * +py_userspace_many(PyObject *self, PyObject *args) +{ + zfs_cmd_t zc = { 0 }; + zfs_userquota_prop_t type; + char *name, *propname; + int bufsz = 1<<20; + void *buf; + PyObject *dict, *file; + int error; + + if (!PyArg_ParseTuple(args, "ss", &name, &propname)) + return (NULL); + + for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) + if (strcmp(propname, zfs_userquota_prop_prefixes[type]) == 0) + break; + if (type == ZFS_NUM_USERQUOTA_PROPS) { + PyErr_SetString(PyExc_KeyError, propname); + return (NULL); + } + + dict = PyDict_New(); + buf = malloc(bufsz); + + (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); + zc.zc_objset_type = type; + zc.zc_cookie = 0; + + while (1) { + zfs_useracct_t *zua = buf; + + zc.zc_nvlist_dst = (uintptr_t)buf; + zc.zc_nvlist_dst_size = bufsz; + + error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_MANY, &zc); + if (error || zc.zc_nvlist_dst_size == 0) + break; + + while (zc.zc_nvlist_dst_size > 0) { + PyObject *pykey, *pyval; + + pykey = Py_BuildValue("sI", + zua->zu_domain, zua->zu_rid); + pyval = Py_BuildValue("K", zua->zu_space); + PyDict_SetItem(dict, pykey, pyval); + Py_DECREF(pykey); + Py_DECREF(pyval); + + zua++; + zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t); + } + } + + free(buf); + + if (error != 0) { + Py_DECREF(dict); + seterr(_("cannot get %s property on %s"), propname, name); + return (NULL); + } + + return (dict); +} + +static PyObject * +py_userspace_upgrade(PyObject *self, PyObject *args) +{ + zfs_cmd_t zc = { 0 }; + char *name; + int error; + + if (!PyArg_ParseTuple(args, "s", &name)) + return (NULL); + + (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); + error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_UPGRADE, &zc); + + if (error != 0) { + seterr(_("cannot initialize user accounting information on %s"), + name); + return (NULL); + } + + Py_RETURN_NONE; +} + +static PyObject * +py_sid_to_id(PyObject *self, PyObject *args) +{ + char *sid; + int err, isuser; + uid_t id; + + if (!PyArg_ParseTuple(args, "si", &sid, &isuser)) + return (NULL); + + err = sid_to_id(sid, isuser, &id); + if (err) { + PyErr_SetString(PyExc_KeyError, sid); + return (NULL); + } + + return (Py_BuildValue("I", id)); +} + +/* + * Translate the sid string ("S-1-...") to the user@domain name, if + * possible. There should be a better way to do this, but for now we + * just translate to the (possibly ephemeral) uid and then back again. + */ +static PyObject * +py_sid_to_name(PyObject *self, PyObject *args) +{ + char *sid; + int err, isuser; + uid_t id; + char *name, *domain; + char buf[256]; + + if (!PyArg_ParseTuple(args, "si", &sid, &isuser)) + return (NULL); + + err = sid_to_id(sid, isuser, &id); + if (err) { + PyErr_SetString(PyExc_KeyError, sid); + return (NULL); + } + + if (isuser) { + err = idmap_getwinnamebyuid(id, + IDMAP_REQ_FLG_USE_CACHE, &name, &domain); + } else { + err = idmap_getwinnamebygid(id, + IDMAP_REQ_FLG_USE_CACHE, &name, &domain); + } + if (err != IDMAP_SUCCESS) { + PyErr_SetString(PyExc_KeyError, sid); + return (NULL); + } + (void) snprintf(buf, sizeof (buf), "%s@%s", name, domain); + free(name); + free(domain); + + return (Py_BuildValue("s", buf)); +} + +static PyObject * +py_isglobalzone(PyObject *self, PyObject *args) +{ + return (Py_BuildValue("i", getzoneid() == GLOBAL_ZONEID)); +} + +static PyObject * +py_set_cmdstr(PyObject *self, PyObject *args) +{ + char *str; + + if (!PyArg_ParseTuple(args, "s", &str)) + return (NULL); + + (void) strlcpy(cmdstr, str, sizeof (cmdstr)); + + Py_RETURN_NONE; +} + +static PyObject * +py_get_proptable(PyObject *self, PyObject *args) +{ + zprop_desc_t *t = zfs_prop_get_table(); + PyObject *d = PyDict_New(); + zfs_prop_t i; + + for (i = 0; i < ZFS_NUM_PROPS; i++) { + zprop_desc_t *p = &t[i]; + PyObject *tuple; + static const char *typetable[] = + {"number", "string", "index"}; + static const char *attrtable[] = + {"default", "readonly", "inherit", "onetime"}; + PyObject *indextable; + + if (p->pd_proptype == PROP_TYPE_INDEX) { + const zprop_index_t *it = p->pd_table; + indextable = PyDict_New(); + int j; + for (j = 0; it[j].pi_name; j++) { + PyDict_SetItemString(indextable, + it[j].pi_name, + Py_BuildValue("K", it[j].pi_value)); + } + } else { + Py_INCREF(Py_None); + indextable = Py_None; + } + + tuple = Py_BuildValue("sissKsissiiO", + p->pd_name, p->pd_propnum, typetable[p->pd_proptype], + p->pd_strdefault, p->pd_numdefault, + attrtable[p->pd_attr], p->pd_types, + p->pd_values, p->pd_colname, + p->pd_rightalign, p->pd_visible, indextable); + PyDict_SetItemString(d, p->pd_name, tuple); + Py_DECREF(tuple); + } + + return (d); +} + +static PyMethodDef zfsmethods[] = { + {"next_dataset", py_next_dataset, METH_VARARGS, + "Get next child dataset or snapshot."}, + {"get_fsacl", py_get_fsacl, METH_VARARGS, "Get allowed permissions."}, + {"set_fsacl", py_set_fsacl, METH_VARARGS, "Set allowed permissions."}, + {"userspace_many", py_userspace_many, METH_VARARGS, + "Get user space accounting."}, + {"userspace_upgrade", py_userspace_upgrade, METH_VARARGS, + "Upgrade fs to enable user space accounting."}, + {"set_cmdstr", py_set_cmdstr, METH_VARARGS, + "Set command string for history logging."}, + {"dataset_props", py_dataset_props, METH_VARARGS, + "Get dataset properties."}, + {"get_proptable", py_get_proptable, METH_NOARGS, + "Get property table."}, + /* Below are not really zfs-specific: */ + {"sid_to_id", py_sid_to_id, METH_VARARGS, "Map SID to UID/GID."}, + {"sid_to_name", py_sid_to_name, METH_VARARGS, + "Map SID to name@domain."}, + {"isglobalzone", py_isglobalzone, METH_NOARGS, + "Determine if this is the global zone."}, + {NULL, NULL, 0, NULL} +}; + +void +initioctl(void) +{ + PyObject *zfs_ioctl = Py_InitModule("zfs.ioctl", zfsmethods); + PyObject *zfs_util = PyImport_ImportModule("zfs.util"); + PyObject *devfile; + + if (zfs_util == NULL) + return; + + ZFSError = PyObject_GetAttrString(zfs_util, "ZFSError"); + devfile = PyObject_GetAttrString(zfs_util, "dev"); + zfsdevfd = PyObject_AsFileDescriptor(devfile); + + zfs_prop_init(); +} diff --git a/usr/src/lib/pyzfs/common/mapfile-vers b/usr/src/lib/pyzfs/common/mapfile-vers new file mode 100644 index 0000000000..cc3d648b8a --- /dev/null +++ b/usr/src/lib/pyzfs/common/mapfile-vers @@ -0,0 +1,45 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +SUNWprivate { + global: + initioctl; + local: + *; +}; diff --git a/usr/src/lib/pyzfs/common/unallow.py b/usr/src/lib/pyzfs/common/unallow.py new file mode 100644 index 0000000000..1458dc1328 --- /dev/null +++ b/usr/src/lib/pyzfs/common/unallow.py @@ -0,0 +1,28 @@ +#! /usr/bin/python2.4 +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +import zfs.allow + +do_unallow = zfs.allow.do_allow diff --git a/usr/src/lib/pyzfs/common/userspace.py b/usr/src/lib/pyzfs/common/userspace.py new file mode 100644 index 0000000000..93c65ca59d --- /dev/null +++ b/usr/src/lib/pyzfs/common/userspace.py @@ -0,0 +1,277 @@ +#! /usr/bin/python2.4 +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +"""This module implements the "zfs userspace" and "zfs groupspace" subcommands. +The only public interface is the zfs.userspace.do_userspace() function.""" + +import zfs.util +import zfs.ioctl +import zfs.dataset +import optparse +import sys +import pwd +import grp +import errno + +_ = zfs.util._ + +# map from property name prefix -> (field name, isgroup) +props = { + "userused@": ("used", False), + "userquota@": ("quota", False), + "groupused@": ("used", True), + "groupquota@": ("quota", True), +} + +def skiptype(options, prop): + """Return True if this property (eg "userquota@") should be skipped.""" + (field, isgroup) = props[prop] + if field not in options.fields: + return True + if isgroup and "posixgroup" not in options.types and \ + "smbgroup" not in options.types: + return True + if not isgroup and "posixuser" not in options.types and \ + "smbuser" not in options.types: + return True + return False + +def updatemax(d, k, v): + d[k] = max(d.get(k, None), v) + +def new_entry(options, isgroup, domain, rid): + """Return a dict("field": value) for this domain (string) + rid (int)""" + + if domain: + idstr = "%s-%u" % (domain, rid) + else: + idstr = "%u" % rid + + (typename, mapfunc) = { + (1, 1): ("SMB Group", lambda id: zfs.ioctl.sid_to_name(id, 0)), + (1, 0): ("POSIX Group", lambda id: grp.getgrgid(int(id)).gr_name), + (0, 1): ("SMB User", lambda id: zfs.ioctl.sid_to_name(id, 1)), + (0, 0): ("POSIX User", lambda id: pwd.getpwuid(int(id)).pw_name) + }[isgroup, bool(domain)] + + if typename.lower().replace(" ", "") not in options.types: + return None + + v = dict() + v["type"] = typename + + # python's getpwuid/getgrgid is confused by ephemeral uids + if not options.noname and rid < 1<<31: + try: + v["name"] = mapfunc(idstr) + except KeyError: + pass + + if "name" not in v: + v["name"] = idstr + if not domain: + # it's just a number, so pad it with spaces so + # that it will sort numerically + v["name.sort"] = "%20d" % rid + # fill in default values + v["used"] = "0" + v["used.sort"] = 0 + v["quota"] = "none" + v["quota.sort"] = 0 + return v + +def process_one_raw(acct, maxfieldlen, options, prop, elem): + """Update the acct and maxfieldlen dicts to incorporate the + information from this elem from Dataset.userspace(prop).""" + + (domain, rid, value) = elem + (field, isgroup) = props[prop] + + if options.translate and domain: + try: + rid = zfs.ioctl.sid_to_id("%s-%u" % (domain, rid), + not isgroup) + domain = None + except KeyError: + pass; + key = (isgroup, domain, rid) + + try: + v = acct[key] + except KeyError: + v = new_entry(options, isgroup, domain, rid) + if not v: + return + acct[key] = v + + # Add our value to an existing value, which may be present if + # options.translate is set. + value = v[field + ".sort"] = value + v[field + ".sort"] + + if options.parsable: + v[field] = str(value) + else: + v[field] = zfs.util.nicenum(value) + for k in v.keys(): + # some of the .sort fields are integers, so have no len() + if isinstance(v[k], str): + updatemax(maxfieldlen, k, len(v[k])) + +def do_userspace(): + """Implements the "zfs userspace" and "zfs groupspace" subcommands.""" + + def usage(msg=None): + parser.print_help() + if msg: + print + parser.exit("zfs: error: " + msg) + else: + parser.exit() + + if sys.argv[1] == "userspace": + defaulttypes = "posixuser,smbuser" + else: + defaulttypes = "posixgroup,smbgroup" + + fields = ("type", "name", "used", "quota") + ljustfields = ("type", "name") + types = ("all", "posixuser", "smbuser", "posixgroup", "smbgroup") + + u = _("%s [-niHp] [-o field[,...]] [-sS field] ... \n") % sys.argv[1] + u += _(" [-t type[,...]] <filesystem|snapshot>") + parser = optparse.OptionParser(usage=u, prog="zfs") + + parser.add_option("-n", action="store_true", dest="noname", + help=_("Print numeric ID instead of user/group name")) + parser.add_option("-i", action="store_true", dest="translate", + help=_("translate SID to posix (possibly ephemeral) ID")) + parser.add_option("-H", action="store_true", dest="noheaders", + help=_("no headers, tab delimited output")) + parser.add_option("-p", action="store_true", dest="parsable", + help=_("exact (parsable) numeric output")) + parser.add_option("-o", dest="fields", metavar="field[,...]", + default="type,name,used,quota", + help=_("print only these fields (eg type,name,used,quota)")) + parser.add_option("-s", dest="sortfields", metavar="field", + type="choice", choices=fields, default=list(), + action="callback", callback=zfs.util.append_with_opt, + help=_("sort field")) + parser.add_option("-S", dest="sortfields", metavar="field", + type="choice", choices=fields, #-s sets the default + action="callback", callback=zfs.util.append_with_opt, + help=_("reverse sort field")) + parser.add_option("-t", dest="types", metavar="type[,...]", + default=defaulttypes, + help=_("print only these types (eg posixuser,smbuser,posixgroup,smbgroup,all)")) + + (options, args) = parser.parse_args(sys.argv[2:]) + if len(args) != 1: + usage(_("wrong number of arguments")) + dsname = args[0] + + options.fields = options.fields.split(",") + for f in options.fields: + if f not in fields: + usage(_("invalid field %s") % f) + + options.types = options.types.split(",") + for t in options.types: + if t not in types: + usage(_("invalid type %s") % t) + + if not options.sortfields: + options.sortfields = [("-s", "type"), ("-s", "name")] + + if "all" in options.types: + options.types = types[1:] + + ds = zfs.dataset.Dataset(dsname, types=("filesystem")) + + if ds.getprop("zoned") and zfs.ioctl.isglobalzone(): + options.noname = True + + if not ds.getprop("useraccounting"): + print(_("Initializing accounting information on old filesystem, please wait...")) + ds.userspace_upgrade() + + acct = dict() + maxfieldlen = dict() + + # gather and process accounting information + for prop in props.keys(): + if skiptype(options, prop): + continue; + for elem in ds.userspace(prop): + process_one_raw(acct, maxfieldlen, options, prop, elem) + + # print out headers + if not options.noheaders: + line = str() + for field in options.fields: + # make sure the field header will fit + updatemax(maxfieldlen, field, len(field)) + + if field in ljustfields: + fmt = "%-*s " + else: + fmt = "%*s " + line += fmt % (maxfieldlen[field], field.upper()) + print(line) + + # custom sorting func + def cmpkey(val): + l = list() + for (opt, field) in options.sortfields: + try: + n = val[field + ".sort"] + except KeyError: + n = val[field] + if opt == "-S": + # reverse sorting + try: + n = -n + except TypeError: + # it's a string; decompose it + # into an array of integers, + # each one the negative of that + # character + n = [-ord(c) for c in n] + l.append(n) + return l + + # print out data lines + for val in sorted(acct.itervalues(), key=cmpkey): + line = str() + for field in options.fields: + if options.noheaders: + line += val[field] + line += "\t" + else: + if field in ljustfields: + fmt = "%-*s " + else: + fmt = "%*s " + line += fmt % (maxfieldlen[field], val[field]) + print(line) diff --git a/usr/src/lib/pyzfs/common/util.py b/usr/src/lib/pyzfs/common/util.py new file mode 100644 index 0000000000..14d05a8bc1 --- /dev/null +++ b/usr/src/lib/pyzfs/common/util.py @@ -0,0 +1,138 @@ +#! /usr/bin/python2.4 +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +"""This module provides utility functions for ZFS. +zfs.util.dev -- a file object of /dev/zfs """ + +import gettext +import errno +import os +# Note: this module (zfs.util) should not import zfs.ioctl, because that +# would introduce a circular dependency + +errno.ECANCELED = 47 +errno.ENOTSUP = 48 + +dev = open("/dev/zfs", "w") + +_ = gettext.translation("SUNW_OST_OSLIB", "/usr/lib/locale", + fallback=True).gettext + +def default_repr(self): + """A simple __repr__ function.""" + if self.__slots__: + str = "<" + self.__class__.__name__ + for v in self.__slots__: + str += " %s: %r" % (v, getattr(self, v)) + return str + ">" + else: + return "<%s %s>" % \ + (self.__class__.__name__, repr(self.__dict__)) + +class ZFSError(StandardError): + """This exception class represents a potentially user-visible + ZFS error. If uncaught, it will be printed and the process will + exit with exit code 1. + + errno -- the error number (eg, from ioctl(2)).""" + + __slots__ = "why", "task", "errno" + __repr__ = default_repr + + def __init__(self, eno, task=None, why=None): + """Create a ZFS exception. + eno -- the error number (errno) + task -- a string describing the task that failed + why -- a string describing why it failed (defaults to + strerror(eno))""" + + self.errno = eno + self.task = task + self.why = why + + def __str__(self): + s = "" + if self.task: + s += self.task + ": " + if self.why: + s += self.why + else: + s += self.strerror + return s + + __strs = { + errno.EPERM: _("permission denied"), + errno.ECANCELED: + _("delegated administration is disabled on pool"), + errno.EINTR: _("signal received"), + errno.EIO: _("I/O error"), + errno.ENOENT: _("dataset does not exist"), + errno.ENOSPC: _("out of space"), + errno.EEXIST: _("dataset already exists"), + errno.EBUSY: _("dataset is busy"), + errno.EROFS: + _("snapshot permissions cannot be modified"), + errno.ENAMETOOLONG: _("dataset name is too long"), + errno.ENOTSUP: _("unsupported version"), + errno.EAGAIN: _("pool I/O is currently suspended"), + } + + __strs[errno.EACCES] = __strs[errno.EPERM] + __strs[errno.ENXIO] = __strs[errno.EIO] + __strs[errno.ENODEV] = __strs[errno.EIO] + __strs[errno.EDQUOT] = __strs[errno.ENOSPC] + + @property + def strerror(self): + return ZFSError.__strs.get(self.errno, os.strerror(self.errno)) + +def nicenum(num): + """Return a nice string (eg "1.23M") for this integer.""" + index = 0; + n = num; + + while n >= 1024: + n /= 1024 + index += 1 + + u = " KMGTPE"[index] + if index == 0: + return "%u" % n; + elif n >= 100 or num & ((1024*index)-1) == 0: + # it's an exact multiple of its index, or it wouldn't + # fit as floating point, so print as an integer + return "%u%c" % (n, u) + else: + # due to rounding, it's tricky to tell what precision to + # use; try each precision and see which one fits + for i in (2, 1, 0): + s = "%.*f%c" % (i, float(num) / (1<<(10*index)), u) + if len(s) <= 5: + return s + +def append_with_opt(option, opt, value, parser): + """A function for OptionParser which appends a tuple (opt, value).""" + getattr(parser.values, option.dest).append((opt, value)) + diff --git a/usr/src/lib/pyzfs/i386/Makefile b/usr/src/lib/pyzfs/i386/Makefile new file mode 100644 index 0000000000..4ce880c2ae --- /dev/null +++ b/usr/src/lib/pyzfs/i386/Makefile @@ -0,0 +1,27 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/pyzfs/sparc/Makefile b/usr/src/lib/pyzfs/sparc/Makefile new file mode 100644 index 0000000000..4ce880c2ae --- /dev/null +++ b/usr/src/lib/pyzfs/sparc/Makefile @@ -0,0 +1,27 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) diff --git a/usr/src/pkgdefs/SUNWzfsu/prototype_com b/usr/src/pkgdefs/SUNWzfsu/prototype_com index 502cbd8862..3f61fd59f6 100644 --- a/usr/src/pkgdefs/SUNWzfsu/prototype_com +++ b/usr/src/pkgdefs/SUNWzfsu/prototype_com @@ -20,11 +20,9 @@ # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# # packaging files i copyright @@ -38,6 +36,8 @@ d none usr 755 root sys d none usr/lib 755 root bin d none usr/lib/zfs 755 root bin f none usr/lib/zfs/availdevs 555 root bin +f none usr/lib/zfs/pyzfs.py 555 root bin +f none usr/lib/zfs/pyzfs.pyc 555 root bin d none usr/lib/devfsadm 755 root sys d none usr/lib/devfsadm/linkmod 755 root sys f none usr/lib/devfsadm/linkmod/SUNW_zfs_link.so 755 root sys @@ -57,6 +57,24 @@ d none usr/lib/mdb 755 root sys d none usr/lib/mdb/kvm 755 root sys d none usr/lib/mdb/proc 755 root sys f none usr/lib/mdb/proc/libzpool.so 555 root sys +d none usr/lib/python2.4 755 root bin +d none usr/lib/python2.4/vendor-packages 755 root bin +d none usr/lib/python2.4/vendor-packages/zfs 755 root bin +f none usr/lib/python2.4/vendor-packages/zfs/__init__.py 644 root bin +f none usr/lib/python2.4/vendor-packages/zfs/__init__.pyc 644 root bin +f none usr/lib/python2.4/vendor-packages/zfs/allow.py 644 root bin +f none usr/lib/python2.4/vendor-packages/zfs/allow.pyc 644 root bin +f none usr/lib/python2.4/vendor-packages/zfs/dataset.py 644 root bin +f none usr/lib/python2.4/vendor-packages/zfs/dataset.pyc 644 root bin +f none usr/lib/python2.4/vendor-packages/zfs/groupspace.py 644 root bin +f none usr/lib/python2.4/vendor-packages/zfs/groupspace.pyc 644 root bin +f none usr/lib/python2.4/vendor-packages/zfs/ioctl.so 755 root bin +f none usr/lib/python2.4/vendor-packages/zfs/unallow.py 644 root bin +f none usr/lib/python2.4/vendor-packages/zfs/unallow.pyc 644 root bin +f none usr/lib/python2.4/vendor-packages/zfs/userspace.py 644 root bin +f none usr/lib/python2.4/vendor-packages/zfs/userspace.pyc 644 root bin +f none usr/lib/python2.4/vendor-packages/zfs/util.py 644 root bin +f none usr/lib/python2.4/vendor-packages/zfs/util.pyc 644 root bin d none usr/lib/sysevent 755 root bin d none usr/lib/sysevent/modules 755 root bin f none usr/lib/sysevent/modules/zfs_mod.so 755 root sys diff --git a/usr/src/tools/scripts/check_rtime.pl b/usr/src/tools/scripts/check_rtime.pl index 35d67a29da..c11d70d727 100644 --- a/usr/src/tools/scripts/check_rtime.pl +++ b/usr/src/tools/scripts/check_rtime.pl @@ -202,7 +202,8 @@ $SkipUnrefObject = qr{ /lib.*\ of\ .*libssl3\.so | # non-OSNET /lib.*\ of\ .*libxml2\.so\.2 | # non-OSNET /lib.*\ of\ .*libxslt\.so\.1 | # non-OSNET - /lib.*\ of\ .*libpq\.so\.4 # non-OSNET + /lib.*\ of\ .*libpq\.so\.4 | # non-OSNET + /lib.*\ of\ .*libpython2\.4\.so\.1\.0 # non-OSNET }x; # Define any files that should only have unused (ldd -u) processing. diff --git a/usr/src/uts/common/fs/zfs/arc.c b/usr/src/uts/common/fs/zfs/arc.c index 042b170722..f4d9e67b34 100644 --- a/usr/src/uts/common/fs/zfs/arc.c +++ b/usr/src/uts/common/fs/zfs/arc.c @@ -2504,7 +2504,6 @@ arc_read(zio_t *pio, spa_t *spa, blkptr_t *bp, arc_buf_t *pbuf, uint32_t *arc_flags, const zbookmark_t *zb) { int err; - arc_buf_hdr_t *hdr = pbuf->b_hdr; ASSERT(!refcount_is_zero(&pbuf->b_hdr->b_refcnt)); ASSERT3U((char *)bp - (char *)pbuf->b_data, <, pbuf->b_hdr->b_size); @@ -2512,9 +2511,8 @@ arc_read(zio_t *pio, spa_t *spa, blkptr_t *bp, arc_buf_t *pbuf, err = arc_read_nolock(pio, spa, bp, done, private, priority, zio_flags, arc_flags, zb); - - ASSERT3P(hdr, ==, pbuf->b_hdr); rw_exit(&pbuf->b_lock); + return (err); } diff --git a/usr/src/uts/common/fs/zfs/dbuf.c b/usr/src/uts/common/fs/zfs/dbuf.c index fdfb73b515..213a9d588e 100644 --- a/usr/src/uts/common/fs/zfs/dbuf.c +++ b/usr/src/uts/common/fs/zfs/dbuf.c @@ -329,7 +329,7 @@ dbuf_verify(dmu_buf_impl_t *db) if (db->db_parent == dn->dn_dbuf) { /* db is pointed to by the dnode */ /* ASSERT3U(db->db_blkid, <, dn->dn_nblkptr); */ - if (db->db.db_object == DMU_META_DNODE_OBJECT) + if (DMU_OBJECT_IS_SPECIAL(db->db.db_object)) ASSERT(db->db_parent == NULL); else ASSERT(db->db_parent != NULL); @@ -908,15 +908,11 @@ dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx) * Shouldn't dirty a regular buffer in syncing context. Private * objects may be dirtied in syncing context, but only if they * were already pre-dirtied in open context. - * XXX We may want to prohibit dirtying in syncing context even - * if they did pre-dirty. */ ASSERT(!dmu_tx_is_syncing(tx) || BP_IS_HOLE(dn->dn_objset->os_rootbp) || - dn->dn_object == DMU_META_DNODE_OBJECT || - dn->dn_objset->os_dsl_dataset == NULL || - dsl_dir_is_private(dn->dn_objset->os_dsl_dataset->ds_dir)); - + DMU_OBJECT_IS_SPECIAL(dn->dn_object) || + dn->dn_objset->os_dsl_dataset == NULL); /* * We make this assert for private objects as well, but after we * check if we're already dirty. They are allowed to re-dirty @@ -975,7 +971,8 @@ dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx) /* * Only valid if not already dirty. */ - ASSERT(dn->dn_dirtyctx == DN_UNDIRTIED || dn->dn_dirtyctx == + ASSERT(dn->dn_object == 0 || + dn->dn_dirtyctx == DN_UNDIRTIED || dn->dn_dirtyctx == (dmu_tx_is_syncing(tx) ? DN_DIRTY_SYNC : DN_DIRTY_OPEN)); ASSERT3U(dn->dn_nlevels, >, db->db_level); @@ -987,15 +984,13 @@ dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx) /* * We should only be dirtying in syncing context if it's the - * mos, a spa os, or we're initializing the os. However, we are - * allowed to dirty in syncing context provided we already - * dirtied it in open context. Hence we must make this - * assertion only if we're not already dirty. + * mos or we're initializing the os or it's a special object. + * However, we are allowed to dirty in syncing context provided + * we already dirtied it in open context. Hence we must make + * this assertion only if we're not already dirty. */ - ASSERT(!dmu_tx_is_syncing(tx) || - os->os_dsl_dataset == NULL || - !dsl_dir_is_private(os->os_dsl_dataset->ds_dir) || - !BP_IS_HOLE(os->os_rootbp)); + ASSERT(!dmu_tx_is_syncing(tx) || DMU_OBJECT_IS_SPECIAL(dn->dn_object) || + os->os_dsl_dataset == NULL || BP_IS_HOLE(os->os_rootbp)); ASSERT(db->db.db_size != 0); dprintf_dbuf(db, "size=%llx\n", (u_longlong_t)db->db.db_size); diff --git a/usr/src/uts/common/fs/zfs/dmu.c b/usr/src/uts/common/fs/zfs/dmu.c index b6205bd500..ca871505da 100644 --- a/usr/src/uts/common/fs/zfs/dmu.c +++ b/usr/src/uts/common/fs/zfs/dmu.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -85,6 +85,8 @@ const dmu_object_type_info_t dmu_ot[DMU_OT_NUMTYPES] = { { byteswap_uint64_array, TRUE, "FUID table size" }, { zap_byteswap, TRUE, "DSL dataset next clones"}, { zap_byteswap, TRUE, "scrub work queue" }, + { zap_byteswap, TRUE, "ZFS user/group used" }, + { zap_byteswap, TRUE, "ZFS user/group quota" }, }; int @@ -442,7 +444,8 @@ dmu_free_long_range_impl(objset_t *os, dnode_t *dn, uint64_t offset, object_size = align == 1 ? dn->dn_datablksz : (dn->dn_maxblkid + 1) << dn->dn_datablkshift; - if (trunc || (end = offset + length) > object_size) + end = offset + length; + if (trunc || end > object_size) end = object_size; if (end <= offset) return (0); @@ -450,6 +453,7 @@ dmu_free_long_range_impl(objset_t *os, dnode_t *dn, uint64_t offset, while (length) { start = end; + /* assert(offset <= start) */ err = get_next_chunk(dn, &start, offset); if (err) return (err); diff --git a/usr/src/uts/common/fs/zfs/dmu_objset.c b/usr/src/uts/common/fs/zfs/dmu_objset.c index 691877b2cc..fad841d1a1 100644 --- a/usr/src/uts/common/fs/zfs/dmu_objset.c +++ b/usr/src/uts/common/fs/zfs/dmu_objset.c @@ -164,10 +164,15 @@ dmu_objset_byteswap(void *buf, size_t size) { objset_phys_t *osp = buf; - ASSERT(size == sizeof (objset_phys_t)); + ASSERT(size == OBJSET_OLD_PHYS_SIZE || size == sizeof (objset_phys_t)); dnode_byteswap(&osp->os_meta_dnode); byteswap_uint64_array(&osp->os_zil_header, sizeof (zil_header_t)); osp->os_type = BSWAP_64(osp->os_type); + osp->os_flags = BSWAP_64(osp->os_flags); + if (size == sizeof (objset_phys_t)) { + dnode_byteswap(&osp->os_userused_dnode); + dnode_byteswap(&osp->os_groupused_dnode); + } } int @@ -210,12 +215,29 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp, err = EIO; return (err); } + + /* Increase the blocksize if we are permitted. */ + if (spa_version(spa) >= SPA_VERSION_USERSPACE && + arc_buf_size(osi->os_phys_buf) < sizeof (objset_phys_t)) { + arc_buf_t *buf = arc_buf_alloc(spa, + sizeof (objset_phys_t), &osi->os_phys_buf, + ARC_BUFC_METADATA); + bzero(buf->b_data, sizeof (objset_phys_t)); + bcopy(osi->os_phys_buf->b_data, buf->b_data, + arc_buf_size(osi->os_phys_buf)); + arc_buf_remove_ref(osi->os_phys_buf, &osi->os_phys_buf); + osi->os_phys_buf = buf; + } + osi->os_phys = osi->os_phys_buf->b_data; + osi->os_flags = osi->os_phys->os_flags; } else { - osi->os_phys_buf = arc_buf_alloc(spa, sizeof (objset_phys_t), + int size = spa_version(spa) >= SPA_VERSION_USERSPACE ? + sizeof (objset_phys_t) : OBJSET_OLD_PHYS_SIZE; + osi->os_phys_buf = arc_buf_alloc(spa, size, &osi->os_phys_buf, ARC_BUFC_METADATA); osi->os_phys = osi->os_phys_buf->b_data; - bzero(osi->os_phys, sizeof (objset_phys_t)); + bzero(osi->os_phys, size); } /* @@ -276,6 +298,12 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp, osi->os_meta_dnode = dnode_special_open(osi, &osi->os_phys->os_meta_dnode, DMU_META_DNODE_OBJECT); + if (arc_buf_size(osi->os_phys_buf) >= sizeof (objset_phys_t)) { + osi->os_userused_dnode = dnode_special_open(osi, + &osi->os_phys->os_userused_dnode, DMU_USERUSED_OBJECT); + osi->os_groupused_dnode = dnode_special_open(osi, + &osi->os_phys->os_groupused_dnode, DMU_GROUPUSED_OBJECT); + } /* * We should be the only thread trying to do this because we @@ -456,13 +484,15 @@ dmu_objset_evict(dsl_dataset_t *ds, void *arg) os.os = osi; (void) dmu_objset_evict_dbufs(&os); - ASSERT3P(list_head(&osi->os_dnodes), ==, osi->os_meta_dnode); - ASSERT3P(list_tail(&osi->os_dnodes), ==, osi->os_meta_dnode); - ASSERT3P(list_head(&osi->os_meta_dnode->dn_dbufs), ==, NULL); - dnode_special_close(osi->os_meta_dnode); + if (osi->os_userused_dnode) { + dnode_special_close(osi->os_userused_dnode); + dnode_special_close(osi->os_groupused_dnode); + } zil_free(osi->os_zil); + ASSERT3P(list_head(&osi->os_dnodes), ==, NULL); + VERIFY(arc_buf_remove_ref(osi->os_phys_buf, &osi->os_phys_buf) == 1); mutex_destroy(&osi->os_lock); mutex_destroy(&osi->os_obj_lock); @@ -520,6 +550,10 @@ dmu_objset_create_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp, ASSERT(type != DMU_OST_ANY); ASSERT(type < DMU_OST_NUMTYPES); osi->os_phys->os_type = type; + if (dmu_objset_userused_enabled(osi)) { + osi->os_phys->os_flags |= OBJSET_FLAG_USERACCOUNTING_COMPLETE; + osi->os_flags = osi->os_phys->os_flags; + } dsl_dataset_dirty(ds, tx); @@ -825,7 +859,7 @@ dmu_objset_snapshot(char *fsname, char *snapname, } static void -dmu_objset_sync_dnodes(list_t *list, dmu_tx_t *tx) +dmu_objset_sync_dnodes(list_t *list, list_t *newlist, dmu_tx_t *tx) { dnode_t *dn; @@ -833,14 +867,20 @@ dmu_objset_sync_dnodes(list_t *list, dmu_tx_t *tx) ASSERT(dn->dn_object != DMU_META_DNODE_OBJECT); ASSERT(dn->dn_dbuf->db_data_pending); /* - * Initialize dn_zio outside dnode_sync() - * to accomodate meta-dnode + * Initialize dn_zio outside dnode_sync() because the + * meta-dnode needs to set it ouside dnode_sync(). */ dn->dn_zio = dn->dn_dbuf->db_data_pending->dr_zio; ASSERT(dn->dn_zio); ASSERT3U(dn->dn_nlevels, <=, DN_MAX_LEVELS); list_remove(list, dn); + + if (newlist) { + (void) dnode_add_ref(dn, newlist); + list_insert_tail(newlist, dn); + } + dnode_sync(dn, tx); } } @@ -859,9 +899,12 @@ ready(zio_t *zio, arc_buf_t *abuf, void *arg) ASSERT(BP_GET_LEVEL(bp) == 0); /* - * Update rootbp fill count. + * Update rootbp fill count: it should be the number of objects + * allocated in the object set (not counting the "special" + * objects that are stored in the objset_phys_t -- the meta + * dnode and user/group accounting objects). */ - bp->blk_fill = 1; /* count the meta-dnode */ + bp->blk_fill = 0; for (int i = 0; i < dnp->dn_nblkptr; i++) bp->blk_fill += dnp->dn_blkptr[i].blk_fill; @@ -884,6 +927,7 @@ dmu_objset_sync(objset_impl_t *os, zio_t *pio, dmu_tx_t *tx) writeprops_t wp = { 0 }; zio_t *zio; list_t *list; + list_t *newlist = NULL; dbuf_dirty_record_t *dr; dprintf_ds(os->os_dsl_dataset, "txg=%llu\n", tx->tx_txg); @@ -921,20 +965,41 @@ dmu_objset_sync(objset_impl_t *os, zio_t *pio, dmu_tx_t *tx) } arc_release(os->os_phys_buf, &os->os_phys_buf); + zio = arc_write(pio, os->os_spa, &wp, DMU_OS_IS_L2CACHEABLE(os), tx->tx_txg, os->os_rootbp, os->os_phys_buf, ready, NULL, os, ZIO_PRIORITY_ASYNC_WRITE, ZIO_FLAG_MUSTSUCCEED, &zb); /* - * Sync meta-dnode - the parent IO for the sync is the root block + * Sync special dnodes - the parent IO for the sync is the root block */ os->os_meta_dnode->dn_zio = zio; dnode_sync(os->os_meta_dnode, tx); + os->os_phys->os_flags = os->os_flags; + + if (os->os_userused_dnode && + os->os_userused_dnode->dn_type != DMU_OT_NONE) { + os->os_userused_dnode->dn_zio = zio; + dnode_sync(os->os_userused_dnode, tx); + os->os_groupused_dnode->dn_zio = zio; + dnode_sync(os->os_groupused_dnode, tx); + } + txgoff = tx->tx_txg & TXG_MASK; - dmu_objset_sync_dnodes(&os->os_free_dnodes[txgoff], tx); - dmu_objset_sync_dnodes(&os->os_dirty_dnodes[txgoff], tx); + if (dmu_objset_userused_enabled(os)) { + newlist = &os->os_synced_dnodes; + /* + * We must create the list here because it uses the + * dn_dirty_link[] of this txg. + */ + list_create(newlist, sizeof (dnode_t), + offsetof(dnode_t, dn_dirty_link[txgoff])); + } + + dmu_objset_sync_dnodes(&os->os_free_dnodes[txgoff], newlist, tx); + dmu_objset_sync_dnodes(&os->os_dirty_dnodes[txgoff], newlist, tx); list = &os->os_meta_dnode->dn_dirty_records[txgoff]; while (dr = list_head(list)) { @@ -951,6 +1016,145 @@ dmu_objset_sync(objset_impl_t *os, zio_t *pio, dmu_tx_t *tx) zio_nowait(zio); } +static objset_used_cb_t *used_cbs[DMU_OST_NUMTYPES]; + +void +dmu_objset_register_type(dmu_objset_type_t ost, objset_used_cb_t *cb) +{ + used_cbs[ost] = cb; +} + +boolean_t +dmu_objset_userused_enabled(objset_impl_t *os) +{ + return (spa_version(os->os_spa) >= SPA_VERSION_USERSPACE && + used_cbs[os->os_phys->os_type] && + os->os_userused_dnode); +} + +void +dmu_objset_do_userquota_callbacks(objset_impl_t *os, dmu_tx_t *tx) +{ + dnode_t *dn; + list_t *list = &os->os_synced_dnodes; + static const char zerobuf[DN_MAX_BONUSLEN] = {0}; + + ASSERT(list_head(list) == NULL || dmu_objset_userused_enabled(os)); + + while (dn = list_head(list)) { + dmu_object_type_t bonustype; + + ASSERT(!DMU_OBJECT_IS_SPECIAL(dn->dn_object)); + ASSERT(dn->dn_oldphys); + ASSERT(dn->dn_phys->dn_type == DMU_OT_NONE || + dn->dn_phys->dn_flags & + DNODE_FLAG_USERUSED_ACCOUNTED); + + /* Allocate the user/groupused objects if necessary. */ + if (os->os_userused_dnode->dn_type == DMU_OT_NONE) { + VERIFY(0 == zap_create_claim(&os->os, + DMU_USERUSED_OBJECT, + DMU_OT_USERGROUP_USED, DMU_OT_NONE, 0, tx)); + VERIFY(0 == zap_create_claim(&os->os, + DMU_GROUPUSED_OBJECT, + DMU_OT_USERGROUP_USED, DMU_OT_NONE, 0, tx)); + } + + /* + * If the object was not previously + * accounted, pretend that it was free. + */ + if (!(dn->dn_oldphys->dn_flags & + DNODE_FLAG_USERUSED_ACCOUNTED)) { + bzero(dn->dn_oldphys, sizeof (dnode_phys_t)); + } + + /* + * If the object was freed, use the previous bonustype. + */ + bonustype = dn->dn_phys->dn_bonustype ? + dn->dn_phys->dn_bonustype : dn->dn_oldphys->dn_bonustype; + ASSERT(dn->dn_phys->dn_type != 0 || + (bcmp(DN_BONUS(dn->dn_phys), zerobuf, + DN_MAX_BONUSLEN) == 0 && + DN_USED_BYTES(dn->dn_phys) == 0)); + ASSERT(dn->dn_oldphys->dn_type != 0 || + (bcmp(DN_BONUS(dn->dn_oldphys), zerobuf, + DN_MAX_BONUSLEN) == 0 && + DN_USED_BYTES(dn->dn_oldphys) == 0)); + used_cbs[os->os_phys->os_type](&os->os, bonustype, + DN_BONUS(dn->dn_oldphys), DN_BONUS(dn->dn_phys), + DN_USED_BYTES(dn->dn_oldphys), + DN_USED_BYTES(dn->dn_phys), tx); + + /* + * The mutex is needed here for interlock with dnode_allocate. + */ + mutex_enter(&dn->dn_mtx); + zio_buf_free(dn->dn_oldphys, sizeof (dnode_phys_t)); + dn->dn_oldphys = NULL; + mutex_exit(&dn->dn_mtx); + + list_remove(list, dn); + dnode_rele(dn, list); + } +} + +boolean_t +dmu_objset_userspace_present(objset_t *os) +{ + return (os->os->os_phys->os_flags & + OBJSET_FLAG_USERACCOUNTING_COMPLETE); +} + +int +dmu_objset_userspace_upgrade(objset_t *os) +{ + uint64_t obj; + int err = 0; + + if (dmu_objset_userspace_present(os)) + return (0); + if (!dmu_objset_userused_enabled(os->os)) + return (ENOTSUP); + if (dmu_objset_is_snapshot(os)) + return (EINVAL); + + /* + * We simply need to mark every object dirty, so that it will be + * synced out and now accounted. If this is called + * concurrently, or if we already did some work before crashing, + * that's fine, since we track each object's accounted state + * independently. + */ + + for (obj = 0; err == 0; err = dmu_object_next(os, &obj, FALSE, 0)) { + dmu_tx_t *tx = dmu_tx_create(os); + dmu_buf_t *db; + int objerr; + + if (issig(JUSTLOOKING) && issig(FORREAL)) + return (EINTR); + + objerr = dmu_bonus_hold(os, obj, FTAG, &db); + if (objerr) + continue; + dmu_tx_hold_bonus(tx, obj); + objerr = dmu_tx_assign(tx, TXG_WAIT); + if (objerr) { + dmu_tx_abort(tx); + continue; + } + dmu_buf_will_dirty(db, tx); + dmu_buf_rele(db, FTAG); + dmu_tx_commit(tx); + } + + os->os->os_flags |= OBJSET_FLAG_USERACCOUNTING_COMPLETE; + txg_wait_synced(dmu_objset_pool(os), 0); + return (0); +} + void dmu_objset_space(objset_t *os, uint64_t *refdbytesp, uint64_t *availbytesp, uint64_t *usedobjsp, uint64_t *availobjsp) @@ -984,6 +1188,8 @@ dmu_objset_stats(objset_t *os, nvlist_t *nv) dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_TYPE, os->os->os_phys->os_type); + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USERACCOUNTING, + dmu_objset_userspace_present(os)); } int diff --git a/usr/src/uts/common/fs/zfs/dmu_send.c b/usr/src/uts/common/fs/zfs/dmu_send.c index f2f94a6559..9ca3999dd2 100644 --- a/usr/src/uts/common/fs/zfs/dmu_send.c +++ b/usr/src/uts/common/fs/zfs/dmu_send.c @@ -161,7 +161,9 @@ backup_cb(spa_t *spa, blkptr_t *bp, const zbookmark_t *zb, if (issig(JUSTLOOKING) && issig(FORREAL)) return (EINTR); - if (bp == NULL && zb->zb_object == 0) { + if (zb->zb_object != 0 && DMU_OBJECT_IS_SPECIAL(zb->zb_object)) { + return (0); + } else if (bp == NULL && zb->zb_object == 0) { uint64_t span = BP_SPAN(dnp, zb->zb_level); uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT; err = dump_freeobjects(ba, dnobj, span >> DNODE_SHIFT); diff --git a/usr/src/uts/common/fs/zfs/dmu_traverse.c b/usr/src/uts/common/fs/zfs/dmu_traverse.c index 6737b31cf2..89cbfad29f 100644 --- a/usr/src/uts/common/fs/zfs/dmu_traverse.c +++ b/usr/src/uts/common/fs/zfs/dmu_traverse.c @@ -64,6 +64,9 @@ struct traverse_data { void *td_arg; }; +static int traverse_dnode(struct traverse_data *td, const dnode_phys_t *dnp, + arc_buf_t *buf, uint64_t objset, uint64_t object); + /* ARGSUSED */ static void traverse_zil_block(zilog_t *zilog, blkptr_t *bp, void *arg, uint64_t claim_txg) @@ -189,7 +192,7 @@ traverse_visitbp(struct traverse_data *td, const dnode_phys_t *dnp, } } else if (BP_GET_TYPE(bp) == DMU_OT_DNODE) { uint32_t flags = ARC_WAIT; - int i, j; + int i; int epb = BP_GET_LSIZE(bp) >> DNODE_SHIFT; err = arc_read(NULL, td->td_spa, bp, pbuf, @@ -201,20 +204,15 @@ traverse_visitbp(struct traverse_data *td, const dnode_phys_t *dnp, /* recursively visitbp() blocks below this */ dnp = buf->b_data; for (i = 0; i < epb && err == 0; i++, dnp++) { - for (j = 0; j < dnp->dn_nblkptr; j++) { - SET_BOOKMARK(&czb, zb->zb_objset, - zb->zb_blkid * epb + i, - dnp->dn_nlevels - 1, j); - err = traverse_visitbp(td, dnp, buf, - (blkptr_t *)&dnp->dn_blkptr[j], &czb); - if (err) - break; - } + err = traverse_dnode(td, dnp, buf, zb->zb_objset, + zb->zb_blkid * epb + i); + if (err) + break; } } else if (BP_GET_TYPE(bp) == DMU_OT_OBJSET) { uint32_t flags = ARC_WAIT; objset_phys_t *osp; - int j; + dnode_phys_t *dnp; err = arc_read_nolock(NULL, td->td_spa, bp, arc_getbuf_func, &buf, @@ -225,14 +223,17 @@ traverse_visitbp(struct traverse_data *td, const dnode_phys_t *dnp, osp = buf->b_data; traverse_zil(td, &osp->os_zil_header); - for (j = 0; j < osp->os_meta_dnode.dn_nblkptr; j++) { - SET_BOOKMARK(&czb, zb->zb_objset, 0, - osp->os_meta_dnode.dn_nlevels - 1, j); - err = traverse_visitbp(td, &osp->os_meta_dnode, buf, - (blkptr_t *)&osp->os_meta_dnode.dn_blkptr[j], - &czb); - if (err) - break; + dnp = &osp->os_meta_dnode; + err = traverse_dnode(td, dnp, buf, zb->zb_objset, 0); + if (err == 0 && arc_buf_size(buf) >= sizeof (objset_phys_t)) { + dnp = &osp->os_userused_dnode; + err = traverse_dnode(td, dnp, buf, zb->zb_objset, + DMU_USERUSED_OBJECT); + } + if (err == 0 && arc_buf_size(buf) >= sizeof (objset_phys_t)) { + dnp = &osp->os_groupused_dnode; + err = traverse_dnode(td, dnp, buf, zb->zb_objset, + DMU_GROUPUSED_OBJECT); } } @@ -245,6 +246,23 @@ traverse_visitbp(struct traverse_data *td, const dnode_phys_t *dnp, return (err); } +static int +traverse_dnode(struct traverse_data *td, const dnode_phys_t *dnp, + arc_buf_t *buf, uint64_t objset, uint64_t object) +{ + int j, err = 0; + zbookmark_t czb; + + for (j = 0; j < dnp->dn_nblkptr; j++) { + SET_BOOKMARK(&czb, objset, object, dnp->dn_nlevels - 1, j); + err = traverse_visitbp(td, dnp, buf, + (blkptr_t *)&dnp->dn_blkptr[j], &czb); + if (err) + break; + } + return (err); +} + /* ARGSUSED */ static int traverse_prefetcher(spa_t *spa, blkptr_t *bp, const zbookmark_t *zb, diff --git a/usr/src/uts/common/fs/zfs/dmu_tx.c b/usr/src/uts/common/fs/zfs/dmu_tx.c index 6738239669..41ad4fff67 100644 --- a/usr/src/uts/common/fs/zfs/dmu_tx.c +++ b/usr/src/uts/common/fs/zfs/dmu_tx.c @@ -628,7 +628,7 @@ dmu_tx_hold_free(dmu_tx_t *tx, uint64_t object, uint64_t off, uint64_t len) } void -dmu_tx_hold_zap(dmu_tx_t *tx, uint64_t object, int add, char *name) +dmu_tx_hold_zap(dmu_tx_t *tx, uint64_t object, int add, const char *name) { dmu_tx_hold_t *txh; dnode_t *dn; diff --git a/usr/src/uts/common/fs/zfs/dnode.c b/usr/src/uts/common/fs/zfs/dnode.c index 6872d86ef5..e43fc5defe 100644 --- a/usr/src/uts/common/fs/zfs/dnode.c +++ b/usr/src/uts/common/fs/zfs/dnode.c @@ -156,7 +156,7 @@ dnode_verify(dnode_t *dn) } if (dn->dn_phys->dn_type != DMU_OT_NONE) ASSERT3U(dn->dn_phys->dn_nlevels, <=, dn->dn_nlevels); - ASSERT(dn->dn_object == DMU_META_DNODE_OBJECT || dn->dn_dbuf != NULL); + ASSERT(DMU_OBJECT_IS_SPECIAL(dn->dn_object) || dn->dn_dbuf != NULL); if (dn->dn_dbuf != NULL) { ASSERT3P(dn->dn_phys, ==, (dnode_phys_t *)dn->dn_dbuf->db.db_data + @@ -321,6 +321,7 @@ dnode_destroy(dnode_t *dn) } ASSERT(NULL == list_head(&dn->dn_dbufs)); #endif + ASSERT(dn->dn_oldphys == NULL); mutex_enter(&os->os_lock); list_remove(&os->os_dnodes, dn); @@ -551,6 +552,22 @@ dnode_hold_impl(objset_impl_t *os, uint64_t object, int flag, */ ASSERT(spa_config_held(os->os_spa, SCL_ALL, RW_WRITER) == 0); + if (object == DMU_USERUSED_OBJECT || object == DMU_GROUPUSED_OBJECT) { + dn = (object == DMU_USERUSED_OBJECT) ? + os->os_userused_dnode : os->os_groupused_dnode; + if (dn == NULL) + return (ENOENT); + type = dn->dn_type; + if ((flag & DNODE_MUST_BE_ALLOCATED) && type == DMU_OT_NONE) + return (ENOENT); + if ((flag & DNODE_MUST_BE_FREE) && type != DMU_OT_NONE) + return (EEXIST); + DNODE_VERIFY(dn); + (void) refcount_add(&dn->dn_holds, tag); + *dnp = dn; + return (0); + } + if (object == 0 || object >= DN_MAX_OBJECT) return (EINVAL); @@ -609,7 +626,8 @@ dnode_hold_impl(objset_impl_t *os, uint64_t object, int flag, type = dn->dn_type; if (dn->dn_free_txg || ((flag & DNODE_MUST_BE_ALLOCATED) && type == DMU_OT_NONE) || - ((flag & DNODE_MUST_BE_FREE) && type != DMU_OT_NONE)) { + ((flag & DNODE_MUST_BE_FREE) && + (type != DMU_OT_NONE || dn->dn_oldphys))) { mutex_exit(&dn->dn_mtx); dbuf_rele(db, FTAG); return (type == DMU_OT_NONE ? ENOENT : EEXIST); @@ -674,8 +692,10 @@ dnode_setdirty(dnode_t *dn, dmu_tx_t *tx) objset_impl_t *os = dn->dn_objset; uint64_t txg = tx->tx_txg; - if (dn->dn_object == DMU_META_DNODE_OBJECT) + if (DMU_OBJECT_IS_SPECIAL(dn->dn_object)) { + dsl_dataset_dirty(os->os_dsl_dataset, tx); return; + } DNODE_VERIFY(dn); @@ -1255,7 +1275,7 @@ dnode_next_offset_level(dnode_t *dn, int flags, uint64_t *offset, dprintf("probing object %llu offset %llx level %d of %u\n", dn->dn_object, *offset, lvl, dn->dn_phys->dn_nlevels); - hole = flags & DNODE_FIND_HOLE; + hole = ((flags & DNODE_FIND_HOLE) != 0); inc = (flags & DNODE_FIND_BACKWARDS) ? -1 : 1; ASSERT(txg == 0 || !hole); diff --git a/usr/src/uts/common/fs/zfs/dnode_sync.c b/usr/src/uts/common/fs/zfs/dnode_sync.c index 23dcb4c7b3..184fe292b5 100644 --- a/usr/src/uts/common/fs/zfs/dnode_sync.c +++ b/usr/src/uts/common/fs/zfs/dnode_sync.c @@ -504,9 +504,6 @@ dnode_sync_free(dnode_t *dn, dmu_tx_t *tx) /* * Write out the dnode's dirty buffers. - * - * NOTE: The dnode is kept in memory by being dirty. Once the - * dirty bit is cleared, it may be evicted. Beware of this! */ void dnode_sync(dnode_t *dn, dmu_tx_t *tx) @@ -515,20 +512,33 @@ dnode_sync(dnode_t *dn, dmu_tx_t *tx) dnode_phys_t *dnp = dn->dn_phys; int txgoff = tx->tx_txg & TXG_MASK; list_t *list = &dn->dn_dirty_records[txgoff]; + static const dnode_phys_t zerodn = { 0 }; ASSERT(dmu_tx_is_syncing(tx)); ASSERT(dnp->dn_type != DMU_OT_NONE || dn->dn_allocated_txg); + ASSERT(dnp->dn_type != DMU_OT_NONE || + bcmp(dnp, &zerodn, DNODE_SIZE) == 0); DNODE_VERIFY(dn); ASSERT(dn->dn_dbuf == NULL || arc_released(dn->dn_dbuf->db_buf)); + if (dmu_objset_userused_enabled(dn->dn_objset) && + !DMU_OBJECT_IS_SPECIAL(dn->dn_object)) { + ASSERT(dn->dn_oldphys == NULL); + dn->dn_oldphys = zio_buf_alloc(sizeof (dnode_phys_t)); + *dn->dn_oldphys = *dn->dn_phys; /* struct assignment */ + dn->dn_phys->dn_flags |= DNODE_FLAG_USERUSED_ACCOUNTED; + } else { + /* Once we account for it, we should always account for it. */ + ASSERT(!(dn->dn_phys->dn_flags & + DNODE_FLAG_USERUSED_ACCOUNTED)); + } + mutex_enter(&dn->dn_mtx); if (dn->dn_allocated_txg == tx->tx_txg) { /* The dnode is newly allocated or reallocated */ if (dnp->dn_type == DMU_OT_NONE) { /* this is a first alloc, not a realloc */ - /* XXX shouldn't the phys already be zeroed? */ - bzero(dnp, DNODE_CORE_SIZE); dnp->dn_nlevels = 1; dnp->dn_nblkptr = dn->dn_nblkptr; } @@ -626,7 +636,7 @@ dnode_sync(dnode_t *dn, dmu_tx_t *tx) dbuf_sync_list(list, tx); - if (dn->dn_object != DMU_META_DNODE_OBJECT) { + if (!DMU_OBJECT_IS_SPECIAL(dn->dn_object)) { ASSERT3P(list_head(list), ==, NULL); dnode_rele(dn, (void *)(uintptr_t)tx->tx_txg); } diff --git a/usr/src/uts/common/fs/zfs/dsl_dataset.c b/usr/src/uts/common/fs/zfs/dsl_dataset.c index c584df64c4..d8dd9db9a0 100644 --- a/usr/src/uts/common/fs/zfs/dsl_dataset.c +++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c @@ -975,6 +975,27 @@ dsl_dataset_destroy(dsl_dataset_t *ds, void *tag) (void) dmu_free_object(os, obj); } + /* + * We need to sync out all in-flight IO before we try to evict + * (the dataset evict func is trying to clear the cached entries + * for this dataset in the ARC). + */ + txg_wait_synced(dd->dd_pool, 0); + + /* + * If we managed to free all the objects in open + * context, the user space accounting should be zero. + */ + if (ds->ds_phys->ds_bp.blk_fill == 0 && + dmu_objset_userused_enabled(os->os)) { + uint64_t count; + + ASSERT(zap_count(os, DMU_USERUSED_OBJECT, &count) != 0 || + count == 0); + ASSERT(zap_count(os, DMU_GROUPUSED_OBJECT, &count) != 0 || + count == 0); + } + dmu_objset_close(os); if (err != ESRCH) goto out; @@ -1059,7 +1080,6 @@ dsl_dataset_get_user_ptr(dsl_dataset_t *ds) return (ds->ds_user_ptr); } - blkptr_t * dsl_dataset_get_blkptr(dsl_dataset_t *ds) { @@ -1488,7 +1508,7 @@ dsl_dataset_destroy_sync(void *arg1, void *tag, cred_t *cr, dmu_tx_t *tx) dmu_buf_will_dirty(ds_prev->ds_dbuf, tx); if (after_branch_point && ds_prev->ds_phys->ds_next_clones_obj != 0) { - VERIFY(0 == zap_remove_int(mos, + VERIFY3U(0, ==, zap_remove_int(mos, ds_prev->ds_phys->ds_next_clones_obj, obj, tx)); if (ds->ds_phys->ds_next_snap_obj != 0) { VERIFY(0 == zap_add_int(mos, diff --git a/usr/src/uts/common/fs/zfs/dsl_dir.c b/usr/src/uts/common/fs/zfs/dsl_dir.c index d580475e3e..f19653d924 100644 --- a/usr/src/uts/common/fs/zfs/dsl_dir.c +++ b/usr/src/uts/common/fs/zfs/dsl_dir.c @@ -227,19 +227,6 @@ dsl_dir_namelen(dsl_dir_t *dd) return (result); } -int -dsl_dir_is_private(dsl_dir_t *dd) -{ - int rv = FALSE; - - if (dd->dd_parent && dsl_dir_is_private(dd->dd_parent)) - rv = TRUE; - if (dataset_name_hidden(dd->dd_myname)) - rv = TRUE; - return (rv); -} - - static int getcomponent(const char *path, char *component, const char **nextp) { diff --git a/usr/src/uts/common/fs/zfs/dsl_pool.c b/usr/src/uts/common/fs/zfs/dsl_pool.c index 8921133124..2c5dfca53b 100644 --- a/usr/src/uts/common/fs/zfs/dsl_pool.c +++ b/usr/src/uts/common/fs/zfs/dsl_pool.c @@ -302,23 +302,51 @@ dsl_pool_sync(dsl_pool_t *dp, uint64_t txg) dp->dp_read_overhead = 0; start = gethrtime(); + zio = zio_root(dp->dp_spa, NULL, NULL, ZIO_FLAG_MUSTSUCCEED); while (ds = txg_list_remove(&dp->dp_dirty_datasets, txg)) { - if (!list_link_active(&ds->ds_synced_link)) - list_insert_tail(&dp->dp_synced_datasets, ds); - else - dmu_buf_rele(ds->ds_dbuf, ds); + /* + * We must not sync any non-MOS datasets twice, because + * we may have taken a snapshot of them. However, we + * may sync newly-created datasets on pass 2. + */ + ASSERT(!list_link_active(&ds->ds_synced_link)); + list_insert_tail(&dp->dp_synced_datasets, ds); dsl_dataset_sync(ds, zio, tx); } DTRACE_PROBE(pool_sync__1setup); - err = zio_wait(zio); + write_time = gethrtime() - start; ASSERT(err == 0); DTRACE_PROBE(pool_sync__2rootzio); - while (dstg = txg_list_remove(&dp->dp_sync_tasks, txg)) + for (ds = list_head(&dp->dp_synced_datasets); ds; + ds = list_next(&dp->dp_synced_datasets, ds)) + dmu_objset_do_userquota_callbacks(ds->ds_user_ptr, tx); + + /* + * Sync the datasets again to push out the changes due to + * userquota updates. This must be done before we process the + * sync tasks, because that could cause a snapshot of a dataset + * whose ds_bp will be rewritten when we do this 2nd sync. + */ + zio = zio_root(dp->dp_spa, NULL, NULL, ZIO_FLAG_MUSTSUCCEED); + while (ds = txg_list_remove(&dp->dp_dirty_datasets, txg)) { + ASSERT(list_link_active(&ds->ds_synced_link)); + dmu_buf_rele(ds->ds_dbuf, ds); + dsl_dataset_sync(ds, zio, tx); + } + err = zio_wait(zio); + + while (dstg = txg_list_remove(&dp->dp_sync_tasks, txg)) { + /* + * No more sync tasks should have been added while we + * were syncing. + */ + ASSERT(spa_sync_pass(dp->dp_spa) == 1); dsl_sync_task_group_sync(dstg, tx); + } DTRACE_PROBE(pool_sync__3task); start = gethrtime(); diff --git a/usr/src/uts/common/fs/zfs/dsl_scrub.c b/usr/src/uts/common/fs/zfs/dsl_scrub.c index dbad7b60ab..d8c383a158 100644 --- a/usr/src/uts/common/fs/zfs/dsl_scrub.c +++ b/usr/src/uts/common/fs/zfs/dsl_scrub.c @@ -45,6 +45,8 @@ typedef int (scrub_cb_t)(dsl_pool_t *, const blkptr_t *, const zbookmark_t *); static scrub_cb_t dsl_pool_scrub_clean_cb; static dsl_syncfunc_t dsl_pool_scrub_cancel_sync; +static void scrub_visitdnode(dsl_pool_t *dp, dnode_phys_t *dnp, arc_buf_t *buf, + uint64_t objset, uint64_t object); int zfs_scrub_min_time = 1; /* scrub for at least 1 sec each txg */ int zfs_resilver_min_time = 3; /* resilver for at least 3 sec each txg */ @@ -483,7 +485,7 @@ scrub_visitbp(dsl_pool_t *dp, dnode_phys_t *dnp, } else if (BP_GET_TYPE(bp) == DMU_OT_DNODE) { uint32_t flags = ARC_WAIT; dnode_phys_t *child_dnp; - int i, j; + int i; int epb = BP_GET_LSIZE(bp) >> DNODE_SHIFT; err = arc_read(NULL, dp->dp_spa, bp, pbuf, @@ -498,20 +500,12 @@ scrub_visitbp(dsl_pool_t *dp, dnode_phys_t *dnp, child_dnp = buf->b_data; for (i = 0; i < epb; i++, child_dnp++) { - for (j = 0; j < child_dnp->dn_nblkptr; j++) { - zbookmark_t czb; - - SET_BOOKMARK(&czb, zb->zb_objset, - zb->zb_blkid * epb + i, - child_dnp->dn_nlevels - 1, j); - scrub_visitbp(dp, child_dnp, buf, - &child_dnp->dn_blkptr[j], &czb); - } + scrub_visitdnode(dp, child_dnp, buf, zb->zb_objset, + zb->zb_blkid * epb + i); } } else if (BP_GET_TYPE(bp) == DMU_OT_OBJSET) { uint32_t flags = ARC_WAIT; objset_phys_t *osp; - int j; err = arc_read_nolock(NULL, dp->dp_spa, bp, arc_getbuf_func, &buf, @@ -527,13 +521,13 @@ scrub_visitbp(dsl_pool_t *dp, dnode_phys_t *dnp, traverse_zil(dp, &osp->os_zil_header); - for (j = 0; j < osp->os_meta_dnode.dn_nblkptr; j++) { - zbookmark_t czb; - - SET_BOOKMARK(&czb, zb->zb_objset, 0, - osp->os_meta_dnode.dn_nlevels - 1, j); - scrub_visitbp(dp, &osp->os_meta_dnode, buf, - &osp->os_meta_dnode.dn_blkptr[j], &czb); + scrub_visitdnode(dp, &osp->os_meta_dnode, + buf, zb->zb_objset, 0); + if (arc_buf_size(buf) >= sizeof (objset_phys_t)) { + scrub_visitdnode(dp, &osp->os_userused_dnode, + buf, zb->zb_objset, 0); + scrub_visitdnode(dp, &osp->os_groupused_dnode, + buf, zb->zb_objset, 0); } } @@ -543,6 +537,21 @@ scrub_visitbp(dsl_pool_t *dp, dnode_phys_t *dnp, } static void +scrub_visitdnode(dsl_pool_t *dp, dnode_phys_t *dnp, arc_buf_t *buf, + uint64_t objset, uint64_t object) +{ + int j; + + for (j = 0; j < dnp->dn_nblkptr; j++) { + zbookmark_t czb; + + SET_BOOKMARK(&czb, objset, object, dnp->dn_nlevels - 1, j); + scrub_visitbp(dp, dnp, buf, &dnp->dn_blkptr[j], &czb); + } + +} + +static void scrub_visit_rootbp(dsl_pool_t *dp, dsl_dataset_t *ds, blkptr_t *bp) { zbookmark_t zb; diff --git a/usr/src/uts/common/fs/zfs/spa_errlog.c b/usr/src/uts/common/fs/zfs/spa_errlog.c index c642bd768b..ac0a20aaf0 100644 --- a/usr/src/uts/common/fs/zfs/spa_errlog.c +++ b/usr/src/uts/common/fs/zfs/spa_errlog.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Routines to manage the on-disk persistent error log. * @@ -61,8 +59,8 @@ * lowercase hexidecimal numbers that don't overflow. */ #ifdef _KERNEL -static uint64_t -strtonum(char *str, char **nptr) +uint64_t +strtonum(const char *str, char **nptr) { uint64_t val = 0; char c; @@ -82,7 +80,8 @@ strtonum(char *str, char **nptr) str++; } - *nptr = str; + if (nptr) + *nptr = (char *)str; return (val); } diff --git a/usr/src/uts/common/fs/zfs/sys/dmu.h b/usr/src/uts/common/fs/zfs/sys/dmu.h index fe6dd3dbf5..39e3363c06 100644 --- a/usr/src/uts/common/fs/zfs/sys/dmu.h +++ b/usr/src/uts/common/fs/zfs/sys/dmu.h @@ -114,6 +114,8 @@ typedef enum dmu_object_type { DMU_OT_FUID_SIZE, /* FUID table size UINT64 */ DMU_OT_NEXT_CLONES, /* ZAP */ DMU_OT_SCRUB_QUEUE, /* ZAP */ + DMU_OT_USERGROUP_USED, /* ZAP */ + DMU_OT_USERGROUP_QUOTA, /* ZAP */ DMU_OT_NUMTYPES } dmu_object_type_t; @@ -156,6 +158,9 @@ void zfs_znode_byteswap(void *buf, size_t size); #define DMU_MAX_ACCESS (10<<20) /* 10MB */ #define DMU_MAX_DELETEBLKCNT (20480) /* ~5MB of indirect blocks */ +#define DMU_USERUSED_OBJECT (-1ULL) +#define DMU_GROUPUSED_OBJECT (-2ULL) + /* * Public routines to create, destroy, open, and close objsets. */ @@ -423,7 +428,7 @@ dmu_tx_t *dmu_tx_create(objset_t *os); void dmu_tx_hold_write(dmu_tx_t *tx, uint64_t object, uint64_t off, int len); void dmu_tx_hold_free(dmu_tx_t *tx, uint64_t object, uint64_t off, uint64_t len); -void dmu_tx_hold_zap(dmu_tx_t *tx, uint64_t object, int add, char *name); +void dmu_tx_hold_zap(dmu_tx_t *tx, uint64_t object, int add, const char *name); void dmu_tx_hold_bonus(dmu_tx_t *tx, uint64_t object); void dmu_tx_abort(dmu_tx_t *tx); int dmu_tx_assign(dmu_tx_t *tx, uint64_t txg_how); @@ -563,6 +568,12 @@ extern int dmu_snapshot_realname(objset_t *os, char *name, char *real, int maxlen, boolean_t *conflict); extern int dmu_dir_list_next(objset_t *os, int namelen, char *name, uint64_t *idp, uint64_t *offp); + +typedef void objset_used_cb_t(objset_t *os, dmu_object_type_t bonustype, + void *oldbonus, void *newbonus, uint64_t oldused, uint64_t newused, + dmu_tx_t *tx); +extern void dmu_objset_register_type(dmu_objset_type_t ost, + objset_used_cb_t *cb); extern void dmu_objset_set_user(objset_t *os, void *user_ptr); extern void *dmu_objset_get_user(objset_t *os); diff --git a/usr/src/uts/common/fs/zfs/sys/dmu_objset.h b/usr/src/uts/common/fs/zfs/sys/dmu_objset.h index 2a7b5c53ee..82cb6ad7d9 100644 --- a/usr/src/uts/common/fs/zfs/sys/dmu_objset.h +++ b/usr/src/uts/common/fs/zfs/sys/dmu_objset.h @@ -42,12 +42,20 @@ struct dsl_dataset; struct dmu_tx; struct objset_impl; +#define OBJSET_PHYS_SIZE 2048 +#define OBJSET_OLD_PHYS_SIZE 1024 + +#define OBJSET_FLAG_USERACCOUNTING_COMPLETE (1ULL<<0) + typedef struct objset_phys { dnode_phys_t os_meta_dnode; zil_header_t os_zil_header; uint64_t os_type; - char os_pad[1024 - sizeof (dnode_phys_t) - sizeof (zil_header_t) - - sizeof (uint64_t)]; + uint64_t os_flags; + char os_pad[OBJSET_PHYS_SIZE - sizeof (dnode_phys_t)*3 - + sizeof (zil_header_t) - sizeof (uint64_t)*2]; + dnode_phys_t os_userused_dnode; + dnode_phys_t os_groupused_dnode; } objset_phys_t; struct objset { @@ -62,6 +70,8 @@ typedef struct objset_impl { arc_buf_t *os_phys_buf; objset_phys_t *os_phys; dnode_t *os_meta_dnode; + dnode_t *os_userused_dnode; + dnode_t *os_groupused_dnode; zilog_t *os_zil; objset_t os; uint8_t os_checksum; /* can change, under dsl_dir's locks */ @@ -74,6 +84,8 @@ typedef struct objset_impl { struct dmu_tx *os_synctx; /* XXX sketchy */ blkptr_t *os_rootbp; zil_header_t os_zil_header; + list_t os_synced_dnodes; + uint64_t os_flags; /* Protected by os_obj_lock */ kmutex_t os_obj_lock; @@ -92,6 +104,7 @@ typedef struct objset_impl { } objset_impl_t; #define DMU_META_DNODE_OBJECT 0 +#define DMU_OBJECT_IS_SPECIAL(obj) ((int64_t)(obj) <= 0) #define DMU_OS_IS_L2CACHEABLE(os) \ ((os)->os_secondary_cache == ZFS_CACHE_ALL || \ @@ -128,6 +141,10 @@ objset_impl_t *dmu_objset_create_impl(spa_t *spa, struct dsl_dataset *ds, int dmu_objset_open_impl(spa_t *spa, struct dsl_dataset *ds, blkptr_t *bp, objset_impl_t **osip); void dmu_objset_evict(struct dsl_dataset *ds, void *arg); +void dmu_objset_do_userquota_callbacks(objset_impl_t *os, dmu_tx_t *tx); +boolean_t dmu_objset_userused_enabled(objset_impl_t *os); +int dmu_objset_userspace_upgrade(objset_t *os); +boolean_t dmu_objset_userspace_present(objset_t *os); #ifdef __cplusplus } diff --git a/usr/src/uts/common/fs/zfs/sys/dnode.h b/usr/src/uts/common/fs/zfs/sys/dnode.h index be9e569083..48e4da8cd6 100644 --- a/usr/src/uts/common/fs/zfs/sys/dnode.h +++ b/usr/src/uts/common/fs/zfs/sys/dnode.h @@ -98,7 +98,8 @@ enum dnode_dirtycontext { }; /* Is dn_used in bytes? if not, it's in multiples of SPA_MINBLOCKSIZE */ -#define DNODE_FLAG_USED_BYTES (1<<0) +#define DNODE_FLAG_USED_BYTES (1<<0) +#define DNODE_FLAG_USERUSED_ACCOUNTED (1<<1) typedef struct dnode_phys { uint8_t dn_type; /* dmu_object_type_t */ @@ -131,10 +132,7 @@ typedef struct dnode { */ krwlock_t dn_struct_rwlock; - /* - * Our link on dataset's dd_dnodes list. - * Protected by dd_accounting_mtx. - */ + /* Our link on dn_objset->os_dnodes list; protected by os_lock. */ list_node_t dn_link; /* immutable: */ @@ -191,6 +189,9 @@ typedef struct dnode { /* parent IO for current sync write */ zio_t *dn_zio; + /* used in syncing context */ + dnode_phys_t *dn_oldphys; + /* holds prefetch structure */ struct zfetch dn_zfetch; } dnode_t; diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h b/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h index a29e44e67d..b064c9228e 100644 --- a/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h +++ b/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_DSL_DELEG_H #define _SYS_DSL_DELEG_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/dmu.h> #include <sys/dsl_pool.h> #include <sys/zfs_context.h> @@ -51,6 +49,10 @@ extern "C" { #define ZFS_DELEG_PERM_ALLOW "allow" #define ZFS_DELEG_PERM_USERPROP "userprop" #define ZFS_DELEG_PERM_VSCAN "vscan" +#define ZFS_DELEG_PERM_USERQUOTA "userquota" +#define ZFS_DELEG_PERM_GROUPQUOTA "groupquota" +#define ZFS_DELEG_PERM_USERUSED "userused" +#define ZFS_DELEG_PERM_GROUPUSED "groupused" /* * Note: the names of properties that are marked delegatable are also diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_dir.h b/usr/src/uts/common/fs/zfs/sys/dsl_dir.h index 86b9636cea..56d06388cc 100644 --- a/usr/src/uts/common/fs/zfs/sys/dsl_dir.h +++ b/usr/src/uts/common/fs/zfs/sys/dsl_dir.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -107,7 +107,6 @@ int dsl_dir_open_obj(dsl_pool_t *dp, uint64_t ddobj, const char *tail, void *tag, dsl_dir_t **); void dsl_dir_name(dsl_dir_t *dd, char *buf); int dsl_dir_namelen(dsl_dir_t *dd); -int dsl_dir_is_private(dsl_dir_t *dd); uint64_t dsl_dir_create_sync(dsl_pool_t *dp, dsl_dir_t *pds, const char *name, dmu_tx_t *tx); dsl_checkfunc_t dsl_dir_destroy_check; diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_acl.h b/usr/src/uts/common/fs/zfs/sys/zfs_acl.h index 53a985a102..e123aa1882 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_acl.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_acl.h @@ -166,7 +166,7 @@ typedef struct zfs_acl { struct zfs_fuid_info; -typedef struct zfs_acl_ids_t { +typedef struct zfs_acl_ids { uint64_t z_fuid; /* file owner fuid */ uint64_t z_fgid; /* file group owner fuid */ uint64_t z_mode; /* mode to set on create */ @@ -195,6 +195,7 @@ struct zfsvfs; int zfs_acl_ids_create(struct znode *, int, vattr_t *, cred_t *, vsecattr_t *, zfs_acl_ids_t *); void zfs_acl_ids_free(zfs_acl_ids_t *); +boolean_t zfs_acl_ids_overquota(struct zfsvfs *, zfs_acl_ids_t *); int zfs_getacl(struct znode *, vsecattr_t *, boolean_t, cred_t *); int zfs_setacl(struct znode *, vsecattr_t *, boolean_t, cred_t *); void zfs_acl_rele(void *); diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_fuid.h b/usr/src/uts/common/fs/zfs/sys/zfs_fuid.h index 8cefd3cc0c..f81ddf4a55 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_fuid.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_fuid.h @@ -49,11 +49,11 @@ typedef enum { * Estimate space needed for one more fuid table entry. * for now assume its current size + 1K */ -#define FUID_SIZE_ESTIMATE(z) (z->z_fuid_size + (SPA_MINBLOCKSIZE << 1)) +#define FUID_SIZE_ESTIMATE(z) ((z)->z_fuid_size + (SPA_MINBLOCKSIZE << 1)) -#define FUID_INDEX(x) (x >> 32) -#define FUID_RID(x) (x & 0xffffffff) -#define FUID_ENCODE(idx, rid) ((idx << 32) | rid) +#define FUID_INDEX(x) ((x) >> 32) +#define FUID_RID(x) ((x) & 0xffffffff) +#define FUID_ENCODE(idx, rid) (((uint64_t)(idx) << 32) | (rid)) /* * FUIDs cause problems for the intent log * we need to replay the creation of the FUID, @@ -111,6 +111,10 @@ extern zfs_fuid_info_t *zfs_fuid_info_alloc(void); extern void zfs_fuid_info_free(zfs_fuid_info_t *); extern boolean_t zfs_groupmember(zfsvfs_t *, uint64_t, cred_t *); void zfs_fuid_sync(zfsvfs_t *, dmu_tx_t *); +extern int zfs_fuid_find_by_domain(zfsvfs_t *, const char *domain, + char **retdomain, boolean_t addok); +extern const char *zfs_fuid_find_by_idx(zfsvfs_t *zfsvfs, uint32_t idx); +extern void zfs_fuid_txhold(zfsvfs_t *zfsvfs, dmu_tx_t *tx); #endif char *zfs_fuid_idx_domain(avl_tree_t *, uint32_t); diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h index c2cdfd7844..25b4dea944 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h @@ -166,6 +166,13 @@ typedef struct zfs_cmd { zinject_record_t zc_inject_record; } zfs_cmd_t; +typedef struct zfs_useracct { + char zu_domain[256]; + uid_t zu_rid; + uint32_t zu_pad; + uint64_t zu_space; +} zfs_useracct_t; + #define ZVOL_MAX_MINOR (1 << 16) #define ZFS_MIN_MINOR (ZVOL_MAX_MINOR + 1) diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h b/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h index ca58bc0d4e..b8ed7b27f4 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h @@ -73,10 +73,12 @@ struct zfsvfs { boolean_t z_vscan; /* virus scan on/off */ boolean_t z_use_fuids; /* version allows fuids */ boolean_t z_replay; /* set during ZIL replay */ - kmutex_t z_online_recv_lock; /* recv in prog grabs as WRITER */ + kmutex_t z_online_recv_lock; /* held while recv in progress */ uint64_t z_version; /* ZPL version */ uint64_t z_shares_dir; /* hidden shares dir */ kmutex_t z_lock; + uint64_t z_userquota_obj; + uint64_t z_groupquota_obj; #define ZFS_OBJ_MTX_SZ 64 kmutex_t z_hold_mtx[ZFS_OBJ_MTX_SZ]; /* znode hold locks */ }; @@ -133,6 +135,17 @@ extern uint_t zfs_fsyncer_key; extern int zfs_suspend_fs(zfsvfs_t *zfsvfs, char *osname, int *mode); extern int zfs_resume_fs(zfsvfs_t *zfsvfs, const char *osname, int mode); +extern int zfs_userspace_one(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, + const char *domain, uint64_t rid, uint64_t *valuep); +extern int zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, + uint64_t *cookiep, void *vbuf, uint64_t *bufsizep); +extern int zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, + const char *domain, uint64_t rid, uint64_t quota); +extern boolean_t zfs_usergroup_overquota(zfsvfs_t *zfsvfs, + boolean_t isgroup, uint64_t fuid); +extern int zfs_set_version(zfsvfs_t *zfsvfs, uint64_t newvers); +extern int zfsvfs_create(const char *name, int mode, zfsvfs_t **zvp); +extern void zfsvfs_free(zfsvfs_t *zfsvfs); #ifdef __cplusplus } diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_znode.h b/usr/src/uts/common/fs/zfs/sys/zfs_znode.h index 6a9e91782d..69f4b50f5b 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_znode.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_znode.h @@ -93,6 +93,8 @@ extern "C" { /* * Special attributes for master node. + * "userquota@" and "groupquota@" are also valid (from + * zfs_userquota_prop_prefixes[]). */ #define ZFS_FSID "FSID" #define ZFS_UNLINKED_SET "DELETE_QUEUE" @@ -310,7 +312,6 @@ extern int zfs_create_op_tables(); extern int zfs_sync(vfs_t *vfsp, short flag, cred_t *cr); extern dev_t zfs_cmpldev(uint64_t); extern int zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value); -extern int zfs_set_version(const char *name, uint64_t newvers); extern int zfs_get_stats(objset_t *os, nvlist_t *nv); extern void zfs_znode_dmu_fini(znode_t *); @@ -337,7 +338,7 @@ extern void zfs_log_acl(zilog_t *zilog, dmu_tx_t *tx, znode_t *zp, vsecattr_t *vsecp, zfs_fuid_info_t *fuidp); extern void zfs_xvattr_set(znode_t *zp, xvattr_t *xvap); extern void zfs_upgrade(zfsvfs_t *zfsvfs, dmu_tx_t *tx); -int zfs_create_share_dir(zfsvfs_t *zfsvfs, dmu_tx_t *tx); +extern int zfs_create_share_dir(zfsvfs_t *zfsvfs, dmu_tx_t *tx); extern caddr_t zfs_map_page(page_t *, enum seg_rw); extern void zfs_unmap_page(page_t *, caddr_t); diff --git a/usr/src/uts/common/fs/zfs/zfs_acl.c b/usr/src/uts/common/fs/zfs/zfs_acl.c index b556b433fa..431580e8ec 100644 --- a/usr/src/uts/common/fs/zfs/zfs_acl.c +++ b/usr/src/uts/common/fs/zfs/zfs_acl.c @@ -1900,6 +1900,12 @@ zfs_acl_ids_free(zfs_acl_ids_t *acl_ids) acl_ids->z_fuidp = NULL; } +boolean_t +zfs_acl_ids_overquota(zfsvfs_t *zfsvfs, zfs_acl_ids_t *acl_ids) +{ + return (zfs_usergroup_overquota(zfsvfs, B_FALSE, acl_ids->z_fuid) || + zfs_usergroup_overquota(zfsvfs, B_TRUE, acl_ids->z_fgid)); +} /* * Retrieve a files ACL @@ -2119,18 +2125,8 @@ top: dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, aclp->z_acl_bytes); } fuid_dirtied = zfsvfs->z_fuid_dirty; - if (fuid_dirtied) { - if (zfsvfs->z_fuid_obj == 0) { - dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); - dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, - FUID_SIZE_ESTIMATE(zfsvfs)); - dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL); - } else { - dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); - dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, - FUID_SIZE_ESTIMATE(zfsvfs)); - } - } + if (fuid_dirtied) + zfs_fuid_txhold(zfsvfs, tx); error = dmu_tx_assign(tx, TXG_NOWAIT); if (error) { diff --git a/usr/src/uts/common/fs/zfs/zfs_dir.c b/usr/src/uts/common/fs/zfs/zfs_dir.c index b1ea881901..b3f7683286 100644 --- a/usr/src/uts/common/fs/zfs/zfs_dir.c +++ b/usr/src/uts/common/fs/zfs/zfs_dir.c @@ -816,23 +816,17 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, vnode_t **xvpp, cred_t *cr) if ((error = zfs_acl_ids_create(zp, IS_XATTR, vap, cr, NULL, &acl_ids)) != 0) return (error); + if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) { + zfs_acl_ids_free(&acl_ids); + return (EDQUOT); + } tx = dmu_tx_create(zfsvfs->z_os); dmu_tx_hold_bonus(tx, zp->z_id); dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); fuid_dirtied = zfsvfs->z_fuid_dirty; - if (fuid_dirtied) { - if (zfsvfs->z_fuid_obj == 0) { - dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); - dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, - FUID_SIZE_ESTIMATE(zfsvfs)); - dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL); - } else { - dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); - dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, - FUID_SIZE_ESTIMATE(zfsvfs)); - } - } + if (fuid_dirtied) + zfs_fuid_txhold(zfsvfs, tx); error = dmu_tx_assign(tx, TXG_NOWAIT); if (error) { zfs_acl_ids_free(&acl_ids); diff --git a/usr/src/uts/common/fs/zfs/zfs_fuid.c b/usr/src/uts/common/fs/zfs/zfs_fuid.c index ebb56d1edf..179a1d7be6 100644 --- a/usr/src/uts/common/fs/zfs/zfs_fuid.c +++ b/usr/src/uts/common/fs/zfs/zfs_fuid.c @@ -294,13 +294,14 @@ zfs_fuid_sync(zfsvfs_t *zfsvfs, dmu_tx_t *tx) /* * Query domain table for a given domain. * - * If domain isn't found it is added to AVL trees and - * the zfsvfs->z_fuid_dirty flag will be set to TRUE. - * it will then be necessary for the caller or another - * thread to detect the dirty table and sync out the changes. + * If domain isn't found and addok is set, it is added to AVL trees and + * the zfsvfs->z_fuid_dirty flag will be set to TRUE. It will then be + * necessary for the caller or another thread to detect the dirty table + * and sync out the changes. */ -static int -zfs_fuid_find_by_domain(zfsvfs_t *zfsvfs, const char *domain, char **retdomain) +int +zfs_fuid_find_by_domain(zfsvfs_t *zfsvfs, const char *domain, + char **retdomain, boolean_t addok) { fuid_domain_t searchnode, *findnode; avl_index_t loc; @@ -312,14 +313,14 @@ zfs_fuid_find_by_domain(zfsvfs_t *zfsvfs, const char *domain, char **retdomain) * for the user nobody. */ if (domain[0] == '\0') { - *retdomain = nulldomain; + if (retdomain) + *retdomain = nulldomain; return (0); } searchnode.f_ksid = ksid_lookupdomain(domain); - if (retdomain) { + if (retdomain) *retdomain = searchnode.f_ksid->kd_name; - } if (!zfsvfs->z_fuid_loaded) zfs_fuid_init(zfsvfs); @@ -331,7 +332,7 @@ retry: rw_exit(&zfsvfs->z_fuid_lock); ksiddomain_rele(searchnode.f_ksid); return (findnode->f_idx); - } else { + } else if (addok) { fuid_domain_t *domnode; uint64_t retidx; @@ -351,6 +352,8 @@ retry: zfsvfs->z_fuid_dirty = B_TRUE; rw_exit(&zfsvfs->z_fuid_lock); return (retidx); + } else { + return (-1); } } @@ -360,7 +363,7 @@ retry: * Returns a pointer from an avl node of the domain string. * */ -static char * +const char * zfs_fuid_find_by_idx(zfsvfs_t *zfsvfs, uint32_t idx) { char *domain; @@ -397,7 +400,7 @@ zfs_fuid_map_id(zfsvfs_t *zfsvfs, uint64_t fuid, cred_t *cr, zfs_fuid_type_t type) { uint32_t index = FUID_INDEX(fuid); - char *domain; + const char *domain; uid_t id; if (index == 0) @@ -514,7 +517,7 @@ zfs_fuid_create_cred(zfsvfs_t *zfsvfs, zfs_fuid_type_t type, rid = ksid_getrid(ksid); domain = ksid_getdomain(ksid); - idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain); + idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, B_TRUE); zfs_fuid_node_add(fuidp, kdomain, rid, idx, id, type); @@ -605,7 +608,7 @@ zfs_fuid_create(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr, } } - idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain); + idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, B_TRUE); if (!zfsvfs->z_replay) zfs_fuid_node_add(fuidpp, kdomain, @@ -702,7 +705,7 @@ zfs_groupmember(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr) return (B_TRUE); } } else { - char *domain; + const char *domain; domain = zfs_fuid_find_by_idx(zfsvfs, idx); ASSERT(domain != NULL); @@ -725,4 +728,19 @@ zfs_groupmember(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr) gid = zfs_fuid_map_id(zfsvfs, id, cr, ZFS_GROUP); return (groupmember(gid, cr)); } + +void +zfs_fuid_txhold(zfsvfs_t *zfsvfs, dmu_tx_t *tx) +{ + if (zfsvfs->z_fuid_obj == 0) { + dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, + FUID_SIZE_ESTIMATE(zfsvfs)); + dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL); + } else { + dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); + dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, + FUID_SIZE_ESTIMATE(zfsvfs)); + } +} #endif diff --git a/usr/src/uts/common/fs/zfs/zfs_ioctl.c b/usr/src/uts/common/fs/zfs/zfs_ioctl.c index 6206cf87b5..239b1e0baf 100644 --- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c +++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c @@ -93,6 +93,15 @@ typedef struct zfs_ioc_vec { boolean_t zvec_pool_check; } zfs_ioc_vec_t; +/* This array is indexed by zfs_userquota_prop_t */ +static const char *userquota_perms[] = { + ZFS_DELEG_PERM_USERUSED, + ZFS_DELEG_PERM_USERQUOTA, + ZFS_DELEG_PERM_GROUPUSED, + ZFS_DELEG_PERM_GROUPQUOTA, +}; + +static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc); static void clear_props(char *dataset, nvlist_t *props, nvlist_t *newprops); static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *, boolean_t *); @@ -703,6 +712,55 @@ zfs_secpolicy_inherit(zfs_cmd_t *zc, cred_t *cr) } } +static int +zfs_secpolicy_userspace_one(zfs_cmd_t *zc, cred_t *cr) +{ + int err = zfs_secpolicy_read(zc, cr); + if (err) + return (err); + + if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) + return (EINVAL); + + if (zc->zc_value[0] == 0) { + /* + * They are asking about a posix uid/gid. If it's + * themself, allow it. + */ + if (zc->zc_objset_type == ZFS_PROP_USERUSED || + zc->zc_objset_type == ZFS_PROP_USERQUOTA) { + if (zc->zc_guid == crgetuid(cr)) + return (0); + } else { + if (groupmember(zc->zc_guid, cr)) + return (0); + } + } + + return (zfs_secpolicy_write_perms(zc->zc_name, + userquota_perms[zc->zc_objset_type], cr)); +} + +static int +zfs_secpolicy_userspace_many(zfs_cmd_t *zc, cred_t *cr) +{ + int err = zfs_secpolicy_read(zc, cr); + if (err) + return (err); + + if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) + return (EINVAL); + + return (zfs_secpolicy_write_perms(zc->zc_name, + userquota_perms[zc->zc_objset_type], cr)); +} + +static int +zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, cred_t *cr) +{ + return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION, cr)); +} + /* * Returns the nvlist as specified by the user in the zfs_cmd_t. */ @@ -762,6 +820,69 @@ put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) } static int +getzfsvfs(const char *dsname, zfsvfs_t **zvp) +{ + objset_t *os; + int error; + + error = dmu_objset_open(dsname, DMU_OST_ZFS, + DS_MODE_USER | DS_MODE_READONLY, &os); + if (error) + return (error); + + mutex_enter(&os->os->os_user_ptr_lock); + *zvp = dmu_objset_get_user(os); + if (*zvp) { + VFS_HOLD((*zvp)->z_vfs); + } else { + error = ESRCH; + } + mutex_exit(&os->os->os_user_ptr_lock); + dmu_objset_close(os); + return (error); +} + +/* + * Find a zfsvfs_t for a mounted filesystem, or create our own, in which + * case its z_vfs will be NULL, and it will be opened as the owner. + */ +static int +zfsvfs_hold(const char *name, boolean_t readonly, void *tag, zfsvfs_t **zvp) +{ + int error = 0; + int mode = DS_MODE_OWNER | (readonly ? DS_MODE_READONLY : 0); + + if (getzfsvfs(name, zvp) != 0) + error = zfsvfs_create(name, mode, zvp); + if (error == 0) { + rrw_enter(&(*zvp)->z_teardown_lock, RW_READER, tag); + if ((*zvp)->z_unmounted) { + /* + * XXX we could probably try again, since the unmounting + * thread should be just about to disassociate the + * objset from the zfsvfs. + */ + rrw_exit(&(*zvp)->z_teardown_lock, tag); + return (EBUSY); + } + } + return (error); +} + +static void +zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag) +{ + rrw_exit(&zfsvfs->z_teardown_lock, tag); + + if (zfsvfs->z_vfs) { + VFS_RELE(zfsvfs->z_vfs); + } else { + dmu_objset_close(zfsvfs->z_os); + zfsvfs_free(zfsvfs); + } +} + +static int zfs_ioc_pool_create(zfs_cmd_t *zc) { int error; @@ -1313,6 +1434,23 @@ zfs_ioc_objset_zplprops(zfs_cmd_t *zc) return (err); } +static boolean_t +dataset_name_hidden(const char *name) +{ + /* + * Skip over datasets that are not visible in this zone, + * internal datasets (which have a $ in their name), and + * temporary datasets (which have a % in their name). + */ + if (strchr(name, '$') != NULL) + return (B_TRUE); + if (strchr(name, '%') != NULL) + return (B_TRUE); + if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL)) + return (B_TRUE); + return (B_FALSE); +} + /* * inputs: * zc_name name of filesystem @@ -1321,6 +1459,7 @@ zfs_ioc_objset_zplprops(zfs_cmd_t *zc) * * outputs: * zc_name name of next filesystem + * zc_cookie zap cursor * zc_objset_stats stats * zc_nvlist_dst property nvlist * zc_nvlist_dst_size size of property nvlist @@ -1362,15 +1501,10 @@ zfs_ioc_dataset_list_next(zfs_cmd_t *zc) NULL, &zc->zc_cookie); if (error == ENOENT) error = ESRCH; - } while (error == 0 && !INGLOBALZONE(curproc) && - !zone_dataset_visible(zc->zc_name, NULL)); + } while (error == 0 && dataset_name_hidden(zc->zc_name)); dmu_objset_close(os); - /* - * If it's a hidden dataset (ie. with a '$' in its name), don't - * try to get stats for it. Userland will skip over it. - */ - if (error == 0 && strchr(zc->zc_name, '$') == NULL) + if (error == 0) error = zfs_ioc_objset_stats(zc); /* fill in the stats */ return (error); @@ -1399,9 +1533,10 @@ zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) if (error) return (error == ENOENT ? ESRCH : error); - if (zc->zc_cookie == 0) + if (zc->zc_cookie == 0) { (void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch, NULL, DS_FIND_SNAPSHOTS); + } /* * A dataset name of maximum length cannot have any snapshots, * so exit immediately. @@ -1434,6 +1569,7 @@ zfs_set_prop_nvlist(const char *name, nvlist_t *nvl) uint64_t intval; char *strval; nvlist_t *genericnvl; + boolean_t issnap = (strchr(name, '@') != NULL); /* * First validate permission to set all of the properties @@ -1448,16 +1584,35 @@ zfs_set_prop_nvlist(const char *name, nvlist_t *nvl) * If this is a user-defined property, it must be a * string, and there is no further validation to do. */ - if (!zfs_prop_user(propname) || - nvpair_type(elem) != DATA_TYPE_STRING) - return (EINVAL); + if (zfs_prop_user(propname) && + nvpair_type(elem) == DATA_TYPE_STRING) { + if (error = zfs_secpolicy_write_perms(name, + ZFS_DELEG_PERM_USERPROP, CRED())) + return (error); + continue; + } - if (error = zfs_secpolicy_write_perms(name, - ZFS_DELEG_PERM_USERPROP, CRED())) - return (error); - continue; + if (!issnap && zfs_prop_userquota(propname) && + nvpair_type(elem) == DATA_TYPE_UINT64_ARRAY) { + const char *perm; + const char *up = zfs_userquota_prop_prefixes + [ZFS_PROP_USERQUOTA]; + if (strncmp(propname, up, strlen(up)) == 0) + perm = ZFS_DELEG_PERM_USERQUOTA; + else + perm = ZFS_DELEG_PERM_GROUPQUOTA; + if (error = zfs_secpolicy_write_perms(name, + perm, CRED())) + return (error); + continue; + } + + return (EINVAL); } + if (issnap) + return (EINVAL); + if ((error = zfs_secpolicy_setprop(name, prop, CRED())) != 0) return (error); @@ -1493,8 +1648,7 @@ zfs_set_prop_nvlist(const char *name, nvlist_t *nvl) break; case ZFS_PROP_COPIES: - if (zfs_earlier_version(name, - SPA_VERSION_DITTO_BLOCKS)) + if (zfs_earlier_version(name, SPA_VERSION_DITTO_BLOCKS)) return (ENOTSUP); break; @@ -1520,13 +1674,44 @@ zfs_set_prop_nvlist(const char *name, nvlist_t *nvl) zfs_prop_t prop = zfs_name_to_prop(propname); if (prop == ZPROP_INVAL) { - VERIFY(nvpair_value_string(elem, &strval) == 0); - error = dsl_prop_set(name, propname, 1, - strlen(strval) + 1, strval); - if (error == 0) - continue; - else - goto out; + if (zfs_prop_userquota(propname)) { + uint64_t *valary; + unsigned int vallen; + const char *domain; + zfs_userquota_prop_t type; + uint64_t rid; + uint64_t quota; + zfsvfs_t *zfsvfs; + + VERIFY(nvpair_value_uint64_array(elem, + &valary, &vallen) == 0); + VERIFY(vallen == 3); + type = valary[0]; + rid = valary[1]; + quota = valary[2]; + domain = propname + + strlen(zfs_userquota_prop_prefixes[type]); + + error = zfsvfs_hold(name, B_FALSE, FTAG, + &zfsvfs); + if (error == 0) { + error = zfs_set_userquota(zfsvfs, + type, domain, rid, quota); + zfsvfs_rele(zfsvfs, FTAG); + } + if (error == 0) + continue; + else + goto out; + } else if (zfs_prop_user(propname)) { + VERIFY(nvpair_value_string(elem, &strval) == 0); + error = dsl_prop_set(name, propname, 1, + strlen(strval) + 1, strval); + if (error == 0) + continue; + else + goto out; + } } switch (prop) { @@ -1570,10 +1755,26 @@ zfs_set_prop_nvlist(const char *name, nvlist_t *nvl) break; case ZFS_PROP_VERSION: - if ((error = nvpair_value_uint64(elem, &intval)) != 0 || - (error = zfs_set_version(name, intval)) != 0) + { + zfsvfs_t *zfsvfs; + + if ((error = nvpair_value_uint64(elem, &intval)) != 0) + goto out; + if ((error = zfsvfs_hold(name, B_FALSE, FTAG, + &zfsvfs)) != 0) + goto out; + error = zfs_set_version(zfsvfs, intval); + zfsvfs_rele(zfsvfs, FTAG); + + if (error == 0 && intval >= ZPL_VERSION_USERSPACE) { + zfs_cmd_t zc = { 0 }; + (void) strcpy(zc.zc_name, name); + (void) zfs_ioc_userspace_upgrade(&zc); + } + if (error) goto out; break; + } default: if (nvpair_type(elem) == DATA_TYPE_STRING) { @@ -1980,11 +2181,10 @@ zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) * processing. */ static int -zfs_fill_zplprops_impl(objset_t *os, uint64_t default_zplver, +zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, boolean_t fuids_ok, nvlist_t *createprops, nvlist_t *zplprops, boolean_t *is_ci) { - uint64_t zplver = default_zplver; uint64_t sense = ZFS_PROP_UNDEFINED; uint64_t norm = ZFS_PROP_UNDEFINED; uint64_t u8 = ZFS_PROP_UNDEFINED; @@ -2072,6 +2272,8 @@ zfs_fill_zplprops(const char *dataset, nvlist_t *createprops, ASSERT(cp != NULL); cp[0] = '\0'; + if (zfs_earlier_version(dataset, SPA_VERSION_USERSPACE)) + zplver = ZPL_VERSION_USERSPACE - 1; if (zfs_earlier_version(dataset, SPA_VERSION_FUID)) { zplver = ZPL_VERSION_FUID - 1; fuids_ok = B_FALSE; @@ -2255,6 +2457,7 @@ zfs_ioc_create(zfs_cmd_t *zc) * zc_name name of filesystem * zc_value short name of snapshot * zc_cookie recursive flag + * zc_nvlist_src[_size] property list * * outputs: none */ @@ -2390,31 +2593,19 @@ zfs_ioc_rollback(zfs_cmd_t *zc) if (error) return (error); - if (dmu_objset_type(os) == DMU_OST_ZFS) { - mutex_enter(&os->os->os_user_ptr_lock); - zfsvfs = dmu_objset_get_user(os); - if (zfsvfs != NULL) - VFS_HOLD(zfsvfs->z_vfs); - mutex_exit(&os->os->os_user_ptr_lock); - } - - if (zfsvfs != NULL) { - char *osname; + if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { int mode; - osname = kmem_alloc(MAXNAMELEN, KM_SLEEP); - error = zfs_suspend_fs(zfsvfs, osname, &mode); + error = zfs_suspend_fs(zfsvfs, NULL, &mode); if (error == 0) { int resume_err; - ASSERT(strcmp(osname, zc->zc_name) == 0); error = dmu_objset_rollback(os); - resume_err = zfs_resume_fs(zfsvfs, osname, mode); + resume_err = zfs_resume_fs(zfsvfs, zc->zc_name, mode); error = error ? error : resume_err; } else { dmu_objset_close(os); } - kmem_free(osname, MAXNAMELEN); VFS_RELE(zfsvfs->z_vfs); } else { error = dmu_objset_rollback(os); @@ -2529,32 +2720,26 @@ zfs_ioc_recv(zfs_cmd_t *zc) return (EBADF); } - if (dmu_objset_open(tofs, DMU_OST_ANY, - DS_MODE_USER | DS_MODE_READONLY, &os) == 0) { - /* - * Try to get the zfsvfs for the receiving objset. - * There won't be one if we're operating on a zvol, - * if the objset doesn't exist yet, or is not mounted. - */ - mutex_enter(&os->os->os_user_ptr_lock); - if (zfsvfs = dmu_objset_get_user(os)) { - if (!mutex_tryenter(&zfsvfs->z_online_recv_lock)) { - mutex_exit(&os->os->os_user_ptr_lock); - dmu_objset_close(os); - zfsvfs = NULL; - error = EBUSY; - goto out; - } - VFS_HOLD(zfsvfs->z_vfs); + if (getzfsvfs(tofs, &zfsvfs) == 0) { + if (!mutex_tryenter(&zfsvfs->z_online_recv_lock)) { + VFS_RELE(zfsvfs->z_vfs); + zfsvfs = NULL; + error = EBUSY; + goto out; } - mutex_exit(&os->os->os_user_ptr_lock); - /* * If new properties are supplied, they are to completely * replace the existing ones, so stash away the existing ones. */ if (props) - (void) dsl_prop_get_all(os, &origprops, TRUE); + (void) dsl_prop_get_all(zfsvfs->z_os, &origprops, TRUE); + } else if (props && dmu_objset_open(tofs, DMU_OST_ANY, + DS_MODE_USER | DS_MODE_READONLY, &os) == 0) { + /* + * Get the props even if there was no zfsvfs (zvol or + * unmounted zpl). + */ + (void) dsl_prop_get_all(os, &origprops, TRUE); dmu_objset_close(os); } @@ -2827,6 +3012,120 @@ zfs_ioc_promote(zfs_cmd_t *zc) } /* + * Retrieve a single {user|group}{used|quota}@... property. + * + * inputs: + * zc_name name of filesystem + * zc_objset_type zfs_userquota_prop_t + * zc_value domain name (eg. "S-1-234-567-89") + * zc_guid RID/UID/GID + * + * outputs: + * zc_cookie property value + */ +static int +zfs_ioc_userspace_one(zfs_cmd_t *zc) +{ + zfsvfs_t *zfsvfs; + int error; + + if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) + return (EINVAL); + + error = zfsvfs_hold(zc->zc_name, B_TRUE, FTAG, &zfsvfs); + if (error) + return (error); + + error = zfs_userspace_one(zfsvfs, + zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie); + zfsvfs_rele(zfsvfs, FTAG); + + return (error); +} + +/* + * inputs: + * zc_name name of filesystem + * zc_cookie zap cursor + * zc_objset_type zfs_userquota_prop_t + * zc_nvlist_dst[_size] buffer to fill (not really an nvlist) + * + * outputs: + * zc_nvlist_dst[_size] data buffer (array of zfs_useracct_t) + * zc_cookie zap cursor + */ +static int +zfs_ioc_userspace_many(zfs_cmd_t *zc) +{ + zfsvfs_t *zfsvfs; + int error; + + error = zfsvfs_hold(zc->zc_name, B_TRUE, FTAG, &zfsvfs); + if (error) + return (error); + + int bufsize = zc->zc_nvlist_dst_size; + void *buf = kmem_alloc(bufsize, KM_SLEEP); + + error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie, + buf, &zc->zc_nvlist_dst_size); + + if (error == 0) { + error = xcopyout(buf, + (void *)(uintptr_t)zc->zc_nvlist_dst, + zc->zc_nvlist_dst_size); + } + kmem_free(buf, bufsize); + zfsvfs_rele(zfsvfs, FTAG); + + return (error); +} + +/* + * inputs: + * zc_name name of filesystem + * + * outputs: + * none + */ +static int +zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) +{ + objset_t *os; + int error; + zfsvfs_t *zfsvfs; + + if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { + if (!dmu_objset_userused_enabled(zfsvfs->z_os->os)) { + /* + * If userused is not enabled, it may be because the + * objset needs to be closed & reopened (to grow the + * objset_phys_t). Suspend/resume the fs will do that. + */ + int mode; + error = zfs_suspend_fs(zfsvfs, NULL, &mode); + if (error == 0) { + error = zfs_resume_fs(zfsvfs, + zc->zc_name, mode); + } + } + if (error == 0) + error = dmu_objset_userspace_upgrade(zfsvfs->z_os); + VFS_RELE(zfsvfs->z_vfs); + } else { + error = dmu_objset_open(zc->zc_name, DMU_OST_ANY, + DS_MODE_USER, &os); + if (error) + return (error); + + error = dmu_objset_userspace_upgrade(os); + dmu_objset_close(os); + } + + return (error); +} + +/* * We don't want to have a hard dependency * against some special symbols in sharefs * nfs, and smbsrv. Determine them if needed when @@ -3206,27 +3505,24 @@ static zfs_ioc_vec_t zfs_ioc_vec[] = { { zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE, B_TRUE }, { zfs_ioc_smb_acl, zfs_secpolicy_smb_acl, DATASET_NAME, B_FALSE, - B_FALSE } + B_FALSE }, + { zfs_ioc_userspace_one, zfs_secpolicy_userspace_one, + DATASET_NAME, B_FALSE, B_FALSE }, + { zfs_ioc_userspace_many, zfs_secpolicy_userspace_many, + DATASET_NAME, B_FALSE, B_FALSE }, + { zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade, + DATASET_NAME, B_FALSE, B_TRUE }, }; int pool_status_check(const char *name, zfs_ioc_namecheck_t type) { spa_t *spa; - char pool[ZFS_MAXNAMELEN]; int error; ASSERT(type == POOL_NAME || type == DATASET_NAME); - (void) strlcpy(pool, name, ZFS_MAXNAMELEN); - if (type == DATASET_NAME) { - char *p; - - if ((p = strpbrk(pool, "/@")) != NULL) - *p = '\0'; - } - - error = spa_open(pool, &spa, FTAG); + error = spa_open(name, &spa, FTAG); if (error == 0) { if (spa_suspended(spa)) error = EAGAIN; @@ -3292,7 +3588,7 @@ zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) rc = xcopyout(zc, (void *)arg, sizeof (zfs_cmd_t)); if (error == 0) { error = rc; - if (zfs_ioc_vec[vec].zvec_his_log == B_TRUE) + if (zfs_ioc_vec[vec].zvec_his_log) zfs_log_history(zc); } diff --git a/usr/src/uts/common/fs/zfs/zfs_vfsops.c b/usr/src/uts/common/fs/zfs/zfs_vfsops.c index d6062f07b0..698b09a166 100644 --- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c +++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c @@ -568,6 +568,393 @@ unregister: } +static void +uidacct(objset_t *os, boolean_t isgroup, uint64_t fuid, + int64_t delta, dmu_tx_t *tx) +{ + uint64_t used = 0; + char buf[32]; + int err; + uint64_t obj = isgroup ? DMU_GROUPUSED_OBJECT : DMU_USERUSED_OBJECT; + + if (delta == 0) + return; + + (void) snprintf(buf, sizeof (buf), "%llx", (longlong_t)fuid); + err = zap_lookup(os, obj, buf, 8, 1, &used); + ASSERT(err == 0 || err == ENOENT); + /* no underflow/overflow */ + ASSERT(delta > 0 || used >= -delta); + ASSERT(delta < 0 || used + delta > used); + used += delta; + if (used == 0) + err = zap_remove(os, obj, buf, tx); + else + err = zap_update(os, obj, buf, 8, 1, &used, tx); + ASSERT(err == 0); +} + +static void +zfs_space_delta_cb(objset_t *os, dmu_object_type_t bonustype, + void *oldbonus, void *newbonus, + uint64_t oldused, uint64_t newused, dmu_tx_t *tx) +{ + znode_phys_t *oldznp = oldbonus; + znode_phys_t *newznp = newbonus; + + if (bonustype != DMU_OT_ZNODE) + return; + + /* We charge 512 for the dnode (if it's allocated). */ + if (oldznp->zp_gen != 0) + oldused += DNODE_SIZE; + if (newznp->zp_gen != 0) + newused += DNODE_SIZE; + + if (oldznp->zp_uid == newznp->zp_uid) { + uidacct(os, B_FALSE, oldznp->zp_uid, newused-oldused, tx); + } else { + uidacct(os, B_FALSE, oldznp->zp_uid, -oldused, tx); + uidacct(os, B_FALSE, newznp->zp_uid, newused, tx); + } + + if (oldznp->zp_gid == newznp->zp_gid) { + uidacct(os, B_TRUE, oldznp->zp_gid, newused-oldused, tx); + } else { + uidacct(os, B_TRUE, oldznp->zp_gid, -oldused, tx); + uidacct(os, B_TRUE, newznp->zp_gid, newused, tx); + } +} + +static void +fuidstr_to_sid(zfsvfs_t *zfsvfs, const char *fuidstr, + char *domainbuf, int buflen, uid_t *ridp) +{ + extern uint64_t strtonum(const char *str, char **nptr); + uint64_t fuid; + const char *domain; + + fuid = strtonum(fuidstr, NULL); + + domain = zfs_fuid_find_by_idx(zfsvfs, FUID_INDEX(fuid)); + if (domain) + (void) strlcpy(domainbuf, domain, buflen); + else + domainbuf[0] = '\0'; + *ridp = FUID_RID(fuid); +} + +static uint64_t +zfs_userquota_prop_to_obj(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type) +{ + switch (type) { + case ZFS_PROP_USERUSED: + return (DMU_USERUSED_OBJECT); + case ZFS_PROP_GROUPUSED: + return (DMU_GROUPUSED_OBJECT); + case ZFS_PROP_USERQUOTA: + return (zfsvfs->z_userquota_obj); + case ZFS_PROP_GROUPQUOTA: + return (zfsvfs->z_groupquota_obj); + } + return (0); +} + +int +zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, + uint64_t *cookiep, void *vbuf, uint64_t *bufsizep) +{ + int error; + zap_cursor_t zc; + zap_attribute_t za; + zfs_useracct_t *buf = vbuf; + uint64_t obj; + + if (!dmu_objset_userspace_present(zfsvfs->z_os)) + return (ENOTSUP); + + obj = zfs_userquota_prop_to_obj(zfsvfs, type); + if (obj == 0) { + *bufsizep = 0; + return (0); + } + + for (zap_cursor_init_serialized(&zc, zfsvfs->z_os, obj, *cookiep); + (error = zap_cursor_retrieve(&zc, &za)) == 0; + zap_cursor_advance(&zc)) { + if ((uintptr_t)buf - (uintptr_t)vbuf + sizeof (zfs_useracct_t) > + *bufsizep) + break; + + fuidstr_to_sid(zfsvfs, za.za_name, + buf->zu_domain, sizeof (buf->zu_domain), &buf->zu_rid); + + buf->zu_space = za.za_first_integer; + buf++; + } + if (error == ENOENT) + error = 0; + + ASSERT3U((uintptr_t)buf - (uintptr_t)vbuf, <=, *bufsizep); + *bufsizep = (uintptr_t)buf - (uintptr_t)vbuf; + *cookiep = zap_cursor_serialize(&zc); + zap_cursor_fini(&zc); + return (error); +} + +/* + * buf must be big enough (eg, 32 bytes) + */ +static int +id_to_fuidstr(zfsvfs_t *zfsvfs, const char *domain, uid_t rid, + char *buf, boolean_t addok) +{ + uint64_t fuid; + int domainid = 0; + + if (domain && domain[0]) { + domainid = zfs_fuid_find_by_domain(zfsvfs, domain, NULL, addok); + if (domainid == -1) + return (ENOENT); + } + fuid = FUID_ENCODE(domainid, rid); + (void) sprintf(buf, "%llx", (longlong_t)fuid); + return (0); +} + +int +zfs_userspace_one(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, + const char *domain, uint64_t rid, uint64_t *valp) +{ + char buf[32]; + int err; + uint64_t obj; + + *valp = 0; + + if (!dmu_objset_userspace_present(zfsvfs->z_os)) + return (ENOTSUP); + + obj = zfs_userquota_prop_to_obj(zfsvfs, type); + if (obj == 0) + return (0); + + err = id_to_fuidstr(zfsvfs, domain, rid, buf, B_FALSE); + if (err) + return (err); + + err = zap_lookup(zfsvfs->z_os, obj, buf, 8, 1, valp); + if (err == ENOENT) + err = 0; + return (err); +} + +int +zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, + const char *domain, uint64_t rid, uint64_t quota) +{ + char buf[32]; + int err; + dmu_tx_t *tx; + uint64_t *objp; + boolean_t fuid_dirtied; + + if (type != ZFS_PROP_USERQUOTA && type != ZFS_PROP_GROUPQUOTA) + return (EINVAL); + + if (zfsvfs->z_version < ZPL_VERSION_USERSPACE) + return (ENOTSUP); + + objp = (type == ZFS_PROP_USERQUOTA) ? &zfsvfs->z_userquota_obj : + &zfsvfs->z_groupquota_obj; + + err = id_to_fuidstr(zfsvfs, domain, rid, buf, B_TRUE); + if (err) + return (err); + fuid_dirtied = zfsvfs->z_fuid_dirty; + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_zap(tx, *objp ? *objp : DMU_NEW_OBJECT, B_TRUE, NULL); + if (*objp == 0) { + dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, B_TRUE, + zfs_userquota_prop_prefixes[type]); + } + if (fuid_dirtied) + zfs_fuid_txhold(zfsvfs, tx); + err = dmu_tx_assign(tx, TXG_WAIT); + if (err) { + dmu_tx_abort(tx); + return (err); + } + + mutex_enter(&zfsvfs->z_lock); + if (*objp == 0) { + *objp = zap_create(zfsvfs->z_os, DMU_OT_USERGROUP_QUOTA, + DMU_OT_NONE, 0, tx); + VERIFY(0 == zap_add(zfsvfs->z_os, MASTER_NODE_OBJ, + zfs_userquota_prop_prefixes[type], 8, 1, objp, tx)); + } + mutex_exit(&zfsvfs->z_lock); + + if (quota == 0) { + err = zap_remove(zfsvfs->z_os, *objp, buf, tx); + if (err == ENOENT) + err = 0; + } else { + err = zap_update(zfsvfs->z_os, *objp, buf, 8, 1, "a, tx); + } + ASSERT(err == 0); + if (fuid_dirtied) + zfs_fuid_sync(zfsvfs, tx); + dmu_tx_commit(tx); + return (err); +} + +boolean_t +zfs_usergroup_overquota(zfsvfs_t *zfsvfs, boolean_t isgroup, uint64_t fuid) +{ + char buf[32]; + uint64_t used, quota, usedobj, quotaobj; + int err; + + usedobj = isgroup ? DMU_GROUPUSED_OBJECT : DMU_USERUSED_OBJECT; + quotaobj = isgroup ? zfsvfs->z_groupquota_obj : zfsvfs->z_userquota_obj; + + if (quotaobj == 0 || zfsvfs->z_replay) + return (B_FALSE); + + (void) sprintf(buf, "%llx", (longlong_t)fuid); + err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, "a); + if (err != 0) + return (B_FALSE); + + err = zap_lookup(zfsvfs->z_os, usedobj, buf, 8, 1, &used); + if (err != 0) + return (B_FALSE); + return (used >= quota); +} + +int +zfsvfs_create(const char *osname, int mode, zfsvfs_t **zvp) +{ + objset_t *os; + zfsvfs_t *zfsvfs; + uint64_t zval; + int i, error; + + if (error = dsl_prop_get_integer(osname, "readonly", &zval, NULL)) + return (error); + if (zval) + mode |= DS_MODE_READONLY; + + error = dmu_objset_open(osname, DMU_OST_ZFS, mode, &os); + if (error == EROFS) { + mode |= DS_MODE_READONLY; + error = dmu_objset_open(osname, DMU_OST_ZFS, mode, &os); + } + if (error) + return (error); + + /* + * Initialize the zfs-specific filesystem structure. + * Should probably make this a kmem cache, shuffle fields, + * and just bzero up to z_hold_mtx[]. + */ + zfsvfs = kmem_zalloc(sizeof (zfsvfs_t), KM_SLEEP); + zfsvfs->z_vfs = NULL; + zfsvfs->z_parent = zfsvfs; + zfsvfs->z_max_blksz = SPA_MAXBLOCKSIZE; + zfsvfs->z_show_ctldir = ZFS_SNAPDIR_VISIBLE; + zfsvfs->z_os = os; + + error = zfs_get_zplprop(os, ZFS_PROP_VERSION, &zfsvfs->z_version); + if (error) { + goto out; + } else if (zfsvfs->z_version > ZPL_VERSION) { + (void) printf("Mismatched versions: File system " + "is version %llu on-disk format, which is " + "incompatible with this software version %lld!", + (u_longlong_t)zfsvfs->z_version, ZPL_VERSION); + error = ENOTSUP; + goto out; + } + + if ((error = zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &zval)) != 0) + goto out; + zfsvfs->z_norm = (int)zval; + + if ((error = zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &zval)) != 0) + goto out; + zfsvfs->z_utf8 = (zval != 0); + + if ((error = zfs_get_zplprop(os, ZFS_PROP_CASE, &zval)) != 0) + goto out; + zfsvfs->z_case = (uint_t)zval; + + /* + * Fold case on file systems that are always or sometimes case + * insensitive. + */ + if (zfsvfs->z_case == ZFS_CASE_INSENSITIVE || + zfsvfs->z_case == ZFS_CASE_MIXED) + zfsvfs->z_norm |= U8_TEXTPREP_TOUPPER; + + zfsvfs->z_use_fuids = USE_FUIDS(zfsvfs->z_version, zfsvfs->z_os); + + error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_ROOT_OBJ, 8, 1, + &zfsvfs->z_root); + if (error) + goto out; + ASSERT(zfsvfs->z_root != 0); + + error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_UNLINKED_SET, 8, 1, + &zfsvfs->z_unlinkedobj); + if (error) + goto out; + + error = zap_lookup(os, MASTER_NODE_OBJ, + zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA], + 8, 1, &zfsvfs->z_userquota_obj); + if (error && error != ENOENT) + goto out; + + error = zap_lookup(os, MASTER_NODE_OBJ, + zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA], + 8, 1, &zfsvfs->z_groupquota_obj); + if (error && error != ENOENT) + goto out; + + error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_FUID_TABLES, 8, 1, + &zfsvfs->z_fuid_obj); + if (error && error != ENOENT) + goto out; + + error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_SHARES_DIR, 8, 1, + &zfsvfs->z_shares_dir); + if (error && error != ENOENT) + goto out; + + mutex_init(&zfsvfs->z_znodes_lock, NULL, MUTEX_DEFAULT, NULL); + mutex_init(&zfsvfs->z_online_recv_lock, NULL, MUTEX_DEFAULT, NULL); + mutex_init(&zfsvfs->z_lock, NULL, MUTEX_DEFAULT, NULL); + list_create(&zfsvfs->z_all_znodes, sizeof (znode_t), + offsetof(znode_t, z_link_node)); + rrw_init(&zfsvfs->z_teardown_lock); + rw_init(&zfsvfs->z_teardown_inactive_lock, NULL, RW_DEFAULT, NULL); + rw_init(&zfsvfs->z_fuid_lock, NULL, RW_DEFAULT, NULL); + for (i = 0; i != ZFS_OBJ_MTX_SZ; i++) + mutex_init(&zfsvfs->z_hold_mtx[i], NULL, MUTEX_DEFAULT, NULL); + + *zvp = zfsvfs; + return (0); + +out: + dmu_objset_close(os); + *zvp = NULL; + kmem_free(zfsvfs, sizeof (zfsvfs_t)); + return (error); +} + static int zfsvfs_setup(zfsvfs_t *zfsvfs, boolean_t mounting) { @@ -646,9 +1033,13 @@ zfsvfs_setup(zfsvfs_t *zfsvfs, boolean_t mounting) return (0); } -static void -zfs_freezfsvfs(zfsvfs_t *zfsvfs) +void +zfsvfs_free(zfsvfs_t *zfsvfs) { + int i; + + zfs_fuid_destroy(zfsvfs); + mutex_destroy(&zfsvfs->z_znodes_lock); mutex_destroy(&zfsvfs->z_online_recv_lock); mutex_destroy(&zfsvfs->z_lock); @@ -656,42 +1047,38 @@ zfs_freezfsvfs(zfsvfs_t *zfsvfs) rrw_destroy(&zfsvfs->z_teardown_lock); rw_destroy(&zfsvfs->z_teardown_inactive_lock); rw_destroy(&zfsvfs->z_fuid_lock); + for (i = 0; i != ZFS_OBJ_MTX_SZ; i++) + mutex_destroy(&zfsvfs->z_hold_mtx[i]); kmem_free(zfsvfs, sizeof (zfsvfs_t)); } +static void +zfs_set_fuid_feature(zfsvfs_t *zfsvfs) +{ + zfsvfs->z_use_fuids = USE_FUIDS(zfsvfs->z_version, zfsvfs->z_os); + if (zfsvfs->z_use_fuids && zfsvfs->z_vfs) { + vfs_set_feature(zfsvfs->z_vfs, VFSFT_XVATTR); + vfs_set_feature(zfsvfs->z_vfs, VFSFT_SYSATTR_VIEWS); + vfs_set_feature(zfsvfs->z_vfs, VFSFT_ACEMASKONACCESS); + vfs_set_feature(zfsvfs->z_vfs, VFSFT_ACLONCREATE); + } +} + static int zfs_domount(vfs_t *vfsp, char *osname) { dev_t mount_dev; - uint64_t recordsize, readonly; + uint64_t recordsize, fsid_guid; int error = 0; - int mode; zfsvfs_t *zfsvfs; - znode_t *zp = NULL; ASSERT(vfsp); ASSERT(osname); - /* - * Initialize the zfs-specific filesystem structure. - * Should probably make this a kmem cache, shuffle fields, - * and just bzero up to z_hold_mtx[]. - */ - zfsvfs = kmem_zalloc(sizeof (zfsvfs_t), KM_SLEEP); + error = zfsvfs_create(osname, DS_MODE_OWNER, &zfsvfs); + if (error) + return (error); zfsvfs->z_vfs = vfsp; - zfsvfs->z_parent = zfsvfs; - zfsvfs->z_max_blksz = SPA_MAXBLOCKSIZE; - zfsvfs->z_show_ctldir = ZFS_SNAPDIR_VISIBLE; - zfsvfs->z_fuid_dirty = B_FALSE; - - mutex_init(&zfsvfs->z_znodes_lock, NULL, MUTEX_DEFAULT, NULL); - mutex_init(&zfsvfs->z_online_recv_lock, NULL, MUTEX_DEFAULT, NULL); - mutex_init(&zfsvfs->z_lock, NULL, MUTEX_DEFAULT, NULL); - list_create(&zfsvfs->z_all_znodes, sizeof (znode_t), - offsetof(znode_t, z_link_node)); - rrw_init(&zfsvfs->z_teardown_lock); - rw_init(&zfsvfs->z_teardown_inactive_lock, NULL, RW_DEFAULT, NULL); - rw_init(&zfsvfs->z_fuid_lock, NULL, RW_DEFAULT, NULL); /* Initialize the generic filesystem structure. */ vfsp->vfs_bcount = 0; @@ -713,39 +1100,24 @@ zfs_domount(vfs_t *vfsp, char *osname) vfsp->vfs_flag |= VFS_NOTRUNC; vfsp->vfs_data = zfsvfs; - if (error = dsl_prop_get_integer(osname, "readonly", &readonly, NULL)) - goto out; - - mode = DS_MODE_OWNER; - if (readonly) - mode |= DS_MODE_READONLY; - - error = dmu_objset_open(osname, DMU_OST_ZFS, mode, &zfsvfs->z_os); - if (error == EROFS) { - mode = DS_MODE_OWNER | DS_MODE_READONLY; - error = dmu_objset_open(osname, DMU_OST_ZFS, mode, - &zfsvfs->z_os); - } - - if (error) - goto out; - - if (error = zfs_init_fs(zfsvfs, &zp)) - goto out; - - /* The call to zfs_init_fs leaves the vnode held, release it here. */ - VN_RELE(ZTOV(zp)); + /* + * The fsid is 64 bits, composed of an 8-bit fs type, which + * separates our fsid from any other filesystem types, and a + * 56-bit objset unique ID. The objset unique ID is unique to + * all objsets open on this system, provided by unique_create(). + * The 8-bit fs type must be put in the low bits of fsid[1] + * because that's where other Solaris filesystems put it. + */ + fsid_guid = dmu_objset_fsid_guid(zfsvfs->z_os); + ASSERT((fsid_guid & ~((1ULL<<56)-1)) == 0); + vfsp->vfs_fsid.val[0] = fsid_guid; + vfsp->vfs_fsid.val[1] = ((fsid_guid>>32) << 8) | + zfsfstype & 0xFF; /* * Set features for file system. */ - zfsvfs->z_use_fuids = USE_FUIDS(zfsvfs->z_version, zfsvfs->z_os); - if (zfsvfs->z_use_fuids) { - vfs_set_feature(vfsp, VFSFT_XVATTR); - vfs_set_feature(vfsp, VFSFT_SYSATTR_VIEWS); - vfs_set_feature(vfsp, VFSFT_ACEMASKONACCESS); - vfs_set_feature(vfsp, VFSFT_ACLONCREATE); - } + zfs_set_fuid_feature(zfsvfs); if (zfsvfs->z_case == ZFS_CASE_INSENSITIVE) { vfs_set_feature(vfsp, VFSFT_DIRENTFLAGS); vfs_set_feature(vfsp, VFSFT_CASEINSENSITIVE); @@ -758,7 +1130,6 @@ zfs_domount(vfs_t *vfsp, char *osname) if (dmu_objset_is_snapshot(zfsvfs->z_os)) { uint64_t pval; - ASSERT(mode & DS_MODE_READONLY); atime_changed_cb(zfsvfs, B_FALSE); readonly_changed_cb(zfsvfs, B_TRUE); if (error = dsl_prop_get_integer(osname, "xattr", &pval, NULL)) @@ -773,9 +1144,8 @@ zfs_domount(vfs_t *vfsp, char *osname) zfsctl_create(zfsvfs); out: if (error) { - if (zfsvfs->z_os) - dmu_objset_close(zfsvfs->z_os); - zfs_freezfsvfs(zfsvfs); + dmu_objset_close(zfsvfs->z_os); + zfsvfs_free(zfsvfs); } else { atomic_add_32(&zfs_active_fs_count, 1); } @@ -1452,15 +1822,16 @@ zfs_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp) * 'z_teardown_inactive_lock' write held. */ int -zfs_suspend_fs(zfsvfs_t *zfsvfs, char *name, int *mode) +zfs_suspend_fs(zfsvfs_t *zfsvfs, char *name, int *modep) { int error; if ((error = zfsvfs_teardown(zfsvfs, B_FALSE)) != 0) return (error); - *mode = zfsvfs->z_os->os_mode; - dmu_objset_name(zfsvfs->z_os, name); + *modep = zfsvfs->z_os->os_mode; + if (name) + dmu_objset_name(zfsvfs->z_os, name); dmu_objset_close(zfsvfs->z_os); return (0); @@ -1519,12 +1890,6 @@ static void zfs_freevfs(vfs_t *vfsp) { zfsvfs_t *zfsvfs = vfsp->vfs_data; - int i; - - for (i = 0; i != ZFS_OBJ_MTX_SZ; i++) - mutex_destroy(&zfsvfs->z_hold_mtx[i]); - - zfs_fuid_destroy(zfsvfs); /* * If this is a snapshot, we have an extra VFS_HOLD on our parent @@ -1533,7 +1898,7 @@ zfs_freevfs(vfs_t *vfsp) if (zfsvfs->z_issnap) VFS_RELE(zfsvfs->z_parent->z_vfs); - zfs_freezfsvfs(zfsvfs); + zfsvfs_free(zfsvfs); atomic_add_32(&zfs_active_fs_count, -1); } @@ -1592,6 +1957,8 @@ zfs_init(void) * Initialize znode cache, vnode ops, etc... */ zfs_znode_init(); + + dmu_objset_register_type(DMU_OST_ZFS, zfs_space_delta_cb); } void @@ -1608,54 +1975,46 @@ zfs_busy(void) } int -zfs_set_version(const char *name, uint64_t newvers) +zfs_set_version(zfsvfs_t *zfsvfs, uint64_t newvers) { int error; - objset_t *os; + objset_t *os = zfsvfs->z_os; dmu_tx_t *tx; - uint64_t curvers; - - /* - * XXX for now, require that the filesystem be unmounted. Would - * be nice to find the zfsvfs_t and just update that if - * possible. - */ if (newvers < ZPL_VERSION_INITIAL || newvers > ZPL_VERSION) return (EINVAL); - error = dmu_objset_open(name, DMU_OST_ZFS, DS_MODE_OWNER, &os); - if (error) - return (error); - - error = zap_lookup(os, MASTER_NODE_OBJ, ZPL_VERSION_STR, - 8, 1, &curvers); - if (error) - goto out; - if (newvers < curvers) { - error = EINVAL; - goto out; - } + if (newvers < zfsvfs->z_version) + return (EINVAL); tx = dmu_tx_create(os); - dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, 0, ZPL_VERSION_STR); + dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, B_FALSE, ZPL_VERSION_STR); error = dmu_tx_assign(tx, TXG_WAIT); if (error) { dmu_tx_abort(tx); - goto out; + return (error); + } + error = zap_update(os, MASTER_NODE_OBJ, ZPL_VERSION_STR, + 8, 1, &newvers, tx); + + if (error) { + dmu_tx_commit(tx); + return (error); } - error = zap_update(os, MASTER_NODE_OBJ, ZPL_VERSION_STR, 8, 1, - &newvers, tx); spa_history_internal_log(LOG_DS_UPGRADE, dmu_objset_spa(os), tx, CRED(), - "oldver=%llu newver=%llu dataset = %llu", curvers, newvers, - dmu_objset_id(os)); + "oldver=%llu newver=%llu dataset = %llu", + zfsvfs->z_version, newvers, dmu_objset_id(os)); + dmu_tx_commit(tx); -out: - dmu_objset_close(os); - return (error); + zfsvfs->z_version = newvers; + + if (zfsvfs->z_version >= ZPL_VERSION_FUID) + zfs_set_fuid_feature(zfsvfs); + + return (0); } /* diff --git a/usr/src/uts/common/fs/zfs/zfs_vnops.c b/usr/src/uts/common/fs/zfs/zfs_vnops.c index 20b8a6d658..cd7033934c 100644 --- a/usr/src/uts/common/fs/zfs/zfs_vnops.c +++ b/usr/src/uts/common/fs/zfs/zfs_vnops.c @@ -667,6 +667,13 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct) /* * Start a transaction. */ + if (zfs_usergroup_overquota(zfsvfs, + B_FALSE, zp->z_phys->zp_uid) || + zfs_usergroup_overquota(zfsvfs, + B_TRUE, zp->z_phys->zp_gid)) { + error = EDQUOT; + break; + } woff = uio->uio_loffset; tx = dmu_tx_create(zfsvfs->z_os); dmu_tx_hold_bonus(tx, zp->z_id); @@ -1183,23 +1190,16 @@ top: if ((error = zfs_acl_ids_create(dzp, 0, vap, cr, vsecp, &acl_ids)) != 0) goto out; + if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) { + error = EDQUOT; + goto out; + } tx = dmu_tx_create(os); dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); fuid_dirtied = zfsvfs->z_fuid_dirty; - if (fuid_dirtied) { - if (zfsvfs->z_fuid_obj == 0) { - dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); - dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, - FUID_SIZE_ESTIMATE(zfsvfs)); - dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, - FALSE, NULL); - } else { - dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); - dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, - FUID_SIZE_ESTIMATE(zfsvfs)); - } - } + if (fuid_dirtied) + zfs_fuid_txhold(zfsvfs, tx); dmu_tx_hold_bonus(tx, dzp->z_id); dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name); if (acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) { @@ -1608,6 +1608,11 @@ top: ZFS_EXIT(zfsvfs); return (error); } + if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) { + zfs_dirent_unlock(dl); + ZFS_EXIT(zfsvfs); + return (EDQUOT); + } /* * Add a new entry to the directory. @@ -1616,18 +1621,8 @@ top: dmu_tx_hold_zap(tx, dzp->z_id, TRUE, dirname); dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); fuid_dirtied = zfsvfs->z_fuid_dirty; - if (fuid_dirtied) { - if (zfsvfs->z_fuid_obj == 0) { - dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); - dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, - FUID_SIZE_ESTIMATE(zfsvfs)); - dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL); - } else { - dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); - dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, - FUID_SIZE_ESTIMATE(zfsvfs)); - } - } + if (fuid_dirtied) + zfs_fuid_txhold(zfsvfs, tx); if (acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, SPA_MAXBLOCKSIZE); @@ -2623,11 +2618,8 @@ top: new_mode = (pmode & S_IFMT) | (vap->va_mode & ~S_IFMT); - if (err = zfs_acl_chmod_setattr(zp, &aclp, new_mode)) { - dmu_tx_abort(tx); - ZFS_EXIT(zfsvfs); - return (err); - } + if (err = zfs_acl_chmod_setattr(zp, &aclp, new_mode)) + goto out; if (pzp->zp_acl.z_acl_extern_obj) { /* Are we upgrading ACL from old V0 format to new V1 */ if (zfsvfs->z_version <= ZPL_VERSION_FUID && @@ -2652,22 +2644,28 @@ top: if (mask & (AT_UID | AT_GID)) { if (pzp->zp_xattr) { err = zfs_zget(zp->z_zfsvfs, pzp->zp_xattr, &attrzp); - if (err) { - dmu_tx_abort(tx); - ZFS_EXIT(zfsvfs); - if (aclp) - zfs_acl_free(aclp); - return (err); - } + if (err) + goto out; dmu_tx_hold_bonus(tx, attrzp->z_id); } if (mask & AT_UID) { new_uid = zfs_fuid_create(zfsvfs, (uint64_t)vap->va_uid, cr, ZFS_OWNER, &fuidp); + if (new_uid != pzp->zp_uid && + zfs_usergroup_overquota(zfsvfs, B_FALSE, new_uid)) { + err = EDQUOT; + goto out; + } } + if (mask & AT_GID) { new_gid = zfs_fuid_create(zfsvfs, (uint64_t)vap->va_gid, cr, ZFS_GROUP, &fuidp); + if (new_gid != pzp->zp_gid && + zfs_usergroup_overquota(zfsvfs, B_TRUE, new_gid)) { + err = EDQUOT; + goto out; + } } fuid_dirtied = zfsvfs->z_fuid_dirty; if (fuid_dirtied) { @@ -2687,22 +2685,9 @@ top: err = dmu_tx_assign(tx, TXG_NOWAIT); if (err) { - if (attrzp) - VN_RELE(ZTOV(attrzp)); - - if (aclp) { - zfs_acl_free(aclp); - aclp = NULL; - } - - if (err == ERESTART) { + if (err == ERESTART) dmu_tx_wait(tx); - dmu_tx_abort(tx); - goto top; - } - dmu_tx_abort(tx); - ZFS_EXIT(zfsvfs); - return (err); + goto out; } dmu_buf_will_dirty(zp->z_dbuf, tx); @@ -2740,9 +2725,6 @@ top: attrzp->z_phys->zp_gid = new_gid; } - if (aclp) - zfs_acl_free(aclp); - if (attrzp) mutex_exit(&attrzp->z_lock); @@ -2810,15 +2792,29 @@ top: if (mask != 0) zfs_log_setattr(zilog, tx, TX_SETATTR, zp, vap, mask, fuidp); - if (fuidp) - zfs_fuid_info_free(fuidp); mutex_exit(&zp->z_lock); - dmu_tx_commit(tx); - +out: if (attrzp) VN_RELE(ZTOV(attrzp)); + if (aclp) { + zfs_acl_free(aclp); + aclp = NULL; + } + + if (fuidp) { + zfs_fuid_info_free(fuidp); + fuidp = NULL; + } + + if (err) + dmu_tx_abort(tx); + else + dmu_tx_commit(tx); + + if (err == ERESTART) + goto top; ZFS_EXIT(zfsvfs); return (err); @@ -3286,6 +3282,12 @@ top: } VERIFY(0 == zfs_acl_ids_create(dzp, 0, vap, cr, NULL, &acl_ids)); + if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) { + zfs_acl_ids_free(&acl_ids); + zfs_dirent_unlock(dl); + ZFS_EXIT(zfsvfs); + return (EDQUOT); + } tx = dmu_tx_create(zfsvfs->z_os); fuid_dirtied = zfsvfs->z_fuid_dirty; dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, MAX(1, len)); @@ -3293,18 +3295,8 @@ top: dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name); if (acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, SPA_MAXBLOCKSIZE); - if (fuid_dirtied) { - if (zfsvfs->z_fuid_obj == 0) { - dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); - dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, - FUID_SIZE_ESTIMATE(zfsvfs)); - dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL); - } else { - dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); - dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, - FUID_SIZE_ESTIMATE(zfsvfs)); - } - } + if (fuid_dirtied) + zfs_fuid_txhold(zfsvfs, tx); error = dmu_tx_assign(tx, TXG_NOWAIT); if (error) { zfs_acl_ids_free(&acl_ids); @@ -3357,7 +3349,6 @@ top: * Insert the new object into the directory. */ (void) zfs_link_create(dl, zp, tx, ZNEW); -out: if (error == 0) { uint64_t txtype = TX_SYMLINK; if (flags & FIGNORECASE) @@ -3642,6 +3633,12 @@ zfs_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp, pvn_write_done(trunc, flags); len = filesz - off; } + + if (zfs_usergroup_overquota(zfsvfs, B_FALSE, zp->z_phys->zp_uid) || + zfs_usergroup_overquota(zfsvfs, B_TRUE, zp->z_phys->zp_gid)) { + err = EDQUOT; + goto out; + } top: tx = dmu_tx_create(zfsvfs->z_os); dmu_tx_hold_write(tx, zp->z_id, off, len); diff --git a/usr/src/uts/common/fs/zfs/zfs_znode.c b/usr/src/uts/common/fs/zfs/zfs_znode.c index 2e8a3808eb..eca43b7848 100644 --- a/usr/src/uts/common/fs/zfs/zfs_znode.c +++ b/usr/src/uts/common/fs/zfs/zfs_znode.c @@ -206,17 +206,6 @@ zfs_znode_move_impl(znode_t *ozp, znode_t *nzp) POINTER_INVALIDATE(&ozp->z_zfsvfs); } -/* - * Wrapper function for ZFS_ENTER that returns 0 if successful and otherwise - * returns a non-zero error code. - */ -static int -zfs_enter(zfsvfs_t *zfsvfs) -{ - ZFS_ENTER(zfsvfs); - return (0); -} - /*ARGSUSED*/ static kmem_cbrc_t zfs_znode_move(void *buf, void *newbuf, size_t size, void *arg) @@ -241,8 +230,11 @@ zfs_znode_move(void *buf, void *newbuf, size_t size, void *arg) /* * Ensure that the filesystem is not unmounted during the move. + * This is the equivalent to ZFS_ENTER(). */ - if (zfs_enter(zfsvfs) != 0) { /* ZFS_ENTER */ + rrw_enter(&zfsvfs->z_teardown_lock, RW_READER, FTAG); + if (zfsvfs->z_unmounted) { + ZFS_EXIT(zfsvfs); ZNODE_STAT_ADD(znode_move_stats.zms_zfsvfs_unmounted); return (KMEM_CBRC_DONT_KNOW); } @@ -472,107 +464,6 @@ zfs_create_share_dir(zfsvfs_t *zfsvfs, dmu_tx_t *tx) } /* - * zfs_init_fs - Initialize the zfsvfs struct and the file system - * incore "master" object. Verify version compatibility. - */ -int -zfs_init_fs(zfsvfs_t *zfsvfs, znode_t **zpp) -{ - extern int zfsfstype; - - objset_t *os = zfsvfs->z_os; - int i, error; - uint64_t fsid_guid; - uint64_t zval; - - *zpp = NULL; - - error = zfs_get_zplprop(os, ZFS_PROP_VERSION, &zfsvfs->z_version); - if (error) { - return (error); - } else if (zfsvfs->z_version > ZPL_VERSION) { - (void) printf("Mismatched versions: File system " - "is version %llu on-disk format, which is " - "incompatible with this software version %lld!", - (u_longlong_t)zfsvfs->z_version, ZPL_VERSION); - return (ENOTSUP); - } - - if ((error = zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &zval)) != 0) - return (error); - zfsvfs->z_norm = (int)zval; - if ((error = zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &zval)) != 0) - return (error); - zfsvfs->z_utf8 = (zval != 0); - if ((error = zfs_get_zplprop(os, ZFS_PROP_CASE, &zval)) != 0) - return (error); - zfsvfs->z_case = (uint_t)zval; - /* - * Fold case on file systems that are always or sometimes case - * insensitive. - */ - if (zfsvfs->z_case == ZFS_CASE_INSENSITIVE || - zfsvfs->z_case == ZFS_CASE_MIXED) - zfsvfs->z_norm |= U8_TEXTPREP_TOUPPER; - - /* - * The fsid is 64 bits, composed of an 8-bit fs type, which - * separates our fsid from any other filesystem types, and a - * 56-bit objset unique ID. The objset unique ID is unique to - * all objsets open on this system, provided by unique_create(). - * The 8-bit fs type must be put in the low bits of fsid[1] - * because that's where other Solaris filesystems put it. - */ - fsid_guid = dmu_objset_fsid_guid(os); - ASSERT((fsid_guid & ~((1ULL<<56)-1)) == 0); - zfsvfs->z_vfs->vfs_fsid.val[0] = fsid_guid; - zfsvfs->z_vfs->vfs_fsid.val[1] = ((fsid_guid>>32) << 8) | - zfsfstype & 0xFF; - - error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_ROOT_OBJ, 8, 1, - &zfsvfs->z_root); - if (error) - return (error); - ASSERT(zfsvfs->z_root != 0); - - error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_UNLINKED_SET, 8, 1, - &zfsvfs->z_unlinkedobj); - if (error) - return (error); - - error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_FUID_TABLES, 8, 1, - &zfsvfs->z_fuid_obj); - if (error == ENOENT) - error = 0; - - error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_SHARES_DIR, 8, 1, - &zfsvfs->z_shares_dir); - if (error && error != ENOENT) - return (error); - - /* - * Initialize zget mutex's - */ - for (i = 0; i != ZFS_OBJ_MTX_SZ; i++) - mutex_init(&zfsvfs->z_hold_mtx[i], NULL, MUTEX_DEFAULT, NULL); - - error = zfs_zget(zfsvfs, zfsvfs->z_root, zpp); - if (error) { - /* - * On error, we destroy the mutexes here since it's not - * possible for the caller to determine if the mutexes were - * initialized properly. - */ - for (i = 0; i != ZFS_OBJ_MTX_SZ; i++) - mutex_destroy(&zfsvfs->z_hold_mtx[i]); - return (error); - } - ASSERT3U((*zpp)->z_id, ==, zfsvfs->z_root); - - return (error); -} - -/* * define a couple of values we need available * for both 64 and 32 bit environments. */ @@ -1542,7 +1433,7 @@ void zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *zplprops, dmu_tx_t *tx) { zfsvfs_t zfsvfs; - uint64_t moid, doid, version; + uint64_t moid, obj, version; uint64_t sense = ZFS_CASE_SENSITIVE; uint64_t norm = 0; nvpair_t *elem; @@ -1568,12 +1459,12 @@ zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *zplprops, dmu_tx_t *tx) /* * Set starting attributes. */ - if (spa_version(dmu_objset_spa(os)) >= SPA_VERSION_FUID) + if (spa_version(dmu_objset_spa(os)) >= SPA_VERSION_USERSPACE) version = ZPL_VERSION; + else if (spa_version(dmu_objset_spa(os)) >= SPA_VERSION_FUID) + version = ZPL_VERSION_USERSPACE - 1; else version = ZPL_VERSION_FUID - 1; - error = zap_update(os, moid, ZPL_VERSION_STR, - 8, 1, &version, tx); elem = NULL; while ((elem = nvlist_next_nvpair(zplprops, elem)) != NULL) { /* For the moment we expect all zpl props to be uint64_ts */ @@ -1584,9 +1475,8 @@ zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *zplprops, dmu_tx_t *tx) VERIFY(nvpair_value_uint64(elem, &val) == 0); name = nvpair_name(elem); if (strcmp(name, zfs_prop_to_name(ZFS_PROP_VERSION)) == 0) { - version = val; - error = zap_update(os, moid, ZPL_VERSION_STR, - 8, 1, &version, tx); + if (val < version) + version = val; } else { error = zap_update(os, moid, name, 8, 1, &val, tx); } @@ -1597,13 +1487,14 @@ zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *zplprops, dmu_tx_t *tx) sense = val; } ASSERT(version != 0); + error = zap_update(os, moid, ZPL_VERSION_STR, 8, 1, &version, tx); /* * Create a delete queue. */ - doid = zap_create(os, DMU_OT_UNLINKED_SET, DMU_OT_NONE, 0, tx); + obj = zap_create(os, DMU_OT_UNLINKED_SET, DMU_OT_NONE, 0, tx); - error = zap_add(os, moid, ZFS_UNLINKED_SET, 8, 1, &doid, tx); + error = zap_add(os, moid, ZFS_UNLINKED_SET, 8, 1, &obj, tx); ASSERT(error == 0); /* diff --git a/usr/src/uts/common/fs/zfs/zil.c b/usr/src/uts/common/fs/zfs/zil.c index 7cccf6c7d7..62409d0759 100644 --- a/usr/src/uts/common/fs/zfs/zil.c +++ b/usr/src/uts/common/fs/zfs/zil.c @@ -1219,6 +1219,13 @@ zil_sync(zilog_t *zilog, dmu_tx_t *tx) spa_t *spa = zilog->zl_spa; lwb_t *lwb; + /* + * We don't zero out zl_destroy_txg, so make sure we don't try + * to destroy it twice. + */ + if (spa_sync_pass(spa) != 1) + return; + mutex_enter(&zilog->zl_lock); ASSERT(zilog->zl_stop_sync == 0); @@ -1229,7 +1236,6 @@ zil_sync(zilog_t *zilog, dmu_tx_t *tx) blkptr_t blk = zh->zh_log; ASSERT(list_head(&zilog->zl_lwb_list) == NULL); - ASSERT(spa_sync_pass(spa) == 1); bzero(zh, sizeof (zil_header_t)); bzero(zilog->zl_replayed_seq, sizeof (zilog->zl_replayed_seq)); diff --git a/usr/src/uts/common/fs/zfs/zio.c b/usr/src/uts/common/fs/zfs/zio.c index bfe85d68f3..8caef25360 100644 --- a/usr/src/uts/common/fs/zfs/zio.c +++ b/usr/src/uts/common/fs/zfs/zio.c @@ -875,16 +875,10 @@ zio_write_bp_init(zio_t *zio) * few passes, stop compressing to ensure convergence. */ pass = spa_sync_pass(zio->io_spa); - ASSERT(pass > 1); if (pass > SYNC_PASS_DONT_COMPRESS) compress = ZIO_COMPRESS_OFF; - /* - * Only MOS (objset 0) data should need to be rewritten. - */ - ASSERT(zio->io_logical->io_bookmark.zb_objset == 0); - /* Make sure someone doesn't change their mind on overwrites */ ASSERT(MIN(zp->zp_ndvas + BP_IS_GANG(bp), spa_max_replication(zio->io_spa)) == BP_GET_NDVAS(bp)); diff --git a/usr/src/uts/common/sys/fs/zfs.h b/usr/src/uts/common/sys/fs/zfs.h index 8bc54b3394..cb05176292 100644 --- a/usr/src/uts/common/sys/fs/zfs.h +++ b/usr/src/uts/common/sys/fs/zfs.h @@ -105,9 +105,20 @@ typedef enum { ZFS_PROP_USEDDS, ZFS_PROP_USEDCHILD, ZFS_PROP_USEDREFRESERV, + ZFS_PROP_USERACCOUNTING, /* not exposed to the user */ ZFS_NUM_PROPS } zfs_prop_t; +typedef enum { + ZFS_PROP_USERUSED, + ZFS_PROP_USERQUOTA, + ZFS_PROP_GROUPUSED, + ZFS_PROP_GROUPQUOTA, + ZFS_NUM_USERQUOTA_PROPS +} zfs_userquota_prop_t; + +extern const char *zfs_userquota_prop_prefixes[ZFS_NUM_USERQUOTA_PROPS]; + /* * Pool properties are identified by these constants and must be added to the * end of this list to ensure that external consumers are not affected @@ -169,6 +180,7 @@ boolean_t zfs_prop_setonce(zfs_prop_t); const char *zfs_prop_to_name(zfs_prop_t); zfs_prop_t zfs_name_to_prop(const char *); boolean_t zfs_prop_user(const char *); +boolean_t zfs_prop_userquota(const char *name); int zfs_prop_index_to_string(zfs_prop_t, uint64_t, const char **); int zfs_prop_string_to_index(zfs_prop_t, const char *, uint64_t *); boolean_t zfs_prop_valid_for_type(int, zfs_type_t); @@ -260,13 +272,15 @@ typedef enum zfs_cache_type { #define SPA_VERSION_12 12ULL #define SPA_VERSION_13 13ULL #define SPA_VERSION_14 14ULL +#define SPA_VERSION_15 15ULL /* * When bumping up SPA_VERSION, make sure GRUB ZFS understands the on-disk * format change. Go to usr/src/grub/grub-0.95/stage2/{zfs-include/, fsys_zfs*}, - * and do the appropriate changes. + * and do the appropriate changes. Also bump the version number in + * usr/src/grub/capability. */ -#define SPA_VERSION SPA_VERSION_14 -#define SPA_VERSION_STRING "14" +#define SPA_VERSION SPA_VERSION_15 +#define SPA_VERSION_STRING "15" /* * Symbolic names for the changes that caused a SPA_VERSION switch. @@ -302,6 +316,7 @@ typedef enum zfs_cache_type { #define SPA_VERSION_SNAP_PROPS SPA_VERSION_12 #define SPA_VERSION_USED_BREAKDOWN SPA_VERSION_13 #define SPA_VERSION_PASSTHROUGH_X SPA_VERSION_14 +#define SPA_VERSION_USERSPACE SPA_VERSION_15 /* * ZPL version - rev'd whenever an incompatible on-disk format change @@ -314,14 +329,16 @@ typedef enum zfs_cache_type { #define ZPL_VERSION_1 1ULL #define ZPL_VERSION_2 2ULL #define ZPL_VERSION_3 3ULL -#define ZPL_VERSION ZPL_VERSION_3 -#define ZPL_VERSION_STRING "3" +#define ZPL_VERSION_4 4ULL +#define ZPL_VERSION ZPL_VERSION_4 +#define ZPL_VERSION_STRING "4" #define ZPL_VERSION_INITIAL ZPL_VERSION_1 #define ZPL_VERSION_DIRENT_TYPE ZPL_VERSION_2 #define ZPL_VERSION_FUID ZPL_VERSION_3 #define ZPL_VERSION_NORMALIZATION ZPL_VERSION_3 #define ZPL_VERSION_SYSATTR ZPL_VERSION_3 +#define ZPL_VERSION_USERSPACE ZPL_VERSION_4 /* * The following are configuration names used in the nvlist describing a pool's @@ -571,7 +588,10 @@ typedef enum zfs_ioc { ZFS_IOC_ISCSI_PERM_CHECK, ZFS_IOC_SHARE, ZFS_IOC_INHERIT_PROP, - ZFS_IOC_SMB_ACL + ZFS_IOC_SMB_ACL, + ZFS_IOC_USERSPACE_ONE, + ZFS_IOC_USERSPACE_MANY, + ZFS_IOC_USERSPACE_UPGRADE } zfs_ioc_t; /* |