]> git.proxmox.com Git - mirror_zfs.git/blobdiff - module/zfs/zfs_vfsops.c
Fix send/recv lost spill block
[mirror_zfs.git] / module / zfs / zfs_vfsops.c
index c55c5008836a2633fe3e84b4cd908a6ee18c1342..18194a5dc407e70128ed34651b2be1bbe49c66f7 100644 (file)
  */
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2014 by Delphix. All rights reserved.
+ * Copyright (c) 2012, 2018 by Delphix. All rights reserved.
  */
 
 /* Portions Copyright 2010 Robert Milkowski */
 
 #include <sys/types.h>
 #include <sys/param.h>
-#include <sys/systm.h>
 #include <sys/sysmacros.h>
 #include <sys/kmem.h>
 #include <sys/pathname.h>
 #include <sys/vnode.h>
 #include <sys/vfs.h>
-#include <sys/vfs_opreg.h>
 #include <sys/mntent.h>
-#include <sys/mount.h>
 #include <sys/cmn_err.h>
-#include "fs/fs_subr.h"
 #include <sys/zfs_znode.h>
 #include <sys/zfs_vnops.h>
 #include <sys/zfs_dir.h>
 #include <sys/zap.h>
 #include <sys/sa.h>
 #include <sys/sa_impl.h>
-#include <sys/varargs.h>
 #include <sys/policy.h>
 #include <sys/atomic.h>
-#include <sys/mkdev.h>
-#include <sys/modctl.h>
-#include <sys/refstr.h>
 #include <sys/zfs_ioctl.h>
 #include <sys/zfs_ctldir.h>
 #include <sys/zfs_fuid.h>
-#include <sys/bootconf.h>
 #include <sys/sunddi.h>
-#include <sys/dnlc.h>
 #include <sys/dmu_objset.h>
 #include <sys/spa_boot.h>
 #include <sys/zpl.h>
+#include <linux/vfs_compat.h>
 #include "zfs_comutil.h"
 
+enum {
+       TOKEN_RO,
+       TOKEN_RW,
+       TOKEN_SETUID,
+       TOKEN_NOSETUID,
+       TOKEN_EXEC,
+       TOKEN_NOEXEC,
+       TOKEN_DEVICES,
+       TOKEN_NODEVICES,
+       TOKEN_DIRXATTR,
+       TOKEN_SAXATTR,
+       TOKEN_XATTR,
+       TOKEN_NOXATTR,
+       TOKEN_ATIME,
+       TOKEN_NOATIME,
+       TOKEN_RELATIME,
+       TOKEN_NORELATIME,
+       TOKEN_NBMAND,
+       TOKEN_NONBMAND,
+       TOKEN_MNTPOINT,
+       TOKEN_LAST,
+};
+
+static const match_table_t zpl_tokens = {
+       { TOKEN_RO,             MNTOPT_RO },
+       { TOKEN_RW,             MNTOPT_RW },
+       { TOKEN_SETUID,         MNTOPT_SETUID },
+       { TOKEN_NOSETUID,       MNTOPT_NOSETUID },
+       { TOKEN_EXEC,           MNTOPT_EXEC },
+       { TOKEN_NOEXEC,         MNTOPT_NOEXEC },
+       { TOKEN_DEVICES,        MNTOPT_DEVICES },
+       { TOKEN_NODEVICES,      MNTOPT_NODEVICES },
+       { TOKEN_DIRXATTR,       MNTOPT_DIRXATTR },
+       { TOKEN_SAXATTR,        MNTOPT_SAXATTR },
+       { TOKEN_XATTR,          MNTOPT_XATTR },
+       { TOKEN_NOXATTR,        MNTOPT_NOXATTR },
+       { TOKEN_ATIME,          MNTOPT_ATIME },
+       { TOKEN_NOATIME,        MNTOPT_NOATIME },
+       { TOKEN_RELATIME,       MNTOPT_RELATIME },
+       { TOKEN_NORELATIME,     MNTOPT_NORELATIME },
+       { TOKEN_NBMAND,         MNTOPT_NBMAND },
+       { TOKEN_NONBMAND,       MNTOPT_NONBMAND },
+       { TOKEN_MNTPOINT,       MNTOPT_MNTPOINT "=%s" },
+       { TOKEN_LAST,           NULL },
+};
+
+static void
+zfsvfs_vfs_free(vfs_t *vfsp)
+{
+       if (vfsp != NULL) {
+               if (vfsp->vfs_mntpoint != NULL)
+                       strfree(vfsp->vfs_mntpoint);
+
+               kmem_free(vfsp, sizeof (vfs_t));
+       }
+}
+
+static int
+zfsvfs_parse_option(char *option, int token, substring_t *args, vfs_t *vfsp)
+{
+       switch (token) {
+       case TOKEN_RO:
+               vfsp->vfs_readonly = B_TRUE;
+               vfsp->vfs_do_readonly = B_TRUE;
+               break;
+       case TOKEN_RW:
+               vfsp->vfs_readonly = B_FALSE;
+               vfsp->vfs_do_readonly = B_TRUE;
+               break;
+       case TOKEN_SETUID:
+               vfsp->vfs_setuid = B_TRUE;
+               vfsp->vfs_do_setuid = B_TRUE;
+               break;
+       case TOKEN_NOSETUID:
+               vfsp->vfs_setuid = B_FALSE;
+               vfsp->vfs_do_setuid = B_TRUE;
+               break;
+       case TOKEN_EXEC:
+               vfsp->vfs_exec = B_TRUE;
+               vfsp->vfs_do_exec = B_TRUE;
+               break;
+       case TOKEN_NOEXEC:
+               vfsp->vfs_exec = B_FALSE;
+               vfsp->vfs_do_exec = B_TRUE;
+               break;
+       case TOKEN_DEVICES:
+               vfsp->vfs_devices = B_TRUE;
+               vfsp->vfs_do_devices = B_TRUE;
+               break;
+       case TOKEN_NODEVICES:
+               vfsp->vfs_devices = B_FALSE;
+               vfsp->vfs_do_devices = B_TRUE;
+               break;
+       case TOKEN_DIRXATTR:
+               vfsp->vfs_xattr = ZFS_XATTR_DIR;
+               vfsp->vfs_do_xattr = B_TRUE;
+               break;
+       case TOKEN_SAXATTR:
+               vfsp->vfs_xattr = ZFS_XATTR_SA;
+               vfsp->vfs_do_xattr = B_TRUE;
+               break;
+       case TOKEN_XATTR:
+               vfsp->vfs_xattr = ZFS_XATTR_DIR;
+               vfsp->vfs_do_xattr = B_TRUE;
+               break;
+       case TOKEN_NOXATTR:
+               vfsp->vfs_xattr = ZFS_XATTR_OFF;
+               vfsp->vfs_do_xattr = B_TRUE;
+               break;
+       case TOKEN_ATIME:
+               vfsp->vfs_atime = B_TRUE;
+               vfsp->vfs_do_atime = B_TRUE;
+               break;
+       case TOKEN_NOATIME:
+               vfsp->vfs_atime = B_FALSE;
+               vfsp->vfs_do_atime = B_TRUE;
+               break;
+       case TOKEN_RELATIME:
+               vfsp->vfs_relatime = B_TRUE;
+               vfsp->vfs_do_relatime = B_TRUE;
+               break;
+       case TOKEN_NORELATIME:
+               vfsp->vfs_relatime = B_FALSE;
+               vfsp->vfs_do_relatime = B_TRUE;
+               break;
+       case TOKEN_NBMAND:
+               vfsp->vfs_nbmand = B_TRUE;
+               vfsp->vfs_do_nbmand = B_TRUE;
+               break;
+       case TOKEN_NONBMAND:
+               vfsp->vfs_nbmand = B_FALSE;
+               vfsp->vfs_do_nbmand = B_TRUE;
+               break;
+       case TOKEN_MNTPOINT:
+               vfsp->vfs_mntpoint = match_strdup(&args[0]);
+               if (vfsp->vfs_mntpoint == NULL)
+                       return (SET_ERROR(ENOMEM));
+
+               break;
+       default:
+               break;
+       }
+
+       return (0);
+}
+
+/*
+ * Parse the raw mntopts and return a vfs_t describing the options.
+ */
+static int
+zfsvfs_parse_options(char *mntopts, vfs_t **vfsp)
+{
+       vfs_t *tmp_vfsp;
+       int error;
+
+       tmp_vfsp = kmem_zalloc(sizeof (vfs_t), KM_SLEEP);
+
+       if (mntopts != NULL) {
+               substring_t args[MAX_OPT_ARGS];
+               char *tmp_mntopts, *p, *t;
+               int token;
+
+               tmp_mntopts = t = strdup(mntopts);
+               if (tmp_mntopts == NULL)
+                       return (SET_ERROR(ENOMEM));
+
+               while ((p = strsep(&t, ",")) != NULL) {
+                       if (!*p)
+                               continue;
+
+                       args[0].to = args[0].from = NULL;
+                       token = match_token(p, zpl_tokens, args);
+                       error = zfsvfs_parse_option(p, token, args, tmp_vfsp);
+                       if (error) {
+                               strfree(tmp_mntopts);
+                               zfsvfs_vfs_free(tmp_vfsp);
+                               return (error);
+                       }
+               }
+
+               strfree(tmp_mntopts);
+       }
+
+       *vfsp = tmp_vfsp;
+
+       return (0);
+}
+
+boolean_t
+zfs_is_readonly(zfsvfs_t *zfsvfs)
+{
+       return (!!(zfsvfs->z_sb->s_flags & SB_RDONLY));
+}
+
 /*ARGSUSED*/
 int
 zfs_sync(struct super_block *sb, int wait, cred_t *cr)
 {
        zfsvfs_t *zfsvfs = sb->s_fs_info;
 
-       /*
-        * Data integrity is job one.  We don't want a compromised kernel
-        * writing to the storage pool, so we never sync during panic.
-        */
-       if (unlikely(oops_in_progress))
-               return (0);
-
        /*
         * Semantically, the only requirement is that the sync be initiated.
         * The DMU syncs out txgs frequently, so there's nothing to do.
@@ -122,16 +300,24 @@ zfs_sync(struct super_block *sb, int wait, cred_t *cr)
        return (0);
 }
 
-boolean_t
-zfs_is_readonly(zfsvfs_t *zfsvfs)
-{
-       return (!!(zfsvfs->z_sb->s_flags & MS_RDONLY));
-}
-
 static void
 atime_changed_cb(void *arg, uint64_t newval)
 {
-       ((zfsvfs_t *)arg)->z_atime = newval;
+       zfsvfs_t *zfsvfs = arg;
+       struct super_block *sb = zfsvfs->z_sb;
+
+       if (sb == NULL)
+               return;
+       /*
+        * Update SB_NOATIME bit in VFS super block.  Since atime update is
+        * determined by atime_needs_update(), atime_needs_update() needs to
+        * return false if atime is turned off, and not unconditionally return
+        * false if atime is turned on.
+        */
+       if (newval)
+               sb->s_flags &= ~SB_NOATIME;
+       else
+               sb->s_flags |= SB_NOATIME;
 }
 
 static void
@@ -165,15 +351,15 @@ acltype_changed_cb(void *arg, uint64_t newval)
        switch (newval) {
        case ZFS_ACLTYPE_OFF:
                zfsvfs->z_acl_type = ZFS_ACLTYPE_OFF;
-               zfsvfs->z_sb->s_flags &= ~MS_POSIXACL;
+               zfsvfs->z_sb->s_flags &= ~SB_POSIXACL;
                break;
        case ZFS_ACLTYPE_POSIXACL:
 #ifdef CONFIG_FS_POSIX_ACL
                zfsvfs->z_acl_type = ZFS_ACLTYPE_POSIXACL;
-               zfsvfs->z_sb->s_flags |= MS_POSIXACL;
+               zfsvfs->z_sb->s_flags |= SB_POSIXACL;
 #else
                zfsvfs->z_acl_type = ZFS_ACLTYPE_OFF;
-               zfsvfs->z_sb->s_flags &= ~MS_POSIXACL;
+               zfsvfs->z_sb->s_flags &= ~SB_POSIXACL;
 #endif /* CONFIG_FS_POSIX_ACL */
                break;
        default:
@@ -202,9 +388,9 @@ readonly_changed_cb(void *arg, uint64_t newval)
                return;
 
        if (newval)
-               sb->s_flags |= MS_RDONLY;
+               sb->s_flags |= SB_RDONLY;
        else
-               sb->s_flags &= ~MS_RDONLY;
+               sb->s_flags &= ~SB_RDONLY;
 }
 
 static void
@@ -232,9 +418,9 @@ nbmand_changed_cb(void *arg, uint64_t newval)
                return;
 
        if (newval == TRUE)
-               sb->s_flags |= MS_MANDLOCK;
+               sb->s_flags |= SB_MANDLOCK;
        else
-               sb->s_flags &= ~MS_MANDLOCK;
+               sb->s_flags &= ~SB_MANDLOCK;
 }
 
 static void
@@ -255,16 +441,18 @@ acl_inherit_changed_cb(void *arg, uint64_t newval)
        ((zfsvfs_t *)arg)->z_acl_inherit = newval;
 }
 
-int
-zfs_register_callbacks(zfsvfs_t *zfsvfs)
+static int
+zfs_register_callbacks(vfs_t *vfsp)
 {
        struct dsl_dataset *ds = NULL;
-       objset_t *os = zfsvfs->z_os;
-       zfs_mntopts_t *zmo = zfsvfs->z_mntopts;
+       objset_t *os = NULL;
+       zfsvfs_t *zfsvfs = NULL;
        int error = 0;
 
+       ASSERT(vfsp);
+       zfsvfs = vfsp->vfs_data;
        ASSERT(zfsvfs);
-       ASSERT(zmo);
+       os = zfsvfs->z_os;
 
        /*
         * The act of registering our callbacks will destroy any mount
@@ -273,8 +461,8 @@ zfs_register_callbacks(zfsvfs_t *zfsvfs)
         * restore them after we register the callbacks.
         */
        if (zfs_is_readonly(zfsvfs) || !spa_writeable(dmu_objset_spa(os))) {
-               zmo->z_do_readonly = B_TRUE;
-               zmo->z_readonly = B_TRUE;
+               vfsp->vfs_do_readonly = B_TRUE;
+               vfsp->vfs_readonly = B_TRUE;
        }
 
        /*
@@ -320,22 +508,22 @@ zfs_register_callbacks(zfsvfs_t *zfsvfs)
        /*
         * Invoke our callbacks to restore temporary mount options.
         */
-       if (zmo->z_do_readonly)
-               readonly_changed_cb(zfsvfs, zmo->z_readonly);
-       if (zmo->z_do_setuid)
-               setuid_changed_cb(zfsvfs, zmo->z_setuid);
-       if (zmo->z_do_exec)
-               exec_changed_cb(zfsvfs, zmo->z_exec);
-       if (zmo->z_do_devices)
-               devices_changed_cb(zfsvfs, zmo->z_devices);
-       if (zmo->z_do_xattr)
-               xattr_changed_cb(zfsvfs, zmo->z_xattr);
-       if (zmo->z_do_atime)
-               atime_changed_cb(zfsvfs, zmo->z_atime);
-       if (zmo->z_do_relatime)
-               relatime_changed_cb(zfsvfs, zmo->z_relatime);
-       if (zmo->z_do_nbmand)
-               nbmand_changed_cb(zfsvfs, zmo->z_nbmand);
+       if (vfsp->vfs_do_readonly)
+               readonly_changed_cb(zfsvfs, vfsp->vfs_readonly);
+       if (vfsp->vfs_do_setuid)
+               setuid_changed_cb(zfsvfs, vfsp->vfs_setuid);
+       if (vfsp->vfs_do_exec)
+               exec_changed_cb(zfsvfs, vfsp->vfs_exec);
+       if (vfsp->vfs_do_devices)
+               devices_changed_cb(zfsvfs, vfsp->vfs_devices);
+       if (vfsp->vfs_do_xattr)
+               xattr_changed_cb(zfsvfs, vfsp->vfs_xattr);
+       if (vfsp->vfs_do_atime)
+               atime_changed_cb(zfsvfs, vfsp->vfs_atime);
+       if (vfsp->vfs_do_relatime)
+               relatime_changed_cb(zfsvfs, vfsp->vfs_relatime);
+       if (vfsp->vfs_do_nbmand)
+               nbmand_changed_cb(zfsvfs, vfsp->vfs_nbmand);
 
        return (0);
 
@@ -346,8 +534,14 @@ unregister:
 
 static int
 zfs_space_delta_cb(dmu_object_type_t bonustype, void *data,
-    uint64_t *userp, uint64_t *groupp)
+    uint64_t *userp, uint64_t *groupp, uint64_t *projectp)
 {
+       sa_hdr_phys_t sa;
+       sa_hdr_phys_t *sap = data;
+       uint64_t flags;
+       int hdrsize;
+       boolean_t swap = B_FALSE;
+
        /*
         * Is it a valid type of object to track?
         */
@@ -367,42 +561,49 @@ zfs_space_delta_cb(dmu_object_type_t bonustype, void *data,
                znode_phys_t *znp = data;
                *userp = znp->zp_uid;
                *groupp = znp->zp_gid;
+               *projectp = ZFS_DEFAULT_PROJID;
+               return (0);
+       }
+
+       if (sap->sa_magic == 0) {
+               /*
+                * This should only happen for newly created files
+                * that haven't had the znode data filled in yet.
+                */
+               *userp = 0;
+               *groupp = 0;
+               *projectp = ZFS_DEFAULT_PROJID;
+               return (0);
+       }
+
+       sa = *sap;
+       if (sa.sa_magic == BSWAP_32(SA_MAGIC)) {
+               sa.sa_magic = SA_MAGIC;
+               sa.sa_layout_info = BSWAP_16(sa.sa_layout_info);
+               swap = B_TRUE;
        } else {
-               int hdrsize;
-               sa_hdr_phys_t *sap = data;
-               sa_hdr_phys_t sa = *sap;
-               boolean_t swap = B_FALSE;
-
-               ASSERT(bonustype == DMU_OT_SA);
-
-               if (sa.sa_magic == 0) {
-                       /*
-                        * This should only happen for newly created
-                        * files that haven't had the znode data filled
-                        * in yet.
-                        */
-                       *userp = 0;
-                       *groupp = 0;
-                       return (0);
-               }
-               if (sa.sa_magic == BSWAP_32(SA_MAGIC)) {
-                       sa.sa_magic = SA_MAGIC;
-                       sa.sa_layout_info = BSWAP_16(sa.sa_layout_info);
-                       swap = B_TRUE;
-               } else {
-                       VERIFY3U(sa.sa_magic, ==, SA_MAGIC);
-               }
+               VERIFY3U(sa.sa_magic, ==, SA_MAGIC);
+       }
 
-               hdrsize = sa_hdrsize(&sa);
-               VERIFY3U(hdrsize, >=, sizeof (sa_hdr_phys_t));
-               *userp = *((uint64_t *)((uintptr_t)data + hdrsize +
-                   SA_UID_OFFSET));
-               *groupp = *((uint64_t *)((uintptr_t)data + hdrsize +
-                   SA_GID_OFFSET));
-               if (swap) {
-                       *userp = BSWAP_64(*userp);
-                       *groupp = BSWAP_64(*groupp);
-               }
+       hdrsize = sa_hdrsize(&sa);
+       VERIFY3U(hdrsize, >=, sizeof (sa_hdr_phys_t));
+
+       *userp = *((uint64_t *)((uintptr_t)data + hdrsize + SA_UID_OFFSET));
+       *groupp = *((uint64_t *)((uintptr_t)data + hdrsize + SA_GID_OFFSET));
+       flags = *((uint64_t *)((uintptr_t)data + hdrsize + SA_FLAGS_OFFSET));
+       if (swap)
+               flags = BSWAP_64(flags);
+
+       if (flags & ZFS_PROJID)
+               *projectp = *((uint64_t *)((uintptr_t)data + hdrsize +
+                   SA_PROJID_OFFSET));
+       else
+               *projectp = ZFS_DEFAULT_PROJID;
+
+       if (swap) {
+               *userp = BSWAP_64(*userp);
+               *groupp = BSWAP_64(*groupp);
+               *projectp = BSWAP_64(*projectp);
        }
        return (0);
 }
@@ -414,7 +615,7 @@ fuidstr_to_sid(zfsvfs_t *zfsvfs, const char *fuidstr,
        uint64_t fuid;
        const char *domain;
 
-       fuid = strtonum(fuidstr, NULL);
+       fuid = zfs_strtonum(fuidstr, NULL);
 
        domain = zfs_fuid_find_by_idx(zfsvfs, FUID_INDEX(fuid));
        if (domain)
@@ -434,6 +635,9 @@ zfs_userquota_prop_to_obj(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type)
        case ZFS_PROP_GROUPUSED:
        case ZFS_PROP_GROUPOBJUSED:
                return (DMU_GROUPUSED_OBJECT);
+       case ZFS_PROP_PROJECTUSED:
+       case ZFS_PROP_PROJECTOBJUSED:
+               return (DMU_PROJECTUSED_OBJECT);
        case ZFS_PROP_USERQUOTA:
                return (zfsvfs->z_userquota_obj);
        case ZFS_PROP_GROUPQUOTA:
@@ -442,6 +646,10 @@ zfs_userquota_prop_to_obj(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type)
                return (zfsvfs->z_userobjquota_obj);
        case ZFS_PROP_GROUPOBJQUOTA:
                return (zfsvfs->z_groupobjquota_obj);
+       case ZFS_PROP_PROJECTQUOTA:
+               return (zfsvfs->z_projectquota_obj);
+       case ZFS_PROP_PROJECTOBJQUOTA:
+               return (zfsvfs->z_projectobjquota_obj);
        default:
                return (ZFS_NO_OBJECT);
        }
@@ -461,8 +669,16 @@ zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
        if (!dmu_objset_userspace_present(zfsvfs->z_os))
                return (SET_ERROR(ENOTSUP));
 
+       if ((type == ZFS_PROP_PROJECTQUOTA || type == ZFS_PROP_PROJECTUSED ||
+           type == ZFS_PROP_PROJECTOBJQUOTA ||
+           type == ZFS_PROP_PROJECTOBJUSED) &&
+           !dmu_objset_projectquota_present(zfsvfs->z_os))
+               return (SET_ERROR(ENOTSUP));
+
        if ((type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
-           type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA) &&
+           type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA ||
+           type == ZFS_PROP_PROJECTOBJUSED ||
+           type == ZFS_PROP_PROJECTOBJQUOTA) &&
            !dmu_objset_userobjspace_present(zfsvfs->z_os))
                return (SET_ERROR(ENOTSUP));
 
@@ -472,7 +688,8 @@ zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
                return (0);
        }
 
-       if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED)
+       if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
+           type == ZFS_PROP_PROJECTOBJUSED)
                offset = DMU_OBJACCT_PREFIX_LEN;
 
        for (zap_cursor_init_serialized(&zc, zfsvfs->z_os, obj, *cookiep);
@@ -541,16 +758,28 @@ zfs_userspace_one(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
                return (SET_ERROR(ENOTSUP));
 
        if ((type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
-           type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA) &&
+           type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA ||
+           type == ZFS_PROP_PROJECTOBJUSED ||
+           type == ZFS_PROP_PROJECTOBJQUOTA) &&
            !dmu_objset_userobjspace_present(zfsvfs->z_os))
                return (SET_ERROR(ENOTSUP));
 
+       if (type == ZFS_PROP_PROJECTQUOTA || type == ZFS_PROP_PROJECTUSED ||
+           type == ZFS_PROP_PROJECTOBJQUOTA ||
+           type == ZFS_PROP_PROJECTOBJUSED) {
+               if (!dmu_objset_projectquota_present(zfsvfs->z_os))
+                       return (SET_ERROR(ENOTSUP));
+               if (!zpl_is_valid_projid(rid))
+                       return (SET_ERROR(EINVAL));
+       }
+
        obj = zfs_userquota_prop_to_obj(zfsvfs, type);
        if (obj == ZFS_NO_OBJECT)
                return (0);
 
-       if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED) {
-               strlcpy(buf, DMU_OBJACCT_PREFIX, DMU_OBJACCT_PREFIX_LEN);
+       if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
+           type == ZFS_PROP_PROJECTOBJUSED) {
+               strlcpy(buf, DMU_OBJACCT_PREFIX, DMU_OBJACCT_PREFIX_LEN + 1);
                offset = DMU_OBJACCT_PREFIX_LEN;
        }
 
@@ -590,6 +819,22 @@ zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
        case ZFS_PROP_GROUPOBJQUOTA:
                objp = &zfsvfs->z_groupobjquota_obj;
                break;
+       case ZFS_PROP_PROJECTQUOTA:
+               if (!dmu_objset_projectquota_enabled(zfsvfs->z_os))
+                       return (SET_ERROR(ENOTSUP));
+               if (!zpl_is_valid_projid(rid))
+                       return (SET_ERROR(EINVAL));
+
+               objp = &zfsvfs->z_projectquota_obj;
+               break;
+       case ZFS_PROP_PROJECTOBJQUOTA:
+               if (!dmu_objset_projectquota_enabled(zfsvfs->z_os))
+                       return (SET_ERROR(ENOTSUP));
+               if (!zpl_is_valid_projid(rid))
+                       return (SET_ERROR(EINVAL));
+
+               objp = &zfsvfs->z_projectobjquota_obj;
+               break;
        default:
                return (SET_ERROR(EINVAL));
        }
@@ -637,30 +882,51 @@ zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
 }
 
 boolean_t
-zfs_fuid_overobjquota(zfsvfs_t *zfsvfs, boolean_t isgroup, uint64_t fuid)
+zfs_id_overobjquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
 {
        char buf[20 + DMU_OBJACCT_PREFIX_LEN];
-       uint64_t used, quota, usedobj, quotaobj;
+       uint64_t used, quota, quotaobj;
        int err;
 
        if (!dmu_objset_userobjspace_present(zfsvfs->z_os)) {
-               if (dmu_objset_userobjspace_upgradable(zfsvfs->z_os))
-                       dmu_objset_userobjspace_upgrade(zfsvfs->z_os);
+               if (dmu_objset_userobjspace_upgradable(zfsvfs->z_os)) {
+                       dsl_pool_config_enter(
+                           dmu_objset_pool(zfsvfs->z_os), FTAG);
+                       dmu_objset_id_quota_upgrade(zfsvfs->z_os);
+                       dsl_pool_config_exit(
+                           dmu_objset_pool(zfsvfs->z_os), FTAG);
+               }
                return (B_FALSE);
        }
 
-       usedobj = isgroup ? DMU_GROUPUSED_OBJECT : DMU_USERUSED_OBJECT;
-       quotaobj = isgroup ? zfsvfs->z_groupobjquota_obj :
-           zfsvfs->z_userobjquota_obj;
+       if (usedobj == DMU_PROJECTUSED_OBJECT) {
+               if (!dmu_objset_projectquota_present(zfsvfs->z_os)) {
+                       if (dmu_objset_projectquota_upgradable(zfsvfs->z_os)) {
+                               dsl_pool_config_enter(
+                                   dmu_objset_pool(zfsvfs->z_os), FTAG);
+                               dmu_objset_id_quota_upgrade(zfsvfs->z_os);
+                               dsl_pool_config_exit(
+                                   dmu_objset_pool(zfsvfs->z_os), FTAG);
+                       }
+                       return (B_FALSE);
+               }
+               quotaobj = zfsvfs->z_projectobjquota_obj;
+       } else if (usedobj == DMU_USERUSED_OBJECT) {
+               quotaobj = zfsvfs->z_userobjquota_obj;
+       } else if (usedobj == DMU_GROUPUSED_OBJECT) {
+               quotaobj = zfsvfs->z_groupobjquota_obj;
+       } else {
+               return (B_FALSE);
+       }
        if (quotaobj == 0 || zfsvfs->z_replay)
                return (B_FALSE);
 
-       (void) sprintf(buf, "%llx", (longlong_t)fuid);
+       (void) sprintf(buf, "%llx", (longlong_t)id);
        err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, &quota);
        if (err != 0)
                return (B_FALSE);
 
-       (void) sprintf(buf, DMU_OBJACCT_PREFIX "%llx", (longlong_t)fuid);
+       (void) sprintf(buf, DMU_OBJACCT_PREFIX "%llx", (longlong_t)id);
        err = zap_lookup(zfsvfs->z_os, usedobj, buf, 8, 1, &used);
        if (err != 0)
                return (B_FALSE);
@@ -668,19 +934,35 @@ zfs_fuid_overobjquota(zfsvfs_t *zfsvfs, boolean_t isgroup, uint64_t fuid)
 }
 
 boolean_t
-zfs_fuid_overquota(zfsvfs_t *zfsvfs, boolean_t isgroup, uint64_t fuid)
+zfs_id_overblockquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
 {
        char buf[20];
-       uint64_t used, quota, usedobj, quotaobj;
+       uint64_t used, quota, quotaobj;
        int err;
 
-       usedobj = isgroup ? DMU_GROUPUSED_OBJECT : DMU_USERUSED_OBJECT;
-       quotaobj = isgroup ? zfsvfs->z_groupquota_obj : zfsvfs->z_userquota_obj;
-
+       if (usedobj == DMU_PROJECTUSED_OBJECT) {
+               if (!dmu_objset_projectquota_present(zfsvfs->z_os)) {
+                       if (dmu_objset_projectquota_upgradable(zfsvfs->z_os)) {
+                               dsl_pool_config_enter(
+                                   dmu_objset_pool(zfsvfs->z_os), FTAG);
+                               dmu_objset_id_quota_upgrade(zfsvfs->z_os);
+                               dsl_pool_config_exit(
+                                   dmu_objset_pool(zfsvfs->z_os), FTAG);
+                       }
+                       return (B_FALSE);
+               }
+               quotaobj = zfsvfs->z_projectquota_obj;
+       } else if (usedobj == DMU_USERUSED_OBJECT) {
+               quotaobj = zfsvfs->z_userquota_obj;
+       } else if (usedobj == DMU_GROUPUSED_OBJECT) {
+               quotaobj = zfsvfs->z_groupquota_obj;
+       } else {
+               return (B_FALSE);
+       }
        if (quotaobj == 0 || zfsvfs->z_replay)
                return (B_FALSE);
 
-       (void) sprintf(buf, "%llx", (longlong_t)fuid);
+       (void) sprintf(buf, "%llx", (longlong_t)id);
        err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, &quota);
        if (err != 0)
                return (B_FALSE);
@@ -692,96 +974,56 @@ zfs_fuid_overquota(zfsvfs_t *zfsvfs, boolean_t isgroup, uint64_t fuid)
 }
 
 boolean_t
-zfs_owner_overquota(zfsvfs_t *zfsvfs, znode_t *zp, boolean_t isgroup)
-{
-       uint64_t fuid;
-       uint64_t quotaobj;
-       struct inode *ip = ZTOI(zp);
-
-       quotaobj = isgroup ? zfsvfs->z_groupquota_obj : zfsvfs->z_userquota_obj;
-
-       fuid = isgroup ? KGID_TO_SGID(ip->i_gid) : KUID_TO_SUID(ip->i_uid);
-
-       if (quotaobj == 0 || zfsvfs->z_replay)
-               return (B_FALSE);
-
-       return (zfs_fuid_overquota(zfsvfs, isgroup, fuid));
-}
-
-zfs_mntopts_t *
-zfs_mntopts_alloc(void)
-{
-       return (kmem_zalloc(sizeof (zfs_mntopts_t), KM_SLEEP));
-}
-
-void
-zfs_mntopts_free(zfs_mntopts_t *zmo)
+zfs_id_overquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
 {
-       if (zmo->z_osname)
-               strfree(zmo->z_osname);
-
-       if (zmo->z_mntpoint)
-               strfree(zmo->z_mntpoint);
-
-       kmem_free(zmo, sizeof (zfs_mntopts_t));
+       return (zfs_id_overblockquota(zfsvfs, usedobj, id) ||
+           zfs_id_overobjquota(zfsvfs, usedobj, id));
 }
 
-int
-zfsvfs_create(const char *osname, zfs_mntopts_t *zmo, zfsvfs_t **zfvp)
+/*
+ * Associate this zfsvfs with the given objset, which must be owned.
+ * This will cache a bunch of on-disk state from the objset in the
+ * zfsvfs.
+ */
+static int
+zfsvfs_init(zfsvfs_t *zfsvfs, objset_t *os)
 {
-       objset_t *os;
-       zfsvfs_t *zfsvfs;
-       uint64_t zval;
-       int i, size, error;
-       uint64_t sa_obj;
-
-       zfsvfs = kmem_zalloc(sizeof (zfsvfs_t), KM_SLEEP);
-
-       /*
-        * Optional temporary mount options, free'd in zfsvfs_free().
-        */
-       zfsvfs->z_mntopts = (zmo ? zmo : zfs_mntopts_alloc());
-
-       /*
-        * We claim to always be readonly so we can open snapshots;
-        * other ZPL code will prevent us from writing to snapshots.
-        */
-       error = dmu_objset_own(osname, DMU_OST_ZFS, B_TRUE, zfsvfs, &os);
-       if (error)
-               goto out_zmo;
+       int error;
+       uint64_t val;
 
-       /*
-        * Initialize the zfs-specific filesystem structure.
-        * Should probably make this a kmem cache, shuffle fields.
-        */
-       zfsvfs->z_sb = NULL;
-       zfsvfs->z_parent = zfsvfs;
        zfsvfs->z_max_blksz = SPA_OLD_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) {
-               error = SET_ERROR(ENOTSUP);
-               goto out;
+       if (error != 0)
+               return (error);
+       if (zfsvfs->z_version >
+           zfs_zpl_version_map(spa_version(dmu_objset_spa(os)))) {
+               (void) printk("Can't mount a version %lld file system "
+                   "on a version %lld pool\n. Pool must be upgraded to mount "
+                   "this file system.", (u_longlong_t)zfsvfs->z_version,
+                   (u_longlong_t)spa_version(dmu_objset_spa(os)));
+               return (SET_ERROR(ENOTSUP));
        }
-       if ((error = zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &zval)) != 0)
-               goto out;
-       zfsvfs->z_norm = (int)zval;
+       error = zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &val);
+       if (error != 0)
+               return (error);
+       zfsvfs->z_norm = (int)val;
 
-       if ((error = zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &zval)) != 0)
-               goto out;
-       zfsvfs->z_utf8 = (zval != 0);
+       error = zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &val);
+       if (error != 0)
+               return (error);
+       zfsvfs->z_utf8 = (val != 0);
 
-       if ((error = zfs_get_zplprop(os, ZFS_PROP_CASE, &zval)) != 0)
-               goto out;
-       zfsvfs->z_case = (uint_t)zval;
+       error = zfs_get_zplprop(os, ZFS_PROP_CASE, &val);
+       if (error != 0)
+               return (error);
+       zfsvfs->z_case = (uint_t)val;
 
-       if ((error = zfs_get_zplprop(os, ZFS_PROP_ACLTYPE, &zval)) != 0)
-               goto out;
-       zfsvfs->z_acl_type = (uint_t)zval;
+       if ((error = zfs_get_zplprop(os, ZFS_PROP_ACLTYPE, &val)) != 0)
+               return (error);
+       zfsvfs->z_acl_type = (uint_t)val;
 
        /*
         * Fold case on file systems that are always or sometimes case
@@ -794,76 +1036,139 @@ zfsvfs_create(const char *osname, zfs_mntopts_t *zmo, zfsvfs_t **zfvp)
        zfsvfs->z_use_fuids = USE_FUIDS(zfsvfs->z_version, zfsvfs->z_os);
        zfsvfs->z_use_sa = USE_SA(zfsvfs->z_version, zfsvfs->z_os);
 
+       uint64_t sa_obj = 0;
        if (zfsvfs->z_use_sa) {
                /* should either have both of these objects or none */
                error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_SA_ATTRS, 8, 1,
                    &sa_obj);
-               if (error)
-                       goto out;
+               if (error != 0)
+                       return (error);
 
-               error = zfs_get_zplprop(os, ZFS_PROP_XATTR, &zval);
-               if ((error == 0) && (zval == ZFS_XATTR_SA))
+               error = zfs_get_zplprop(os, ZFS_PROP_XATTR, &val);
+               if ((error == 0) && (val == ZFS_XATTR_SA))
                        zfsvfs->z_xattr_sa = B_TRUE;
-       } else {
-               /*
-                * Pre SA versions file systems should never touch
-                * either the attribute registration or layout objects.
-                */
-               sa_obj = 0;
        }
 
-       error = sa_setup(os, sa_obj, zfs_attr_table, ZPL_END,
-           &zfsvfs->z_attr_table);
-       if (error)
-               goto out;
-
-       if (zfsvfs->z_version >= ZPL_VERSION_SA)
-               sa_register_update_callback(os, zfs_sa_upgrade);
-
        error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_ROOT_OBJ, 8, 1,
            &zfsvfs->z_root);
-       if (error)
-               goto out;
+       if (error != 0)
+               return (error);
        ASSERT(zfsvfs->z_root != 0);
 
        error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_UNLINKED_SET, 8, 1,
            &zfsvfs->z_unlinkedobj);
-       if (error)
-               goto out;
+       if (error != 0)
+               return (error);
 
        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;
+       if (error == ENOENT)
+               zfsvfs->z_userquota_obj = 0;
+       else if (error != 0)
+               return (error);
 
        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;
+       if (error == ENOENT)
+               zfsvfs->z_groupquota_obj = 0;
+       else if (error != 0)
+               return (error);
+
+       error = zap_lookup(os, MASTER_NODE_OBJ,
+           zfs_userquota_prop_prefixes[ZFS_PROP_PROJECTQUOTA],
+           8, 1, &zfsvfs->z_projectquota_obj);
+       if (error == ENOENT)
+               zfsvfs->z_projectquota_obj = 0;
+       else if (error != 0)
+               return (error);
 
        error = zap_lookup(os, MASTER_NODE_OBJ,
            zfs_userquota_prop_prefixes[ZFS_PROP_USEROBJQUOTA],
            8, 1, &zfsvfs->z_userobjquota_obj);
-       if (error && error != ENOENT)
-               goto out;
+       if (error == ENOENT)
+               zfsvfs->z_userobjquota_obj = 0;
+       else if (error != 0)
+               return (error);
 
        error = zap_lookup(os, MASTER_NODE_OBJ,
            zfs_userquota_prop_prefixes[ZFS_PROP_GROUPOBJQUOTA],
            8, 1, &zfsvfs->z_groupobjquota_obj);
-       if (error && error != ENOENT)
-               goto out;
+       if (error == ENOENT)
+               zfsvfs->z_groupobjquota_obj = 0;
+       else if (error != 0)
+               return (error);
+
+       error = zap_lookup(os, MASTER_NODE_OBJ,
+           zfs_userquota_prop_prefixes[ZFS_PROP_PROJECTOBJQUOTA],
+           8, 1, &zfsvfs->z_projectobjquota_obj);
+       if (error == ENOENT)
+               zfsvfs->z_projectobjquota_obj = 0;
+       else if (error != 0)
+               return (error);
 
        error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_FUID_TABLES, 8, 1,
            &zfsvfs->z_fuid_obj);
-       if (error && error != ENOENT)
-               goto out;
+       if (error == ENOENT)
+               zfsvfs->z_fuid_obj = 0;
+       else if (error != 0)
+               return (error);
 
        error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_SHARES_DIR, 8, 1,
            &zfsvfs->z_shares_dir);
-       if (error && error != ENOENT)
-               goto out;
+       if (error == ENOENT)
+               zfsvfs->z_shares_dir = 0;
+       else if (error != 0)
+               return (error);
+
+       error = sa_setup(os, sa_obj, zfs_attr_table, ZPL_END,
+           &zfsvfs->z_attr_table);
+       if (error != 0)
+               return (error);
+
+       if (zfsvfs->z_version >= ZPL_VERSION_SA)
+               sa_register_update_callback(os, zfs_sa_upgrade);
+
+       return (0);
+}
+
+int
+zfsvfs_create(const char *osname, boolean_t readonly, zfsvfs_t **zfvp)
+{
+       objset_t *os;
+       zfsvfs_t *zfsvfs;
+       int error;
+       boolean_t ro = (readonly || (strchr(osname, '@') != NULL));
+
+       zfsvfs = kmem_zalloc(sizeof (zfsvfs_t), KM_SLEEP);
+
+       error = dmu_objset_own(osname, DMU_OST_ZFS, ro, B_TRUE, zfsvfs, &os);
+       if (error != 0) {
+               kmem_free(zfsvfs, sizeof (zfsvfs_t));
+               return (error);
+       }
+
+       error = zfsvfs_create_impl(zfvp, zfsvfs, os);
+       if (error != 0) {
+               dmu_objset_disown(os, B_TRUE, zfsvfs);
+       }
+       return (error);
+}
+
+
+/*
+ * Note: zfsvfs is assumed to be malloc'd, and will be freed by this function
+ * on a failure.  Do not pass in a statically allocated zfsvfs.
+ */
+int
+zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os)
+{
+       int error;
+
+       zfsvfs->z_vfs = NULL;
+       zfsvfs->z_sb = NULL;
+       zfsvfs->z_parent = zfsvfs;
 
        mutex_init(&zfsvfs->z_znodes_lock, NULL, MUTEX_DEFAULT, NULL);
        mutex_init(&zfsvfs->z_lock, NULL, MUTEX_DEFAULT, NULL);
@@ -873,35 +1178,40 @@ zfsvfs_create(const char *osname, zfs_mntopts_t *zmo, zfsvfs_t **zfvp)
        rw_init(&zfsvfs->z_teardown_inactive_lock, NULL, RW_DEFAULT, NULL);
        rw_init(&zfsvfs->z_fuid_lock, NULL, RW_DEFAULT, NULL);
 
-       size = MIN(1 << (highbit64(zfs_object_mutex_size)-1), ZFS_OBJ_MTX_MAX);
+       int size = MIN(1 << (highbit64(zfs_object_mutex_size) - 1),
+           ZFS_OBJ_MTX_MAX);
        zfsvfs->z_hold_size = size;
        zfsvfs->z_hold_trees = vmem_zalloc(sizeof (avl_tree_t) * size,
            KM_SLEEP);
        zfsvfs->z_hold_locks = vmem_zalloc(sizeof (kmutex_t) * size, KM_SLEEP);
-       for (i = 0; i != size; i++) {
+       for (int i = 0; i != size; i++) {
                avl_create(&zfsvfs->z_hold_trees[i], zfs_znode_hold_compare,
                    sizeof (znode_hold_t), offsetof(znode_hold_t, zh_node));
                mutex_init(&zfsvfs->z_hold_locks[i], NULL, MUTEX_DEFAULT, NULL);
        }
 
+       error = zfsvfs_init(zfsvfs, os);
+       if (error != 0) {
+               *zfvp = NULL;
+               zfsvfs_free(zfsvfs);
+               return (error);
+       }
+
+       zfsvfs->z_drain_task = TASKQID_INVALID;
+       zfsvfs->z_draining = B_FALSE;
+       zfsvfs->z_drain_cancel = B_TRUE;
+
        *zfvp = zfsvfs;
        return (0);
-
-out:
-       dmu_objset_disown(os, zfsvfs);
-out_zmo:
-       *zfvp = NULL;
-       zfs_mntopts_free(zfsvfs->z_mntopts);
-       kmem_free(zfsvfs, sizeof (zfsvfs_t));
-       return (error);
 }
 
-int
+static int
 zfsvfs_setup(zfsvfs_t *zfsvfs, boolean_t mounting)
 {
        int error;
+       boolean_t readonly = zfs_is_readonly(zfsvfs);
 
-       error = zfs_register_callbacks(zfsvfs);
+       error = zfs_register_callbacks(zfsvfs->z_vfs);
        if (error)
                return (error);
 
@@ -913,17 +1223,27 @@ zfsvfs_setup(zfsvfs_t *zfsvfs, boolean_t mounting)
         * operations out since we closed the ZIL.
         */
        if (mounting) {
-               boolean_t readonly;
+               ASSERT3P(zfsvfs->z_kstat.dk_kstats, ==, NULL);
+               dataset_kstats_create(&zfsvfs->z_kstat, zfsvfs->z_os);
 
                /*
                 * During replay we remove the read only flag to
                 * allow replays to succeed.
                 */
-               readonly = zfs_is_readonly(zfsvfs);
-               if (readonly != 0)
+               if (readonly != 0) {
                        readonly_changed_cb(zfsvfs, B_FALSE);
-               else
+               } else {
+                       zap_stats_t zs;
+                       if (zap_get_stats(zfsvfs->z_os, zfsvfs->z_unlinkedobj,
+                           &zs) == 0) {
+                               dataset_kstats_update_nunlinks_kstat(
+                                   &zfsvfs->z_kstat, zs.zs_num_entries);
+                       }
+                       dprintf_ds(zfsvfs->z_os->os_dsl_dataset,
+                           "num_entries in unlinked set: %llu",
+                           zs.zs_num_entries);
                        zfs_unlinked_drain(zfsvfs);
+               }
 
                /*
                 * Parse and replay the intent log.
@@ -997,7 +1317,8 @@ zfsvfs_free(zfsvfs_t *zfsvfs)
        }
        vmem_free(zfsvfs->z_hold_trees, sizeof (avl_tree_t) * size);
        vmem_free(zfsvfs->z_hold_locks, sizeof (kmutex_t) * size);
-       zfs_mntopts_free(zfsvfs->z_mntopts);
+       zfsvfs_vfs_free(zfsvfs->z_vfs);
+       dataset_kstats_destroy(&zfsvfs->z_kstat);
        kmem_free(zfsvfs, sizeof (zfsvfs_t));
 }
 
@@ -1046,20 +1367,96 @@ zfs_check_global_label(const char *dsname, const char *hexsl)
 }
 #endif /* HAVE_MLSLABEL */
 
+static int
+zfs_statfs_project(zfsvfs_t *zfsvfs, znode_t *zp, struct kstatfs *statp,
+    uint32_t bshift)
+{
+       char buf[20 + DMU_OBJACCT_PREFIX_LEN];
+       uint64_t offset = DMU_OBJACCT_PREFIX_LEN;
+       uint64_t quota;
+       uint64_t used;
+       int err;
+
+       strlcpy(buf, DMU_OBJACCT_PREFIX, DMU_OBJACCT_PREFIX_LEN + 1);
+       err = id_to_fuidstr(zfsvfs, NULL, zp->z_projid, buf + offset, B_FALSE);
+       if (err)
+               return (err);
+
+       if (zfsvfs->z_projectquota_obj == 0)
+               goto objs;
+
+       err = zap_lookup(zfsvfs->z_os, zfsvfs->z_projectquota_obj,
+           buf + offset, 8, 1, &quota);
+       if (err == ENOENT)
+               goto objs;
+       else if (err)
+               return (err);
+
+       err = zap_lookup(zfsvfs->z_os, DMU_PROJECTUSED_OBJECT,
+           buf + offset, 8, 1, &used);
+       if (unlikely(err == ENOENT)) {
+               uint32_t blksize;
+               u_longlong_t nblocks;
+
+               /*
+                * Quota accounting is async, so it is possible race case.
+                * There is at least one object with the given project ID.
+                */
+               sa_object_size(zp->z_sa_hdl, &blksize, &nblocks);
+               if (unlikely(zp->z_blksz == 0))
+                       blksize = zfsvfs->z_max_blksz;
+
+               used = blksize * nblocks;
+       } else if (err) {
+               return (err);
+       }
+
+       statp->f_blocks = quota >> bshift;
+       statp->f_bfree = (quota > used) ? ((quota - used) >> bshift) : 0;
+       statp->f_bavail = statp->f_bfree;
+
+objs:
+       if (zfsvfs->z_projectobjquota_obj == 0)
+               return (0);
+
+       err = zap_lookup(zfsvfs->z_os, zfsvfs->z_projectobjquota_obj,
+           buf + offset, 8, 1, &quota);
+       if (err == ENOENT)
+               return (0);
+       else if (err)
+               return (err);
+
+       err = zap_lookup(zfsvfs->z_os, DMU_PROJECTUSED_OBJECT,
+           buf, 8, 1, &used);
+       if (unlikely(err == ENOENT)) {
+               /*
+                * Quota accounting is async, so it is possible race case.
+                * There is at least one object with the given project ID.
+                */
+               used = 1;
+       } else if (err) {
+               return (err);
+       }
+
+       statp->f_files = quota;
+       statp->f_ffree = (quota > used) ? (quota - used) : 0;
+
+       return (0);
+}
+
 int
 zfs_statvfs(struct dentry *dentry, struct kstatfs *statp)
 {
        zfsvfs_t *zfsvfs = dentry->d_sb->s_fs_info;
        uint64_t refdbytes, availbytes, usedobjs, availobjs;
-       uint64_t fsid;
-       uint32_t bshift;
+       int err = 0;
 
        ZFS_ENTER(zfsvfs);
 
        dmu_objset_space(zfsvfs->z_os,
            &refdbytes, &availbytes, &usedobjs, &availobjs);
 
-       fsid = dmu_objset_fsid_guid(zfsvfs->z_os);
+       uint64_t fsid = dmu_objset_fsid_guid(zfsvfs->z_os);
        /*
         * The underlying storage pool actually uses multiple block
         * size.  Under Solaris frsize (fragment size) is reported as
@@ -1071,7 +1468,7 @@ zfs_statvfs(struct dentry *dentry, struct kstatfs *statp)
         */
        statp->f_frsize = zfsvfs->z_max_blksz;
        statp->f_bsize = zfsvfs->z_max_blksz;
-       bshift = fls(statp->f_bsize) - 1;
+       uint32_t bshift = fls(statp->f_bsize) - 1;
 
        /*
         * The following report "total" blocks of various kinds in
@@ -1079,6 +1476,8 @@ zfs_statvfs(struct dentry *dentry, struct kstatfs *statp)
         * "preferred" size.
         */
 
+       /* Round up so we never have a filesytem using 0 blocks. */
+       refdbytes = P2ROUNDUP(refdbytes, statp->f_bsize);
        statp->f_blocks = (refdbytes + availbytes) >> bshift;
        statp->f_bfree = availbytes >> bshift;
        statp->f_bavail = statp->f_bfree; /* no root reservation */
@@ -1088,7 +1487,7 @@ zfs_statvfs(struct dentry *dentry, struct kstatfs *statp)
         * static metadata.  ZFS doesn't preallocate files, so the best
         * we can do is report the max that could possibly fit in f_files,
         * and that minus the number actually used in f_ffree.
-        * For f_ffree, report the smaller of the number of object available
+        * For f_ffree, report the smaller of the number of objects available
         * and the number of blocks (each object will take at least a block).
         */
        statp->f_ffree = MIN(availobjs, availbytes >> DNODE_SHIFT);
@@ -1104,8 +1503,17 @@ zfs_statvfs(struct dentry *dentry, struct kstatfs *statp)
         */
        bzero(statp->f_spare, sizeof (statp->f_spare));
 
+       if (dmu_objset_projectquota_enabled(zfsvfs->z_os) &&
+           dmu_objset_projectquota_present(zfsvfs->z_os)) {
+               znode_t *zp = ITOZ(dentry->d_inode);
+
+               if (zp->z_pflags & ZFS_PROJINHERIT && zp->z_projid &&
+                   zpl_is_valid_projid(zp->z_projid))
+                       err = zfs_statfs_project(zfsvfs, zp, statp, bshift);
+       }
+
        ZFS_EXIT(zfsvfs);
-       return (0);
+       return (err);
 }
 
 int
@@ -1258,6 +1666,8 @@ zfsvfs_teardown(zfsvfs_t *zfsvfs, boolean_t unmounting)
 {
        znode_t *zp;
 
+       zfs_unlinked_drain_stop_wait(zfsvfs);
+
        /*
         * If someone has not already unmounted this file system,
         * drain the iput_taskq to ensure all active references to the
@@ -1364,10 +1774,10 @@ zfsvfs_teardown(zfsvfs_t *zfsvfs, boolean_t unmounting)
        zfs_unregister_callbacks(zfsvfs);
 
        /*
-        * Evict cached data
+        * Evict cached data. We must write out any dirty data before
+        * disowning the dataset.
         */
-       if (dsl_dataset_is_dirty(dmu_objset_ds(zfsvfs->z_os)) &&
-           !zfs_is_readonly(zfsvfs))
+       if (!zfs_is_readonly(zfsvfs))
                txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), 0);
        dmu_objset_evict_dbufs(zfsvfs->z_os);
 
@@ -1380,22 +1790,36 @@ atomic_long_t zfs_bdi_seq = ATOMIC_LONG_INIT(0);
 #endif
 
 int
-zfs_domount(struct super_block *sb, zfs_mntopts_t *zmo, int silent)
+zfs_domount(struct super_block *sb, zfs_mnt_t *zm, int silent)
 {
-       const char *osname = zmo->z_osname;
-       zfsvfs_t *zfsvfs;
+       const char *osname = zm->mnt_osname;
        struct inode *root_inode;
        uint64_t recordsize;
-       int error;
+       int error = 0;
+       zfsvfs_t *zfsvfs = NULL;
+       vfs_t *vfs = NULL;
+
+       ASSERT(zm);
+       ASSERT(osname);
 
-       error = zfsvfs_create(osname, zmo, &zfsvfs);
+       error = zfsvfs_parse_options(zm->mnt_data, &vfs);
        if (error)
                return (error);
 
+       error = zfsvfs_create(osname, vfs->vfs_readonly, &zfsvfs);
+       if (error) {
+               zfsvfs_vfs_free(vfs);
+               goto out;
+       }
+
        if ((error = dsl_prop_get_integer(osname, "recordsize",
-           &recordsize, NULL)))
+           &recordsize, NULL))) {
+               zfsvfs_vfs_free(vfs);
                goto out;
+       }
 
+       vfs->vfs_data = zfsvfs;
+       zfsvfs->z_vfs = vfs;
        zfsvfs->z_sb = sb;
        sb->s_fs_info = zfsvfs;
        sb->s_magic = ZFS_SUPER_MAGIC;
@@ -1403,13 +1827,13 @@ zfs_domount(struct super_block *sb, zfs_mntopts_t *zmo, int silent)
        sb->s_time_gran = 1;
        sb->s_blocksize = recordsize;
        sb->s_blocksize_bits = ilog2(recordsize);
-       zfsvfs->z_bdi.ra_pages = 0;
-       sb->s_bdi = &zfsvfs->z_bdi;
 
-       error = -zpl_bdi_setup_and_register(&zfsvfs->z_bdi, "zfs");
+       error = -zpl_bdi_setup(sb, "zfs");
        if (error)
                goto out;
 
+       sb->s_bdi->ra_pages = 0;
+
        /* Set callback operations for the file system. */
        sb->s_op = &zpl_super_operations;
        sb->s_xattr = zpl_xattr_handlers;
@@ -1467,8 +1891,10 @@ zfs_domount(struct super_block *sb, zfs_mntopts_t *zmo, int silent)
        zfsvfs->z_arc_prune = arc_add_prune_callback(zpl_prune_sb, sb);
 out:
        if (error) {
-               dmu_objset_disown(zfsvfs->z_os, zfsvfs);
-               zfsvfs_free(zfsvfs);
+               if (zfsvfs != NULL) {
+                       dmu_objset_disown(zfsvfs->z_os, B_TRUE, zfsvfs);
+                       zfsvfs_free(zfsvfs);
+               }
                /*
                 * make sure we don't have dangling sb->s_fs_info which
                 * zfs_preumount will use.
@@ -1493,6 +1919,7 @@ zfs_preumount(struct super_block *sb)
 
        /* zfsvfs is NULL when zfs_domount fails during mount */
        if (zfsvfs) {
+               zfs_unlinked_drain_stop_wait(zfsvfs);
                zfsctl_destroy(sb->s_fs_info);
                /*
                 * Wait for iput_async before entering evict_inodes in
@@ -1527,10 +1954,11 @@ zfs_umount(struct super_block *sb)
        zfsvfs_t *zfsvfs = sb->s_fs_info;
        objset_t *os;
 
-       arc_remove_prune_callback(zfsvfs->z_arc_prune);
+       if (zfsvfs->z_arc_prune != NULL)
+               arc_remove_prune_callback(zfsvfs->z_arc_prune);
        VERIFY(zfsvfs_teardown(zfsvfs, B_TRUE) == 0);
        os = zfsvfs->z_os;
-       bdi_destroy(sb->s_bdi);
+       zpl_bdi_destroy(sb);
 
        /*
         * z_os will be NULL if there was an error in
@@ -1547,7 +1975,7 @@ zfs_umount(struct super_block *sb)
                /*
                 * Finally release the objset
                 */
-               dmu_objset_disown(os, zfsvfs);
+               dmu_objset_disown(os, B_TRUE, zfsvfs);
        }
 
        zfsvfs_free(zfsvfs);
@@ -1555,13 +1983,33 @@ zfs_umount(struct super_block *sb)
 }
 
 int
-zfs_remount(struct super_block *sb, int *flags, zfs_mntopts_t *zmo)
+zfs_remount(struct super_block *sb, int *flags, zfs_mnt_t *zm)
 {
        zfsvfs_t *zfsvfs = sb->s_fs_info;
+       vfs_t *vfsp;
+       boolean_t issnap = dmu_objset_is_snapshot(zfsvfs->z_os);
        int error;
 
+       if ((issnap || !spa_writeable(dmu_objset_spa(zfsvfs->z_os))) &&
+           !(*flags & SB_RDONLY)) {
+               *flags |= SB_RDONLY;
+               return (EROFS);
+       }
+
+       error = zfsvfs_parse_options(zm->mnt_data, &vfsp);
+       if (error)
+               return (error);
+
+       if (!zfs_is_readonly(zfsvfs) && (*flags & SB_RDONLY))
+               txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), 0);
+
        zfs_unregister_callbacks(zfsvfs);
-       error = zfs_register_callbacks(zfsvfs);
+       zfsvfs_vfs_free(zfsvfs->z_vfs);
+
+       vfsp->vfs_data = zfsvfs;
+       zfsvfs->z_vfs = vfsp;
+       if (!issnap)
+               (void) zfs_register_callbacks(vfsp);
 
        return (error);
 }
@@ -1694,14 +2142,17 @@ zfs_suspend_fs(zfsvfs_t *zfsvfs)
 }
 
 /*
- * Reopen zfsvfs_t and release VFS ops.
+ * Rebuild SA and release VOPs.  Note that ownership of the underlying dataset
+ * is an invariant across any of the operations that can be performed while the
+ * filesystem was suspended.  Whether it succeeded or failed, the preconditions
+ * are the same: the relevant objset and associated dataset are owned by
+ * zfsvfs, held, and long held on entry.
  */
 int
 zfs_resume_fs(zfsvfs_t *zfsvfs, dsl_dataset_t *ds)
 {
        int err, err2;
        znode_t *zp;
-       uint64_t sa_obj = 0;
 
        ASSERT(RRM_WRITE_HELD(&zfsvfs->z_teardown_lock));
        ASSERT(RW_WRITE_HELD(&zfsvfs->z_teardown_inactive_lock));
@@ -1710,34 +2161,15 @@ zfs_resume_fs(zfsvfs_t *zfsvfs, dsl_dataset_t *ds)
         * We already own this, so just update the objset_t, as the one we
         * had before may have been evicted.
         */
+       objset_t *os;
        VERIFY3P(ds->ds_owner, ==, zfsvfs);
        VERIFY(dsl_dataset_long_held(ds));
-       VERIFY0(dmu_objset_from_ds(ds, &zfsvfs->z_os));
-
-       /*
-        * Make sure version hasn't changed
-        */
-
-       err = zfs_get_zplprop(zfsvfs->z_os, ZFS_PROP_VERSION,
-           &zfsvfs->z_version);
-
-       if (err)
-               goto bail;
-
-       err = zap_lookup(zfsvfs->z_os, MASTER_NODE_OBJ,
-           ZFS_SA_ATTRS, 8, 1, &sa_obj);
-
-       if (err && zfsvfs->z_version >= ZPL_VERSION_SA)
-               goto bail;
+       VERIFY0(dmu_objset_from_ds(ds, &os));
 
-       if ((err = sa_setup(zfsvfs->z_os, sa_obj,
-           zfs_attr_table,  ZPL_END, &zfsvfs->z_attr_table)) != 0)
+       err = zfsvfs_init(zfsvfs, os);
+       if (err != 0)
                goto bail;
 
-       if (zfsvfs->z_version >= ZPL_VERSION_SA)
-               sa_register_update_callback(zfsvfs->z_os,
-                   zfs_sa_upgrade);
-
        VERIFY(zfsvfs_setup(zfsvfs, B_FALSE) == 0);
 
        zfs_set_fuid_feature(zfsvfs);
@@ -1763,6 +2195,15 @@ zfs_resume_fs(zfsvfs_t *zfsvfs, dsl_dataset_t *ds)
        }
        mutex_exit(&zfsvfs->z_znodes_lock);
 
+       if (!zfs_is_readonly(zfsvfs) && !zfsvfs->z_unmounted) {
+               /*
+                * zfs_suspend_fs() could have interrupted freeing
+                * of dnodes. We need to restart this freeing so
+                * that we don't "leak" the space.
+                */
+               zfs_unlinked_drain(zfsvfs);
+       }
+
 bail:
        /* release the VFS ops */
        rw_exit(&zfsvfs->z_teardown_inactive_lock);
@@ -1839,6 +2280,7 @@ zfs_set_version(zfsvfs_t *zfsvfs, uint64_t newvers)
        dmu_tx_commit(tx);
 
        zfsvfs->z_version = newvers;
+       os->os_version = newvers;
 
        zfs_set_fuid_feature(zfsvfs);
 
@@ -1851,20 +2293,51 @@ zfs_set_version(zfsvfs_t *zfsvfs, uint64_t newvers)
 int
 zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value)
 {
-       const char *pname;
-       int error = SET_ERROR(ENOENT);
+       uint64_t *cached_copy = NULL;
 
        /*
-        * Look up the file system's value for the property.  For the
-        * version property, we look up a slightly different string.
+        * Figure out where in the objset_t the cached copy would live, if it
+        * is available for the requested property.
         */
+       if (os != NULL) {
+               switch (prop) {
+               case ZFS_PROP_VERSION:
+                       cached_copy = &os->os_version;
+                       break;
+               case ZFS_PROP_NORMALIZE:
+                       cached_copy = &os->os_normalization;
+                       break;
+               case ZFS_PROP_UTF8ONLY:
+                       cached_copy = &os->os_utf8only;
+                       break;
+               case ZFS_PROP_CASE:
+                       cached_copy = &os->os_casesensitivity;
+                       break;
+               default:
+                       break;
+               }
+       }
+       if (cached_copy != NULL && *cached_copy != OBJSET_PROP_UNINITIALIZED) {
+               *value = *cached_copy;
+               return (0);
+       }
+
+       /*
+        * If the property wasn't cached, look up the file system's value for
+        * the property. For the version property, we look up a slightly
+        * different string.
+        */
+       const char *pname;
+       int error = ENOENT;
        if (prop == ZFS_PROP_VERSION)
                pname = ZPL_VERSION_STR;
        else
                pname = zfs_prop_to_name(prop);
 
-       if (os != NULL)
+       if (os != NULL) {
+               ASSERT3U(os->os_phys->os_type, ==, DMU_OST_ZFS);
                error = zap_lookup(os, MASTER_NODE_OBJ, pname, 8, 1, value);
+       }
 
        if (error == ENOENT) {
                /* No value set, use the default value */
@@ -1887,6 +2360,15 @@ zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value)
                }
                error = 0;
        }
+
+       /*
+        * If one of the methods for getting the property value above worked,
+        * copy it into the objset_t's cache.
+        */
+       if (error == 0 && cached_copy != NULL) {
+               *cached_copy = *value;
+       }
+
        return (error);
 }
 
@@ -1934,15 +2416,15 @@ zfs_fini(void)
        zfsctl_fini();
 }
 
-#if defined(_KERNEL) && defined(HAVE_SPL)
+#if defined(_KERNEL)
 EXPORT_SYMBOL(zfs_suspend_fs);
 EXPORT_SYMBOL(zfs_resume_fs);
 EXPORT_SYMBOL(zfs_userspace_one);
 EXPORT_SYMBOL(zfs_userspace_many);
 EXPORT_SYMBOL(zfs_set_userquota);
-EXPORT_SYMBOL(zfs_owner_overquota);
-EXPORT_SYMBOL(zfs_fuid_overquota);
-EXPORT_SYMBOL(zfs_fuid_overobjquota);
+EXPORT_SYMBOL(zfs_id_overblockquota);
+EXPORT_SYMBOL(zfs_id_overobjquota);
+EXPORT_SYMBOL(zfs_id_overquota);
 EXPORT_SYMBOL(zfs_set_version);
 EXPORT_SYMBOL(zfsvfs_create);
 EXPORT_SYMBOL(zfsvfs_free);