]> git.proxmox.com Git - mirror_zfs.git/blobdiff - module/zfs/zfs_ioctl.c
Add `zfs allow` and `zfs unallow` support
[mirror_zfs.git] / module / zfs / zfs_ioctl.c
index 0030799023fdd36b91ed8f5ab0e36d2f0d86f28f..c63af167af644a0745d8cb0036cbf10c1003de8f 100644 (file)
  * 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;
 
@@ -1499,8 +1507,7 @@ zfs_ioc_pool_destroy(zfs_cmd_t *zc)
        int error;
        zfs_log_history(zc);
        error = spa_destroy(zc->zc_name);
-       if (error == 0)
-               zvol_remove_minors(zc->zc_name);
+
        return (error);
 }
 
@@ -1536,9 +1543,7 @@ zfs_ioc_pool_import(zfs_cmd_t *zc)
        }
 
        nvlist_free(config);
-
-       if (props)
-               nvlist_free(props);
+       nvlist_free(props);
 
        return (error);
 }
@@ -1552,8 +1557,7 @@ zfs_ioc_pool_export(zfs_cmd_t *zc)
 
        zfs_log_history(zc);
        error = spa_export(zc->zc_name, NULL, force, hardforce);
-       if (error == 0)
-               zvol_remove_minors(zc->zc_name);
+
        return (error);
 }
 
@@ -2394,7 +2398,7 @@ zfs_prop_set_special(const char *dsname, zprop_source_t source,
                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:
        {
@@ -3185,15 +3189,26 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
        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);
 }
 
@@ -3236,12 +3251,6 @@ zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
                if (error != 0)
                        (void) dsl_destroy_head(fsname);
        }
-
-#ifdef _KERNEL
-       if (error == 0)
-               zvol_create_minors(fsname);
-#endif
-
        return (error);
 }
 
@@ -3304,11 +3313,6 @@ zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
 
        error = dsl_dataset_snapshot(snaps, props, outnvl);
 
-#ifdef _KERNEL
-       if (error == 0)
-               zvol_create_minors(poolname);
-#endif
-
        return (error);
 }
 
@@ -3434,7 +3438,6 @@ zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
        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));
@@ -3560,8 +3563,7 @@ zfs_ioc_destroy(zfs_cmd_t *zc)
                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);
 }
 
@@ -3788,7 +3790,7 @@ zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
                         */
                        if (zfs_is_bootfs(dsname) &&
                            intval > SPA_OLD_MAXBLOCKSIZE) {
-                               return (SET_ERROR(EDOM));
+                               return (SET_ERROR(ERANGE));
                        }
 
                        /*
@@ -3797,7 +3799,7 @@ zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
                         */
                        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);
@@ -4127,11 +4129,6 @@ zfs_ioc_recv(zfs_cmd_t *zc)
        }
 #endif
 
-#ifdef _KERNEL
-       if (error == 0)
-               zvol_create_minors(tofs);
-#endif
-
        /*
         * On error, restore the original props.
         */
@@ -4847,6 +4844,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);
+                       VN_RELE(ZTOV(sharedir));
                        ZFS_EXIT(zsb);
                        return (error);
                }
@@ -5821,7 +5819,23 @@ zfsdev_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
        }
 
        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)
@@ -5855,8 +5869,11 @@ zfsdev_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
        }
 
 
-       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;
@@ -6031,16 +6048,16 @@ _init(void)
                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);
@@ -6056,11 +6073,10 @@ _init(void)
 
        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);
@@ -6072,9 +6088,9 @@ static void __exit
 _fini(void)
 {
        zfs_detach();
-       zvol_fini();
        zfs_fini();
        spa_fini();
+       zvol_fini();
 
        tsd_destroy(&zfs_fsyncer_key);
        tsd_destroy(&rrw_tsd_key);