X-Git-Url: https://git.camperquake.de/gitweb.cgi?a=blobdiff_plain;f=module%2Fzfs%2Fzfs_ioctl.c;h=e1b8543d3ff214f9f363294daaed774388a7cee4;hb=c99c90015ece64746e20b74245caca41d1dbefe1;hp=45e118e537396fc05ea0433ca148bfd4ea50adc0;hpb=b04cffc9b03db08148f19bfbc92a53e5dc7b30d5;p=zfs.git diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index 45e118e..e1b8543 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -18,8 +18,16 @@ * * CDDL HEADER END */ + /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Portions Copyright 2011 Martin Matuska + * Portions Copyright 2012 Pawel Jakub Dawidek + * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2013 by Saso Kiselkov. All rights reserved. */ #include @@ -48,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +76,8 @@ #include #include +#include + #include #include "zfs_namecheck.h" @@ -120,41 +131,11 @@ static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *, boolean_t *); int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t **); -/* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */ -void -__dprintf(const char *file, const char *func, int line, const char *fmt, ...) -{ - const char *newfile; - char buf[512]; - va_list adx; - - /* - * Get rid of annoying "../common/" prefix to filename. - */ - newfile = strrchr(file, '/'); - if (newfile != NULL) { - newfile = newfile + 1; /* Get rid of leading / */ - } else { - newfile = file; - } - - va_start(adx, fmt); - (void) vsnprintf(buf, sizeof (buf), fmt, adx); - va_end(adx); - - /* - * To get this data, use the zfs-dprintf probe as so: - * dtrace -q -n 'zfs-dprintf \ - * /stringof(arg0) == "dbuf.c"/ \ - * {printf("%s: %s", stringof(arg1), stringof(arg3))}' - * arg0 = file name - * arg1 = function name - * arg2 = line number - * arg3 = message - */ - DTRACE_PROBE4(zfs__dprintf, - char *, newfile, char *, func, int, line, char *, buf); -} +static int zfs_prop_activate_feature(dsl_pool_t *dp, zfeature_info_t *feature); +static int zfs_prop_activate_feature_check(void *arg1, void *arg2, + dmu_tx_t *tx); +static void zfs_prop_activate_feature_sync(void *arg1, void *arg2, + dmu_tx_t *tx); static void history_str_free(char *buf) @@ -348,17 +329,37 @@ zfs_dozonecheck_ds(const char *dataset, dsl_dataset_t *ds, cred_t *cr) return (zfs_dozonecheck_impl(dataset, zoned, cr)); } +/* + * If name ends in a '@', then require recursive permissions. + */ int zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr) { int error; + boolean_t descendent = B_FALSE; + dsl_dataset_t *ds; + char *at; + + at = strchr(name, '@'); + if (at != NULL && at[1] == '\0') { + *at = '\0'; + descendent = B_TRUE; + } + + error = dsl_dataset_hold(name, FTAG, &ds); + if (at != NULL) + *at = '@'; + if (error != 0) + return (error); - error = zfs_dozonecheck(name, cr); + error = zfs_dozonecheck_ds(name, ds, cr); if (error == 0) { error = secpolicy_zfs(cr); if (error) - error = dsl_deleg_access(name, perm, cr); + error = dsl_deleg_access_impl(ds, descendent, perm, cr); } + + dsl_dataset_rele(ds, FTAG); return (error); } @@ -372,7 +373,7 @@ zfs_secpolicy_write_perms_ds(const char *name, dsl_dataset_t *ds, if (error == 0) { error = secpolicy_zfs(cr); if (error) - error = dsl_deleg_access_impl(ds, perm, cr); + error = dsl_deleg_access_impl(ds, B_FALSE, perm, cr); } return (error); } @@ -433,7 +434,7 @@ zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr) /* * If the existing dataset label is nondefault, check if the * dataset is mounted (label cannot be changed while mounted). - * Get the zfsvfs; if there isn't one, then the dataset isn't + * Get the zfs_sb_t; if there isn't one, then the dataset isn't * mounted (or isn't a dataset, doesn't exist, ...). */ if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) { @@ -592,7 +593,7 @@ zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr) return (error); } -#ifdef HAVE_ZPL +#ifdef HAVE_SMB_SHARE static int zfs_secpolicy_deleg_share(zfs_cmd_t *zc, cred_t *cr) { @@ -616,12 +617,12 @@ zfs_secpolicy_deleg_share(zfs_cmd_t *zc, cred_t *cr) return (dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_SHARE, cr)); } -#endif /* HAVE_ZPL */ +#endif /* HAVE_SMB_SHARE */ int zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr) { -#ifdef HAVE_ZPL +#ifdef HAVE_SMB_SHARE if (!INGLOBALZONE(curproc)) return (EPERM); @@ -632,13 +633,13 @@ zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr) } #else return (ENOTSUP); -#endif /* HAVE_ZPL */ +#endif /* HAVE_SMB_SHARE */ } int zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr) { -#ifdef HAVE_ZPL +#ifdef HAVE_SMB_SHARE if (!INGLOBALZONE(curproc)) return (EPERM); @@ -649,7 +650,7 @@ zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr) } #else return (ENOTSUP); -#endif /* HAVE_ZPL */ +#endif /* HAVE_SMB_SHARE */ } static int @@ -695,23 +696,18 @@ zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr) /* * Destroying snapshots with delegated permissions requires * descendent mount and destroy permissions. - * Reassemble the full filesystem@snap name so dsl_deleg_access() - * can do the correct permission check. - * - * Since this routine is used when doing a recursive destroy of snapshots - * and destroying snapshots requires descendent permissions, a successfull - * check of the top level snapshot applies to snapshots of all descendent - * datasets as well. */ static int -zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, cred_t *cr) +zfs_secpolicy_destroy_recursive(zfs_cmd_t *zc, cred_t *cr) { int error; char *dsname; - dsname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value); + dsname = kmem_asprintf("%s@", zc->zc_name); error = zfs_secpolicy_destroy_perms(dsname, cr); + if (error == ENOENT) + error = zfs_secpolicy_destroy_perms(zc->zc_name, cr); strfree(dsname); return (error); @@ -850,20 +846,6 @@ zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr) return (error); } -#ifdef HAVE_ZPL -static int -zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr) -{ - int error; - - error = secpolicy_fs_unmount(cr, NULL); - if (error) { - error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr); - } - return (error); -} -#endif /* HAVE_ZPL */ - /* * Policy for pool operations - create/destroy pools, add vdevs, etc. Requires * SYS_CONFIG privilege, which is not available in a local zone. @@ -1106,9 +1088,8 @@ put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) return (error); } -#ifdef HAVE_ZPL static int -getzfsvfs(const char *dsname, zfsvfs_t **zfvp) +get_zfs_sb(const char *dsname, zfs_sb_t **zsbp) { objset_t *os; int error; @@ -1122,9 +1103,9 @@ getzfsvfs(const char *dsname, zfsvfs_t **zfvp) } mutex_enter(&os->os_user_ptr_lock); - *zfvp = dmu_objset_get_user(os); - if (*zfvp) { - VFS_HOLD((*zfvp)->z_vfs); + *zsbp = dmu_objset_get_user(os); + if (*zsbp && (*zsbp)->z_sb) { + atomic_inc(&((*zsbp)->z_sb->s_active)); } else { error = ESRCH; } @@ -1132,52 +1113,47 @@ getzfsvfs(const char *dsname, zfsvfs_t **zfvp) dmu_objset_rele(os, FTAG); return (error); } -#endif /* - * 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. + * Find a zfs_sb_t for a mounted filesystem, or create our own, in which + * case its z_sb will be NULL, and it will be opened as the owner. + * If 'writer' is set, the z_teardown_lock will be held for RW_WRITER, + * which prevents all inode ops from running. */ static int -zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zfvp, boolean_t writer) +zfs_sb_hold(const char *name, void *tag, zfs_sb_t **zsbp, boolean_t writer) { -#ifdef HAVE_ZPL int error = 0; - if (getzfsvfs(name, zfvp) != 0) - error = zfsvfs_create(name, zfvp); + if (get_zfs_sb(name, zsbp) != 0) + error = zfs_sb_create(name, zsbp); if (error == 0) { - rrw_enter(&(*zfvp)->z_teardown_lock, (writer) ? RW_WRITER : + rrw_enter(&(*zsbp)->z_teardown_lock, (writer) ? RW_WRITER : RW_READER, tag); - if ((*zfvp)->z_unmounted) { + if ((*zsbp)->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(&(*zfvp)->z_teardown_lock, tag); + rrw_exit(&(*zsbp)->z_teardown_lock, tag); return (EBUSY); } } return (error); -#else - return ENOTSUP; -#endif } static void -zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag) +zfs_sb_rele(zfs_sb_t *zsb, void *tag) { -#ifdef HAVE_ZPL - rrw_exit(&zfsvfs->z_teardown_lock, tag); + rrw_exit(&zsb->z_teardown_lock, tag); - if (zfsvfs->z_vfs) { - VFS_RELE(zfsvfs->z_vfs); + if (zsb->z_sb) { + deactivate_super(zsb->z_sb); } else { - dmu_objset_disown(zfsvfs->z_os, zfsvfs); - zfsvfs_free(zfsvfs); + dmu_objset_disown(zsb->z_os, zsb); + zfs_sb_free(zsb); } -#endif } static int @@ -1206,7 +1182,7 @@ zfs_ioc_pool_create(zfs_cmd_t *zc) (void) nvlist_lookup_uint64(props, zpool_prop_to_name(ZPOOL_PROP_VERSION), &version); - if (version < SPA_VERSION_INITIAL || version > SPA_VERSION) { + if (!SPA_VERSION_IS_SUPPORTED(version)) { error = EINVAL; goto pool_props_bad; } @@ -1333,6 +1309,15 @@ zfs_ioc_pool_configs(zfs_cmd_t *zc) return (error); } +/* + * inputs: + * zc_name name of the pool + * + * outputs: + * zc_cookie real errno + * zc_nvlist_dst config nvlist + * zc_nvlist_dst_size size of config nvlist + */ static int zfs_ioc_pool_stats(zfs_cmd_t *zc) { @@ -1434,7 +1419,8 @@ zfs_ioc_pool_upgrade(zfs_cmd_t *zc) if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) return (error); - if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) { + if (zc->zc_cookie < spa_version(spa) || + !SPA_VERSION_IS_SUPPORTED(zc->zc_cookie)) { spa_close(spa, FTAG); return (EINVAL); } @@ -1464,7 +1450,7 @@ zfs_ioc_pool_get_history(zfs_cmd_t *zc) return (ENOTSUP); } - hist_buf = kmem_alloc(size, KM_SLEEP); + hist_buf = vmem_alloc(size, KM_SLEEP); if ((error = spa_history_get(spa, &zc->zc_history_offset, &zc->zc_history_len, hist_buf)) == 0) { error = ddi_copyout(hist_buf, @@ -1473,7 +1459,21 @@ zfs_ioc_pool_get_history(zfs_cmd_t *zc) } spa_close(spa, FTAG); - kmem_free(hist_buf, size); + vmem_free(hist_buf, size); + return (error); +} + +static int +zfs_ioc_pool_reguid(zfs_cmd_t *zc) +{ + spa_t *spa; + int error; + + error = spa_open(zc->zc_name, &spa, FTAG); + if (error == 0) { + error = spa_change_guid(spa); + spa_close(spa, FTAG); + } return (error); } @@ -1774,9 +1774,12 @@ zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os) * inconsistent. So this is a bit of a workaround... * XXX reading with out owning */ - if (!zc->zc_objset_stats.dds_inconsistent) { - if (dmu_objset_type(os) == DMU_OST_ZVOL) - error = zvol_get_stats(os, nv); + if (!zc->zc_objset_stats.dds_inconsistent && + dmu_objset_type(os) == DMU_OST_ZVOL) { + error = zvol_get_stats(os, nv); + if (error == EIO) + return (error); + VERIFY0(error); } if (error == 0) error = put_nvlist(zc, nv); @@ -1974,8 +1977,10 @@ top: uint64_t cookie = 0; int len = sizeof (zc->zc_name) - (p - zc->zc_name); - while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0) - (void) dmu_objset_prefetch(p, NULL); + while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0) { + if (!dataset_name_hidden(zc->zc_name)) + (void) dmu_objset_prefetch(zc->zc_name, NULL); + } } do { @@ -1984,8 +1989,7 @@ top: NULL, &zc->zc_cookie); if (error == ENOENT) error = ESRCH; - } while (error == 0 && dataset_name_hidden(zc->zc_name) && - !(zc->zc_iflags & FKIOCTL)); + } while (error == 0 && dataset_name_hidden(zc->zc_name)); dmu_objset_rele(os, FTAG); /* @@ -2022,7 +2026,7 @@ zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) int error; top: - if (zc->zc_cookie == 0) + if (zc->zc_cookie == 0 && !zc->zc_simple) (void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch, NULL, DS_FIND_SNAPSHOTS); @@ -2044,7 +2048,7 @@ top: zc->zc_name + strlen(zc->zc_name), &zc->zc_obj, &zc->zc_cookie, NULL); - if (error == 0) { + if (error == 0 && !zc->zc_simple) { dsl_dataset_t *ds; dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool; @@ -2087,7 +2091,6 @@ top: static int zfs_prop_set_userquota(const char *dsname, nvpair_t *pair) { -#ifdef HAVE_ZPL const char *propname = nvpair_name(pair); uint64_t *valary; unsigned int vallen; @@ -2096,7 +2099,7 @@ zfs_prop_set_userquota(const char *dsname, nvpair_t *pair) zfs_userquota_prop_t type; uint64_t rid; uint64_t quota; - zfsvfs_t *zfsvfs; + zfs_sb_t *zsb; int err; if (nvpair_type(pair) == DATA_TYPE_NVLIST) { @@ -2121,16 +2124,13 @@ zfs_prop_set_userquota(const char *dsname, nvpair_t *pair) rid = valary[1]; quota = valary[2]; - err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_FALSE); + err = zfs_sb_hold(dsname, FTAG, &zsb, B_FALSE); if (err == 0) { - err = zfs_set_userquota(zfsvfs, type, domain, rid, quota); - zfsvfs_rele(zfsvfs, FTAG); + err = zfs_set_userquota(zsb, type, domain, rid, quota); + zfs_sb_rele(zsb, FTAG); } return (err); -#else - return ENOTSUP; -#endif } /* @@ -2184,28 +2184,64 @@ zfs_prop_set_special(const char *dsname, zprop_source_t source, case ZFS_PROP_VOLSIZE: err = zvol_set_volsize(dsname, intval); break; + case ZFS_PROP_SNAPDEV: + err = zvol_set_snapdev(dsname, intval); + break; case ZFS_PROP_VERSION: { - zfsvfs_t *zfsvfs; + zfs_sb_t *zsb; - if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_TRUE)) != 0) + if ((err = zfs_sb_hold(dsname, FTAG, &zsb, B_TRUE)) != 0) break; -#ifdef HAVE_ZPL - err = zfs_set_version(zfsvfs, intval); -#endif - zfsvfs_rele(zfsvfs, FTAG); + err = zfs_set_version(zsb, intval); + zfs_sb_rele(zsb, FTAG); if (err == 0 && intval >= ZPL_VERSION_USERSPACE) { zfs_cmd_t *zc; - zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); + zc = kmem_zalloc(sizeof (zfs_cmd_t), + KM_SLEEP | KM_NODEBUG); (void) strcpy(zc->zc_name, dsname); (void) zfs_ioc_userspace_upgrade(zc); kmem_free(zc, sizeof (zfs_cmd_t)); } break; } + case ZFS_PROP_COMPRESSION: + { + if (intval == ZIO_COMPRESS_LZ4) { + zfeature_info_t *feature = + &spa_feature_table[SPA_FEATURE_LZ4_COMPRESS]; + spa_t *spa; + dsl_pool_t *dp; + + if ((err = spa_open(dsname, &spa, FTAG)) != 0) + return (err); + + dp = spa->spa_dsl_pool; + + /* + * Setting the LZ4 compression algorithm activates + * the feature. + */ + if (!spa_feature_is_active(spa, feature)) { + if ((err = zfs_prop_activate_feature(dp, + feature)) != 0) { + spa_close(spa, FTAG); + return (err); + } + } + + spa_close(spa, FTAG); + } + /* + * We still want the default set action to be performed in the + * caller, we only performed zfeature settings here. + */ + err = -1; + break; + } default: err = -1; @@ -2268,6 +2304,8 @@ retry: if (nvpair_type(propval) != DATA_TYPE_UINT64_ARRAY) err = EINVAL; + } else { + err = EINVAL; } } else if (err == 0) { if (nvpair_type(propval) == DATA_TYPE_STRING) { @@ -2749,33 +2787,6 @@ zfs_ioc_get_fsacl(zfs_cmd_t *zc) return (error); } -#ifdef HAVE_ZPL -/* - * Search the vfs list for a specified resource. Returns a pointer to it - * or NULL if no suitable entry is found. The caller of this routine - * is responsible for releasing the returned vfs pointer. - */ -static vfs_t * -zfs_get_vfs(const char *resource) -{ - struct vfs *vfsp; - struct vfs *vfs_found = NULL; - - vfs_list_read_lock(); - vfsp = rootvfs; - do { - if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) { - VFS_HOLD(vfsp); - vfs_found = vfsp; - break; - } - vfsp = vfsp->vfs_next; - } while (vfsp != rootvfs); - vfs_list_unlock(); - return (vfs_found); -} -#endif /* HAVE_ZPL */ - /* ARGSUSED */ static void zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) @@ -2815,6 +2826,7 @@ zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, uint64_t sense = ZFS_PROP_UNDEFINED; uint64_t norm = ZFS_PROP_UNDEFINED; uint64_t u8 = ZFS_PROP_UNDEFINED; + int error; ASSERT(zplprops != NULL); @@ -2858,8 +2870,9 @@ zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, VERIFY(nvlist_add_uint64(zplprops, zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0); - if (norm == ZFS_PROP_UNDEFINED) - VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0); + if (norm == ZFS_PROP_UNDEFINED && + (error = zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm)) != 0) + return (error); VERIFY(nvlist_add_uint64(zplprops, zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0); @@ -2868,13 +2881,15 @@ zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, */ if (norm) u8 = 1; - if (u8 == ZFS_PROP_UNDEFINED) - VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0); + if (u8 == ZFS_PROP_UNDEFINED && + (error = zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8)) != 0) + return (error); VERIFY(nvlist_add_uint64(zplprops, zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0); - if (sense == ZFS_PROP_UNDEFINED) - VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0); + if (sense == ZFS_PROP_UNDEFINED && + (error = zfs_get_zplprop(os, ZFS_PROP_CASE, &sense)) != 0) + return (error); VERIFY(nvlist_add_uint64(zplprops, zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0); @@ -3126,61 +3141,96 @@ out: return (error); } +/* + * inputs: + * name dataset name, or when 'arg == NULL' the full snapshot name + * arg short snapshot name (i.e. part after the '@') + */ int zfs_unmount_snap(const char *name, void *arg) { -#ifdef HAVE_ZPL - vfs_t *vfsp = NULL; + zfs_sb_t *zsb = NULL; + char *dsname; + char *snapname; + char *fullname; + char *ptr; + int error; if (arg) { - char *snapname = arg; - char *fullname = kmem_asprintf("%s@%s", name, snapname); - vfsp = zfs_get_vfs(fullname); - strfree(fullname); - } else if (strchr(name, '@')) { - vfsp = zfs_get_vfs(name); + dsname = strdup(name); + snapname = strdup(arg); + } else { + ptr = strchr(name, '@'); + if (ptr) { + dsname = strdup(name); + dsname[ptr - name] = '\0'; + snapname = strdup(ptr + 1); + } else { + return (0); + } } - if (vfsp) { - /* - * Always force the unmount for snapshots. - */ - int flag = MS_FORCE; - int err; + fullname = kmem_asprintf("%s@%s", dsname, snapname); - if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) { - VFS_RELE(vfsp); - return (err); - } - VFS_RELE(vfsp); - if ((err = dounmount(vfsp, flag, kcred)) != 0) - return (err); + error = zfs_sb_hold(dsname, FTAG, &zsb, B_FALSE); + if (error == 0) { + error = zfsctl_unmount_snapshot(zsb, fullname, MNT_FORCE); + zfs_sb_rele(zsb, FTAG); + + /* Allow ENOENT for consistency with upstream */ + if (error == ENOENT) + error = 0; } -#endif /* HAVE_ZPL */ - return (0); + + strfree(dsname); + strfree(snapname); + strfree(fullname); + + return (error); } /* * inputs: - * zc_name name of filesystem - * zc_value short name of snapshot + * zc_name name of filesystem, snaps must be under it + * zc_nvlist_src[_size] full names of snapshots to destroy * zc_defer_destroy mark for deferred destroy * - * outputs: none + * outputs: + * zc_name on failure, name of failed snapshot */ static int -zfs_ioc_destroy_snaps(zfs_cmd_t *zc) +zfs_ioc_destroy_snaps_nvl(zfs_cmd_t *zc) { - int err; + int err, len; + nvlist_t *nvl; + nvpair_t *pair; - if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) - return (EINVAL); - err = dmu_objset_find(zc->zc_name, - zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN); - if (err) + if ((err = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, + zc->zc_iflags, &nvl)) != 0) return (err); - return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value, - zc->zc_defer_destroy)); + + len = strlen(zc->zc_name); + for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL; + pair = nvlist_next_nvpair(nvl, pair)) { + const char *name = nvpair_name(pair); + /* + * The snap name must be underneath the zc_name. This ensures + * that our permission checks were legitimate. + */ + if (strncmp(zc->zc_name, name, len) != 0 || + (name[len] != '@' && name[len] != '/')) { + nvlist_free(nvl); + return (EINVAL); + } + + (void) zfs_unmount_snap(name, NULL); + (void) zvol_remove_minor(name); + } + + err = dmu_snapshots_destroy_nvl(nvl, zc->zc_defer_destroy, + zc->zc_name); + nvlist_free(nvl); + return (err); } /* @@ -3216,10 +3266,9 @@ zfs_ioc_destroy(zfs_cmd_t *zc) static int zfs_ioc_rollback(zfs_cmd_t *zc) { -#ifdef HAVE_ZPL dsl_dataset_t *ds, *clone; int error; - zfsvfs_t *zfsvfs; + zfs_sb_t *zsb; char *clone_name; error = dsl_dataset_hold(zc->zc_name, FTAG, &ds); @@ -3253,8 +3302,8 @@ zfs_ioc_rollback(zfs_cmd_t *zc) /* * Do clone swap. */ - if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { - error = zfs_suspend_fs(zfsvfs); + if (get_zfs_sb(zc->zc_name, &zsb) == 0) { + error = zfs_suspend_fs(zsb); if (error == 0) { int resume_err; @@ -3266,10 +3315,10 @@ zfs_ioc_rollback(zfs_cmd_t *zc) } else { error = EBUSY; } - resume_err = zfs_resume_fs(zfsvfs, zc->zc_name); + resume_err = zfs_resume_fs(zsb, zc->zc_name); error = error ? error : resume_err; } - VFS_RELE(zfsvfs->z_vfs); + deactivate_super(zsb->z_sb); } else { if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) { error = dsl_dataset_clone_swap(clone, ds, B_TRUE); @@ -3290,9 +3339,6 @@ out: if (ds) dsl_dataset_rele(ds, FTAG); return (error); -#else - return (ENOTSUP); -#endif /* HAVE_ZPL */ } /* @@ -3307,6 +3353,7 @@ static int zfs_ioc_rename(zfs_cmd_t *zc) { boolean_t recursive = zc->zc_cookie & 1; + int err; zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || @@ -3320,13 +3367,18 @@ zfs_ioc_rename(zfs_cmd_t *zc) */ if (!recursive && strchr(zc->zc_name, '@') != NULL && zc->zc_objset_type == DMU_OST_ZFS) { - int err = zfs_unmount_snap(zc->zc_name, NULL); + err = zfs_unmount_snap(zc->zc_name, NULL); if (err) return (err); } - if (zc->zc_objset_type == DMU_OST_ZVOL) + + err = dmu_objset_rename(zc->zc_name, zc->zc_value, recursive); + if ((err == 0) && (zc->zc_objset_type == DMU_OST_ZVOL)) { (void) zvol_remove_minor(zc->zc_name); - return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive)); + (void) zvol_create_minor(zc->zc_value); + } + + return (err); } static int @@ -3410,6 +3462,22 @@ zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) SPA_VERSION_ZLE_COMPRESSION)) return (ENOTSUP); + if (intval == ZIO_COMPRESS_LZ4) { + zfeature_info_t *feature = + &spa_feature_table[ + SPA_FEATURE_LZ4_COMPRESS]; + spa_t *spa; + + if ((err = spa_open(dsname, &spa, FTAG)) != 0) + return (err); + + if (!spa_feature_is_enabled(spa, feature)) { + spa_close(spa, FTAG); + return (ENOTSUP); + } + spa_close(spa, FTAG); + } + /* * If this is a bootable dataset then * verify that the compression algorithm @@ -3456,6 +3524,56 @@ zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) } /* + * Activates a feature on a pool in response to a property setting. This + * creates a new sync task which modifies the pool to reflect the feature + * as being active. + */ +static int +zfs_prop_activate_feature(dsl_pool_t *dp, zfeature_info_t *feature) +{ + int err; + + /* EBUSY here indicates that the feature is already active */ + err = dsl_sync_task_do(dp, zfs_prop_activate_feature_check, + zfs_prop_activate_feature_sync, dp->dp_spa, feature, 2); + + if (err != 0 && err != EBUSY) + return (err); + else + return (0); +} + +/* + * Checks for a race condition to make sure we don't increment a feature flag + * multiple times. + */ +/*ARGSUSED*/ +static int +zfs_prop_activate_feature_check(void *arg1, void *arg2, dmu_tx_t *tx) +{ + spa_t *spa = arg1; + zfeature_info_t *feature = arg2; + + if (!spa_feature_is_active(spa, feature)) + return (0); + else + return (EBUSY); +} + +/* + * The callback invoked on feature activation in the sync task caused by + * zfs_prop_activate_feature. + */ +static void +zfs_prop_activate_feature_sync(void *arg1, void *arg2, dmu_tx_t *tx) +{ + spa_t *spa = arg1; + zfeature_info_t *feature = arg2; + + spa_feature_incr(spa, feature, tx); +} + +/* * Removes properties from the given props list that fail permission checks * needed to clear them and to restore them in case of a receive error. For each * property, make sure we have both set and inherit permissions. @@ -3482,7 +3600,7 @@ zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist) VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); - zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP); + zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP | KM_NODEBUG); (void) strcpy(zc->zc_name, dataset); pair = nvlist_next_nvpair(props, NULL); while (pair != NULL) { @@ -3736,29 +3854,25 @@ zfs_ioc_recv(zfs_cmd_t *zc) &zc->zc_action_handle); if (error == 0) { -#ifdef HAVE_ZPL - zfsvfs_t *zfsvfs = NULL; + zfs_sb_t *zsb = NULL; - if (getzfsvfs(tofs, &zfsvfs) == 0) { + if (get_zfs_sb(tofs, &zsb) == 0) { /* online recv */ int end_err; - error = zfs_suspend_fs(zfsvfs); + error = zfs_suspend_fs(zsb); /* * If the suspend fails, then the recv_end will * likely also fail, and clean up after itself. */ end_err = dmu_recv_end(&drc); if (error == 0) - error = zfs_resume_fs(zfsvfs, tofs); + error = zfs_resume_fs(zsb, tofs); error = error ? error : end_err; - VFS_RELE(zfsvfs->z_vfs); + deactivate_super(zsb->z_sb); } else { error = dmu_recv_end(&drc); } -#else - error = dmu_recv_end(&drc); -#endif /* HAVE_ZPL */ } zc->zc_cookie = off - fp->f_offset; @@ -3833,6 +3947,8 @@ out: * zc_obj fromorigin flag (mutually exclusive with zc_fromobj) * zc_sendobj objsetid of snapshot to send * zc_fromobj objsetid of incremental fromsnap (may be zero) + * zc_guid if set, estimate size of stream only. zc_cookie is ignored. + * output size in zc_objset_type. * * outputs: none */ @@ -3841,13 +3957,13 @@ zfs_ioc_send(zfs_cmd_t *zc) { objset_t *fromsnap = NULL; objset_t *tosnap; - file_t *fp; int error; offset_t off; dsl_dataset_t *ds; dsl_dataset_t *dsfrom = NULL; spa_t *spa; dsl_pool_t *dp; + boolean_t estimate = (zc->zc_guid != 0); error = spa_open(zc->zc_name, &spa, FTAG); if (error) @@ -3888,26 +4004,76 @@ zfs_ioc_send(zfs_cmd_t *zc) spa_close(spa, FTAG); } - fp = getf(zc->zc_cookie); - if (fp == NULL) { - dsl_dataset_rele(ds, FTAG); - if (dsfrom) - dsl_dataset_rele(dsfrom, FTAG); - return (EBADF); - } + if (estimate) { + error = dmu_send_estimate(tosnap, fromsnap, zc->zc_obj, + &zc->zc_objset_type); + } else { + file_t *fp = getf(zc->zc_cookie); + if (fp == NULL) { + dsl_dataset_rele(ds, FTAG); + if (dsfrom) + dsl_dataset_rele(dsfrom, FTAG); + return (EBADF); + } - off = fp->f_offset; - error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off); + off = fp->f_offset; + error = dmu_send(tosnap, fromsnap, zc->zc_obj, + zc->zc_cookie, fp->f_vnode, &off); - if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) - fp->f_offset = off; - releasef(zc->zc_cookie); + if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) + fp->f_offset = off; + releasef(zc->zc_cookie); + } if (dsfrom) dsl_dataset_rele(dsfrom, FTAG); dsl_dataset_rele(ds, FTAG); return (error); } +/* + * inputs: + * zc_name name of snapshot on which to report progress + * zc_cookie file descriptor of send stream + * + * outputs: + * zc_cookie number of bytes written in send stream thus far + */ +static int +zfs_ioc_send_progress(zfs_cmd_t *zc) +{ + dsl_dataset_t *ds; + dmu_sendarg_t *dsp = NULL; + int error; + + if ((error = dsl_dataset_hold(zc->zc_name, FTAG, &ds)) != 0) + return (error); + + mutex_enter(&ds->ds_sendstream_lock); + + /* + * Iterate over all the send streams currently active on this dataset. + * If there's one which matches the specified file descriptor _and_ the + * stream was started by the current process, return the progress of + * that stream. + */ + + for (dsp = list_head(&ds->ds_sendstreams); dsp != NULL; + dsp = list_next(&ds->ds_sendstreams, dsp)) { + if (dsp->dsa_outfd == zc->zc_cookie && + dsp->dsa_proc->group_leader == curproc->group_leader) + break; + } + + if (dsp != NULL) + zc->zc_cookie = *(dsp->dsa_off); + else + error = ENOENT; + + mutex_exit(&ds->ds_sendstream_lock); + dsl_dataset_rele(ds, FTAG); + return (error); +} + static int zfs_ioc_inject_fault(zfs_cmd_t *zc) { @@ -4042,6 +4208,32 @@ zfs_ioc_clear(zfs_cmd_t *zc) return (error); } +static int +zfs_ioc_pool_reopen(zfs_cmd_t *zc) +{ + spa_t *spa; + int error; + + error = spa_open(zc->zc_name, &spa, FTAG); + if (error) + return (error); + + spa_vdev_state_enter(spa, SCL_NONE); + + /* + * If a resilver is already in progress then set the + * spa_scrub_reopen flag to B_TRUE so that we don't restart + * the scan as a side effect of the reopen. Otherwise, let + * vdev_open() decided if a resilver is required. + */ + spa->spa_scrub_reopen = dsl_scan_resilvering(spa->spa_dsl_pool); + vdev_reopen(spa->spa_root_vdev); + spa->spa_scrub_reopen = B_FALSE; + + (void) spa_vdev_state_exit(spa, NULL, 0); + spa_close(spa, FTAG); + return (0); +} /* * inputs: * zc_name name of filesystem @@ -4082,25 +4274,21 @@ zfs_ioc_promote(zfs_cmd_t *zc) static int zfs_ioc_userspace_one(zfs_cmd_t *zc) { -#ifdef HAVE_ZPL - zfsvfs_t *zfsvfs; + zfs_sb_t *zsb; int error; if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) return (EINVAL); - error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); + error = zfs_sb_hold(zc->zc_name, FTAG, &zsb, B_FALSE); if (error) return (error); - error = zfs_userspace_one(zfsvfs, + error = zfs_userspace_one(zsb, zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie); - zfsvfs_rele(zfsvfs, FTAG); + zfs_sb_rele(zsb, FTAG); return (error); -#else - return (ENOTSUP); -#endif /* HAVE_ZPL */ } /* @@ -4117,20 +4305,21 @@ zfs_ioc_userspace_one(zfs_cmd_t *zc) static int zfs_ioc_userspace_many(zfs_cmd_t *zc) { -#ifdef HAVE_ZPL - zfsvfs_t *zfsvfs; + zfs_sb_t *zsb; int bufsize = zc->zc_nvlist_dst_size; + int error; + void *buf; if (bufsize <= 0) return (ENOMEM); - int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); + error = zfs_sb_hold(zc->zc_name, FTAG, &zsb, B_FALSE); if (error) return (error); - void *buf = kmem_alloc(bufsize, KM_SLEEP); + buf = vmem_alloc(bufsize, KM_SLEEP); - error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie, + error = zfs_userspace_many(zsb, zc->zc_objset_type, &zc->zc_cookie, buf, &zc->zc_nvlist_dst_size); if (error == 0) { @@ -4138,13 +4327,10 @@ zfs_ioc_userspace_many(zfs_cmd_t *zc) (void *)(uintptr_t)zc->zc_nvlist_dst, zc->zc_nvlist_dst_size); } - kmem_free(buf, bufsize); - zfsvfs_rele(zfsvfs, FTAG); + vmem_free(buf, bufsize); + zfs_sb_rele(zsb, FTAG); return (error); -#else - return (ENOTSUP); -#endif /* HAVE_ZPL */ } /* @@ -4157,25 +4343,24 @@ zfs_ioc_userspace_many(zfs_cmd_t *zc) static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) { -#ifdef HAVE_ZPL objset_t *os; int error = 0; - zfsvfs_t *zfsvfs; + zfs_sb_t *zsb; - if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { - if (!dmu_objset_userused_enabled(zfsvfs->z_os)) { + if (get_zfs_sb(zc->zc_name, &zsb) == 0) { + if (!dmu_objset_userused_enabled(zsb->z_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. */ - error = zfs_suspend_fs(zfsvfs); + error = zfs_suspend_fs(zsb); if (error == 0) - error = zfs_resume_fs(zfsvfs, zc->zc_name); + error = zfs_resume_fs(zsb, zc->zc_name); } if (error == 0) - error = dmu_objset_userspace_upgrade(zfsvfs->z_os); - VFS_RELE(zfsvfs->z_vfs); + error = dmu_objset_userspace_upgrade(zsb->z_os); + deactivate_super(zsb->z_sb); } else { /* XXX kind of reading contents without owning */ error = dmu_objset_hold(zc->zc_name, FTAG, &os); @@ -4187,148 +4372,12 @@ zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) } return (error); -#else - return (ENOTSUP); -#endif /* HAVE_ZPL */ } -/* - * We don't want to have a hard dependency - * against some special symbols in sharefs - * nfs, and smbsrv. Determine them if needed when - * the first file system is shared. - * Neither sharefs, nfs or smbsrv are unloadable modules. - */ -#ifdef HAVE_ZPL -int (*znfsexport_fs)(void *arg); -int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t); -int (*zsmbexport_fs)(void *arg, boolean_t add_share); - -int zfs_nfsshare_inited; -int zfs_smbshare_inited; - -ddi_modhandle_t nfs_mod; -ddi_modhandle_t sharefs_mod; -ddi_modhandle_t smbsrv_mod; -kmutex_t zfs_share_lock; - -static int -zfs_init_sharefs() -{ - int error; - - ASSERT(MUTEX_HELD(&zfs_share_lock)); - /* Both NFS and SMB shares also require sharetab support. */ - if (sharefs_mod == NULL && ((sharefs_mod = - ddi_modopen("fs/sharefs", - KRTLD_MODE_FIRST, &error)) == NULL)) { - return (ENOSYS); - } - if (zshare_fs == NULL && ((zshare_fs = - (int (*)(enum sharefs_sys_op, share_t *, uint32_t)) - ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) { - return (ENOSYS); - } - return (0); -} -#endif /* HAVE_ZPL */ - static int zfs_ioc_share(zfs_cmd_t *zc) { -#ifdef HAVE_ZPL - int error; - int opcode; - - switch (zc->zc_share.z_sharetype) { - case ZFS_SHARE_NFS: - case ZFS_UNSHARE_NFS: - if (zfs_nfsshare_inited == 0) { - mutex_enter(&zfs_share_lock); - if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs", - KRTLD_MODE_FIRST, &error)) == NULL)) { - mutex_exit(&zfs_share_lock); - return (ENOSYS); - } - if (znfsexport_fs == NULL && - ((znfsexport_fs = (int (*)(void *)) - ddi_modsym(nfs_mod, - "nfs_export", &error)) == NULL)) { - mutex_exit(&zfs_share_lock); - return (ENOSYS); - } - error = zfs_init_sharefs(); - if (error) { - mutex_exit(&zfs_share_lock); - return (ENOSYS); - } - zfs_nfsshare_inited = 1; - mutex_exit(&zfs_share_lock); - } - break; - case ZFS_SHARE_SMB: - case ZFS_UNSHARE_SMB: - if (zfs_smbshare_inited == 0) { - mutex_enter(&zfs_share_lock); - if (smbsrv_mod == NULL && ((smbsrv_mod = - ddi_modopen("drv/smbsrv", - KRTLD_MODE_FIRST, &error)) == NULL)) { - mutex_exit(&zfs_share_lock); - return (ENOSYS); - } - if (zsmbexport_fs == NULL && ((zsmbexport_fs = - (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod, - "smb_server_share", &error)) == NULL)) { - mutex_exit(&zfs_share_lock); - return (ENOSYS); - } - error = zfs_init_sharefs(); - if (error) { - mutex_exit(&zfs_share_lock); - return (ENOSYS); - } - zfs_smbshare_inited = 1; - mutex_exit(&zfs_share_lock); - } - break; - default: - return (EINVAL); - } - - switch (zc->zc_share.z_sharetype) { - case ZFS_SHARE_NFS: - case ZFS_UNSHARE_NFS: - if (error = - znfsexport_fs((void *) - (uintptr_t)zc->zc_share.z_exportdata)) - return (error); - break; - case ZFS_SHARE_SMB: - case ZFS_UNSHARE_SMB: - if (error = zsmbexport_fs((void *) - (uintptr_t)zc->zc_share.z_exportdata, - zc->zc_share.z_sharetype == ZFS_SHARE_SMB ? - B_TRUE: B_FALSE)) { - return (error); - } - break; - } - - opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS || - zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ? - SHAREFS_ADD : SHAREFS_REMOVE; - - /* - * Add or remove share from sharetab - */ - error = zshare_fs(opcode, - (void *)(uintptr_t)zc->zc_share.z_sharedata, - zc->zc_share.z_sharemax); - - return (error); -#else - return (ENOTSUP); -#endif /* HAVE_ZPL */ + return (ENOSYS); } ace_t full_access[] = { @@ -4445,16 +4494,16 @@ zfs_ioc_diff(zfs_cmd_t *zc) /* * Remove all ACL files in shares dir */ -#ifdef HAVE_ZPL +#ifdef HAVE_SMB_SHARE static int zfs_smb_acl_purge(znode_t *dzp) { zap_cursor_t zc; zap_attribute_t zap; - zfsvfs_t *zfsvfs = dzp->z_zfsvfs; + zfs_sb_t *zsb = ZTOZSB(dzp); int error; - for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id); + for (zap_cursor_init(&zc, zsb->z_os, dzp->z_id); (error = zap_cursor_retrieve(&zc, &zap)) == 0; zap_cursor_advance(&zc)) { if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred, @@ -4464,17 +4513,17 @@ zfs_smb_acl_purge(znode_t *dzp) zap_cursor_fini(&zc); return (error); } -#endif /* HAVE ZPL */ +#endif /* HAVE_SMB_SHARE */ static int zfs_ioc_smb_acl(zfs_cmd_t *zc) { -#ifdef HAVE_ZPL +#ifdef HAVE_SMB_SHARE vnode_t *vp; znode_t *dzp; vnode_t *resourcevp = NULL; znode_t *sharedir; - zfsvfs_t *zfsvfs; + zfs_sb_t *zsb; nvlist_t *nvlist; char *src, *target; vattr_t vattr; @@ -4495,17 +4544,17 @@ zfs_ioc_smb_acl(zfs_cmd_t *zc) } dzp = VTOZ(vp); - zfsvfs = dzp->z_zfsvfs; - ZFS_ENTER(zfsvfs); + zsb = ZTOZSB(dzp); + ZFS_ENTER(zsb); /* * Create share dir if its missing. */ - mutex_enter(&zfsvfs->z_lock); - if (zfsvfs->z_shares_dir == 0) { + mutex_enter(&zsb->z_lock); + if (zsb->z_shares_dir == 0) { dmu_tx_t *tx; - tx = dmu_tx_create(zfsvfs->z_os); + tx = dmu_tx_create(zsb->z_os); dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE, ZFS_SHARES_DIR); dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); @@ -4513,29 +4562,28 @@ zfs_ioc_smb_acl(zfs_cmd_t *zc) if (error) { dmu_tx_abort(tx); } else { - error = zfs_create_share_dir(zfsvfs, tx); + error = zfs_create_share_dir(zsb, tx); dmu_tx_commit(tx); } if (error) { - mutex_exit(&zfsvfs->z_lock); + mutex_exit(&zsb->z_lock); VN_RELE(vp); - ZFS_EXIT(zfsvfs); + ZFS_EXIT(zsb); return (error); } } - mutex_exit(&zfsvfs->z_lock); + mutex_exit(&zsb->z_lock); - ASSERT(zfsvfs->z_shares_dir); - if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) { + ASSERT(zsb->z_shares_dir); + if ((error = zfs_zget(zsb, zsb->z_shares_dir, &sharedir)) != 0) { VN_RELE(vp); - ZFS_EXIT(zfsvfs); + ZFS_EXIT(zsb); return (error); } switch (zc->zc_cookie) { case ZFS_SMB_ACL_ADD: vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE; - vattr.va_type = VREG; vattr.va_mode = S_IFREG|0777; vattr.va_uid = 0; vattr.va_gid = 0; @@ -4560,7 +4608,7 @@ zfs_ioc_smb_acl(zfs_cmd_t *zc) if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) { VN_RELE(vp); - ZFS_EXIT(zfsvfs); + ZFS_EXIT(zsb); return (error); } if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) || @@ -4568,7 +4616,7 @@ zfs_ioc_smb_acl(zfs_cmd_t *zc) &target)) { VN_RELE(vp); VN_RELE(ZTOV(sharedir)); - ZFS_EXIT(zfsvfs); + ZFS_EXIT(zsb); nvlist_free(nvlist); return (error); } @@ -4589,12 +4637,12 @@ zfs_ioc_smb_acl(zfs_cmd_t *zc) VN_RELE(vp); VN_RELE(ZTOV(sharedir)); - ZFS_EXIT(zfsvfs); + ZFS_EXIT(zsb); return (error); #else return (ENOTSUP); -#endif /* HAVE_ZPL */ +#endif /* HAVE_SMB_SHARE */ } /* @@ -4781,6 +4829,70 @@ zfs_ioc_events_clear(zfs_cmd_t *zc) } /* + * inputs: + * zc_name name of new filesystem or snapshot + * zc_value full name of old snapshot + * + * outputs: + * zc_cookie space in bytes + * zc_objset_type compressed space in bytes + * zc_perm_action uncompressed space in bytes + */ +static int +zfs_ioc_space_written(zfs_cmd_t *zc) +{ + int error; + dsl_dataset_t *new, *old; + + error = dsl_dataset_hold(zc->zc_name, FTAG, &new); + if (error != 0) + return (error); + error = dsl_dataset_hold(zc->zc_value, FTAG, &old); + if (error != 0) { + dsl_dataset_rele(new, FTAG); + return (error); + } + + error = dsl_dataset_space_written(old, new, &zc->zc_cookie, + &zc->zc_objset_type, &zc->zc_perm_action); + dsl_dataset_rele(old, FTAG); + dsl_dataset_rele(new, FTAG); + return (error); +} + +/* + * inputs: + * zc_name full name of last snapshot + * zc_value full name of first snapshot + * + * outputs: + * zc_cookie space in bytes + * zc_objset_type compressed space in bytes + * zc_perm_action uncompressed space in bytes + */ +static int +zfs_ioc_space_snaps(zfs_cmd_t *zc) +{ + int error; + dsl_dataset_t *new, *old; + + error = dsl_dataset_hold(zc->zc_name, FTAG, &new); + if (error != 0) + return (error); + error = dsl_dataset_hold(zc->zc_value, FTAG, &old); + if (error != 0) { + dsl_dataset_rele(new, FTAG); + return (error); + } + + error = dsl_dataset_space_wouldfree(old, new, &zc->zc_cookie, + &zc->zc_objset_type, &zc->zc_perm_action); + dsl_dataset_rele(old, FTAG); + dsl_dataset_rele(new, FTAG); + return (error); +} + +/* * pool create, destroy, and export don't log the history as part of * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export * do the logging of those commands. @@ -4846,7 +4958,7 @@ static zfs_ioc_vec_t zfs_ioc_vec[] = { POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, { zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, - { zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE, + { zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_FALSE, POOL_CHECK_NONE }, { zfs_ioc_inject_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE, POOL_CHECK_NONE }, @@ -4860,8 +4972,8 @@ static zfs_ioc_vec_t zfs_ioc_vec[] = { POOL_CHECK_NONE }, { zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, - { zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, DATASET_NAME, - B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, + { zfs_ioc_destroy_snaps_nvl, zfs_secpolicy_destroy_recursive, + DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, { zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, { zfs_ioc_dsobj_to_dsname, zfs_secpolicy_diff, POOL_NAME, B_FALSE, @@ -4910,6 +5022,16 @@ static zfs_ioc_vec_t zfs_ioc_vec[] = { POOL_CHECK_NONE }, { zfs_ioc_events_clear, zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_NONE }, + { zfs_ioc_pool_reguid, zfs_secpolicy_config, POOL_NAME, B_TRUE, + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, + { zfs_ioc_space_written, zfs_secpolicy_read, DATASET_NAME, B_FALSE, + POOL_CHECK_SUSPENDED }, + { zfs_ioc_space_snaps, zfs_secpolicy_read, DATASET_NAME, B_FALSE, + POOL_CHECK_SUSPENDED }, + { zfs_ioc_pool_reopen, zfs_secpolicy_config, POOL_NAME, B_TRUE, + POOL_CHECK_SUSPENDED }, + { zfs_ioc_send_progress, zfs_secpolicy_read, DATASET_NAME, B_FALSE, + POOL_CHECK_NONE } }; int @@ -5089,7 +5211,7 @@ zfsdev_ioctl(struct file *filp, unsigned cmd, unsigned long arg) error = EFAULT; if ((error == 0) && !(flag & FKIOCTL)) - error = zfs_ioc_vec[vec].zvec_secpolicy(zc, NULL); + error = zfs_ioc_vec[vec].zvec_secpolicy(zc, CRED()); /* * Ensure that all pool/dataset names are valid before we pass down to @@ -5190,10 +5312,8 @@ zfs_detach(void) list_destroy(&zfsdev_state_list); } -#ifdef HAVE_ZPL uint_t zfs_fsyncer_key; extern uint_t rrw_tsd_key; -#endif #ifdef DEBUG #define ZFS_DEBUG_STR " (DEBUG mode)" @@ -5215,15 +5335,13 @@ _init(void) if ((error = zfs_attach()) != 0) goto out2; -#ifdef HAVE_ZPL tsd_create(&zfs_fsyncer_key, NULL); tsd_create(&rrw_tsd_key, NULL); - mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL); -#endif /* HAVE_ZPL */ - - printk(KERN_NOTICE "ZFS: Loaded ZFS Filesystem v%s%s\n", - ZFS_META_VERSION, ZFS_DEBUG_STR); + printk(KERN_NOTICE "ZFS: Loaded module v%s-%s%s, " + "ZFS pool version %s, ZFS filesystem version %s\n", + ZFS_META_VERSION, ZFS_META_RELEASE, ZFS_DEBUG_STR, + SPA_VERSION_STRING, ZPL_VERSION_STRING); return (0); @@ -5232,8 +5350,9 @@ out2: out1: zfs_fini(); spa_fini(); - printk(KERN_NOTICE "ZFS: Failed to Load ZFS Filesystem v%s%s" - ", rc = %d\n", ZFS_META_VERSION, ZFS_DEBUG_STR, error); + printk(KERN_NOTICE "ZFS: Failed to Load ZFS Filesystem v%s-%s%s" + ", rc = %d\n", ZFS_META_VERSION, ZFS_META_RELEASE, + ZFS_DEBUG_STR, error); return (error); } @@ -5245,20 +5364,12 @@ _fini(void) zvol_fini(); zfs_fini(); spa_fini(); -#ifdef HAVE_ZPL - if (zfs_nfsshare_inited) - (void) ddi_modclose(nfs_mod); - if (zfs_smbshare_inited) - (void) ddi_modclose(smbsrv_mod); - if (zfs_nfsshare_inited || zfs_smbshare_inited) - (void) ddi_modclose(sharefs_mod); - - mutex_destroy(&zfs_share_lock); + tsd_destroy(&zfs_fsyncer_key); -#endif /* HAVE_ZPL */ + tsd_destroy(&rrw_tsd_key); - printk(KERN_NOTICE "ZFS: Unloaded ZFS Filesystem v%s%s\n", - ZFS_META_VERSION, ZFS_DEBUG_STR); + printk(KERN_NOTICE "ZFS: Unloaded module v%s-%s%s\n", + ZFS_META_VERSION, ZFS_META_RELEASE, ZFS_DEBUG_STR); return (0); }