* Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014, Joyent, Inc. All rights reserved.
- * Copyright (c) 2011, 2014 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved.
+ * Copyright (c) 2016 Actifio, Inc. All rights reserved.
*/
/*
#include <sys/zfeature.h>
#include <linux/miscdevice.h>
+#include <linux/slab.h>
#include "zfs_namecheck.h"
#include "zfs_prop.h"
#include "zfs_deleg.h"
#include "zfs_comutil.h"
+/*
+ * Limit maximum nvlist size. We don't want users passing in insane values
+ * for zc->zc_nvlist_src_size, since we will need to allocate that much memory.
+ */
+#define MAX_NVLIST_SRC_SIZE KMALLOC_MAX_SIZE
+
kmutex_t zfsdev_state_lock;
zfsdev_state_t *zfsdev_state_list;
int error;
zfs_log_history(zc);
error = spa_destroy(zc->zc_name);
- if (error == 0)
- zvol_remove_minors(zc->zc_name);
+
return (error);
}
}
nvlist_free(config);
-
- if (props)
- nvlist_free(props);
+ nvlist_free(props);
return (error);
}
zfs_log_history(zc);
error = spa_export(zc->zc_name, NULL, force, hardforce);
- if (error == 0)
- zvol_remove_minors(zc->zc_name);
+
return (error);
}
err = zvol_set_volsize(dsname, intval);
break;
case ZFS_PROP_SNAPDEV:
- err = zvol_set_snapdev(dsname, intval);
+ err = zvol_set_snapdev(dsname, source, intval);
break;
case ZFS_PROP_VERSION:
{
if (error == 0) {
error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL,
nvprops, outnvl);
- if (error != 0)
- (void) dsl_destroy_head(fsname);
- }
-
-#ifdef _KERNEL
- if (error == 0 && type == DMU_OST_ZVOL)
- zvol_create_minors(fsname);
-#endif
+ if (error != 0) {
+ spa_t *spa;
+ int error2;
+ /*
+ * Volumes will return EBUSY and cannot be destroyed
+ * until all asynchronous minor handling has completed.
+ * Wait for the spa_zvol_taskq to drain then retry.
+ */
+ error2 = dsl_destroy_head(fsname);
+ while ((error2 == EBUSY) && (type == DMU_OST_ZVOL)) {
+ error2 = spa_open(fsname, &spa, FTAG);
+ if (error2 == 0) {
+ taskq_wait(spa->spa_zvol_taskq);
+ spa_close(spa, FTAG);
+ }
+ error2 = dsl_destroy_head(fsname);
+ }
+ }
+ }
return (error);
}
if (error != 0)
(void) dsl_destroy_head(fsname);
}
-
-#ifdef _KERNEL
- if (error == 0)
- zvol_create_minors(fsname);
-#endif
-
return (error);
}
error = dsl_dataset_snapshot(snaps, props, outnvl);
-#ifdef _KERNEL
- if (error == 0)
- zvol_create_minors(poolname);
-#endif
-
return (error);
}
for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
pair = nvlist_next_nvpair(snaps, pair)) {
(void) zfs_unmount_snap(nvpair_name(pair));
- (void) zvol_remove_minor(nvpair_name(pair));
}
return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl));
err = dsl_destroy_snapshot(zc->zc_name, zc->zc_defer_destroy);
else
err = dsl_destroy_head(zc->zc_name);
- if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
- (void) zvol_remove_minor(zc->zc_name);
+
return (err);
}
*/
if (zfs_is_bootfs(dsname) &&
intval > SPA_OLD_MAXBLOCKSIZE) {
- return (SET_ERROR(EDOM));
+ return (SET_ERROR(ERANGE));
}
/*
*/
if (intval > zfs_max_recordsize ||
intval > SPA_MAXBLOCKSIZE)
- return (SET_ERROR(EDOM));
+ return (SET_ERROR(ERANGE));
if ((err = spa_open(dsname, &spa, FTAG)) != 0)
return (err);
}
#endif
-#ifdef _KERNEL
- if (error == 0)
- zvol_create_minors(tofs);
-#endif
-
/*
* On error, restore the original props.
*/
if ((error = get_nvlist(zc->zc_nvlist_src,
zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) {
VN_RELE(vp);
+ VN_RELE(ZTOV(sharedir));
ZFS_EXIT(zsb);
return (error);
}
}
zc->zc_iflags = flag & FKIOCTL;
- if (zc->zc_nvlist_src_size != 0) {
+ if (zc->zc_nvlist_src_size > MAX_NVLIST_SRC_SIZE) {
+ /*
+ * Make sure the user doesn't pass in an insane value for
+ * zc_nvlist_src_size. We have to check, since we will end
+ * up allocating that much memory inside of get_nvlist(). This
+ * prevents a nefarious user from allocating tons of kernel
+ * memory.
+ *
+ * Also, we return EINVAL instead of ENOMEM here. The reason
+ * being that returning ENOMEM from an ioctl() has a special
+ * connotation; that the user's size value is too small and
+ * needs to be expanded to hold the nvlist. See
+ * zcmd_expand_dst_nvlist() for details.
+ */
+ error = SET_ERROR(EINVAL); /* User's size too big */
+
+ } else if (zc->zc_nvlist_src_size != 0) {
error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
zc->zc_iflags, &innvl);
if (error != 0)
}
- if (error == 0 && !(flag & FKIOCTL))
+ if (error == 0 && !(flag & FKIOCTL)) {
+ cookie = spl_fstrans_mark();
error = vec->zvec_secpolicy(zc, innvl, CRED());
+ spl_fstrans_unmark(cookie);
+ }
if (error != 0)
goto out;
return (error);
}
+ if ((error = -zvol_init()) != 0)
+ return (error);
+
spa_init(FREAD | FWRITE);
zfs_init();
- if ((error = -zvol_init()) != 0)
- goto out1;
-
zfs_ioctl_init();
if ((error = zfs_attach()) != 0)
- goto out2;
+ goto out;
tsd_create(&zfs_fsyncer_key, NULL);
tsd_create(&rrw_tsd_key, rrw_tsd_destroy);
return (0);
-out2:
- (void) zvol_fini();
-out1:
+out:
zfs_fini();
spa_fini();
+ (void) zvol_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);
_fini(void)
{
zfs_detach();
- zvol_fini();
zfs_fini();
spa_fini();
+ zvol_fini();
tsd_destroy(&zfs_fsyncer_key);
tsd_destroy(&rrw_tsd_key);