| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| |
| /* |
| * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. |
| * Portions Copyright 2011 Martin Matuska |
| * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. |
| * Portions Copyright 2012 Pawel Jakub Dawidek <pawel@dawidek.net> |
| * Copyright (c) 2014, 2016 Joyent, Inc. All rights reserved. |
| * Copyright 2016 Nexenta Systems, Inc. All rights reserved. |
| * Copyright (c) 2014, Joyent, Inc. All rights reserved. |
| * Copyright (c) 2011, 2018 by Delphix. All rights reserved. |
| * Copyright (c) 2013 by Saso Kiselkov. All rights reserved. |
| * Copyright (c) 2013 Steven Hartland. All rights reserved. |
| * Copyright (c) 2014 Integros [integros.com] |
| * Copyright 2016 Toomas Soome <tsoome@me.com> |
| * Copyright (c) 2016 Actifio, Inc. All rights reserved. |
| * Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>. All rights reserved. |
| * Copyright 2017 RackTop Systems. |
| * Copyright (c) 2017 Open-E, Inc. All Rights Reserved. |
| * Copyright (c) 2019 Datto Inc. |
| */ |
| |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/errno.h> |
| #include <sys/uio.h> |
| #include <sys/file.h> |
| #include <sys/kmem.h> |
| #include <sys/stat.h> |
| #include <sys/zfs_ioctl.h> |
| #include <sys/zfs_vfsops.h> |
| #include <sys/zap.h> |
| #include <sys/spa.h> |
| #include <sys/nvpair.h> |
| #include <sys/fs/zfs.h> |
| #include <sys/zfs_ctldir.h> |
| #include <sys/zfs_dir.h> |
| #include <sys/zfs_onexit.h> |
| #include <sys/zvol.h> |
| #include <sys/fm/util.h> |
| #include <sys/dsl_crypt.h> |
| |
| #include <sys/zfs_ioctl_impl.h> |
| |
| #include <sys/zfs_sysfs.h> |
| #include <linux/miscdevice.h> |
| #include <linux/slab.h> |
| |
| boolean_t |
| zfs_vfs_held(zfsvfs_t *zfsvfs) |
| { |
| return (zfsvfs->z_sb != NULL); |
| } |
| |
| int |
| zfs_vfs_ref(zfsvfs_t **zfvp) |
| { |
| if (*zfvp == NULL || (*zfvp)->z_sb == NULL || |
| !atomic_inc_not_zero(&((*zfvp)->z_sb->s_active))) { |
| return (SET_ERROR(ESRCH)); |
| } |
| return (0); |
| } |
| |
| void |
| zfs_vfs_rele(zfsvfs_t *zfsvfs) |
| { |
| deactivate_super(zfsvfs->z_sb); |
| } |
| |
| static int |
| zfsdev_state_init(struct file *filp) |
| { |
| zfsdev_state_t *zs, *zsprev = NULL; |
| minor_t minor; |
| boolean_t newzs = B_FALSE; |
| |
| ASSERT(MUTEX_HELD(&zfsdev_state_lock)); |
| |
| minor = zfsdev_minor_alloc(); |
| if (minor == 0) |
| return (SET_ERROR(ENXIO)); |
| |
| for (zs = zfsdev_state_list; zs != NULL; zs = zs->zs_next) { |
| if (zs->zs_minor == -1) |
| break; |
| zsprev = zs; |
| } |
| |
| if (!zs) { |
| zs = kmem_zalloc(sizeof (zfsdev_state_t), KM_SLEEP); |
| newzs = B_TRUE; |
| } |
| |
| filp->private_data = zs; |
| |
| zfs_onexit_init((zfs_onexit_t **)&zs->zs_onexit); |
| zfs_zevent_init((zfs_zevent_t **)&zs->zs_zevent); |
| |
| /* |
| * In order to provide for lock-free concurrent read access |
| * to the minor list in zfsdev_get_state_impl(), new entries |
| * must be completely written before linking them into the |
| * list whereas existing entries are already linked; the last |
| * operation must be updating zs_minor (from -1 to the new |
| * value). |
| */ |
| if (newzs) { |
| zs->zs_minor = minor; |
| smp_wmb(); |
| zsprev->zs_next = zs; |
| } else { |
| smp_wmb(); |
| zs->zs_minor = minor; |
| } |
| |
| return (0); |
| } |
| |
| static int |
| zfsdev_state_destroy(struct file *filp) |
| { |
| zfsdev_state_t *zs; |
| |
| ASSERT(MUTEX_HELD(&zfsdev_state_lock)); |
| ASSERT(filp->private_data != NULL); |
| |
| zs = filp->private_data; |
| zs->zs_minor = -1; |
| zfs_onexit_destroy(zs->zs_onexit); |
| zfs_zevent_destroy(zs->zs_zevent); |
| zs->zs_onexit = NULL; |
| zs->zs_zevent = NULL; |
| |
| return (0); |
| } |
| |
| static int |
| zfsdev_open(struct inode *ino, struct file *filp) |
| { |
| int error; |
| |
| mutex_enter(&zfsdev_state_lock); |
| error = zfsdev_state_init(filp); |
| mutex_exit(&zfsdev_state_lock); |
| |
| return (-error); |
| } |
| |
| static int |
| zfsdev_release(struct inode *ino, struct file *filp) |
| { |
| int error; |
| |
| mutex_enter(&zfsdev_state_lock); |
| error = zfsdev_state_destroy(filp); |
| mutex_exit(&zfsdev_state_lock); |
| |
| return (-error); |
| } |
| |
| static long |
| zfsdev_ioctl(struct file *filp, unsigned cmd, unsigned long arg) |
| { |
| uint_t vecnum; |
| zfs_cmd_t *zc; |
| int error, rc; |
| |
| vecnum = cmd - ZFS_IOC_FIRST; |
| |
| zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); |
| |
| if (ddi_copyin((void *)(uintptr_t)arg, zc, sizeof (zfs_cmd_t), 0)) { |
| error = -SET_ERROR(EFAULT); |
| goto out; |
| } |
| error = -zfsdev_ioctl_common(vecnum, zc, 0); |
| rc = ddi_copyout(zc, (void *)(uintptr_t)arg, sizeof (zfs_cmd_t), 0); |
| if (error == 0 && rc != 0) |
| error = -SET_ERROR(EFAULT); |
| out: |
| kmem_free(zc, sizeof (zfs_cmd_t)); |
| return (error); |
| |
| } |
| |
| uint64_t |
| zfs_max_nvlist_src_size_os(void) |
| { |
| if (zfs_max_nvlist_src_size != 0) |
| return (zfs_max_nvlist_src_size); |
| |
| return (MIN(ptob(zfs_totalram_pages) / 4, 128 * 1024 * 1024)); |
| } |
| |
| /* Update the VFS's cache of mountpoint properties */ |
| void |
| zfs_ioctl_update_mount_cache(const char *dsname) |
| { |
| } |
| |
| void |
| zfs_ioctl_init_os(void) |
| { |
| } |
| |
| #ifdef CONFIG_COMPAT |
| static long |
| zfsdev_compat_ioctl(struct file *filp, unsigned cmd, unsigned long arg) |
| { |
| return (zfsdev_ioctl(filp, cmd, arg)); |
| } |
| #else |
| #define zfsdev_compat_ioctl NULL |
| #endif |
| |
| 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, |
| }; |
| |
| static struct miscdevice zfs_misc = { |
| .minor = ZFS_DEVICE_MINOR, |
| .name = ZFS_DRIVER, |
| .fops = &zfsdev_fops, |
| }; |
| |
| MODULE_ALIAS_MISCDEV(ZFS_DEVICE_MINOR); |
| MODULE_ALIAS("devname:zfs"); |
| |
| int |
| zfsdev_attach(void) |
| { |
| int error; |
| |
| error = misc_register(&zfs_misc); |
| if (error == -EBUSY) { |
| /* |
| * Fallback to dynamic minor allocation in the event of a |
| * collision with a reserved minor in linux/miscdevice.h. |
| * In this case the kernel modules must be manually loaded. |
| */ |
| printk(KERN_INFO "ZFS: misc_register() with static minor %d " |
| "failed %d, retrying with MISC_DYNAMIC_MINOR\n", |
| ZFS_DEVICE_MINOR, error); |
| |
| zfs_misc.minor = MISC_DYNAMIC_MINOR; |
| error = misc_register(&zfs_misc); |
| } |
| |
| if (error) |
| printk(KERN_INFO "ZFS: misc_register() failed %d\n", error); |
| |
| return (error); |
| } |
| |
| void |
| zfsdev_detach(void) |
| { |
| misc_deregister(&zfs_misc); |
| } |
| |
| #ifdef ZFS_DEBUG |
| #define ZFS_DEBUG_STR " (DEBUG mode)" |
| #else |
| #define ZFS_DEBUG_STR "" |
| #endif |
| |
| zidmap_t *zfs_init_idmap; |
| |
| static int __init |
| openzfs_init(void) |
| { |
| int error; |
| |
| if ((error = zfs_kmod_init()) != 0) { |
| 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); |
| } |
| |
| zfs_sysfs_init(); |
| |
| 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); |
| #ifndef CONFIG_FS_POSIX_ACL |
| printk(KERN_NOTICE "ZFS: Posix ACLs disabled by kernel\n"); |
| #endif /* CONFIG_FS_POSIX_ACL */ |
| |
| zfs_init_idmap = (zidmap_t *)zfs_get_init_idmap(); |
| |
| return (0); |
| } |
| |
| static void __exit |
| openzfs_fini(void) |
| { |
| zfs_sysfs_fini(); |
| zfs_kmod_fini(); |
| |
| printk(KERN_NOTICE "ZFS: Unloaded module v%s-%s%s\n", |
| ZFS_META_VERSION, ZFS_META_RELEASE, ZFS_DEBUG_STR); |
| } |
| |
| #if defined(_KERNEL) |
| module_init(openzfs_init); |
| module_exit(openzfs_fini); |
| #endif |
| |
| ZFS_MODULE_DESCRIPTION("ZFS"); |
| ZFS_MODULE_AUTHOR(ZFS_META_AUTHOR); |
| ZFS_MODULE_LICENSE(ZFS_META_LICENSE); |
| ZFS_MODULE_VERSION(ZFS_META_VERSION "-" ZFS_META_RELEASE); |