*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Portions Copyright 2011 Martin Matuska
+ * Portions Copyright 2012 Pawel Jakub Dawidek <pawel@dawidek.net>
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
*/
#include <sys/types.h>
#include <sys/dsl_scan.h>
#include <sharefs/share.h>
#include <sys/dmu_objset.h>
+#include <sys/fm/util.h>
+
+#include <linux/miscdevice.h>
#include "zfs_namecheck.h"
#include "zfs_prop.h"
#include "zfs_deleg.h"
#include "zfs_comutil.h"
-extern struct modlfs zfs_modlfs;
+kmutex_t zfsdev_state_lock;
+list_t zfsdev_state_list;
extern void zfs_init(void);
extern void zfs_fini(void);
-ldi_ident_t zfs_li = NULL;
-dev_info_t *zfs_dip;
-
typedef int zfs_ioc_func_t(zfs_cmd_t *);
typedef int zfs_secpolicy_func_t(zfs_cmd_t *, cred_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 void
history_str_free(char *buf)
{
if (zc->zc_history == 0)
return (NULL);
- buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
+ buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP | KM_NODEBUG);
if (copyinstr((void *)(uintptr_t)zc->zc_history,
buf, HIS_MAX_RECORD_LEN, NULL) != 0) {
history_str_free(buf);
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 = zfs_dozonecheck(name, cr);
+ error = dsl_dataset_hold(name, FTAG, &ds);
+ if (at != NULL)
+ *at = '@';
+ if (error != 0)
+ return (error);
+
+ 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);
}
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);
}
static int
zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr)
{
+#ifdef HAVE_MLSLABEL
char ds_hexsl[MAXNAMELEN];
bslabel_t ds_sl, new_sl;
boolean_t new_default = FALSE;
/*
* 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) {
if (needed_priv != -1)
return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL));
return (0);
+#else
+ return ENOTSUP;
+#endif /* HAVE_MLSLABEL */
}
static int
return (error);
}
+#ifdef HAVE_SMB_SHARE
static int
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_SMB_SHARE */
int
zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr)
{
+#ifdef HAVE_SMB_SHARE
if (!INGLOBALZONE(curproc))
return (EPERM);
} else {
return (zfs_secpolicy_deleg_share(zc, cr));
}
+#else
+ return (ENOTSUP);
+#endif /* HAVE_SMB_SHARE */
}
int
zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr)
{
+#ifdef HAVE_SMB_SHARE
if (!INGLOBALZONE(curproc))
return (EPERM);
} else {
return (zfs_secpolicy_deleg_share(zc, cr));
}
+#else
+ return (ENOTSUP);
+#endif /* HAVE_SMB_SHARE */
}
static int
/*
* 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);
return (error);
}
-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);
-}
-
/*
* Policy for pool operations - create/destroy pools, add vdevs, etc. Requires
* SYS_CONFIG privilege, which is not available in a local zone.
if (size == 0)
return (EINVAL);
- packed = kmem_alloc(size, KM_SLEEP);
+ packed = kmem_alloc(size, KM_SLEEP | KM_NODEBUG);
if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
iflag)) != 0) {
if (size > zc->zc_nvlist_dst_size) {
error = ENOMEM;
} else {
- packed = kmem_alloc(size, KM_SLEEP);
+ packed = kmem_alloc(size, KM_SLEEP | KM_NODEBUG);
VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
KM_SLEEP) == 0);
if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst,
}
static int
-getzfsvfs(const char *dsname, zfsvfs_t **zfvp)
+get_zfs_sb(const char *dsname, zfs_sb_t **zsbp)
{
objset_t *os;
int error;
}
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;
}
}
/*
- * 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.
*/
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)
{
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);
}
}
}
static void
-zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag)
+zfs_sb_rele(zfs_sb_t *zsb, void *tag)
{
- 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);
}
}
nvlist_t *zplprops = NULL;
char *buf;
- if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
- zc->zc_iflags, &config))
+ if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
+ zc->zc_iflags, &config)))
return (error);
if (zc->zc_nvlist_src_size != 0 && (error =
error = err;
}
+ if (error == 0)
+ zvol_create_minors(zc->zc_name);
+
nvlist_free(config);
if (props)
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,
}
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);
}
{
int error;
- if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value))
+ if ((error = dsl_dsobj_to_dsname(zc->zc_name,zc->zc_obj,zc->zc_value)))
return (error);
return (0);
if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
return (error);
- if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
- zc->zc_iflags, &config)) {
+ if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
+ zc->zc_iflags, &config))) {
spa_close(spa, FTAG);
return (error);
}
* 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)
- VERIFY(zvol_get_stats(os, nv) == 0);
+ 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);
+ VERIFY3S(error, ==, 0);
}
- error = put_nvlist(zc, nv);
+ if (error == 0)
+ error = put_nvlist(zc, nv);
nvlist_free(nv);
}
objset_t *os = NULL;
int error;
- if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
+ if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)))
return (error);
error = zfs_ioc_objset_stats_impl(zc, os);
int error;
nvlist_t *nv;
- if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
+ if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)))
return (error);
/*
int err;
/* XXX reading without owning */
- if (err = dmu_objset_hold(zc->zc_name, FTAG, &os))
+ if ((err = dmu_objset_hold(zc->zc_name, FTAG, &os)))
return (err);
dmu_objset_fast_stat(os, &zc->zc_objset_stats);
size_t orig_len = strlen(zc->zc_name);
top:
- if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) {
+ if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os))) {
if (error == ENOENT)
error = ESRCH;
return (error);
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 {
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);
/*
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);
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;
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) {
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);
err = dsl_dataset_set_reservation(dsname, source, intval);
break;
case ZFS_PROP_VOLSIZE:
- err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip),
- intval);
+ err = zvol_set_volsize(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;
- err = zfs_set_version(zfsvfs, intval);
- 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));
if (nvpair_type(propval) !=
DATA_TYPE_UINT64_ARRAY)
err = EINVAL;
+ } else {
+ err = EINVAL;
}
} else if (err == 0) {
if (nvpair_type(propval) == DATA_TYPE_STRING) {
nvpair_type(pair) != DATA_TYPE_STRING)
return (EINVAL);
- if (error = zfs_secpolicy_write_perms(fsname,
- ZFS_DELEG_PERM_USERPROP, CRED()))
+ if ((error = zfs_secpolicy_write_perms(fsname,
+ ZFS_DELEG_PERM_USERPROP, CRED())))
return (error);
if (strlen(propname) >= ZAP_MAXNAMELEN)
int error;
nvpair_t *pair;
- if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
- zc->zc_iflags, &props))
+ if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ zc->zc_iflags, &props)))
return (error);
/*
return (error);
}
+/*
+ * inputs:
+ * zc_name name of volume
+ *
+ * outputs: none
+ */
+static int
+zfs_ioc_create_minor(zfs_cmd_t *zc)
+{
+ return (zvol_create_minor(zc->zc_name));
+}
+
+/*
+ * inputs:
+ * zc_name name of volume
+ *
+ * outputs: none
+ */
+static int
+zfs_ioc_remove_minor(zfs_cmd_t *zc)
+{
+ return (zvol_remove_minor(zc->zc_name));
+}
+
/*
* inputs:
* zc_name name of filesystem
return (error);
}
-/*
- * 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);
-}
-
/* ARGSUSED */
static void
zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
uint64_t sense = ZFS_PROP_UNDEFINED;
uint64_t norm = ZFS_PROP_UNDEFINED;
uint64_t u8 = ZFS_PROP_UNDEFINED;
+ int error;
ASSERT(zplprops != NULL);
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);
*/
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);
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)
{
- 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;
}
- 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);
}
/*
{
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);
/*
* 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;
} 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);
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 ||
*/
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
if (prop == ZPROP_INVAL) {
if (zfs_prop_user(propname)) {
- if (err = zfs_secpolicy_write_perms(dsname,
- ZFS_DELEG_PERM_USERPROP, cr))
+ if ((err = zfs_secpolicy_write_perms(dsname,
+ ZFS_DELEG_PERM_USERPROP, cr)))
return (err);
return (0);
}
return (EINVAL);
}
- if (err = zfs_secpolicy_write_perms(dsname, perm, cr))
+ if ((err = zfs_secpolicy_write_perms(dsname, perm, cr)))
return (err);
return (0);
}
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) {
&zc->zc_action_handle);
if (error == 0) {
- 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);
}
* 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
*/
{
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)
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_sendbackup(tosnap, fromsnap, zc->zc_obj,
+ 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);
}
+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);
+ vdev_reopen(spa->spa_root_vdev);
+ (void) spa_vdev_state_exit(spa, NULL, 0);
+ spa_close(spa, FTAG);
+ return (0);
+}
/*
* inputs:
* zc_name name of filesystem
static int
zfs_ioc_userspace_one(zfs_cmd_t *zc)
{
- 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);
}
static int
zfs_ioc_userspace_many(zfs_cmd_t *zc)
{
- 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) {
(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);
}
{
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);
return (error);
}
-/*
- * We don't want to have a hard dependency
- * against some special symbols in sharefs
- * nfs, and smbsrv. Determine them if needed when
- * the first file system is shared.
- * Neither sharefs, nfs or smbsrv are unloadable modules.
- */
-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);
-}
-
static int
zfs_ioc_share(zfs_cmd_t *zc)
{
- 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);
-
+ return (ENOSYS);
}
ace_t full_access[] = {
/*
* Remove all ACL files in shares dir
*/
+#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,
zap_cursor_fini(&zc);
return (error);
}
+#endif /* HAVE_SMB_SHARE */
static int
zfs_ioc_smb_acl(zfs_cmd_t *zc)
{
+#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;
}
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);
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;
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) ||
&target)) {
VN_RELE(vp);
VN_RELE(ZTOV(sharedir));
- ZFS_EXIT(zfsvfs);
+ ZFS_EXIT(zsb);
nvlist_free(nvlist);
return (error);
}
VN_RELE(vp);
VN_RELE(ZTOV(sharedir));
- ZFS_EXIT(zfsvfs);
+ ZFS_EXIT(zsb);
return (error);
+#else
+ return (ENOTSUP);
+#endif /* HAVE_SMB_SHARE */
}
/*
return (error);
}
+/*
+ * inputs:
+ * zc_guid flags (ZEVENT_NONBLOCK)
+ *
+ * outputs:
+ * zc_nvlist_dst next nvlist event
+ * zc_cookie dropped events since last get
+ * zc_cleanup_fd cleanup-on-exit file descriptor
+ */
+static int
+zfs_ioc_events_next(zfs_cmd_t *zc)
+{
+ zfs_zevent_t *ze;
+ nvlist_t *event = NULL;
+ minor_t minor;
+ uint64_t dropped = 0;
+ int error;
+
+ error = zfs_zevent_fd_hold(zc->zc_cleanup_fd, &minor, &ze);
+ if (error != 0)
+ return (error);
+
+ do {
+ error = zfs_zevent_next(ze, &event,
+ &zc->zc_nvlist_dst_size, &dropped);
+ if (event != NULL) {
+ zc->zc_cookie = dropped;
+ error = put_nvlist(zc, event);
+ nvlist_free(event);
+ }
+
+ if (zc->zc_guid & ZEVENT_NONBLOCK)
+ break;
+
+ if ((error == 0) || (error != ENOENT))
+ break;
+
+ error = zfs_zevent_wait(ze);
+ if (error)
+ break;
+ } while (1);
+
+ zfs_zevent_fd_rele(zc->zc_cleanup_fd);
+
+ return (error);
+}
+
+/*
+ * outputs:
+ * zc_cookie cleared events count
+ */
+static int
+zfs_ioc_events_clear(zfs_cmd_t *zc)
+{
+ int count;
+
+ zfs_zevent_drain_all(&count);
+ zc->zc_cookie = count;
+
+ return 0;
+}
+
+/*
+ * 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
POOL_CHECK_SUSPENDED },
{ zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE,
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
+ { zfs_ioc_create_minor, zfs_secpolicy_config, DATASET_NAME, B_FALSE,
+ POOL_CHECK_NONE },
+ { zfs_ioc_remove_minor, zfs_secpolicy_config, DATASET_NAME, B_FALSE,
+ POOL_CHECK_NONE },
{ zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE,
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
{ zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE,
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 },
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,
{ zfs_ioc_tmp_snapshot, zfs_secpolicy_tmp_snapshot, DATASET_NAME,
B_FALSE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
{ zfs_ioc_obj_to_stats, zfs_secpolicy_diff, DATASET_NAME, B_FALSE,
- POOL_CHECK_SUSPENDED }
+ POOL_CHECK_SUSPENDED },
+ { zfs_ioc_events_next, zfs_secpolicy_config, NO_NAME, B_FALSE,
+ 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 },
};
int
return (error);
}
+static void *
+zfsdev_get_state_impl(minor_t minor, enum zfsdev_state_type which)
+{
+ zfsdev_state_t *zs;
+
+ ASSERT(MUTEX_HELD(&zfsdev_state_lock));
+
+ for (zs = list_head(&zfsdev_state_list); zs != NULL;
+ zs = list_next(&zfsdev_state_list, zs)) {
+ if (zs->zs_minor == minor) {
+ switch (which) {
+ case ZST_ONEXIT: return (zs->zs_onexit);
+ case ZST_ZEVENT: return (zs->zs_zevent);
+ case ZST_ALL: return (zs);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void *
+zfsdev_get_state(minor_t minor, enum zfsdev_state_type which)
+{
+ void *ptr;
+
+ mutex_enter(&zfsdev_state_lock);
+ ptr = zfsdev_get_state_impl(minor, which);
+ mutex_exit(&zfsdev_state_lock);
+
+ return ptr;
+}
+
+minor_t
+zfsdev_getminor(struct file *filp)
+{
+ ASSERT(filp != NULL);
+ ASSERT(filp->private_data != NULL);
+
+ return (((zfsdev_state_t *)filp->private_data)->zs_minor);
+}
+
/*
- * Find a free minor number.
+ * Find a free minor number. The zfsdev_state_list is expected to
+ * be short since it is only a list of currently open file handles.
*/
minor_t
zfsdev_minor_alloc(void)
{
- static minor_t last_minor;
+ static minor_t last_minor = 0;
minor_t m;
ASSERT(MUTEX_HELD(&zfsdev_state_lock));
for (m = last_minor + 1; m != last_minor; m++) {
if (m > ZFSDEV_MAX_MINOR)
m = 1;
- if (ddi_get_soft_state(zfsdev_state, m) == NULL) {
+ if (zfsdev_get_state_impl(m, ZST_ALL) == NULL) {
last_minor = m;
return (m);
}
}
static int
-zfs_ctldev_init(dev_t *devp)
+zfsdev_state_init(struct file *filp)
{
+ zfsdev_state_t *zs;
minor_t minor;
- zfs_soft_state_t *zs;
ASSERT(MUTEX_HELD(&zfsdev_state_lock));
- ASSERT(getminor(*devp) == 0);
- minor = zfsdev_minor_alloc();
- if (minor == 0)
- return (ENXIO);
+ minor = zfsdev_minor_alloc();
+ if (minor == 0)
+ return (ENXIO);
+
+ zs = kmem_zalloc( sizeof(zfsdev_state_t), KM_SLEEP);
+ if (zs == NULL)
+ return (ENOMEM);
- if (ddi_soft_state_zalloc(zfsdev_state, minor) != DDI_SUCCESS)
- return (EAGAIN);
+ zs->zs_file = filp;
+ zs->zs_minor = minor;
+ filp->private_data = zs;
- *devp = makedevice(getemajor(*devp), minor);
+ zfs_onexit_init((zfs_onexit_t **)&zs->zs_onexit);
+ zfs_zevent_init((zfs_zevent_t **)&zs->zs_zevent);
- zs = ddi_get_soft_state(zfsdev_state, minor);
- zs->zss_type = ZSST_CTLDEV;
- zfs_onexit_init((zfs_onexit_t **)&zs->zss_data);
+ list_insert_tail(&zfsdev_state_list, zs);
return (0);
}
-static void
-zfs_ctldev_destroy(zfs_onexit_t *zo, minor_t minor)
+static int
+zfsdev_state_destroy(struct file *filp)
{
+ zfsdev_state_t *zs;
+
ASSERT(MUTEX_HELD(&zfsdev_state_lock));
+ ASSERT(filp->private_data != NULL);
- zfs_onexit_destroy(zo);
- ddi_soft_state_free(zfsdev_state, minor);
-}
+ zs = filp->private_data;
+ zfs_onexit_destroy(zs->zs_onexit);
+ zfs_zevent_destroy(zs->zs_zevent);
-void *
-zfsdev_get_soft_state(minor_t minor, enum zfs_soft_state_type which)
-{
- zfs_soft_state_t *zp;
-
- zp = ddi_get_soft_state(zfsdev_state, minor);
- if (zp == NULL || zp->zss_type != which)
- return (NULL);
+ list_remove(&zfsdev_state_list, zs);
+ kmem_free(zs, sizeof(zfsdev_state_t));
- return (zp->zss_data);
+ return 0;
}
static int
-zfsdev_open(dev_t *devp, int flag, int otyp, cred_t *cr)
+zfsdev_open(struct inode *ino, struct file *filp)
{
- int error = 0;
-
- if (getminor(*devp) != 0)
- return (zvol_open(devp, flag, otyp, cr));
+ int error;
- /* This is the control device. Allocate a new minor if requested. */
- if (flag & FEXCL) {
- mutex_enter(&zfsdev_state_lock);
- error = zfs_ctldev_init(devp);
- mutex_exit(&zfsdev_state_lock);
- }
+ mutex_enter(&zfsdev_state_lock);
+ error = zfsdev_state_init(filp);
+ mutex_exit(&zfsdev_state_lock);
- return (error);
+ return (-error);
}
static int
-zfsdev_close(dev_t dev, int flag, int otyp, cred_t *cr)
+zfsdev_release(struct inode *ino, struct file *filp)
{
- zfs_onexit_t *zo;
- minor_t minor = getminor(dev);
-
- if (minor == 0)
- return (0);
+ int error;
mutex_enter(&zfsdev_state_lock);
- zo = zfsdev_get_soft_state(minor, ZSST_CTLDEV);
- if (zo == NULL) {
- mutex_exit(&zfsdev_state_lock);
- return (zvol_close(dev, flag, otyp, cr));
- }
- zfs_ctldev_destroy(zo, minor);
+ error = zfsdev_state_destroy(filp);
mutex_exit(&zfsdev_state_lock);
- return (0);
+ return (-error);
}
-static int
-zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
+static long
+zfsdev_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
{
zfs_cmd_t *zc;
uint_t vec;
- int error, rc;
- minor_t minor = getminor(dev);
-
- if (minor != 0 &&
- zfsdev_get_soft_state(minor, ZSST_CTLDEV) == NULL)
- return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp));
+ int error, rc, flag = 0;
vec = cmd - ZFS_IOC;
- ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip));
-
if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0]))
- return (EINVAL);
+ return (-EINVAL);
- zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
+ zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP | KM_NODEBUG);
error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag);
if (error != 0)
error = EFAULT;
if ((error == 0) && !(flag & FKIOCTL))
- error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr);
+ error = zfs_ioc_vec[vec].zvec_secpolicy(zc, CRED());
/*
* Ensure that all pool/dataset names are valid before we pass down to
}
kmem_free(zc, sizeof (zfs_cmd_t));
- return (error);
+ return (-error);
}
-static int
-zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+#ifdef CONFIG_COMPAT
+static long
+zfsdev_compat_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
{
- if (cmd != DDI_ATTACH)
- return (DDI_FAILURE);
-
- if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0,
- DDI_PSEUDO, 0) == DDI_FAILURE)
- return (DDI_FAILURE);
-
- zfs_dip = dip;
+ return zfsdev_ioctl(filp, cmd, arg);
+}
+#else
+#define zfsdev_compat_ioctl NULL
+#endif
- ddi_report_dev(dip);
+static const struct file_operations zfsdev_fops = {
+ .open = zfsdev_open,
+ .release = zfsdev_release,
+ .unlocked_ioctl = zfsdev_ioctl,
+ .compat_ioctl = zfsdev_compat_ioctl,
+ .owner = THIS_MODULE,
+};
- return (DDI_SUCCESS);
-}
+static struct miscdevice zfs_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = ZFS_DRIVER,
+ .fops = &zfsdev_fops,
+};
static int
-zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+zfs_attach(void)
{
- if (spa_busy() || zfs_busy() || zvol_busy())
- return (DDI_FAILURE);
-
- if (cmd != DDI_DETACH)
- return (DDI_FAILURE);
+ int error;
- zfs_dip = NULL;
+ mutex_init(&zfsdev_state_lock, NULL, MUTEX_DEFAULT, NULL);
+ list_create(&zfsdev_state_list, sizeof (zfsdev_state_t),
+ offsetof(zfsdev_state_t, zs_next));
- ddi_prop_remove_all(dip);
- ddi_remove_minor_node(dip, NULL);
+ error = misc_register(&zfs_misc);
+ if (error) {
+ printk(KERN_INFO "ZFS: misc_register() failed %d\n", error);
+ return (error);
+ }
- return (DDI_SUCCESS);
+ return (0);
}
-/*ARGSUSED*/
-static int
-zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+static void
+zfs_detach(void)
{
- switch (infocmd) {
- case DDI_INFO_DEVT2DEVINFO:
- *result = zfs_dip;
- return (DDI_SUCCESS);
+ int error;
- case DDI_INFO_DEVT2INSTANCE:
- *result = (void *)0;
- return (DDI_SUCCESS);
- }
+ error = misc_deregister(&zfs_misc);
+ if (error)
+ printk(KERN_INFO "ZFS: misc_deregister() failed %d\n", error);
- return (DDI_FAILURE);
+ mutex_destroy(&zfsdev_state_lock);
+ list_destroy(&zfsdev_state_list);
}
-/*
- * OK, so this is a little weird.
- *
- * /dev/zfs is the control node, i.e. minor 0.
- * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0.
- *
- * /dev/zfs has basically nothing to do except serve up ioctls,
- * so most of the standard driver entry points are in zvol.c.
- */
-static struct cb_ops zfs_cb_ops = {
- zfsdev_open, /* open */
- zfsdev_close, /* close */
- zvol_strategy, /* strategy */
- nodev, /* print */
- zvol_dump, /* dump */
- zvol_read, /* read */
- zvol_write, /* write */
- zfsdev_ioctl, /* ioctl */
- nodev, /* devmap */
- nodev, /* mmap */
- nodev, /* segmap */
- nochpoll, /* poll */
- ddi_prop_op, /* prop_op */
- NULL, /* streamtab */
- D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */
- CB_REV, /* version */
- nodev, /* async read */
- nodev, /* async write */
-};
-
-static struct dev_ops zfs_dev_ops = {
- DEVO_REV, /* version */
- 0, /* refcnt */
- zfs_info, /* info */
- nulldev, /* identify */
- nulldev, /* probe */
- zfs_attach, /* attach */
- zfs_detach, /* detach */
- nodev, /* reset */
- &zfs_cb_ops, /* driver operations */
- NULL, /* no bus operations */
- NULL, /* power */
- ddi_quiesce_not_needed, /* quiesce */
-};
-
-static struct modldrv zfs_modldrv = {
- &mod_driverops,
- "ZFS storage pool",
- &zfs_dev_ops
-};
-
-static struct modlinkage modlinkage = {
- MODREV_1,
- (void *)&zfs_modlfs,
- (void *)&zfs_modldrv,
- NULL
-};
-
-
uint_t zfs_fsyncer_key;
extern uint_t rrw_tsd_key;
+#ifdef DEBUG
+#define ZFS_DEBUG_STR " (DEBUG mode)"
+#else
+#define ZFS_DEBUG_STR ""
+#endif
+
int
_init(void)
{
spa_init(FREAD | FWRITE);
zfs_init();
- zvol_init();
- if ((error = mod_install(&modlinkage)) != 0) {
- zvol_fini();
- zfs_fini();
- spa_fini();
- return (error);
- }
+ if ((error = zvol_init()) != 0)
+ goto out1;
+
+ if ((error = zfs_attach()) != 0)
+ goto out2;
tsd_create(&zfs_fsyncer_key, NULL);
tsd_create(&rrw_tsd_key, NULL);
- error = ldi_ident_from_mod(&modlinkage, &zfs_li);
- ASSERT(error == 0);
- mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL);
+ 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);
+
+out2:
+ (void) zvol_fini();
+out1:
+ zfs_fini();
+ spa_fini();
+ 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);
}
int
_fini(void)
{
- int error;
-
- if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled)
- return (EBUSY);
-
- if ((error = mod_remove(&modlinkage)) != 0)
- return (error);
-
+ zfs_detach();
zvol_fini();
zfs_fini();
spa_fini();
- 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);
tsd_destroy(&zfs_fsyncer_key);
- ldi_ident_release(zfs_li);
- zfs_li = NULL;
- mutex_destroy(&zfs_share_lock);
+ tsd_destroy(&rrw_tsd_key);
- return (error);
-}
+ printk(KERN_NOTICE "ZFS: Unloaded module v%s-%s%s\n",
+ ZFS_META_VERSION, ZFS_META_RELEASE, ZFS_DEBUG_STR);
-int
-_info(struct modinfo *modinfop)
-{
- return (mod_info(&modlinkage, modinfop));
+ return (0);
}
+
+#ifdef HAVE_SPL
+spl_module_init(_init);
+spl_module_exit(_fini);
+
+MODULE_DESCRIPTION("ZFS");
+MODULE_AUTHOR(ZFS_META_AUTHOR);
+MODULE_LICENSE(ZFS_META_LICENSE);
+#endif /* HAVE_SPL */