diff --git a/lib/libshare/os/freebsd/nfs.c b/lib/libshare/os/freebsd/nfs.c index d9fc66106369..d4cdb07a4947 100644 --- a/lib/libshare/os/freebsd/nfs.c +++ b/lib/libshare/os/freebsd/nfs.c @@ -1,213 +1,210 @@ /* * Copyright (c) 2007 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Copyright (c) 2020, 2022 by Delphix. All rights reserved. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include #include #include #include "libshare_impl.h" #include "nfs.h" #define _PATH_MOUNTDPID "/var/run/mountd.pid" #define OPTSSIZE 1024 #define MAXLINESIZE (PATH_MAX + OPTSSIZE) #define ZFS_EXPORTS_FILE "/etc/zfs/exports" #define ZFS_EXPORTS_LOCK ZFS_EXPORTS_FILE".lock" /* * This function translates options to a format acceptable by exports(5), eg. * * -ro -network=192.168.0.0 -mask=255.255.255.0 -maproot=0 \ * zfs.freebsd.org 69.147.83.54 * * Accepted input formats: * * ro,network=192.168.0.0,mask=255.255.255.0,maproot=0,zfs.freebsd.org * ro network=192.168.0.0 mask=255.255.255.0 maproot=0 zfs.freebsd.org * -ro,-network=192.168.0.0,-mask=255.255.255.0,-maproot=0,zfs.freebsd.org * -ro -network=192.168.0.0 -mask=255.255.255.0 -maproot=0 \ * zfs.freebsd.org * * Recognized keywords: * * ro, maproot, mapall, mask, network, sec, alldirs, public, webnfs, * index, quiet */ static int translate_opts(const char *shareopts, FILE *out) { static const char *const known_opts[] = { "ro", "maproot", "mapall", "mask", "network", "sec", "alldirs", "public", "webnfs", "index", "quiet" }; char oldopts[OPTSSIZE], newopts[OPTSSIZE]; char *o, *s = NULL; unsigned int i; size_t len; strlcpy(oldopts, shareopts, sizeof (oldopts)); newopts[0] = '\0'; s = oldopts; while ((o = strsep(&s, "-, ")) != NULL) { if (o[0] == '\0') continue; for (i = 0; i < ARRAY_SIZE(known_opts); ++i) { len = strlen(known_opts[i]); if (strncmp(known_opts[i], o, len) == 0 && (o[len] == '\0' || o[len] == '=')) { strlcat(newopts, "-", sizeof (newopts)); break; } } strlcat(newopts, o, sizeof (newopts)); strlcat(newopts, " ", sizeof (newopts)); } return (fputs(newopts, out)); } static int nfs_enable_share_impl(sa_share_impl_t impl_share, FILE *tmpfile) { const char *shareopts = impl_share->sa_shareopts; if (strcmp(shareopts, "on") == 0) shareopts = ""; boolean_t need_free; char *mp; int rc = nfs_escape_mountpoint(impl_share->sa_mountpoint, &mp, &need_free); if (rc != SA_OK) return (rc); if (fputs(mp, tmpfile) == EOF || fputc('\t', tmpfile) == EOF || translate_opts(shareopts, tmpfile) == EOF || fputc('\n', tmpfile) == EOF) { fprintf(stderr, "failed to write to temporary file\n"); rc = SA_SYSTEM_ERR; } if (need_free) free(mp); return (rc); } static int nfs_enable_share(sa_share_impl_t impl_share) { return (nfs_toggle_share( ZFS_EXPORTS_LOCK, ZFS_EXPORTS_FILE, NULL, impl_share, nfs_enable_share_impl)); } static int nfs_disable_share_impl(sa_share_impl_t impl_share, FILE *tmpfile) { (void) impl_share, (void) tmpfile; return (SA_OK); } static int nfs_disable_share(sa_share_impl_t impl_share) { return (nfs_toggle_share( ZFS_EXPORTS_LOCK, ZFS_EXPORTS_FILE, NULL, impl_share, nfs_disable_share_impl)); } static boolean_t nfs_is_shared(sa_share_impl_t impl_share) { return (nfs_is_shared_impl(ZFS_EXPORTS_FILE, impl_share)); } static int nfs_validate_shareopts(const char *shareopts) { if (strlen(shareopts) == 0) return (SA_SYNTAX_ERR); return (SA_OK); } /* * Commit the shares by restarting mountd. */ static int nfs_commit_shares(void) { struct pidfh *pfh; pid_t mountdpid; start: pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &mountdpid); if (pfh != NULL) { /* mountd(8) is not running. */ pidfile_remove(pfh); return (SA_OK); } if (errno != EEXIST) { /* Cannot open pidfile for some reason. */ return (SA_SYSTEM_ERR); } if (mountdpid == -1) { /* mountd(8) exists, but didn't write the PID yet */ usleep(500); goto start; } /* We have mountd(8) PID in mountdpid variable. */ kill(mountdpid, SIGHUP); return (SA_OK); } static void nfs_truncate_shares(void) { nfs_reset_shares(ZFS_EXPORTS_LOCK, ZFS_EXPORTS_FILE); } const sa_fstype_t libshare_nfs_type = { .enable_share = nfs_enable_share, .disable_share = nfs_disable_share, .is_shared = nfs_is_shared, .validate_shareopts = nfs_validate_shareopts, .commit_shares = nfs_commit_shares, .truncate_shares = nfs_truncate_shares, }; diff --git a/lib/libspl/os/freebsd/mnttab.c b/lib/libspl/os/freebsd/mnttab.c index a4673084ad5f..26a4cd992cfb 100644 --- a/lib/libspl/os/freebsd/mnttab.c +++ b/lib/libspl/os/freebsd/mnttab.c @@ -1,237 +1,234 @@ /* * Copyright (c) 2006 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This file implements Solaris compatible getmntany() and hasmntopt() * functions. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include static char * mntopt(char **p) { char *cp = *p; char *retstr; while (*cp && isspace(*cp)) cp++; retstr = cp; while (*cp && *cp != ',') cp++; if (*cp) { *cp = '\0'; cp++; } *p = cp; return (retstr); } char * hasmntopt(struct mnttab *mnt, const char *opt) { char tmpopts[MNT_LINE_MAX]; char *f, *opts = tmpopts; if (mnt->mnt_mntopts == NULL) return (NULL); (void) strlcpy(opts, mnt->mnt_mntopts, MNT_LINE_MAX); f = mntopt(&opts); for (; *f; f = mntopt(&opts)) { if (strncmp(opt, f, strlen(opt)) == 0) return (f - tmpopts + mnt->mnt_mntopts); } return (NULL); } static void optadd(char *mntopts, size_t size, const char *opt) { if (mntopts[0] != '\0') strlcat(mntopts, ",", size); strlcat(mntopts, opt, size); } static __thread char gfstypename[MFSNAMELEN]; static __thread char gmntfromname[MNAMELEN]; static __thread char gmntonname[MNAMELEN]; static __thread char gmntopts[MNTMAXSTR]; void statfs2mnttab(struct statfs *sfs, struct mnttab *mp) { long flags; strlcpy(gfstypename, sfs->f_fstypename, sizeof (gfstypename)); mp->mnt_fstype = gfstypename; strlcpy(gmntfromname, sfs->f_mntfromname, sizeof (gmntfromname)); mp->mnt_special = gmntfromname; strlcpy(gmntonname, sfs->f_mntonname, sizeof (gmntonname)); mp->mnt_mountp = gmntonname; flags = sfs->f_flags; gmntopts[0] = '\0'; #define OPTADD(opt) optadd(gmntopts, sizeof (gmntopts), (opt)) if (flags & MNT_RDONLY) OPTADD(MNTOPT_RO); else OPTADD(MNTOPT_RW); if (flags & MNT_NOSUID) OPTADD(MNTOPT_NOSETUID); else OPTADD(MNTOPT_SETUID); if (flags & MNT_UPDATE) OPTADD(MNTOPT_REMOUNT); if (flags & MNT_NOATIME) OPTADD(MNTOPT_NOATIME); else OPTADD(MNTOPT_ATIME); OPTADD(MNTOPT_NOXATTR); if (flags & MNT_NOEXEC) OPTADD(MNTOPT_NOEXEC); else OPTADD(MNTOPT_EXEC); #undef OPTADD mp->mnt_mntopts = gmntopts; } static pthread_rwlock_t gsfs_lock = PTHREAD_RWLOCK_INITIALIZER; static struct statfs *gsfs = NULL; static int allfs = 0; static int statfs_init(void) { struct statfs *sfs; int error; (void) pthread_rwlock_wrlock(&gsfs_lock); if (gsfs != NULL) { free(gsfs); gsfs = NULL; } allfs = getfsstat(NULL, 0, MNT_NOWAIT); if (allfs == -1) goto fail; gsfs = malloc(sizeof (gsfs[0]) * allfs * 2); if (gsfs == NULL) goto fail; allfs = getfsstat(gsfs, (long)(sizeof (gsfs[0]) * allfs * 2), MNT_NOWAIT); if (allfs == -1) goto fail; sfs = realloc(gsfs, allfs * sizeof (gsfs[0])); if (sfs != NULL) gsfs = sfs; (void) pthread_rwlock_unlock(&gsfs_lock); return (0); fail: error = errno; if (gsfs != NULL) free(gsfs); gsfs = NULL; allfs = 0; (void) pthread_rwlock_unlock(&gsfs_lock); return (error); } int getmntany(FILE *fd __unused, struct mnttab *mgetp, struct mnttab *mrefp) { int i, error; error = statfs_init(); if (error != 0) return (error); (void) pthread_rwlock_rdlock(&gsfs_lock); for (i = 0; i < allfs; i++) { if (mrefp->mnt_special != NULL && strcmp(mrefp->mnt_special, gsfs[i].f_mntfromname) != 0) { continue; } if (mrefp->mnt_mountp != NULL && strcmp(mrefp->mnt_mountp, gsfs[i].f_mntonname) != 0) { continue; } if (mrefp->mnt_fstype != NULL && strcmp(mrefp->mnt_fstype, gsfs[i].f_fstypename) != 0) { continue; } statfs2mnttab(&gsfs[i], mgetp); (void) pthread_rwlock_unlock(&gsfs_lock); return (0); } (void) pthread_rwlock_unlock(&gsfs_lock); return (-1); } int getmntent(FILE *fp, struct mnttab *mp) { int error, nfs; nfs = (int)lseek(fileno(fp), 0, SEEK_CUR); if (nfs == -1) return (errno); /* If nfs is 0, we want to refresh out cache. */ if (nfs == 0 || gsfs == NULL) { error = statfs_init(); if (error != 0) return (error); } (void) pthread_rwlock_rdlock(&gsfs_lock); if (nfs >= allfs) { (void) pthread_rwlock_unlock(&gsfs_lock); return (-1); } statfs2mnttab(&gsfs[nfs], mp); (void) pthread_rwlock_unlock(&gsfs_lock); if (lseek(fileno(fp), 1, SEEK_CUR) == -1) return (errno); return (0); } diff --git a/lib/libzfs/os/freebsd/libzfs_zmount.c b/lib/libzfs/os/freebsd/libzfs_zmount.c index 34976f7bbf46..3c50daf471b7 100644 --- a/lib/libzfs/os/freebsd/libzfs_zmount.c +++ b/lib/libzfs/os/freebsd/libzfs_zmount.c @@ -1,139 +1,136 @@ /* * Copyright (c) 2006 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This file implements Solaris compatible zmount() function. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include #include #include "../../libzfs_impl.h" static void build_iovec(struct iovec **iov, int *iovlen, const char *name, void *val, size_t len) { int i; if (*iovlen < 0) return; i = *iovlen; *iov = realloc(*iov, sizeof (**iov) * (i + 2)); if (*iov == NULL) { *iovlen = -1; return; } (*iov)[i].iov_base = strdup(name); (*iov)[i].iov_len = strlen(name) + 1; i++; (*iov)[i].iov_base = val; if (len == (size_t)-1) { if (val != NULL) len = strlen(val) + 1; else len = 0; } (*iov)[i].iov_len = (int)len; *iovlen = ++i; } int do_mount(zfs_handle_t *zhp, const char *mntpt, const char *opts, int flags) { struct iovec *iov; char *optstr, *p, *tofree; int iovlen, rv; const char *spec = zfs_get_name(zhp); assert(spec != NULL); assert(mntpt != NULL); assert(opts != NULL); tofree = optstr = strdup(opts); assert(optstr != NULL); iov = NULL; iovlen = 0; if (strstr(optstr, MNTOPT_REMOUNT) != NULL) build_iovec(&iov, &iovlen, "update", NULL, 0); if (flags & MS_RDONLY) build_iovec(&iov, &iovlen, "ro", NULL, 0); build_iovec(&iov, &iovlen, "fstype", __DECONST(char *, MNTTYPE_ZFS), (size_t)-1); build_iovec(&iov, &iovlen, "fspath", __DECONST(char *, mntpt), (size_t)-1); build_iovec(&iov, &iovlen, "from", __DECONST(char *, spec), (size_t)-1); while ((p = strsep(&optstr, ",/")) != NULL) build_iovec(&iov, &iovlen, p, NULL, (size_t)-1); rv = nmount(iov, iovlen, 0); free(tofree); if (rv < 0) return (errno); return (rv); } int do_unmount(zfs_handle_t *zhp, const char *mntpt, int flags) { (void) zhp; if (unmount(mntpt, flags) < 0) return (errno); return (0); } int zfs_mount_delegation_check(void) { return (0); } /* Called from the tail end of zpool_disable_datasets() */ void zpool_disable_datasets_os(zpool_handle_t *zhp, boolean_t force) { (void) zhp, (void) force; } /* Called from the tail end of zfs_unmount() */ void zpool_disable_volume_os(const char *name) { (void) name; } diff --git a/module/os/freebsd/spl/spl_acl.c b/module/os/freebsd/spl/spl_acl.c index 4d67cbb183ec..c820d7a6d22d 100644 --- a/module/os/freebsd/spl/spl_acl.c +++ b/module/os/freebsd/spl/spl_acl.c @@ -1,222 +1,219 @@ /* * Copyright (c) 2008, 2009 Edward Tomasz NapieraƂa * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include struct zfs2bsd { uint32_t zb_zfs; int zb_bsd; }; static const struct zfs2bsd perms[] = {{ACE_READ_DATA, ACL_READ_DATA}, {ACE_WRITE_DATA, ACL_WRITE_DATA}, {ACE_EXECUTE, ACL_EXECUTE}, {ACE_APPEND_DATA, ACL_APPEND_DATA}, {ACE_DELETE_CHILD, ACL_DELETE_CHILD}, {ACE_DELETE, ACL_DELETE}, {ACE_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, {ACE_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, {ACE_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, {ACE_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, {ACE_READ_ACL, ACL_READ_ACL}, {ACE_WRITE_ACL, ACL_WRITE_ACL}, {ACE_WRITE_OWNER, ACL_WRITE_OWNER}, {ACE_SYNCHRONIZE, ACL_SYNCHRONIZE}, {0, 0}}; static const struct zfs2bsd flags[] = {{ACE_FILE_INHERIT_ACE, ACL_ENTRY_FILE_INHERIT}, {ACE_DIRECTORY_INHERIT_ACE, ACL_ENTRY_DIRECTORY_INHERIT}, {ACE_NO_PROPAGATE_INHERIT_ACE, ACL_ENTRY_NO_PROPAGATE_INHERIT}, {ACE_INHERIT_ONLY_ACE, ACL_ENTRY_INHERIT_ONLY}, {ACE_INHERITED_ACE, ACL_ENTRY_INHERITED}, {ACE_SUCCESSFUL_ACCESS_ACE_FLAG, ACL_ENTRY_SUCCESSFUL_ACCESS}, {ACE_FAILED_ACCESS_ACE_FLAG, ACL_ENTRY_FAILED_ACCESS}, {0, 0}}; static int _bsd_from_zfs(uint32_t zfs, const struct zfs2bsd *table) { const struct zfs2bsd *tmp; int bsd = 0; for (tmp = table; tmp->zb_zfs != 0; tmp++) { if (zfs & tmp->zb_zfs) bsd |= tmp->zb_bsd; } return (bsd); } static uint32_t _zfs_from_bsd(int bsd, const struct zfs2bsd *table) { const struct zfs2bsd *tmp; uint32_t zfs = 0; for (tmp = table; tmp->zb_bsd != 0; tmp++) { if (bsd & tmp->zb_bsd) zfs |= tmp->zb_zfs; } return (zfs); } int acl_from_aces(struct acl *aclp, const ace_t *aces, int nentries) { int i; struct acl_entry *entry; const ace_t *ace; if (nentries < 1) { printf("acl_from_aces: empty ZFS ACL; returning EINVAL.\n"); return (EINVAL); } if (nentries > ACL_MAX_ENTRIES) { /* * I believe it may happen only when moving a pool * from SunOS to FreeBSD. */ printf("acl_from_aces: ZFS ACL too big to fit " "into 'struct acl'; returning EINVAL.\n"); return (EINVAL); } memset(aclp, 0, sizeof (*aclp)); aclp->acl_maxcnt = ACL_MAX_ENTRIES; aclp->acl_cnt = nentries; for (i = 0; i < nentries; i++) { entry = &(aclp->acl_entry[i]); ace = &(aces[i]); if (ace->a_flags & ACE_OWNER) entry->ae_tag = ACL_USER_OBJ; else if (ace->a_flags & ACE_GROUP) entry->ae_tag = ACL_GROUP_OBJ; else if (ace->a_flags & ACE_EVERYONE) entry->ae_tag = ACL_EVERYONE; else if (ace->a_flags & ACE_IDENTIFIER_GROUP) entry->ae_tag = ACL_GROUP; else entry->ae_tag = ACL_USER; if (entry->ae_tag == ACL_USER || entry->ae_tag == ACL_GROUP) entry->ae_id = ace->a_who; else entry->ae_id = ACL_UNDEFINED_ID; entry->ae_perm = _bsd_from_zfs(ace->a_access_mask, perms); entry->ae_flags = _bsd_from_zfs(ace->a_flags, flags); switch (ace->a_type) { case ACE_ACCESS_ALLOWED_ACE_TYPE: entry->ae_entry_type = ACL_ENTRY_TYPE_ALLOW; break; case ACE_ACCESS_DENIED_ACE_TYPE: entry->ae_entry_type = ACL_ENTRY_TYPE_DENY; break; case ACE_SYSTEM_AUDIT_ACE_TYPE: entry->ae_entry_type = ACL_ENTRY_TYPE_AUDIT; break; case ACE_SYSTEM_ALARM_ACE_TYPE: entry->ae_entry_type = ACL_ENTRY_TYPE_ALARM; break; default: panic("acl_from_aces: a_type is 0x%x", ace->a_type); } } return (0); } void aces_from_acl(ace_t *aces, int *nentries, const struct acl *aclp) { int i; const struct acl_entry *entry; ace_t *ace; memset(aces, 0, sizeof (*aces) * aclp->acl_cnt); *nentries = aclp->acl_cnt; for (i = 0; i < aclp->acl_cnt; i++) { entry = &(aclp->acl_entry[i]); ace = &(aces[i]); ace->a_who = entry->ae_id; if (entry->ae_tag == ACL_USER_OBJ) ace->a_flags = ACE_OWNER; else if (entry->ae_tag == ACL_GROUP_OBJ) ace->a_flags = (ACE_GROUP | ACE_IDENTIFIER_GROUP); else if (entry->ae_tag == ACL_GROUP) ace->a_flags = ACE_IDENTIFIER_GROUP; else if (entry->ae_tag == ACL_EVERYONE) ace->a_flags = ACE_EVERYONE; else /* ACL_USER */ ace->a_flags = 0; ace->a_access_mask = _zfs_from_bsd(entry->ae_perm, perms); ace->a_flags |= _zfs_from_bsd(entry->ae_flags, flags); switch (entry->ae_entry_type) { case ACL_ENTRY_TYPE_ALLOW: ace->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; break; case ACL_ENTRY_TYPE_DENY: ace->a_type = ACE_ACCESS_DENIED_ACE_TYPE; break; case ACL_ENTRY_TYPE_ALARM: ace->a_type = ACE_SYSTEM_ALARM_ACE_TYPE; break; case ACL_ENTRY_TYPE_AUDIT: ace->a_type = ACE_SYSTEM_AUDIT_ACE_TYPE; break; default: panic("aces_from_acl: ae_entry_type is 0x%x", entry->ae_entry_type); } } } diff --git a/module/os/freebsd/spl/spl_atomic.c b/module/os/freebsd/spl/spl_atomic.c index 80040fc6a3e3..cdfd37f3e05f 100644 --- a/module/os/freebsd/spl/spl_atomic.c +++ b/module/os/freebsd/spl/spl_atomic.c @@ -1,123 +1,120 @@ /* * Copyright (c) 2007 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #if !defined(__LP64__) && !defined(__mips_n32) && \ !defined(ARM_HAVE_ATOMIC64) && !defined(I386_HAVE_ATOMIC64) && \ !defined(HAS_EMULATED_ATOMIC64) #ifdef _KERNEL #include struct mtx atomic_mtx; MTX_SYSINIT(atomic, &atomic_mtx, "atomic", MTX_DEF); #else #include #define mtx_lock(lock) pthread_mutex_lock(lock) #define mtx_unlock(lock) pthread_mutex_unlock(lock) static pthread_mutex_t atomic_mtx; static __attribute__((constructor)) void atomic_init(void) { pthread_mutex_init(&atomic_mtx, NULL); } #endif void atomic_add_64(volatile uint64_t *target, int64_t delta) { mtx_lock(&atomic_mtx); *target += delta; mtx_unlock(&atomic_mtx); } void atomic_dec_64(volatile uint64_t *target) { mtx_lock(&atomic_mtx); *target -= 1; mtx_unlock(&atomic_mtx); } uint64_t atomic_swap_64(volatile uint64_t *a, uint64_t value) { uint64_t ret; mtx_lock(&atomic_mtx); ret = *a; *a = value; mtx_unlock(&atomic_mtx); return (ret); } uint64_t atomic_load_64(volatile uint64_t *a) { uint64_t ret; mtx_lock(&atomic_mtx); ret = *a; mtx_unlock(&atomic_mtx); return (ret); } uint64_t atomic_add_64_nv(volatile uint64_t *target, int64_t delta) { uint64_t newval; mtx_lock(&atomic_mtx); newval = (*target += delta); mtx_unlock(&atomic_mtx); return (newval); } uint64_t atomic_cas_64(volatile uint64_t *target, uint64_t cmp, uint64_t newval) { uint64_t oldval; mtx_lock(&atomic_mtx); oldval = *target; if (oldval == cmp) *target = newval; mtx_unlock(&atomic_mtx); return (oldval); } #endif diff --git a/module/os/freebsd/spl/spl_dtrace.c b/module/os/freebsd/spl/spl_dtrace.c index 6b2872bcc066..4b9cc65d641e 100644 --- a/module/os/freebsd/spl/spl_dtrace.c +++ b/module/os/freebsd/spl/spl_dtrace.c @@ -1,38 +1,35 @@ /* * Copyright 2014 The FreeBSD Project. * All rights reserved. * * This software was developed by Steven Hartland. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include /* CSTYLED */ SDT_PROBE_DEFINE1(sdt, , , set__error, "int"); diff --git a/module/os/freebsd/spl/spl_kmem.c b/module/os/freebsd/spl/spl_kmem.c index ca9a677567d9..95af6200cd01 100644 --- a/module/os/freebsd/spl/spl_kmem.c +++ b/module/os/freebsd/spl/spl_kmem.c @@ -1,352 +1,349 @@ /* * Copyright (c) 2006-2007 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KMEM_DEBUG #include #include #endif #ifdef _KERNEL MALLOC_DEFINE(M_SOLARIS, "solaris", "Solaris"); #else #define malloc(size, type, flags) malloc(size) #define free(addr, type) free(addr) #endif #ifdef KMEM_DEBUG struct kmem_item { struct stack stack; LIST_ENTRY(kmem_item) next; }; static LIST_HEAD(, kmem_item) kmem_items; static struct mtx kmem_items_mtx; MTX_SYSINIT(kmem_items_mtx, &kmem_items_mtx, "kmem_items", MTX_DEF); #endif /* KMEM_DEBUG */ #include void * zfs_kmem_alloc(size_t size, int kmflags) { void *p; #ifdef KMEM_DEBUG struct kmem_item *i; size += sizeof (struct kmem_item); #endif p = malloc(MAX(size, 16), M_SOLARIS, kmflags); #ifndef _KERNEL if (kmflags & KM_SLEEP) assert(p != NULL); #endif #ifdef KMEM_DEBUG if (p != NULL) { i = p; p = (uint8_t *)p + sizeof (struct kmem_item); stack_save(&i->stack); mtx_lock(&kmem_items_mtx); LIST_INSERT_HEAD(&kmem_items, i, next); mtx_unlock(&kmem_items_mtx); } #endif return (p); } void zfs_kmem_free(void *buf, size_t size __unused) { #ifdef KMEM_DEBUG if (buf == NULL) { printf("%s: attempt to free NULL\n", __func__); return; } struct kmem_item *i; buf = (uint8_t *)buf - sizeof (struct kmem_item); mtx_lock(&kmem_items_mtx); LIST_FOREACH(i, &kmem_items, next) { if (i == buf) break; } ASSERT3P(i, !=, NULL); LIST_REMOVE(i, next); mtx_unlock(&kmem_items_mtx); memset(buf, 0xDC, MAX(size, 16)); #endif free(buf, M_SOLARIS); } static uint64_t kmem_size_val; static void kmem_size_init(void *unused __unused) { kmem_size_val = (uint64_t)vm_cnt.v_page_count * PAGE_SIZE; if (kmem_size_val > vm_kmem_size) kmem_size_val = vm_kmem_size; } SYSINIT(kmem_size_init, SI_SUB_KMEM, SI_ORDER_ANY, kmem_size_init, NULL); uint64_t kmem_size(void) { return (kmem_size_val); } static int kmem_std_constructor(void *mem, int size __unused, void *private, int flags) { struct kmem_cache *cache = private; return (cache->kc_constructor(mem, cache->kc_private, flags)); } static void kmem_std_destructor(void *mem, int size __unused, void *private) { struct kmem_cache *cache = private; cache->kc_destructor(mem, cache->kc_private); } kmem_cache_t * kmem_cache_create(const char *name, size_t bufsize, size_t align, int (*constructor)(void *, void *, int), void (*destructor)(void *, void *), void (*reclaim)(void *) __unused, void *private, vmem_t *vmp, int cflags) { kmem_cache_t *cache; ASSERT3P(vmp, ==, NULL); cache = kmem_alloc(sizeof (*cache), KM_SLEEP); strlcpy(cache->kc_name, name, sizeof (cache->kc_name)); cache->kc_constructor = constructor; cache->kc_destructor = destructor; cache->kc_private = private; #if defined(_KERNEL) && !defined(KMEM_DEBUG) cache->kc_zone = uma_zcreate(cache->kc_name, bufsize, constructor != NULL ? kmem_std_constructor : NULL, destructor != NULL ? kmem_std_destructor : NULL, NULL, NULL, align > 0 ? align - 1 : 0, cflags); #else cache->kc_size = bufsize; #endif return (cache); } void kmem_cache_destroy(kmem_cache_t *cache) { #if defined(_KERNEL) && !defined(KMEM_DEBUG) uma_zdestroy(cache->kc_zone); #endif kmem_free(cache, sizeof (*cache)); } void * kmem_cache_alloc(kmem_cache_t *cache, int flags) { #if defined(_KERNEL) && !defined(KMEM_DEBUG) return (uma_zalloc_arg(cache->kc_zone, cache, flags)); #else void *p; p = kmem_alloc(cache->kc_size, flags); if (p != NULL && cache->kc_constructor != NULL) kmem_std_constructor(p, cache->kc_size, cache, flags); return (p); #endif } void kmem_cache_free(kmem_cache_t *cache, void *buf) { #if defined(_KERNEL) && !defined(KMEM_DEBUG) uma_zfree_arg(cache->kc_zone, buf, cache); #else if (cache->kc_destructor != NULL) kmem_std_destructor(buf, cache->kc_size, cache); kmem_free(buf, cache->kc_size); #endif } /* * Allow our caller to determine if there are running reaps. * * This call is very conservative and may return B_TRUE even when * reaping activity isn't active. If it returns B_FALSE, then reaping * activity is definitely inactive. */ boolean_t kmem_cache_reap_active(void) { return (B_FALSE); } /* * Reap (almost) everything soon. * * Note: this does not wait for the reap-tasks to complete. Caller * should use kmem_cache_reap_active() (above) and/or moderation to * avoid scheduling too many reap-tasks. */ #ifdef _KERNEL void kmem_cache_reap_soon(kmem_cache_t *cache) { #ifndef KMEM_DEBUG #if __FreeBSD_version >= 1300043 uma_zone_reclaim(cache->kc_zone, UMA_RECLAIM_DRAIN); #else zone_drain(cache->kc_zone); #endif #endif } void kmem_reap(void) { #if __FreeBSD_version >= 1300043 uma_reclaim(UMA_RECLAIM_TRIM); #else uma_reclaim(); #endif } #else void kmem_cache_reap_soon(kmem_cache_t *cache __unused) { } void kmem_reap(void) { } #endif int kmem_debugging(void) { return (0); } void * calloc(size_t n, size_t s) { return (kmem_zalloc(n * s, KM_NOSLEEP)); } char * kmem_vasprintf(const char *fmt, va_list adx) { char *msg; va_list adx2; va_copy(adx2, adx); msg = kmem_alloc(vsnprintf(NULL, 0, fmt, adx) + 1, KM_SLEEP); (void) vsprintf(msg, fmt, adx2); va_end(adx2); return (msg); } #include #include #ifdef KMEM_DEBUG #error "KMEM_DEBUG not currently supported" #endif uint64_t spl_kmem_cache_inuse(kmem_cache_t *cache) { return (uma_zone_get_cur(cache->kc_zone)); } uint64_t spl_kmem_cache_entry_size(kmem_cache_t *cache) { return (cache->kc_zone->uz_size); } /* * Register a move callback for cache defragmentation. * XXX: Unimplemented but harmless to stub out for now. */ void spl_kmem_cache_set_move(kmem_cache_t *skc, kmem_cbrc_t (move)(void *, void *, size_t, void *)) { ASSERT3P(move, !=, NULL); } #ifdef KMEM_DEBUG void kmem_show(void *); void kmem_show(void *dummy __unused) { struct kmem_item *i; mtx_lock(&kmem_items_mtx); if (LIST_EMPTY(&kmem_items)) printf("KMEM_DEBUG: No leaked elements.\n"); else { printf("KMEM_DEBUG: Leaked elements:\n\n"); LIST_FOREACH(i, &kmem_items, next) { printf("address=%p\n", i); stack_print_ddb(&i->stack); printf("\n"); } } mtx_unlock(&kmem_items_mtx); } SYSUNINIT(sol_kmem, SI_SUB_CPU, SI_ORDER_FIRST, kmem_show, NULL); #endif /* KMEM_DEBUG */ diff --git a/module/os/freebsd/spl/spl_kstat.c b/module/os/freebsd/spl/spl_kstat.c index 43cd4da02e30..f657ef2a3acb 100644 --- a/module/os/freebsd/spl/spl_kstat.c +++ b/module/os/freebsd/spl/spl_kstat.c @@ -1,573 +1,570 @@ /* * Copyright (c) 2007 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Links to Illumos.org for more information on kstat function: * [1] https://illumos.org/man/1M/kstat * [2] https://illumos.org/man/9f/kstat_create */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_KSTAT, "kstat_data", "Kernel statistics"); SYSCTL_ROOT_NODE(OID_AUTO, kstat, CTLFLAG_RW, 0, "Kernel statistics"); void __kstat_set_raw_ops(kstat_t *ksp, int (*headers)(char *buf, size_t size), int (*data)(char *buf, size_t size, void *data), void *(*addr)(kstat_t *ksp, loff_t index)) { ksp->ks_raw_ops.headers = headers; ksp->ks_raw_ops.data = data; ksp->ks_raw_ops.addr = addr; } void __kstat_set_seq_raw_ops(kstat_t *ksp, int (*headers)(struct seq_file *f), int (*data)(char *buf, size_t size, void *data), void *(*addr)(kstat_t *ksp, loff_t index)) { ksp->ks_raw_ops.seq_headers = headers; ksp->ks_raw_ops.data = data; ksp->ks_raw_ops.addr = addr; } static int kstat_default_update(kstat_t *ksp, int rw) { ASSERT3P(ksp, !=, NULL); if (rw == KSTAT_WRITE) return (EACCES); return (0); } static int kstat_resize_raw(kstat_t *ksp) { if (ksp->ks_raw_bufsize == KSTAT_RAW_MAX) return (ENOMEM); free(ksp->ks_raw_buf, M_TEMP); ksp->ks_raw_bufsize = MIN(ksp->ks_raw_bufsize * 2, KSTAT_RAW_MAX); ksp->ks_raw_buf = malloc(ksp->ks_raw_bufsize, M_TEMP, M_WAITOK); return (0); } static void * kstat_raw_default_addr(kstat_t *ksp, loff_t n) { if (n == 0) return (ksp->ks_data); return (NULL); } static int kstat_sysctl(SYSCTL_HANDLER_ARGS) { kstat_t *ksp = arg1; kstat_named_t *ksent; uint64_t val; ksent = ksp->ks_data; /* Select the correct element */ ksent += arg2; /* Update the aggsums before reading */ (void) ksp->ks_update(ksp, KSTAT_READ); val = ksent->value.ui64; return (sysctl_handle_64(oidp, &val, 0, req)); } static int kstat_sysctl_string(SYSCTL_HANDLER_ARGS) { kstat_t *ksp = arg1; kstat_named_t *ksent = ksp->ks_data; char *val; uint32_t len = 0; /* Select the correct element */ ksent += arg2; /* Update the aggsums before reading */ (void) ksp->ks_update(ksp, KSTAT_READ); val = KSTAT_NAMED_STR_PTR(ksent); len = KSTAT_NAMED_STR_BUFLEN(ksent); val[len-1] = '\0'; return (sysctl_handle_string(oidp, val, len, req)); } static int kstat_sysctl_dataset(SYSCTL_HANDLER_ARGS) { kstat_t *ksp = arg1; kstat_named_t *ksent; kstat_named_t *ksent_ds; uint64_t val; char *ds_name; uint32_t ds_len = 0; ksent_ds = ksent = ksp->ks_data; ds_name = KSTAT_NAMED_STR_PTR(ksent_ds); ds_len = KSTAT_NAMED_STR_BUFLEN(ksent_ds); ds_name[ds_len-1] = '\0'; if (!zone_dataset_visible(ds_name, NULL)) { return (EPERM); } /* Select the correct element */ ksent += arg2; /* Update the aggsums before reading */ (void) ksp->ks_update(ksp, KSTAT_READ); val = ksent->value.ui64; return (sysctl_handle_64(oidp, &val, 0, req)); } static int kstat_sysctl_dataset_string(SYSCTL_HANDLER_ARGS) { kstat_t *ksp = arg1; kstat_named_t *ksent = ksp->ks_data; char *val; uint32_t len = 0; /* Select the correct element */ ksent += arg2; val = KSTAT_NAMED_STR_PTR(ksent); len = KSTAT_NAMED_STR_BUFLEN(ksent); val[len-1] = '\0'; if (!zone_dataset_visible(val, NULL)) { return (EPERM); } return (sysctl_handle_string(oidp, val, len, req)); } static int kstat_sysctl_io(SYSCTL_HANDLER_ARGS) { struct sbuf sb; kstat_t *ksp = arg1; kstat_io_t *kip = ksp->ks_data; int rc; sbuf_new_for_sysctl(&sb, NULL, 0, req); /* Update the aggsums before reading */ (void) ksp->ks_update(ksp, KSTAT_READ); /* though wlentime & friends are signed, they will never be negative */ sbuf_printf(&sb, "%-8llu %-8llu %-8u %-8u %-8llu %-8llu " "%-8llu %-8llu %-8llu %-8llu %-8u %-8u\n", kip->nread, kip->nwritten, kip->reads, kip->writes, kip->wtime, kip->wlentime, kip->wlastupdate, kip->rtime, kip->rlentime, kip->rlastupdate, kip->wcnt, kip->rcnt); rc = sbuf_finish(&sb); sbuf_delete(&sb); return (rc); } static int kstat_sysctl_raw(SYSCTL_HANDLER_ARGS) { struct sbuf sb; void *data; kstat_t *ksp = arg1; void *(*addr_op)(kstat_t *ksp, loff_t index); int n, has_header, rc = 0; sbuf_new_for_sysctl(&sb, NULL, PAGE_SIZE, req); if (ksp->ks_raw_ops.addr) addr_op = ksp->ks_raw_ops.addr; else addr_op = kstat_raw_default_addr; mutex_enter(ksp->ks_lock); /* Update the aggsums before reading */ (void) ksp->ks_update(ksp, KSTAT_READ); ksp->ks_raw_bufsize = PAGE_SIZE; ksp->ks_raw_buf = malloc(PAGE_SIZE, M_TEMP, M_WAITOK); n = 0; has_header = (ksp->ks_raw_ops.headers || ksp->ks_raw_ops.seq_headers); restart_headers: if (ksp->ks_raw_ops.headers) { rc = ksp->ks_raw_ops.headers( ksp->ks_raw_buf, ksp->ks_raw_bufsize); } else if (ksp->ks_raw_ops.seq_headers) { struct seq_file f; f.sf_buf = ksp->ks_raw_buf; f.sf_size = ksp->ks_raw_bufsize; rc = ksp->ks_raw_ops.seq_headers(&f); } if (has_header) { if (rc == ENOMEM && !kstat_resize_raw(ksp)) goto restart_headers; if (rc == 0) { sbuf_cat(&sb, "\n"); sbuf_cat(&sb, ksp->ks_raw_buf); } } while ((data = addr_op(ksp, n)) != NULL) { restart: if (ksp->ks_raw_ops.data) { rc = ksp->ks_raw_ops.data(ksp->ks_raw_buf, ksp->ks_raw_bufsize, data); if (rc == ENOMEM && !kstat_resize_raw(ksp)) goto restart; if (rc == 0) sbuf_cat(&sb, ksp->ks_raw_buf); } else { ASSERT3U(ksp->ks_ndata, ==, 1); sbuf_hexdump(&sb, ksp->ks_data, ksp->ks_data_size, NULL, 0); } n++; } free(ksp->ks_raw_buf, M_TEMP); mutex_exit(ksp->ks_lock); rc = sbuf_finish(&sb); sbuf_delete(&sb); return (rc); } kstat_t * __kstat_create(const char *module, int instance, const char *name, const char *class, uchar_t ks_type, uint_t ks_ndata, uchar_t flags) { char buf[KSTAT_STRLEN]; struct sysctl_oid *root; kstat_t *ksp; char *pool; KASSERT(instance == 0, ("instance=%d", instance)); if ((ks_type == KSTAT_TYPE_INTR) || (ks_type == KSTAT_TYPE_IO)) ASSERT3U(ks_ndata, ==, 1); if (class == NULL) class = "misc"; /* * Allocate the main structure. We don't need to keep a copy of * module in here, because it is only used for sysctl node creation * done in this function. */ ksp = malloc(sizeof (*ksp), M_KSTAT, M_WAITOK|M_ZERO); ksp->ks_crtime = gethrtime(); ksp->ks_snaptime = ksp->ks_crtime; ksp->ks_instance = instance; (void) strlcpy(ksp->ks_name, name, KSTAT_STRLEN); (void) strlcpy(ksp->ks_class, class, KSTAT_STRLEN); ksp->ks_type = ks_type; ksp->ks_flags = flags; ksp->ks_update = kstat_default_update; mutex_init(&ksp->ks_private_lock, NULL, MUTEX_DEFAULT, NULL); ksp->ks_lock = &ksp->ks_private_lock; switch (ksp->ks_type) { case KSTAT_TYPE_RAW: ksp->ks_ndata = 1; ksp->ks_data_size = ks_ndata; break; case KSTAT_TYPE_NAMED: ksp->ks_ndata = ks_ndata; ksp->ks_data_size = ks_ndata * sizeof (kstat_named_t); break; case KSTAT_TYPE_INTR: ksp->ks_ndata = ks_ndata; ksp->ks_data_size = ks_ndata * sizeof (kstat_intr_t); break; case KSTAT_TYPE_IO: ksp->ks_ndata = ks_ndata; ksp->ks_data_size = ks_ndata * sizeof (kstat_io_t); break; case KSTAT_TYPE_TIMER: ksp->ks_ndata = ks_ndata; ksp->ks_data_size = ks_ndata * sizeof (kstat_timer_t); break; default: panic("Undefined kstat type %d\n", ksp->ks_type); } if (ksp->ks_flags & KSTAT_FLAG_VIRTUAL) ksp->ks_data = NULL; else ksp->ks_data = kmem_zalloc(ksp->ks_data_size, KM_SLEEP); /* * Some kstats use a module name like "zfs/poolname" to distinguish a * set of kstats belonging to a specific pool. Split on '/' to add an * extra node for the pool name if needed. */ (void) strlcpy(buf, module, KSTAT_STRLEN); module = buf; pool = strchr(module, '/'); if (pool != NULL) *pool++ = '\0'; /* * Create sysctl tree for those statistics: * * kstat.[.].. */ sysctl_ctx_init(&ksp->ks_sysctl_ctx); root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, SYSCTL_STATIC_CHILDREN(_kstat), OID_AUTO, module, CTLFLAG_RW, 0, ""); if (root == NULL) { printf("%s: Cannot create kstat.%s tree!\n", __func__, module); sysctl_ctx_free(&ksp->ks_sysctl_ctx); free(ksp, M_KSTAT); return (NULL); } if (pool != NULL) { root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(root), OID_AUTO, pool, CTLFLAG_RW, 0, ""); if (root == NULL) { printf("%s: Cannot create kstat.%s.%s tree!\n", __func__, module, pool); sysctl_ctx_free(&ksp->ks_sysctl_ctx); free(ksp, M_KSTAT); return (NULL); } } root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(root), OID_AUTO, class, CTLFLAG_RW, 0, ""); if (root == NULL) { if (pool != NULL) printf("%s: Cannot create kstat.%s.%s.%s tree!\n", __func__, module, pool, class); else printf("%s: Cannot create kstat.%s.%s tree!\n", __func__, module, class); sysctl_ctx_free(&ksp->ks_sysctl_ctx); free(ksp, M_KSTAT); return (NULL); } if (ksp->ks_type == KSTAT_TYPE_NAMED) { root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(root), OID_AUTO, name, CTLFLAG_RW, 0, ""); if (root == NULL) { if (pool != NULL) printf("%s: Cannot create kstat.%s.%s.%s.%s " "tree!\n", __func__, module, pool, class, name); else printf("%s: Cannot create kstat.%s.%s.%s " "tree!\n", __func__, module, class, name); sysctl_ctx_free(&ksp->ks_sysctl_ctx); free(ksp, M_KSTAT); return (NULL); } } ksp->ks_sysctl_root = root; return (ksp); } static void kstat_install_named(kstat_t *ksp) { kstat_named_t *ksent; char *namelast; int typelast; ksent = ksp->ks_data; VERIFY((ksp->ks_flags & KSTAT_FLAG_VIRTUAL) || ksent != NULL); typelast = 0; namelast = NULL; for (int i = 0; i < ksp->ks_ndata; i++, ksent++) { if (ksent->data_type != 0) { typelast = ksent->data_type; namelast = ksent->name; } switch (typelast) { case KSTAT_DATA_CHAR: /* Not Implemented */ break; case KSTAT_DATA_INT32: SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(ksp->ks_sysctl_root), OID_AUTO, namelast, CTLTYPE_S32 | CTLFLAG_RD | CTLFLAG_MPSAFE, ksp, i, kstat_sysctl, "I", namelast); break; case KSTAT_DATA_UINT32: SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(ksp->ks_sysctl_root), OID_AUTO, namelast, CTLTYPE_U32 | CTLFLAG_RD | CTLFLAG_MPSAFE, ksp, i, kstat_sysctl, "IU", namelast); break; case KSTAT_DATA_INT64: SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(ksp->ks_sysctl_root), OID_AUTO, namelast, CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ksp, i, kstat_sysctl, "Q", namelast); break; case KSTAT_DATA_UINT64: if (strcmp(ksp->ks_class, "dataset") == 0) { SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(ksp->ks_sysctl_root), OID_AUTO, namelast, CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ksp, i, kstat_sysctl_dataset, "QU", namelast); } else { SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(ksp->ks_sysctl_root), OID_AUTO, namelast, CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ksp, i, kstat_sysctl, "QU", namelast); } break; case KSTAT_DATA_LONG: SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(ksp->ks_sysctl_root), OID_AUTO, namelast, CTLTYPE_LONG | CTLFLAG_RD | CTLFLAG_MPSAFE, ksp, i, kstat_sysctl, "L", namelast); break; case KSTAT_DATA_ULONG: SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(ksp->ks_sysctl_root), OID_AUTO, namelast, CTLTYPE_ULONG | CTLFLAG_RD | CTLFLAG_MPSAFE, ksp, i, kstat_sysctl, "LU", namelast); break; case KSTAT_DATA_STRING: if (strcmp(ksp->ks_class, "dataset") == 0) { SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(ksp->ks_sysctl_root), OID_AUTO, namelast, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, ksp, i, kstat_sysctl_dataset_string, "A", namelast); } else { SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(ksp->ks_sysctl_root), OID_AUTO, namelast, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, ksp, i, kstat_sysctl_string, "A", namelast); } break; default: panic("unsupported type: %d", typelast); } } } void kstat_install(kstat_t *ksp) { struct sysctl_oid *root; if (ksp->ks_ndata == UINT32_MAX) VERIFY3U(ksp->ks_type, ==, KSTAT_TYPE_RAW); switch (ksp->ks_type) { case KSTAT_TYPE_NAMED: return (kstat_install_named(ksp)); case KSTAT_TYPE_RAW: if (ksp->ks_raw_ops.data) { root = SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(ksp->ks_sysctl_root), OID_AUTO, ksp->ks_name, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE | CTLFLAG_SKIP, ksp, 0, kstat_sysctl_raw, "A", ksp->ks_name); } else { root = SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(ksp->ks_sysctl_root), OID_AUTO, ksp->ks_name, CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE | CTLFLAG_SKIP, ksp, 0, kstat_sysctl_raw, "", ksp->ks_name); } break; case KSTAT_TYPE_IO: root = SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(ksp->ks_sysctl_root), OID_AUTO, ksp->ks_name, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, ksp, 0, kstat_sysctl_io, "A", ksp->ks_name); break; case KSTAT_TYPE_TIMER: case KSTAT_TYPE_INTR: default: panic("unsupported kstat type %d\n", ksp->ks_type); } VERIFY3P(root, !=, NULL); ksp->ks_sysctl_root = root; } void kstat_delete(kstat_t *ksp) { sysctl_ctx_free(&ksp->ks_sysctl_ctx); ksp->ks_lock = NULL; mutex_destroy(&ksp->ks_private_lock); if (!(ksp->ks_flags & KSTAT_FLAG_VIRTUAL)) kmem_free(ksp->ks_data, ksp->ks_data_size); free(ksp, M_KSTAT); } diff --git a/module/os/freebsd/spl/spl_misc.c b/module/os/freebsd/spl/spl_misc.c index e3653167323b..a5fc996b6550 100644 --- a/module/os/freebsd/spl/spl_misc.c +++ b/module/os/freebsd/spl/spl_misc.c @@ -1,109 +1,106 @@ /* * Copyright (c) 2007 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include static struct opensolaris_utsname hw_utsname = { .machine = MACHINE }; utsname_t * utsname(void) { return (&hw_utsname); } static void opensolaris_utsname_init(void *arg) { hw_utsname.sysname = ostype; hw_utsname.nodename = prison0.pr_hostname; hw_utsname.release = osrelease; snprintf(hw_utsname.version, sizeof (hw_utsname.version), "%d", osreldate); } char * kmem_strdup(const char *s) { char *buf; buf = kmem_alloc(strlen(s) + 1, KM_SLEEP); strcpy(buf, s); return (buf); } int ddi_copyin(const void *from, void *to, size_t len, int flags) { /* Fake ioctl() issued by kernel, 'from' is a kernel address */ if (flags & FKIOCTL) { memcpy(to, from, len); return (0); } return (copyin(from, to, len)); } int ddi_copyout(const void *from, void *to, size_t len, int flags) { /* Fake ioctl() issued by kernel, 'from' is a kernel address */ if (flags & FKIOCTL) { memcpy(to, from, len); return (0); } return (copyout(from, to, len)); } void spl_panic(const char *file, const char *func, int line, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vpanic(fmt, ap); va_end(ap); } SYSINIT(opensolaris_utsname_init, SI_SUB_TUNABLES, SI_ORDER_ANY, opensolaris_utsname_init, NULL); diff --git a/module/os/freebsd/spl/spl_policy.c b/module/os/freebsd/spl/spl_policy.c index 5ecd3d310361..f2dd7c8e7f8a 100644 --- a/module/os/freebsd/spl/spl_policy.c +++ b/module/os/freebsd/spl/spl_policy.c @@ -1,438 +1,435 @@ /* * Copyright (c) 2007 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include #include int secpolicy_nfs(cred_t *cr) { return (spl_priv_check_cred(cr, PRIV_NFS_DAEMON)); } int secpolicy_zfs(cred_t *cr) { return (spl_priv_check_cred(cr, PRIV_VFS_MOUNT)); } int secpolicy_zfs_proc(cred_t *cr, proc_t *proc) { return (spl_priv_check_cred(cr, PRIV_VFS_MOUNT)); } int secpolicy_sys_config(cred_t *cr, int checkonly __unused) { return (spl_priv_check_cred(cr, PRIV_ZFS_POOL_CONFIG)); } int secpolicy_zinject(cred_t *cr) { return (spl_priv_check_cred(cr, PRIV_ZFS_INJECT)); } int secpolicy_fs_unmount(cred_t *cr, struct mount *vfsp __unused) { return (spl_priv_check_cred(cr, PRIV_VFS_UNMOUNT)); } int secpolicy_fs_owner(struct mount *mp, cred_t *cr) { if (zfs_super_owner) { if (cr->cr_uid == mp->mnt_cred->cr_uid && cr->cr_prison == mp->mnt_cred->cr_prison) { return (0); } } return (EPERM); } /* * This check is done in kern_link(), so we could just return 0 here. */ extern int hardlink_check_uid; int secpolicy_basic_link(vnode_t *vp, cred_t *cr) { if (!hardlink_check_uid) return (0); if (secpolicy_fs_owner(vp->v_mount, cr) == 0) return (0); return (spl_priv_check_cred(cr, PRIV_VFS_LINK)); } int secpolicy_vnode_stky_modify(cred_t *cr) { return (EPERM); } int secpolicy_vnode_remove(vnode_t *vp, cred_t *cr) { if (secpolicy_fs_owner(vp->v_mount, cr) == 0) return (0); return (spl_priv_check_cred(cr, PRIV_VFS_ADMIN)); } int secpolicy_vnode_access(cred_t *cr, vnode_t *vp, uid_t owner, accmode_t accmode) { if (secpolicy_fs_owner(vp->v_mount, cr) == 0) return (0); if ((accmode & VREAD) && spl_priv_check_cred(cr, PRIV_VFS_READ) != 0) return (EACCES); if ((accmode & VWRITE) && spl_priv_check_cred(cr, PRIV_VFS_WRITE) != 0) { return (EACCES); } if (accmode & VEXEC) { if (vp->v_type == VDIR) { if (spl_priv_check_cred(cr, PRIV_VFS_LOOKUP) != 0) return (EACCES); } else { if (spl_priv_check_cred(cr, PRIV_VFS_EXEC) != 0) return (EACCES); } } return (0); } /* * Like secpolicy_vnode_access() but we get the actual wanted mode and the * current mode of the file, not the missing bits. */ int secpolicy_vnode_access2(cred_t *cr, vnode_t *vp, uid_t owner, accmode_t curmode, accmode_t wantmode) { accmode_t mode; mode = ~curmode & wantmode; if (mode == 0) return (0); return (secpolicy_vnode_access(cr, vp, owner, mode)); } int secpolicy_vnode_any_access(cred_t *cr, vnode_t *vp, uid_t owner) { static int privs[] = { PRIV_VFS_ADMIN, PRIV_VFS_READ, PRIV_VFS_WRITE, PRIV_VFS_EXEC, PRIV_VFS_LOOKUP }; int i; if (secpolicy_fs_owner(vp->v_mount, cr) == 0) return (0); /* Same as secpolicy_vnode_setdac */ if (owner == cr->cr_uid) return (0); for (i = 0; i < sizeof (privs)/sizeof (int); i++) { int priv; switch (priv = privs[i]) { case PRIV_VFS_EXEC: if (vp->v_type == VDIR) continue; break; case PRIV_VFS_LOOKUP: if (vp->v_type != VDIR) continue; break; } if (spl_priv_check_cred(cr, priv) == 0) return (0); } return (EPERM); } int secpolicy_vnode_setdac(vnode_t *vp, cred_t *cr, uid_t owner) { if (owner == cr->cr_uid) return (0); if (secpolicy_fs_owner(vp->v_mount, cr) == 0) return (0); return (spl_priv_check_cred(cr, PRIV_VFS_ADMIN)); } int secpolicy_vnode_setattr(cred_t *cr, vnode_t *vp, struct vattr *vap, const struct vattr *ovap, int flags, int unlocked_access(void *, int, cred_t *), void *node) { int mask = vap->va_mask; int error; if (mask & AT_SIZE) { if (vp->v_type == VDIR) return (EISDIR); error = unlocked_access(node, VWRITE, cr); if (error) return (error); } if (mask & AT_MODE) { /* * If not the owner of the file then check privilege * for two things: the privilege to set the mode at all * and, if we're setting setuid, we also need permissions * to add the set-uid bit, if we're not the owner. * In the specific case of creating a set-uid root * file, we need even more permissions. */ error = secpolicy_vnode_setdac(vp, cr, ovap->va_uid); if (error) return (error); error = secpolicy_setid_setsticky_clear(vp, vap, ovap, cr); if (error) return (error); } else { vap->va_mode = ovap->va_mode; } if (mask & (AT_UID | AT_GID)) { error = secpolicy_vnode_setdac(vp, cr, ovap->va_uid); if (error) return (error); /* * To change the owner of a file, or change the group of * a file to a group of which we are not a member, the * caller must have privilege. */ if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) || ((mask & AT_GID) && vap->va_gid != ovap->va_gid && !groupmember(vap->va_gid, cr))) { if (secpolicy_fs_owner(vp->v_mount, cr) != 0) { error = spl_priv_check_cred(cr, PRIV_VFS_CHOWN); if (error) return (error); } } if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) || ((mask & AT_GID) && vap->va_gid != ovap->va_gid)) { secpolicy_setid_clear(vap, vp, cr); } } if (mask & (AT_ATIME | AT_MTIME)) { /* * From utimes(2): * If times is NULL, ... The caller must be the owner of * the file, have permission to write the file, or be the * super-user. * If times is non-NULL, ... The caller must be the owner of * the file or be the super-user. */ error = secpolicy_vnode_setdac(vp, cr, ovap->va_uid); if (error && (vap->va_vaflags & VA_UTIMES_NULL)) error = unlocked_access(node, VWRITE, cr); if (error) return (error); } return (0); } int secpolicy_vnode_create_gid(cred_t *cr) { return (EPERM); } int secpolicy_vnode_setids_setgids(vnode_t *vp, cred_t *cr, gid_t gid) { if (groupmember(gid, cr)) return (0); if (secpolicy_fs_owner(vp->v_mount, cr) == 0) return (0); return (spl_priv_check_cred(cr, PRIV_VFS_SETGID)); } int secpolicy_vnode_setid_retain(znode_t *zp, cred_t *cr, boolean_t issuidroot __unused) { if (secpolicy_fs_owner(ZTOV(zp)->v_mount, cr) == 0) return (0); return (spl_priv_check_cred(cr, PRIV_VFS_RETAINSUGID)); } void secpolicy_setid_clear(struct vattr *vap, vnode_t *vp, cred_t *cr) { if (secpolicy_fs_owner(vp->v_mount, cr) == 0) return; if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0) { if (spl_priv_check_cred(cr, PRIV_VFS_RETAINSUGID)) { vap->va_mask |= AT_MODE; vap->va_mode &= ~(S_ISUID|S_ISGID); } } } int secpolicy_setid_setsticky_clear(vnode_t *vp, struct vattr *vap, const struct vattr *ovap, cred_t *cr) { int error; if (secpolicy_fs_owner(vp->v_mount, cr) == 0) return (0); /* * Privileged processes may set the sticky bit on non-directories, * as well as set the setgid bit on a file with a group that the process * is not a member of. Both of these are allowed in jail(8). */ if (vp->v_type != VDIR && (vap->va_mode & S_ISTXT)) { if (spl_priv_check_cred(cr, PRIV_VFS_STICKYFILE)) return (EFTYPE); } /* * Check for privilege if attempting to set the * group-id bit. */ if ((vap->va_mode & S_ISGID) != 0) { error = secpolicy_vnode_setids_setgids(vp, cr, ovap->va_gid); if (error) return (error); } /* * Deny setting setuid if we are not the file owner. */ if ((vap->va_mode & S_ISUID) && ovap->va_uid != cr->cr_uid) { error = spl_priv_check_cred(cr, PRIV_VFS_ADMIN); if (error) return (error); } return (0); } int secpolicy_fs_mount(cred_t *cr, vnode_t *mvp, struct mount *vfsp) { return (spl_priv_check_cred(cr, PRIV_VFS_MOUNT)); } int secpolicy_vnode_owner(vnode_t *vp, cred_t *cr, uid_t owner) { if (owner == cr->cr_uid) return (0); if (secpolicy_fs_owner(vp->v_mount, cr) == 0) return (0); /* XXX: vfs_suser()? */ return (spl_priv_check_cred(cr, PRIV_VFS_MOUNT_OWNER)); } int secpolicy_vnode_chown(vnode_t *vp, cred_t *cr, uid_t owner) { if (secpolicy_fs_owner(vp->v_mount, cr) == 0) return (0); return (spl_priv_check_cred(cr, PRIV_VFS_CHOWN)); } void secpolicy_fs_mount_clearopts(cred_t *cr, struct mount *vfsp) { if (spl_priv_check_cred(cr, PRIV_VFS_MOUNT_NONUSER) != 0) { MNT_ILOCK(vfsp); vfsp->vfs_flag |= VFS_NOSETUID | MNT_USER; vfs_clearmntopt(vfsp, MNTOPT_SETUID); vfs_setmntopt(vfsp, MNTOPT_NOSETUID, NULL, 0); MNT_IUNLOCK(vfsp); } } /* * Check privileges for setting xvattr attributes */ int secpolicy_xvattr(vnode_t *vp, xvattr_t *xvap, uid_t owner, cred_t *cr, vtype_t vtype) { if (secpolicy_fs_owner(vp->v_mount, cr) == 0) return (0); return (spl_priv_check_cred(cr, PRIV_VFS_SYSFLAGS)); } int secpolicy_smb(cred_t *cr) { return (spl_priv_check_cred(cr, PRIV_NETSMB)); } diff --git a/module/os/freebsd/spl/spl_procfs_list.c b/module/os/freebsd/spl/spl_procfs_list.c index e8448ce00686..77d33ee2e1f3 100644 --- a/module/os/freebsd/spl/spl_procfs_list.c +++ b/module/os/freebsd/spl/spl_procfs_list.c @@ -1,161 +1,158 @@ /* * Copyright (c) 2020 iXsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include typedef struct procfs_list_iter { procfs_list_t *pli_pl; void *pli_elt; } pli_t; void seq_printf(struct seq_file *f, const char *fmt, ...) { va_list adx; va_start(adx, fmt); (void) vsnprintf(f->sf_buf, f->sf_size, fmt, adx); va_end(adx); } static int procfs_list_update(kstat_t *ksp, int rw) { procfs_list_t *pl = ksp->ks_private; if (rw == KSTAT_WRITE) pl->pl_clear(pl); return (0); } static int procfs_list_data(char *buf, size_t size, void *data) { pli_t *p; void *elt; procfs_list_t *pl; struct seq_file f; p = data; pl = p->pli_pl; elt = p->pli_elt; free(p, M_TEMP); f.sf_buf = buf; f.sf_size = size; return (pl->pl_show(&f, elt)); } static void * procfs_list_addr(kstat_t *ksp, loff_t n) { procfs_list_t *pl = ksp->ks_private; void *elt = ksp->ks_private1; pli_t *p = NULL; if (n == 0) ksp->ks_private1 = list_head(&pl->pl_list); else if (elt) ksp->ks_private1 = list_next(&pl->pl_list, elt); if (ksp->ks_private1) { p = malloc(sizeof (*p), M_TEMP, M_WAITOK); p->pli_pl = pl; p->pli_elt = ksp->ks_private1; } return (p); } void procfs_list_install(const char *module, const char *submodule, const char *name, mode_t mode, procfs_list_t *procfs_list, int (*show)(struct seq_file *f, void *p), int (*show_header)(struct seq_file *f), int (*clear)(procfs_list_t *procfs_list), size_t procfs_list_node_off) { kstat_t *procfs_kstat; mutex_init(&procfs_list->pl_lock, NULL, MUTEX_DEFAULT, NULL); list_create(&procfs_list->pl_list, procfs_list_node_off + sizeof (procfs_list_node_t), procfs_list_node_off + offsetof(procfs_list_node_t, pln_link)); procfs_list->pl_show = show; procfs_list->pl_show_header = show_header; procfs_list->pl_clear = clear; procfs_list->pl_next_id = 1; procfs_list->pl_node_offset = procfs_list_node_off; procfs_kstat = kstat_create(module, 0, name, submodule, KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); if (procfs_kstat) { procfs_kstat->ks_lock = &procfs_list->pl_lock; procfs_kstat->ks_ndata = UINT32_MAX; procfs_kstat->ks_private = procfs_list; procfs_kstat->ks_update = procfs_list_update; kstat_set_seq_raw_ops(procfs_kstat, show_header, procfs_list_data, procfs_list_addr); kstat_install(procfs_kstat); procfs_list->pl_private = procfs_kstat; } } void procfs_list_uninstall(procfs_list_t *procfs_list) {} void procfs_list_destroy(procfs_list_t *procfs_list) { ASSERT(list_is_empty(&procfs_list->pl_list)); kstat_delete(procfs_list->pl_private); list_destroy(&procfs_list->pl_list); mutex_destroy(&procfs_list->pl_lock); } #define NODE_ID(procfs_list, obj) \ (((procfs_list_node_t *)(((char *)obj) + \ (procfs_list)->pl_node_offset))->pln_id) void procfs_list_add(procfs_list_t *procfs_list, void *p) { ASSERT(MUTEX_HELD(&procfs_list->pl_lock)); NODE_ID(procfs_list, p) = procfs_list->pl_next_id++; list_insert_tail(&procfs_list->pl_list, p); } diff --git a/module/os/freebsd/spl/spl_sunddi.c b/module/os/freebsd/spl/spl_sunddi.c index 2a3c027c9389..4c97c9f12caf 100644 --- a/module/os/freebsd/spl/spl_sunddi.c +++ b/module/os/freebsd/spl/spl_sunddi.c @@ -1,62 +1,59 @@ /* * Copyright (c) 2010 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include int ddi_strtol(const char *str, char **nptr, int base, long *result) { *result = strtol(str, nptr, base); return (0); } int ddi_strtoull(const char *str, char **nptr, int base, unsigned long long *result) { *result = (unsigned long long)strtouq(str, nptr, base); return (0); } int ddi_strtoll(const char *str, char **nptr, int base, long long *result) { *result = (long long)strtoq(str, nptr, base); return (0); } diff --git a/module/os/freebsd/spl/spl_sysevent.c b/module/os/freebsd/spl/spl_sysevent.c index 4a2d02350f62..dc5ed81057b8 100644 --- a/module/os/freebsd/spl/spl_sysevent.c +++ b/module/os/freebsd/spl/spl_sysevent.c @@ -1,272 +1,269 @@ /* * Copyright (c) 2010 Pawel Jakub Dawidek * Copyright (c) 2020 iXsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int log_sysevent(nvlist_t *event) { struct sbuf *sb; const char *type; char typestr[128]; nvpair_t *elem = NULL; sb = sbuf_new_auto(); if (sb == NULL) return (ENOMEM); type = NULL; while ((elem = nvlist_next_nvpair(event, elem)) != NULL) { switch (nvpair_type(elem)) { case DATA_TYPE_BOOLEAN: { boolean_t value; (void) nvpair_value_boolean_value(elem, &value); sbuf_printf(sb, " %s=%s", nvpair_name(elem), value ? "true" : "false"); break; } case DATA_TYPE_UINT8: { uint8_t value; (void) nvpair_value_uint8(elem, &value); sbuf_printf(sb, " %s=%hhu", nvpair_name(elem), value); break; } case DATA_TYPE_INT32: { int32_t value; (void) nvpair_value_int32(elem, &value); sbuf_printf(sb, " %s=%jd", nvpair_name(elem), (intmax_t)value); break; } case DATA_TYPE_UINT32: { uint32_t value; (void) nvpair_value_uint32(elem, &value); sbuf_printf(sb, " %s=%ju", nvpair_name(elem), (uintmax_t)value); break; } case DATA_TYPE_INT64: { int64_t value; (void) nvpair_value_int64(elem, &value); sbuf_printf(sb, " %s=%jd", nvpair_name(elem), (intmax_t)value); break; } case DATA_TYPE_UINT64: { uint64_t value; (void) nvpair_value_uint64(elem, &value); sbuf_printf(sb, " %s=%ju", nvpair_name(elem), (uintmax_t)value); break; } case DATA_TYPE_STRING: { const char *value; (void) nvpair_value_string(elem, &value); sbuf_printf(sb, " %s=%s", nvpair_name(elem), value); if (strcmp(FM_CLASS, nvpair_name(elem)) == 0) type = value; break; } case DATA_TYPE_UINT8_ARRAY: { uint8_t *value; uint_t ii, nelem; (void) nvpair_value_uint8_array(elem, &value, &nelem); sbuf_printf(sb, " %s=", nvpair_name(elem)); for (ii = 0; ii < nelem; ii++) sbuf_printf(sb, "%02hhx", value[ii]); break; } case DATA_TYPE_UINT16_ARRAY: { uint16_t *value; uint_t ii, nelem; (void) nvpair_value_uint16_array(elem, &value, &nelem); sbuf_printf(sb, " %s=", nvpair_name(elem)); for (ii = 0; ii < nelem; ii++) sbuf_printf(sb, "%04hx", value[ii]); break; } case DATA_TYPE_UINT32_ARRAY: { uint32_t *value; uint_t ii, nelem; (void) nvpair_value_uint32_array(elem, &value, &nelem); sbuf_printf(sb, " %s=", nvpair_name(elem)); for (ii = 0; ii < nelem; ii++) sbuf_printf(sb, "%08jx", (uintmax_t)value[ii]); break; } case DATA_TYPE_INT64_ARRAY: { int64_t *value; uint_t ii, nelem; (void) nvpair_value_int64_array(elem, &value, &nelem); sbuf_printf(sb, " %s=", nvpair_name(elem)); for (ii = 0; ii < nelem; ii++) sbuf_printf(sb, "%016lld", (long long)value[ii]); break; } case DATA_TYPE_UINT64_ARRAY: { uint64_t *value; uint_t ii, nelem; (void) nvpair_value_uint64_array(elem, &value, &nelem); sbuf_printf(sb, " %s=", nvpair_name(elem)); for (ii = 0; ii < nelem; ii++) sbuf_printf(sb, "%016jx", (uintmax_t)value[ii]); break; } case DATA_TYPE_STRING_ARRAY: { const char **strarr; uint_t ii, nelem; (void) nvpair_value_string_array(elem, &strarr, &nelem); for (ii = 0; ii < nelem; ii++) { if (strarr[ii] == NULL) { sbuf_printf(sb, " "); continue; } sbuf_printf(sb, " %s", strarr[ii]); if (strcmp(FM_CLASS, strarr[ii]) == 0) type = strarr[ii]; } break; } case DATA_TYPE_NVLIST: /* XXX - requires recursing in log_sysevent */ break; default: printf("%s: type %d is not implemented\n", __func__, nvpair_type(elem)); break; } } if (sbuf_finish(sb) != 0) { sbuf_delete(sb); return (ENOMEM); } if (type == NULL) type = ""; if (strncmp(type, "ESC_ZFS_", 8) == 0) { snprintf(typestr, sizeof (typestr), "misc.fs.zfs.%s", type + 8); type = typestr; } devctl_notify("ZFS", "ZFS", type, sbuf_data(sb)); sbuf_delete(sb); return (0); } static void sysevent_worker(void *arg __unused) { zfs_zevent_t *ze; nvlist_t *event; uint64_t dropped = 0; uint64_t dst_size; int error; zfs_zevent_init(&ze); for (;;) { dst_size = 131072; dropped = 0; event = NULL; error = zfs_zevent_next(ze, &event, &dst_size, &dropped); if (error) { error = zfs_zevent_wait(ze); if (error == ESHUTDOWN) break; } else { VERIFY3P(event, !=, NULL); log_sysevent(event); nvlist_free(event); } } /* * We avoid zfs_zevent_destroy() here because we're otherwise racing * against fm_fini() destroying the zevent_lock. zfs_zevent_destroy() * will currently only clear `ze->ze_zevent` from an event list then * free `ze`, so just inline the free() here -- events have already * been drained. */ VERIFY3P(ze->ze_zevent, ==, NULL); kmem_free(ze, sizeof (zfs_zevent_t)); kthread_exit(); } void ddi_sysevent_init(void) { kproc_kthread_add(sysevent_worker, NULL, &system_proc, NULL, 0, 0, "zfskern", "sysevent"); } diff --git a/module/os/freebsd/spl/spl_taskq.c b/module/os/freebsd/spl/spl_taskq.c index cc276e568320..67c0a4c94134 100644 --- a/module/os/freebsd/spl/spl_taskq.c +++ b/module/os/freebsd/spl/spl_taskq.c @@ -1,535 +1,532 @@ /* * Copyright (c) 2009 Pawel Jakub Dawidek * All rights reserved. * * Copyright (c) 2012 Spectra Logic Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #if defined(__i386__) || defined(__amd64__) || defined(__aarch64__) #include #endif #include #if __FreeBSD_version < 1201522 #define taskqueue_start_threads_in_proc(tqp, count, pri, proc, name, ...) \ taskqueue_start_threads(tqp, count, pri, name, __VA_ARGS__) #endif static uint_t taskq_tsd; static uma_zone_t taskq_zone; /* * Global system-wide dynamic task queue available for all consumers. This * taskq is not intended for long-running tasks; instead, a dedicated taskq * should be created. */ taskq_t *system_taskq = NULL; taskq_t *system_delay_taskq = NULL; taskq_t *dynamic_taskq = NULL; proc_t *system_proc; static MALLOC_DEFINE(M_TASKQ, "taskq", "taskq structures"); static LIST_HEAD(tqenthashhead, taskq_ent) *tqenthashtbl; static unsigned long tqenthash; static unsigned long tqenthashlock; static struct sx *tqenthashtbl_lock; static taskqid_t tqidnext; #define TQIDHASH(tqid) (&tqenthashtbl[(tqid) & tqenthash]) #define TQIDHASHLOCK(tqid) (&tqenthashtbl_lock[((tqid) & tqenthashlock)]) #define NORMAL_TASK 0 #define TIMEOUT_TASK 1 static void system_taskq_init(void *arg) { int i; tsd_create(&taskq_tsd, NULL); tqenthashtbl = hashinit(mp_ncpus * 8, M_TASKQ, &tqenthash); tqenthashlock = (tqenthash + 1) / 8; if (tqenthashlock > 0) tqenthashlock--; tqenthashtbl_lock = malloc(sizeof (*tqenthashtbl_lock) * (tqenthashlock + 1), M_TASKQ, M_WAITOK | M_ZERO); for (i = 0; i < tqenthashlock + 1; i++) sx_init_flags(&tqenthashtbl_lock[i], "tqenthash", SX_DUPOK); taskq_zone = uma_zcreate("taskq_zone", sizeof (taskq_ent_t), NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0); system_taskq = taskq_create("system_taskq", mp_ncpus, minclsyspri, 0, 0, 0); system_delay_taskq = taskq_create("system_delay_taskq", mp_ncpus, minclsyspri, 0, 0, 0); } SYSINIT(system_taskq_init, SI_SUB_CONFIGURE, SI_ORDER_ANY, system_taskq_init, NULL); static void system_taskq_fini(void *arg) { int i; taskq_destroy(system_delay_taskq); taskq_destroy(system_taskq); uma_zdestroy(taskq_zone); tsd_destroy(&taskq_tsd); for (i = 0; i < tqenthashlock + 1; i++) sx_destroy(&tqenthashtbl_lock[i]); for (i = 0; i < tqenthash + 1; i++) VERIFY(LIST_EMPTY(&tqenthashtbl[i])); free(tqenthashtbl_lock, M_TASKQ); free(tqenthashtbl, M_TASKQ); } SYSUNINIT(system_taskq_fini, SI_SUB_CONFIGURE, SI_ORDER_ANY, system_taskq_fini, NULL); #ifdef __LP64__ static taskqid_t __taskq_genid(void) { taskqid_t tqid; /* * Assume a 64-bit counter will not wrap in practice. */ tqid = atomic_add_64_nv(&tqidnext, 1); VERIFY(tqid); return (tqid); } #else static taskqid_t __taskq_genid(void) { taskqid_t tqid; for (;;) { tqid = atomic_add_32_nv(&tqidnext, 1); if (__predict_true(tqid != 0)) break; } VERIFY(tqid); return (tqid); } #endif static taskq_ent_t * taskq_lookup(taskqid_t tqid) { taskq_ent_t *ent = NULL; if (tqid == 0) return (NULL); sx_slock(TQIDHASHLOCK(tqid)); LIST_FOREACH(ent, TQIDHASH(tqid), tqent_hash) { if (ent->tqent_id == tqid) break; } if (ent != NULL) refcount_acquire(&ent->tqent_rc); sx_sunlock(TQIDHASHLOCK(tqid)); return (ent); } static taskqid_t taskq_insert(taskq_ent_t *ent) { taskqid_t tqid = __taskq_genid(); ent->tqent_id = tqid; sx_xlock(TQIDHASHLOCK(tqid)); LIST_INSERT_HEAD(TQIDHASH(tqid), ent, tqent_hash); sx_xunlock(TQIDHASHLOCK(tqid)); return (tqid); } static void taskq_remove(taskq_ent_t *ent) { taskqid_t tqid = ent->tqent_id; if (tqid == 0) return; sx_xlock(TQIDHASHLOCK(tqid)); if (ent->tqent_id != 0) { LIST_REMOVE(ent, tqent_hash); ent->tqent_id = 0; } sx_xunlock(TQIDHASHLOCK(tqid)); } static void taskq_tsd_set(void *context) { taskq_t *tq = context; #if defined(__amd64__) || defined(__i386__) || defined(__aarch64__) if (context != NULL && tsd_get(taskq_tsd) == NULL) fpu_kern_thread(FPU_KERN_NORMAL); #endif tsd_set(taskq_tsd, tq); } static taskq_t * taskq_create_impl(const char *name, int nthreads, pri_t pri, proc_t *proc __maybe_unused, uint_t flags) { taskq_t *tq; if ((flags & TASKQ_THREADS_CPU_PCT) != 0) nthreads = MAX((mp_ncpus * nthreads) / 100, 1); tq = kmem_alloc(sizeof (*tq), KM_SLEEP); tq->tq_nthreads = nthreads; tq->tq_queue = taskqueue_create(name, M_WAITOK, taskqueue_thread_enqueue, &tq->tq_queue); taskqueue_set_callback(tq->tq_queue, TASKQUEUE_CALLBACK_TYPE_INIT, taskq_tsd_set, tq); taskqueue_set_callback(tq->tq_queue, TASKQUEUE_CALLBACK_TYPE_SHUTDOWN, taskq_tsd_set, NULL); (void) taskqueue_start_threads_in_proc(&tq->tq_queue, nthreads, pri, proc, "%s", name); return ((taskq_t *)tq); } taskq_t * taskq_create(const char *name, int nthreads, pri_t pri, int minalloc __unused, int maxalloc __unused, uint_t flags) { return (taskq_create_impl(name, nthreads, pri, system_proc, flags)); } taskq_t * taskq_create_proc(const char *name, int nthreads, pri_t pri, int minalloc __unused, int maxalloc __unused, proc_t *proc, uint_t flags) { return (taskq_create_impl(name, nthreads, pri, proc, flags)); } void taskq_destroy(taskq_t *tq) { taskqueue_free(tq->tq_queue); kmem_free(tq, sizeof (*tq)); } static void taskq_sync_assign(void *arg); typedef struct taskq_sync_arg { kthread_t *tqa_thread; kcondvar_t tqa_cv; kmutex_t tqa_lock; int tqa_ready; } taskq_sync_arg_t; static void taskq_sync_assign(void *arg) { taskq_sync_arg_t *tqa = arg; mutex_enter(&tqa->tqa_lock); tqa->tqa_thread = curthread; tqa->tqa_ready = 1; cv_signal(&tqa->tqa_cv); while (tqa->tqa_ready == 1) cv_wait(&tqa->tqa_cv, &tqa->tqa_lock); mutex_exit(&tqa->tqa_lock); } /* * Create a taskq with a specified number of pool threads. Allocate * and return an array of nthreads kthread_t pointers, one for each * thread in the pool. The array is not ordered and must be freed * by the caller. */ taskq_t * taskq_create_synced(const char *name, int nthreads, pri_t pri, int minalloc, int maxalloc, uint_t flags, kthread_t ***ktpp) { taskq_t *tq; taskq_sync_arg_t *tqs = kmem_zalloc(sizeof (*tqs) * nthreads, KM_SLEEP); kthread_t **kthreads = kmem_zalloc(sizeof (*kthreads) * nthreads, KM_SLEEP); flags &= ~(TASKQ_DYNAMIC | TASKQ_THREADS_CPU_PCT | TASKQ_DC_BATCH); tq = taskq_create(name, nthreads, minclsyspri, nthreads, INT_MAX, flags | TASKQ_PREPOPULATE); VERIFY(tq != NULL); VERIFY(tq->tq_nthreads == nthreads); /* spawn all syncthreads */ for (int i = 0; i < nthreads; i++) { cv_init(&tqs[i].tqa_cv, NULL, CV_DEFAULT, NULL); mutex_init(&tqs[i].tqa_lock, NULL, MUTEX_DEFAULT, NULL); (void) taskq_dispatch(tq, taskq_sync_assign, &tqs[i], TQ_FRONT); } /* wait on all syncthreads to start */ for (int i = 0; i < nthreads; i++) { mutex_enter(&tqs[i].tqa_lock); while (tqs[i].tqa_ready == 0) cv_wait(&tqs[i].tqa_cv, &tqs[i].tqa_lock); mutex_exit(&tqs[i].tqa_lock); } /* let all syncthreads resume, finish */ for (int i = 0; i < nthreads; i++) { mutex_enter(&tqs[i].tqa_lock); tqs[i].tqa_ready = 2; cv_broadcast(&tqs[i].tqa_cv); mutex_exit(&tqs[i].tqa_lock); } taskq_wait(tq); for (int i = 0; i < nthreads; i++) { kthreads[i] = tqs[i].tqa_thread; mutex_destroy(&tqs[i].tqa_lock); cv_destroy(&tqs[i].tqa_cv); } kmem_free(tqs, sizeof (*tqs) * nthreads); *ktpp = kthreads; return (tq); } int taskq_member(taskq_t *tq, kthread_t *thread) { return (taskqueue_member(tq->tq_queue, thread)); } taskq_t * taskq_of_curthread(void) { return (tsd_get(taskq_tsd)); } static void taskq_free(taskq_ent_t *task) { taskq_remove(task); if (refcount_release(&task->tqent_rc)) uma_zfree(taskq_zone, task); } int taskq_cancel_id(taskq_t *tq, taskqid_t tid) { uint32_t pend; int rc; taskq_ent_t *ent; if ((ent = taskq_lookup(tid)) == NULL) return (0); if (ent->tqent_type == NORMAL_TASK) { rc = taskqueue_cancel(tq->tq_queue, &ent->tqent_task, &pend); if (rc == EBUSY) taskqueue_drain(tq->tq_queue, &ent->tqent_task); } else { rc = taskqueue_cancel_timeout(tq->tq_queue, &ent->tqent_timeout_task, &pend); if (rc == EBUSY) { taskqueue_drain_timeout(tq->tq_queue, &ent->tqent_timeout_task); } } if (pend) { /* * Tasks normally free themselves when run, but here the task * was cancelled so it did not free itself. */ taskq_free(ent); } /* Free the extra reference we added with taskq_lookup. */ taskq_free(ent); return (rc); } static void taskq_run(void *arg, int pending) { taskq_ent_t *task = arg; if (pending == 0) return; task->tqent_func(task->tqent_arg); taskq_free(task); } taskqid_t taskq_dispatch_delay(taskq_t *tq, task_func_t func, void *arg, uint_t flags, clock_t expire_time) { taskq_ent_t *task; taskqid_t tqid; clock_t timo; int mflag; timo = expire_time - ddi_get_lbolt(); if (timo <= 0) return (taskq_dispatch(tq, func, arg, flags)); if ((flags & (TQ_SLEEP | TQ_NOQUEUE)) == TQ_SLEEP) mflag = M_WAITOK; else mflag = M_NOWAIT; task = uma_zalloc(taskq_zone, mflag); if (task == NULL) return (0); task->tqent_func = func; task->tqent_arg = arg; task->tqent_type = TIMEOUT_TASK; refcount_init(&task->tqent_rc, 1); tqid = taskq_insert(task); TIMEOUT_TASK_INIT(tq->tq_queue, &task->tqent_timeout_task, 0, taskq_run, task); taskqueue_enqueue_timeout(tq->tq_queue, &task->tqent_timeout_task, timo); return (tqid); } taskqid_t taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t flags) { taskq_ent_t *task; int mflag, prio; taskqid_t tqid; if ((flags & (TQ_SLEEP | TQ_NOQUEUE)) == TQ_SLEEP) mflag = M_WAITOK; else mflag = M_NOWAIT; /* * If TQ_FRONT is given, we want higher priority for this task, so it * can go at the front of the queue. */ prio = !!(flags & TQ_FRONT); task = uma_zalloc(taskq_zone, mflag); if (task == NULL) return (0); refcount_init(&task->tqent_rc, 1); task->tqent_func = func; task->tqent_arg = arg; task->tqent_type = NORMAL_TASK; tqid = taskq_insert(task); TASK_INIT(&task->tqent_task, prio, taskq_run, task); taskqueue_enqueue(tq->tq_queue, &task->tqent_task); return (tqid); } static void taskq_run_ent(void *arg, int pending) { taskq_ent_t *task = arg; if (pending == 0) return; task->tqent_func(task->tqent_arg); } void taskq_dispatch_ent(taskq_t *tq, task_func_t func, void *arg, uint32_t flags, taskq_ent_t *task) { /* * If TQ_FRONT is given, we want higher priority for this task, so it * can go at the front of the queue. */ task->tqent_task.ta_priority = !!(flags & TQ_FRONT); task->tqent_func = func; task->tqent_arg = arg; taskqueue_enqueue(tq->tq_queue, &task->tqent_task); } void taskq_init_ent(taskq_ent_t *task) { TASK_INIT(&task->tqent_task, 0, taskq_run_ent, task); task->tqent_func = NULL; task->tqent_arg = NULL; task->tqent_id = 0; task->tqent_type = NORMAL_TASK; task->tqent_rc = 0; } int taskq_empty_ent(taskq_ent_t *task) { return (task->tqent_task.ta_pending == 0); } void taskq_wait(taskq_t *tq) { taskqueue_quiesce(tq->tq_queue); } void taskq_wait_id(taskq_t *tq, taskqid_t tid) { taskq_ent_t *ent; if ((ent = taskq_lookup(tid)) == NULL) return; if (ent->tqent_type == NORMAL_TASK) taskqueue_drain(tq->tq_queue, &ent->tqent_task); else taskqueue_drain_timeout(tq->tq_queue, &ent->tqent_timeout_task); taskq_free(ent); } void taskq_wait_outstanding(taskq_t *tq, taskqid_t id __unused) { taskqueue_drain_all(tq->tq_queue); } diff --git a/module/os/freebsd/spl/spl_vfs.c b/module/os/freebsd/spl/spl_vfs.c index a07098afc5b4..d67bd7178735 100644 --- a/module/os/freebsd/spl/spl_vfs.c +++ b/module/os/freebsd/spl/spl_vfs.c @@ -1,279 +1,276 @@ /* * Copyright (c) 2006-2007 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DECLARE(M_MOUNT); void vfs_setmntopt(vfs_t *vfsp, const char *name, const char *arg, int flags __unused) { struct vfsopt *opt; size_t namesize; int locked; if (!(locked = mtx_owned(MNT_MTX(vfsp)))) MNT_ILOCK(vfsp); if (vfsp->mnt_opt == NULL) { void *opts; MNT_IUNLOCK(vfsp); opts = malloc(sizeof (*vfsp->mnt_opt), M_MOUNT, M_WAITOK); MNT_ILOCK(vfsp); if (vfsp->mnt_opt == NULL) { vfsp->mnt_opt = opts; TAILQ_INIT(vfsp->mnt_opt); } else { free(opts, M_MOUNT); } } MNT_IUNLOCK(vfsp); opt = malloc(sizeof (*opt), M_MOUNT, M_WAITOK); namesize = strlen(name) + 1; opt->name = malloc(namesize, M_MOUNT, M_WAITOK); strlcpy(opt->name, name, namesize); opt->pos = -1; opt->seen = 1; if (arg == NULL) { opt->value = NULL; opt->len = 0; } else { opt->len = strlen(arg) + 1; opt->value = malloc(opt->len, M_MOUNT, M_WAITOK); memcpy(opt->value, arg, opt->len); } MNT_ILOCK(vfsp); TAILQ_INSERT_TAIL(vfsp->mnt_opt, opt, link); if (!locked) MNT_IUNLOCK(vfsp); } void vfs_clearmntopt(vfs_t *vfsp, const char *name) { int locked; if (!(locked = mtx_owned(MNT_MTX(vfsp)))) MNT_ILOCK(vfsp); vfs_deleteopt(vfsp->mnt_opt, name); if (!locked) MNT_IUNLOCK(vfsp); } int vfs_optionisset(const vfs_t *vfsp, const char *opt, char **argp) { struct vfsoptlist *opts = vfsp->mnt_optnew; int error; if (opts == NULL) return (0); error = vfs_getopt(opts, opt, (void **)argp, NULL); return (error != 0 ? 0 : 1); } int mount_snapshot(kthread_t *td, vnode_t **vpp, const char *fstype, char *fspath, char *fspec, int fsflags) { struct vfsconf *vfsp; struct mount *mp; vnode_t *vp, *mvp; int error; ASSERT_VOP_ELOCKED(*vpp, "mount_snapshot"); vp = *vpp; *vpp = NULL; error = 0; /* * Be ultra-paranoid about making sure the type and fspath * variables will fit in our mp buffers, including the * terminating NUL. */ if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN) error = ENAMETOOLONG; if (error == 0 && (vfsp = vfs_byname_kld(fstype, td, &error)) == NULL) error = ENODEV; if (error == 0 && vp->v_type != VDIR) error = ENOTDIR; /* * We need vnode lock to protect v_mountedhere and vnode interlock * to protect v_iflag. */ if (error == 0) { VI_LOCK(vp); if ((vp->v_iflag & VI_MOUNT) == 0 && vp->v_mountedhere == NULL) vp->v_iflag |= VI_MOUNT; else error = EBUSY; VI_UNLOCK(vp); } if (error != 0) { vput(vp); return (error); } vn_seqc_write_begin(vp); VOP_UNLOCK1(vp); /* * Allocate and initialize the filesystem. * We don't want regular user that triggered snapshot mount to be able * to unmount it, so pass credentials of the parent mount. */ mp = vfs_mount_alloc(vp, vfsp, fspath, vp->v_mount->mnt_cred); mp->mnt_optnew = NULL; vfs_setmntopt(mp, "from", fspec, 0); mp->mnt_optnew = mp->mnt_opt; mp->mnt_opt = NULL; /* * Set the mount level flags. */ mp->mnt_flag = fsflags & MNT_UPDATEMASK; /* * Snapshots are always read-only. */ mp->mnt_flag |= MNT_RDONLY; /* * We don't want snapshots to allow access to vulnerable setuid * programs, so we turn off setuid when mounting snapshots. */ mp->mnt_flag |= MNT_NOSUID; /* * We don't want snapshots to be visible in regular * mount(8) and df(1) output. */ mp->mnt_flag |= MNT_IGNORE; error = VFS_MOUNT(mp); if (error != 0) { /* * Clear VI_MOUNT and decrement the use count "atomically", * under the vnode lock. This is not strictly required, * but makes it easier to reason about the life-cycle and * ownership of the covered vnode. */ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); VI_LOCK(vp); vp->v_iflag &= ~VI_MOUNT; VI_UNLOCK(vp); vn_seqc_write_end(vp); vput(vp); vfs_unbusy(mp); vfs_freeopts(mp->mnt_optnew); mp->mnt_vnodecovered = NULL; vfs_mount_destroy(mp); return (error); } if (mp->mnt_opt != NULL) vfs_freeopts(mp->mnt_opt); mp->mnt_opt = mp->mnt_optnew; (void) VFS_STATFS(mp, &mp->mnt_stat); /* * Prevent external consumers of mount options from reading * mnt_optnew. */ mp->mnt_optnew = NULL; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); #ifdef FREEBSD_NAMECACHE cache_purge(vp); #endif VI_LOCK(vp); vp->v_iflag &= ~VI_MOUNT; #ifdef VIRF_MOUNTPOINT vn_irflag_set_locked(vp, VIRF_MOUNTPOINT); #endif vp->v_mountedhere = mp; VI_UNLOCK(vp); /* Put the new filesystem on the mount list. */ mtx_lock(&mountlist_mtx); TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); mtx_unlock(&mountlist_mtx); vfs_event_signal(NULL, VQ_MOUNT, 0); if (VFS_ROOT(mp, LK_EXCLUSIVE, &mvp)) panic("mount: lost mount"); vn_seqc_write_end(vp); VOP_UNLOCK1(vp); #if __FreeBSD_version >= 1300048 vfs_op_exit(mp); #endif vfs_unbusy(mp); *vpp = mvp; return (0); } /* * Like vn_rele() except if we are going to call VOP_INACTIVE() then do it * asynchronously using a taskq. This can avoid deadlocks caused by re-entering * the file system as a result of releasing the vnode. Note, file systems * already have to handle the race where the vnode is incremented before the * inactive routine is called and does its locking. * * Warning: Excessive use of this routine can lead to performance problems. * This is because taskqs throttle back allocation if too many are created. */ void vn_rele_async(vnode_t *vp, taskq_t *taskq) { VERIFY3U(vp->v_usecount, >, 0); if (refcount_release_if_not_last(&vp->v_usecount)) { #if __FreeBSD_version < 1300045 vdrop(vp); #endif return; } VERIFY3U(taskq_dispatch((taskq_t *)taskq, (task_func_t *)vrele, vp, TQ_SLEEP), !=, 0); } diff --git a/module/os/freebsd/spl/spl_vm.c b/module/os/freebsd/spl/spl_vm.c index 739ddb05e895..e6f019cb9a46 100644 --- a/module/os/freebsd/spl/spl_vm.c +++ b/module/os/freebsd/spl/spl_vm.c @@ -1,75 +1,72 @@ /* * Copyright (c) 2013 EMC Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include #include #include const int zfs_vm_pagerret_bad = VM_PAGER_BAD; const int zfs_vm_pagerret_error = VM_PAGER_ERROR; const int zfs_vm_pagerret_ok = VM_PAGER_OK; const int zfs_vm_pagerput_sync = VM_PAGER_PUT_SYNC; const int zfs_vm_pagerput_inval = VM_PAGER_PUT_INVAL; void zfs_vmobject_assert_wlocked(vm_object_t object) { /* * This is not ideal because FILE/LINE used by assertions will not * be too helpful, but it must be an hard function for * compatibility reasons. */ VM_OBJECT_ASSERT_WLOCKED(object); } void zfs_vmobject_wlock(vm_object_t object) { VM_OBJECT_WLOCK(object); } void zfs_vmobject_wunlock(vm_object_t object) { VM_OBJECT_WUNLOCK(object); } diff --git a/module/os/freebsd/spl/spl_zlib.c b/module/os/freebsd/spl/spl_zlib.c index 8bd3bdedf268..6cfea889a272 100644 --- a/module/os/freebsd/spl/spl_zlib.c +++ b/module/os/freebsd/spl/spl_zlib.c @@ -1,237 +1,234 @@ /* * Copyright (c) 2020 iXsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #if __FreeBSD_version >= 1300041 #include #else #include #endif #include static void * zcalloc(void *opaque, uint_t items, uint_t size) { (void) opaque; return (malloc((size_t)items*size, M_SOLARIS, M_NOWAIT)); } static void zcfree(void *opaque, void *ptr) { (void) opaque; free(ptr, M_SOLARIS); } static int zlib_deflateInit(z_stream *stream, int level) { stream->zalloc = zcalloc; stream->opaque = NULL; stream->zfree = zcfree; return (deflateInit(stream, level)); } static int zlib_deflate(z_stream *stream, int flush) { return (deflate(stream, flush)); } static int zlib_deflateEnd(z_stream *stream) { return (deflateEnd(stream)); } static int zlib_inflateInit(z_stream *stream) { stream->zalloc = zcalloc; stream->opaque = NULL; stream->zfree = zcfree; return (inflateInit(stream)); } static int zlib_inflate(z_stream *stream, int finish) { #if __FreeBSD_version >= 1300024 return (inflate(stream, finish)); #else return (_zlib104_inflate(stream, finish)); #endif } static int zlib_inflateEnd(z_stream *stream) { return (inflateEnd(stream)); } /* * A kmem_cache is used for the zlib workspaces to avoid having to vmalloc * and vfree for every call. Using a kmem_cache also has the advantage * that improves the odds that the memory used will be local to this cpu. * To further improve things it might be wise to create a dedicated per-cpu * workspace for use. This would take some additional care because we then * must disable preemption around the critical section, and verify that * zlib_deflate* and zlib_inflate* never internally call schedule(). */ static void * zlib_workspace_alloc(int flags) { // return (kmem_cache_alloc(zlib_workspace_cache, flags)); return (NULL); } static void zlib_workspace_free(void *workspace) { // kmem_cache_free(zlib_workspace_cache, workspace); } /* * Compresses the source buffer into the destination buffer. The level * parameter has the same meaning as in deflateInit. sourceLen is the byte * length of the source buffer. Upon entry, destLen is the total size of the * destination buffer, which must be at least 0.1% larger than sourceLen plus * 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. * * compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough * memory, Z_BUF_ERROR if there was not enough room in the output buffer, * Z_STREAM_ERROR if the level parameter is invalid. */ int z_compress_level(void *dest, size_t *destLen, const void *source, size_t sourceLen, int level) { z_stream stream = {0}; int err; stream.next_in = (Byte *)source; stream.avail_in = (uInt)sourceLen; stream.next_out = dest; stream.avail_out = (uInt)*destLen; stream.opaque = NULL; if ((size_t)stream.avail_out != *destLen) return (Z_BUF_ERROR); stream.opaque = zlib_workspace_alloc(KM_SLEEP); #if 0 if (!stream.opaque) return (Z_MEM_ERROR); #endif err = zlib_deflateInit(&stream, level); if (err != Z_OK) { zlib_workspace_free(stream.opaque); return (err); } err = zlib_deflate(&stream, Z_FINISH); if (err != Z_STREAM_END) { zlib_deflateEnd(&stream); zlib_workspace_free(stream.opaque); return (err == Z_OK ? Z_BUF_ERROR : err); } *destLen = stream.total_out; err = zlib_deflateEnd(&stream); zlib_workspace_free(stream.opaque); return (err); } /* * Decompresses the source buffer into the destination buffer. sourceLen is * the byte length of the source buffer. Upon entry, destLen is the total * size of the destination buffer, which must be large enough to hold the * entire uncompressed data. (The size of the uncompressed data must have * been saved previously by the compressor and transmitted to the decompressor * by some mechanism outside the scope of this compression library.) * Upon exit, destLen is the actual size of the compressed buffer. * This function can be used to decompress a whole file at once if the * input file is mmap'ed. * * uncompress returns Z_OK if success, Z_MEM_ERROR if there was not * enough memory, Z_BUF_ERROR if there was not enough room in the output * buffer, or Z_DATA_ERROR if the input data was corrupted. */ int z_uncompress(void *dest, size_t *destLen, const void *source, size_t sourceLen) { z_stream stream = {0}; int err; stream.next_in = (Byte *)source; stream.avail_in = (uInt)sourceLen; stream.next_out = dest; stream.avail_out = (uInt)*destLen; if ((size_t)stream.avail_out != *destLen) return (Z_BUF_ERROR); stream.opaque = zlib_workspace_alloc(KM_SLEEP); #if 0 if (!stream.opaque) return (Z_MEM_ERROR); #endif err = zlib_inflateInit(&stream); if (err != Z_OK) { zlib_workspace_free(stream.opaque); return (err); } err = zlib_inflate(&stream, Z_FINISH); if (err != Z_STREAM_END) { zlib_inflateEnd(&stream); zlib_workspace_free(stream.opaque); if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) return (Z_DATA_ERROR); return (err); } *destLen = stream.total_out; err = zlib_inflateEnd(&stream); zlib_workspace_free(stream.opaque); return (err); } diff --git a/module/os/freebsd/spl/spl_zone.c b/module/os/freebsd/spl/spl_zone.c index 658ef0bf056d..7f2b5c712c42 100644 --- a/module/os/freebsd/spl/spl_zone.c +++ b/module/os/freebsd/spl/spl_zone.c @@ -1,260 +1,257 @@ /* * Copyright (c) 2007 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_ZONES, "zones_data", "Zones data"); /* * Structure to record list of ZFS datasets exported to a zone. */ typedef struct zone_dataset { LIST_ENTRY(zone_dataset) zd_next; char zd_dataset[0]; } zone_dataset_t; LIST_HEAD(zone_dataset_head, zone_dataset); static int zone_slot; int zone_dataset_attach(struct ucred *cred, const char *dataset, int jailid) { struct zone_dataset_head *head; zone_dataset_t *zd, *zd2; struct prison *pr; int dofree, error; if ((error = spl_priv_check_cred(cred, PRIV_ZFS_JAIL)) != 0) return (error); /* Allocate memory before we grab prison's mutex. */ zd = malloc(sizeof (*zd) + strlen(dataset) + 1, M_ZONES, M_WAITOK); sx_slock(&allprison_lock); pr = prison_find(jailid); /* Locks &pr->pr_mtx. */ sx_sunlock(&allprison_lock); if (pr == NULL) { free(zd, M_ZONES); return (ENOENT); } head = osd_jail_get(pr, zone_slot); if (head != NULL) { dofree = 0; LIST_FOREACH(zd2, head, zd_next) { if (strcmp(dataset, zd2->zd_dataset) == 0) { free(zd, M_ZONES); error = EEXIST; goto end; } } } else { dofree = 1; prison_hold_locked(pr); mtx_unlock(&pr->pr_mtx); head = malloc(sizeof (*head), M_ZONES, M_WAITOK); LIST_INIT(head); mtx_lock(&pr->pr_mtx); error = osd_jail_set(pr, zone_slot, head); KASSERT(error == 0, ("osd_jail_set() failed (error=%d)", error)); } strcpy(zd->zd_dataset, dataset); LIST_INSERT_HEAD(head, zd, zd_next); end: if (dofree) prison_free_locked(pr); else mtx_unlock(&pr->pr_mtx); return (error); } int zone_dataset_detach(struct ucred *cred, const char *dataset, int jailid) { struct zone_dataset_head *head; zone_dataset_t *zd; struct prison *pr; int error; if ((error = spl_priv_check_cred(cred, PRIV_ZFS_JAIL)) != 0) return (error); sx_slock(&allprison_lock); pr = prison_find(jailid); sx_sunlock(&allprison_lock); if (pr == NULL) return (ENOENT); head = osd_jail_get(pr, zone_slot); if (head == NULL) { error = ENOENT; goto end; } LIST_FOREACH(zd, head, zd_next) { if (strcmp(dataset, zd->zd_dataset) == 0) break; } if (zd == NULL) error = ENOENT; else { LIST_REMOVE(zd, zd_next); free(zd, M_ZONES); if (LIST_EMPTY(head)) osd_jail_del(pr, zone_slot); error = 0; } end: mtx_unlock(&pr->pr_mtx); return (error); } /* * Returns true if the named dataset is visible in the current zone. * The 'write' parameter is set to 1 if the dataset is also writable. */ int zone_dataset_visible(const char *dataset, int *write) { struct zone_dataset_head *head; zone_dataset_t *zd; struct prison *pr; size_t len; int ret = 0; if (dataset[0] == '\0') return (0); if (INGLOBALZONE(curproc)) { if (write != NULL) *write = 1; return (1); } pr = curthread->td_ucred->cr_prison; mtx_lock(&pr->pr_mtx); head = osd_jail_get(pr, zone_slot); if (head == NULL) goto end; /* * Walk the list once, looking for datasets which match exactly, or * specify a dataset underneath an exported dataset. If found, return * true and note that it is writable. */ LIST_FOREACH(zd, head, zd_next) { len = strlen(zd->zd_dataset); if (strlen(dataset) >= len && memcmp(dataset, zd->zd_dataset, len) == 0 && (dataset[len] == '\0' || dataset[len] == '/' || dataset[len] == '@')) { if (write) *write = 1; ret = 1; goto end; } } /* * Walk the list a second time, searching for datasets which are parents * of exported datasets. These should be visible, but read-only. * * Note that we also have to support forms such as 'pool/dataset/', with * a trailing slash. */ LIST_FOREACH(zd, head, zd_next) { len = strlen(dataset); if (dataset[len - 1] == '/') len--; /* Ignore trailing slash */ if (len < strlen(zd->zd_dataset) && memcmp(dataset, zd->zd_dataset, len) == 0 && zd->zd_dataset[len] == '/') { if (write) *write = 0; ret = 1; goto end; } } end: mtx_unlock(&pr->pr_mtx); return (ret); } static void zone_destroy(void *arg) { struct zone_dataset_head *head; zone_dataset_t *zd; head = arg; while ((zd = LIST_FIRST(head)) != NULL) { LIST_REMOVE(zd, zd_next); free(zd, M_ZONES); } free(head, M_ZONES); } uint32_t zone_get_hostid(void *ptr) { KASSERT(ptr == NULL, ("only NULL pointer supported in %s", __func__)); return ((uint32_t)curthread->td_ucred->cr_prison->pr_hostid); } static void zone_sysinit(void *arg __unused) { zone_slot = osd_jail_register(zone_destroy, NULL); } static void zone_sysuninit(void *arg __unused) { osd_jail_deregister(zone_slot); } SYSINIT(zone_sysinit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysinit, NULL); SYSUNINIT(zone_sysuninit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysuninit, NULL); diff --git a/module/os/freebsd/zfs/crypto_os.c b/module/os/freebsd/zfs/crypto_os.c index 1f139ea5b807..ed8d2407613e 100644 --- a/module/os/freebsd/zfs/crypto_os.c +++ b/module/os/freebsd/zfs/crypto_os.c @@ -1,635 +1,632 @@ /* * Copyright (c) 2005-2010 Pawel Jakub Dawidek * Copyright (c) 2018 Sean Eric Fagan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Portions of this file are derived from sys/geom/eli/g_eli_hmac.c */ -#include -__FBSDID("$FreeBSD$"); - #include #include #ifdef _KERNEL #include #include #include #include #include #endif #include #include #include #include #define SHA512_HMAC_BLOCK_SIZE 128 static int crypt_sessions = 0; SYSCTL_DECL(_vfs_zfs); SYSCTL_INT(_vfs_zfs, OID_AUTO, crypt_sessions, CTLFLAG_RD, &crypt_sessions, 0, "Number of cryptographic sessions created"); void crypto_mac_init(struct hmac_ctx *ctx, const crypto_key_t *c_key) { uint8_t k_ipad[SHA512_HMAC_BLOCK_SIZE], k_opad[SHA512_HMAC_BLOCK_SIZE], key[SHA512_HMAC_BLOCK_SIZE]; SHA512_CTX lctx; int i; size_t cl_bytes = CRYPTO_BITS2BYTES(c_key->ck_length); /* * This code is based on the similar code in geom/eli/g_eli_hmac.c */ memset(key, 0, sizeof (key)); if (c_key->ck_length == 0) /* do nothing */; else if (cl_bytes <= SHA512_HMAC_BLOCK_SIZE) memcpy(key, c_key->ck_data, cl_bytes); else { /* * If key is longer than 128 bytes reset it to * key = SHA512(key). */ SHA512_Init(&lctx); SHA512_Update(&lctx, c_key->ck_data, cl_bytes); SHA512_Final(key, &lctx); } /* XOR key with ipad and opad values. */ for (i = 0; i < sizeof (key); i++) { k_ipad[i] = key[i] ^ 0x36; k_opad[i] = key[i] ^ 0x5c; } memset(key, 0, sizeof (key)); /* Start inner SHA512. */ SHA512_Init(&ctx->innerctx); SHA512_Update(&ctx->innerctx, k_ipad, sizeof (k_ipad)); memset(k_ipad, 0, sizeof (k_ipad)); /* Start outer SHA512. */ SHA512_Init(&ctx->outerctx); SHA512_Update(&ctx->outerctx, k_opad, sizeof (k_opad)); memset(k_opad, 0, sizeof (k_opad)); } void crypto_mac_update(struct hmac_ctx *ctx, const void *data, size_t datasize) { SHA512_Update(&ctx->innerctx, data, datasize); } void crypto_mac_final(struct hmac_ctx *ctx, void *md, size_t mdsize) { uint8_t digest[SHA512_DIGEST_LENGTH]; /* Complete inner hash */ SHA512_Final(digest, &ctx->innerctx); /* Complete outer hash */ SHA512_Update(&ctx->outerctx, digest, sizeof (digest)); SHA512_Final(digest, &ctx->outerctx); memset(ctx, 0, sizeof (*ctx)); /* mdsize == 0 means "Give me the whole hash!" */ if (mdsize == 0) mdsize = SHA512_DIGEST_LENGTH; memcpy(md, digest, mdsize); memset(digest, 0, sizeof (digest)); } void crypto_mac(const crypto_key_t *key, const void *in_data, size_t in_data_size, void *out_data, size_t out_data_size) { struct hmac_ctx ctx; crypto_mac_init(&ctx, key); crypto_mac_update(&ctx, in_data, in_data_size); crypto_mac_final(&ctx, out_data, out_data_size); } static int freebsd_zfs_crypt_done(struct cryptop *crp) { freebsd_crypt_session_t *ses; ses = crp->crp_opaque; mtx_lock(&ses->fs_lock); ses->fs_done = true; mtx_unlock(&ses->fs_lock); wakeup(crp); return (0); } static int freebsd_zfs_crypt_done_sync(struct cryptop *crp) { return (0); } void freebsd_crypt_freesession(freebsd_crypt_session_t *sess) { mtx_destroy(&sess->fs_lock); crypto_freesession(sess->fs_sid); memset(sess, 0, sizeof (*sess)); } static int zfs_crypto_dispatch(freebsd_crypt_session_t *session, struct cryptop *crp) { int error; crp->crp_opaque = session; for (;;) { #if __FreeBSD_version < 1400004 boolean_t async = ((crypto_ses2caps(crp->crp_session) & CRYPTOCAP_F_SYNC) == 0); #else boolean_t async = !CRYPTO_SESS_SYNC(crp->crp_session); #endif crp->crp_callback = async ? freebsd_zfs_crypt_done : freebsd_zfs_crypt_done_sync; error = crypto_dispatch(crp); if (error == 0) { if (async) { mtx_lock(&session->fs_lock); while (session->fs_done == false) { msleep(crp, &session->fs_lock, 0, "zfs_crypto", 0); } mtx_unlock(&session->fs_lock); } error = crp->crp_etype; } if (error == ENOMEM) { pause("zcrnomem", 1); } else if (error != EAGAIN) { break; } crp->crp_etype = 0; crp->crp_flags &= ~CRYPTO_F_DONE; session->fs_done = false; #if __FreeBSD_version < 1300087 /* * Session ID changed, so we should record that, * and try again */ session->fs_sid = crp->crp_session; #endif } return (error); } static void freebsd_crypt_uio_debug_log(boolean_t encrypt, freebsd_crypt_session_t *input_sessionp, const struct zio_crypt_info *c_info, zfs_uio_t *data_uio, crypto_key_t *key, uint8_t *ivbuf, size_t datalen, size_t auth_len) { #ifdef FCRYPTO_DEBUG struct cryptodesc *crd; uint8_t *p = NULL; size_t total = 0; printf("%s(%s, %p, { %s, %d, %d, %s }, %p, { %p, %u }, " "%p, %u, %u)\n", __FUNCTION__, encrypt ? "encrypt" : "decrypt", input_sessionp, c_info->ci_algname, c_info->ci_crypt_type, (unsigned int)c_info->ci_keylen, c_info->ci_name, data_uio, key->ck_data, (unsigned int)key->ck_length, ivbuf, (unsigned int)datalen, (unsigned int)auth_len); printf("\tkey = { "); for (int i = 0; i < key->ck_length / 8; i++) { uint8_t *b = (uint8_t *)key->ck_data; printf("%02x ", b[i]); } printf("}\n"); for (int i = 0; i < zfs_uio_iovcnt(data_uio); i++) { printf("\tiovec #%d: <%p, %u>\n", i, zfs_uio_iovbase(data_uio, i), (unsigned int)zfs_uio_iovlen(data_uio, i)); total += zfs_uio_iovlen(data_uio, i); } zfs_uio_resid(data_uio) = total; #endif } /* * Create a new cryptographic session. This should * happen every time the key changes (including when * it's first loaded). */ #if __FreeBSD_version >= 1300087 int freebsd_crypt_newsession(freebsd_crypt_session_t *sessp, const struct zio_crypt_info *c_info, crypto_key_t *key) { struct crypto_session_params csp = {0}; int error = 0; #ifdef FCRYPTO_DEBUG printf("%s(%p, { %s, %d, %d, %s }, { %p, %u })\n", __FUNCTION__, sessp, c_info->ci_algname, c_info->ci_crypt_type, (unsigned int)c_info->ci_keylen, c_info->ci_name, key->ck_data, (unsigned int)key->ck_length); printf("\tkey = { "); for (int i = 0; i < key->ck_length / 8; i++) { uint8_t *b = (uint8_t *)key->ck_data; printf("%02x ", b[i]); } printf("}\n"); #endif csp.csp_mode = CSP_MODE_AEAD; csp.csp_cipher_key = key->ck_data; csp.csp_cipher_klen = key->ck_length / 8; switch (c_info->ci_crypt_type) { case ZC_TYPE_GCM: csp.csp_cipher_alg = CRYPTO_AES_NIST_GCM_16; csp.csp_ivlen = AES_GCM_IV_LEN; switch (key->ck_length/8) { case AES_128_GMAC_KEY_LEN: case AES_192_GMAC_KEY_LEN: case AES_256_GMAC_KEY_LEN: break; default: error = EINVAL; goto bad; } break; case ZC_TYPE_CCM: csp.csp_cipher_alg = CRYPTO_AES_CCM_16; csp.csp_ivlen = AES_CCM_IV_LEN; switch (key->ck_length/8) { case AES_128_CBC_MAC_KEY_LEN: case AES_192_CBC_MAC_KEY_LEN: case AES_256_CBC_MAC_KEY_LEN: break; default: error = EINVAL; goto bad; break; } break; default: error = ENOTSUP; goto bad; } /* * Disable the use of hardware drivers on FreeBSD 13 and later since * common crypto offload drivers impose constraints on AES-GCM AAD * lengths that make them unusable for ZFS, and we currently do not have * a mechanism to fall back to a software driver for requests not * handled by a hardware driver. * * On 12 we continue to permit the use of hardware drivers since * CPU-accelerated drivers such as aesni(4) register themselves as * hardware drivers. */ error = crypto_newsession(&sessp->fs_sid, &csp, CRYPTOCAP_F_SOFTWARE); mtx_init(&sessp->fs_lock, "FreeBSD Cryptographic Session Lock", NULL, MTX_DEF); crypt_sessions++; bad: #ifdef FCRYPTO_DEBUG if (error) printf("%s: returning error %d\n", __FUNCTION__, error); #endif return (error); } int freebsd_crypt_uio(boolean_t encrypt, freebsd_crypt_session_t *input_sessionp, const struct zio_crypt_info *c_info, zfs_uio_t *data_uio, crypto_key_t *key, uint8_t *ivbuf, size_t datalen, size_t auth_len) { struct cryptop *crp; freebsd_crypt_session_t *session = NULL; int error = 0; size_t total = 0; freebsd_crypt_uio_debug_log(encrypt, input_sessionp, c_info, data_uio, key, ivbuf, datalen, auth_len); for (int i = 0; i < zfs_uio_iovcnt(data_uio); i++) total += zfs_uio_iovlen(data_uio, i); zfs_uio_resid(data_uio) = total; if (input_sessionp == NULL) { session = kmem_zalloc(sizeof (*session), KM_SLEEP); error = freebsd_crypt_newsession(session, c_info, key); if (error) goto out; } else session = input_sessionp; crp = crypto_getreq(session->fs_sid, M_WAITOK); if (encrypt) { crp->crp_op = CRYPTO_OP_ENCRYPT | CRYPTO_OP_COMPUTE_DIGEST; } else { crp->crp_op = CRYPTO_OP_DECRYPT | CRYPTO_OP_VERIFY_DIGEST; } crp->crp_flags = CRYPTO_F_CBIFSYNC | CRYPTO_F_IV_SEPARATE; crypto_use_uio(crp, GET_UIO_STRUCT(data_uio)); crp->crp_aad_start = 0; crp->crp_aad_length = auth_len; crp->crp_payload_start = auth_len; crp->crp_payload_length = datalen; crp->crp_digest_start = auth_len + datalen; memcpy(crp->crp_iv, ivbuf, ZIO_DATA_IV_LEN); error = zfs_crypto_dispatch(session, crp); crypto_freereq(crp); out: #ifdef FCRYPTO_DEBUG if (error) printf("%s: returning error %d\n", __FUNCTION__, error); #endif if (input_sessionp == NULL) { freebsd_crypt_freesession(session); kmem_free(session, sizeof (*session)); } return (error); } #else int freebsd_crypt_newsession(freebsd_crypt_session_t *sessp, const struct zio_crypt_info *c_info, crypto_key_t *key) { struct cryptoini cria = {0}, crie = {0}, *crip; struct enc_xform *xform; struct auth_hash *xauth; int error = 0; crypto_session_t sid; #ifdef FCRYPTO_DEBUG printf("%s(%p, { %s, %d, %d, %s }, { %p, %u })\n", __FUNCTION__, sessp, c_info->ci_algname, c_info->ci_crypt_type, (unsigned int)c_info->ci_keylen, c_info->ci_name, key->ck_data, (unsigned int)key->ck_length); printf("\tkey = { "); for (int i = 0; i < key->ck_length / 8; i++) { uint8_t *b = (uint8_t *)key->ck_data; printf("%02x ", b[i]); } printf("}\n"); #endif switch (c_info->ci_crypt_type) { case ZC_TYPE_GCM: xform = &enc_xform_aes_nist_gcm; switch (key->ck_length/8) { case AES_128_GMAC_KEY_LEN: xauth = &auth_hash_nist_gmac_aes_128; break; case AES_192_GMAC_KEY_LEN: xauth = &auth_hash_nist_gmac_aes_192; break; case AES_256_GMAC_KEY_LEN: xauth = &auth_hash_nist_gmac_aes_256; break; default: error = EINVAL; goto bad; } break; case ZC_TYPE_CCM: xform = &enc_xform_ccm; switch (key->ck_length/8) { case AES_128_CBC_MAC_KEY_LEN: xauth = &auth_hash_ccm_cbc_mac_128; break; case AES_192_CBC_MAC_KEY_LEN: xauth = &auth_hash_ccm_cbc_mac_192; break; case AES_256_CBC_MAC_KEY_LEN: xauth = &auth_hash_ccm_cbc_mac_256; break; default: error = EINVAL; goto bad; break; } break; default: error = ENOTSUP; goto bad; } #ifdef FCRYPTO_DEBUG printf("%s(%d): Using crypt %s (key length %u [%u bytes]), " "auth %s (key length %d)\n", __FUNCTION__, __LINE__, xform->name, (unsigned int)key->ck_length, (unsigned int)key->ck_length/8, xauth->name, xauth->keysize); #endif crie.cri_alg = xform->type; crie.cri_key = key->ck_data; crie.cri_klen = key->ck_length; cria.cri_alg = xauth->type; cria.cri_key = key->ck_data; cria.cri_klen = key->ck_length; cria.cri_next = &crie; crie.cri_next = NULL; crip = &cria; // Everything else is zero-initialised error = crypto_newsession(&sid, crip, CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE); if (error != 0) { printf("%s(%d): crypto_newsession failed with %d\n", __FUNCTION__, __LINE__, error); goto bad; } sessp->fs_sid = sid; mtx_init(&sessp->fs_lock, "FreeBSD Cryptographic Session Lock", NULL, MTX_DEF); crypt_sessions++; bad: return (error); } /* * The meat of encryption/decryption. * If sessp is NULL, then it will create a * temporary cryptographic session, and release * it when done. */ int freebsd_crypt_uio(boolean_t encrypt, freebsd_crypt_session_t *input_sessionp, const struct zio_crypt_info *c_info, zfs_uio_t *data_uio, crypto_key_t *key, uint8_t *ivbuf, size_t datalen, size_t auth_len) { struct cryptop *crp; struct cryptodesc *enc_desc, *auth_desc; struct enc_xform *xform; struct auth_hash *xauth; freebsd_crypt_session_t *session = NULL; int error; freebsd_crypt_uio_debug_log(encrypt, input_sessionp, c_info, data_uio, key, ivbuf, datalen, auth_len); switch (c_info->ci_crypt_type) { case ZC_TYPE_GCM: xform = &enc_xform_aes_nist_gcm; switch (key->ck_length/8) { case AES_128_GMAC_KEY_LEN: xauth = &auth_hash_nist_gmac_aes_128; break; case AES_192_GMAC_KEY_LEN: xauth = &auth_hash_nist_gmac_aes_192; break; case AES_256_GMAC_KEY_LEN: xauth = &auth_hash_nist_gmac_aes_256; break; default: error = EINVAL; goto bad; } break; case ZC_TYPE_CCM: xform = &enc_xform_ccm; switch (key->ck_length/8) { case AES_128_CBC_MAC_KEY_LEN: xauth = &auth_hash_ccm_cbc_mac_128; break; case AES_192_CBC_MAC_KEY_LEN: xauth = &auth_hash_ccm_cbc_mac_192; break; case AES_256_CBC_MAC_KEY_LEN: xauth = &auth_hash_ccm_cbc_mac_256; break; default: error = EINVAL; goto bad; break; } break; default: error = ENOTSUP; goto bad; } #ifdef FCRYPTO_DEBUG printf("%s(%d): Using crypt %s (key length %u [%u bytes]), " "auth %s (key length %d)\n", __FUNCTION__, __LINE__, xform->name, (unsigned int)key->ck_length, (unsigned int)key->ck_length/8, xauth->name, xauth->keysize); #endif if (input_sessionp == NULL) { session = kmem_zalloc(sizeof (*session), KM_SLEEP); error = freebsd_crypt_newsession(session, c_info, key); if (error) goto out; } else session = input_sessionp; crp = crypto_getreq(2); if (crp == NULL) { error = ENOMEM; goto bad; } auth_desc = crp->crp_desc; enc_desc = auth_desc->crd_next; crp->crp_session = session->fs_sid; crp->crp_ilen = auth_len + datalen; crp->crp_buf = (void*)GET_UIO_STRUCT(data_uio); crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIFSYNC; auth_desc->crd_skip = 0; auth_desc->crd_len = auth_len; auth_desc->crd_inject = auth_len + datalen; auth_desc->crd_alg = xauth->type; #ifdef FCRYPTO_DEBUG printf("%s: auth: skip = %u, len = %u, inject = %u\n", __FUNCTION__, auth_desc->crd_skip, auth_desc->crd_len, auth_desc->crd_inject); #endif enc_desc->crd_skip = auth_len; enc_desc->crd_len = datalen; enc_desc->crd_inject = auth_len; enc_desc->crd_alg = xform->type; enc_desc->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT; memcpy(enc_desc->crd_iv, ivbuf, ZIO_DATA_IV_LEN); enc_desc->crd_next = NULL; #ifdef FCRYPTO_DEBUG printf("%s: enc: skip = %u, len = %u, inject = %u\n", __FUNCTION__, enc_desc->crd_skip, enc_desc->crd_len, enc_desc->crd_inject); #endif if (encrypt) enc_desc->crd_flags |= CRD_F_ENCRYPT; error = zfs_crypto_dispatch(session, crp); crypto_freereq(crp); out: if (input_sessionp == NULL) { freebsd_crypt_freesession(session); kmem_free(session, sizeof (*session)); } bad: #ifdef FCRYPTO_DEBUG if (error) printf("%s: returning error %d\n", __FUNCTION__, error); #endif return (error); } #endif diff --git a/module/os/freebsd/zfs/dmu_os.c b/module/os/freebsd/zfs/dmu_os.c index a5f486b95db4..ee6fb2dc657b 100644 --- a/module/os/freebsd/zfs/dmu_os.c +++ b/module/os/freebsd/zfs/dmu_os.c @@ -1,333 +1,330 @@ /* * Copyright (c) 2020 iXsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef IDX_TO_OFF #define IDX_TO_OFF(idx) (((vm_ooffset_t)(idx)) << PAGE_SHIFT) #endif #if __FreeBSD_version < 1300051 #define VM_ALLOC_BUSY_FLAGS VM_ALLOC_NOBUSY #else #define VM_ALLOC_BUSY_FLAGS VM_ALLOC_SBUSY | VM_ALLOC_IGN_SBUSY #endif #if __FreeBSD_version < 1300072 #define dmu_page_lock(m) vm_page_lock(m) #define dmu_page_unlock(m) vm_page_unlock(m) #else #define dmu_page_lock(m) #define dmu_page_unlock(m) #endif int dmu_write_pages(objset_t *os, uint64_t object, uint64_t offset, uint64_t size, vm_page_t *ma, dmu_tx_t *tx) { dmu_buf_t **dbp; struct sf_buf *sf; int numbufs, i; int err; if (size == 0) return (0); err = dmu_buf_hold_array(os, object, offset, size, FALSE, FTAG, &numbufs, &dbp); if (err) return (err); for (i = 0; i < numbufs; i++) { int tocpy, copied, thiscpy; int bufoff; dmu_buf_t *db = dbp[i]; caddr_t va; ASSERT3U(size, >, 0); ASSERT3U(db->db_size, >=, PAGESIZE); bufoff = offset - db->db_offset; tocpy = (int)MIN(db->db_size - bufoff, size); ASSERT(i == 0 || i == numbufs-1 || tocpy == db->db_size); if (tocpy == db->db_size) dmu_buf_will_fill(db, tx); else dmu_buf_will_dirty(db, tx); for (copied = 0; copied < tocpy; copied += PAGESIZE) { ASSERT3U(ptoa((*ma)->pindex), ==, db->db_offset + bufoff); thiscpy = MIN(PAGESIZE, tocpy - copied); va = zfs_map_page(*ma, &sf); memcpy((char *)db->db_data + bufoff, va, thiscpy); zfs_unmap_page(sf); ma += 1; bufoff += PAGESIZE; } if (tocpy == db->db_size) dmu_buf_fill_done(db, tx); offset += tocpy; size -= tocpy; } dmu_buf_rele_array(dbp, numbufs, FTAG); return (err); } int dmu_read_pages(objset_t *os, uint64_t object, vm_page_t *ma, int count, int *rbehind, int *rahead, int last_size) { struct sf_buf *sf; vm_object_t vmobj; vm_page_t m; dmu_buf_t **dbp; dmu_buf_t *db; caddr_t va; int numbufs, i; int bufoff, pgoff, tocpy; int mi, di; int err; ASSERT3U(ma[0]->pindex + count - 1, ==, ma[count - 1]->pindex); ASSERT3S(last_size, <=, PAGE_SIZE); err = dmu_buf_hold_array(os, object, IDX_TO_OFF(ma[0]->pindex), IDX_TO_OFF(count - 1) + last_size, TRUE, FTAG, &numbufs, &dbp); if (err != 0) return (err); #ifdef ZFS_DEBUG IMPLY(last_size < PAGE_SIZE, *rahead == 0); if (dbp[0]->db_offset != 0 || numbufs > 1) { for (i = 0; i < numbufs; i++) { ASSERT(ISP2(dbp[i]->db_size)); ASSERT3U((dbp[i]->db_offset % dbp[i]->db_size), ==, 0); ASSERT3U(dbp[i]->db_size, ==, dbp[0]->db_size); } } #endif vmobj = ma[0]->object; zfs_vmobject_wlock_12(vmobj); db = dbp[0]; for (i = 0; i < *rbehind; i++) { m = vm_page_grab_unlocked(vmobj, ma[0]->pindex - 1 - i, VM_ALLOC_NORMAL | VM_ALLOC_NOWAIT | VM_ALLOC_BUSY_FLAGS); if (m == NULL) break; if (!vm_page_none_valid(m)) { ASSERT3U(m->valid, ==, VM_PAGE_BITS_ALL); vm_page_do_sunbusy(m); break; } ASSERT3U(m->dirty, ==, 0); ASSERT(!pmap_page_is_write_mapped(m)); ASSERT3U(db->db_size, >, PAGE_SIZE); bufoff = IDX_TO_OFF(m->pindex) % db->db_size; va = zfs_map_page(m, &sf); memcpy(va, (char *)db->db_data + bufoff, PAGESIZE); zfs_unmap_page(sf); vm_page_valid(m); dmu_page_lock(m); if ((m->busy_lock & VPB_BIT_WAITERS) != 0) vm_page_activate(m); else vm_page_deactivate(m); dmu_page_unlock(m); vm_page_do_sunbusy(m); } *rbehind = i; bufoff = IDX_TO_OFF(ma[0]->pindex) % db->db_size; pgoff = 0; for (mi = 0, di = 0; mi < count && di < numbufs; ) { if (pgoff == 0) { m = ma[mi]; if (m != bogus_page) { vm_page_assert_xbusied(m); ASSERT(vm_page_none_valid(m)); ASSERT3U(m->dirty, ==, 0); ASSERT(!pmap_page_is_write_mapped(m)); va = zfs_map_page(m, &sf); } } if (bufoff == 0) db = dbp[di]; if (m != bogus_page) { ASSERT3U(IDX_TO_OFF(m->pindex) + pgoff, ==, db->db_offset + bufoff); } /* * We do not need to clamp the copy size by the file * size as the last block is zero-filled beyond the * end of file anyway. */ tocpy = MIN(db->db_size - bufoff, PAGESIZE - pgoff); ASSERT3S(tocpy, >=, 0); if (m != bogus_page) memcpy(va + pgoff, (char *)db->db_data + bufoff, tocpy); pgoff += tocpy; ASSERT3S(pgoff, >=, 0); ASSERT3S(pgoff, <=, PAGESIZE); if (pgoff == PAGESIZE) { if (m != bogus_page) { zfs_unmap_page(sf); vm_page_valid(m); } ASSERT3S(mi, <, count); mi++; pgoff = 0; } bufoff += tocpy; ASSERT3S(bufoff, >=, 0); ASSERT3S(bufoff, <=, db->db_size); if (bufoff == db->db_size) { ASSERT3S(di, <, numbufs); di++; bufoff = 0; } } #ifdef ZFS_DEBUG /* * Three possibilities: * - last requested page ends at a buffer boundary and , thus, * all pages and buffers have been iterated; * - all requested pages are filled, but the last buffer * has not been exhausted; * the read-ahead is possible only in this case; * - all buffers have been read, but the last page has not been * fully filled; * this is only possible if the file has only a single buffer * with a size that is not a multiple of the page size. */ if (mi == count) { ASSERT3S(di, >=, numbufs - 1); IMPLY(*rahead != 0, di == numbufs - 1); IMPLY(*rahead != 0, bufoff != 0); ASSERT0(pgoff); } if (di == numbufs) { ASSERT3S(mi, >=, count - 1); ASSERT0(*rahead); IMPLY(pgoff == 0, mi == count); if (pgoff != 0) { ASSERT3S(mi, ==, count - 1); ASSERT3U((dbp[0]->db_size & PAGE_MASK), !=, 0); } } #endif if (pgoff != 0) { ASSERT3P(m, !=, bogus_page); memset(va + pgoff, 0, PAGESIZE - pgoff); zfs_unmap_page(sf); vm_page_valid(m); } for (i = 0; i < *rahead; i++) { m = vm_page_grab_unlocked(vmobj, ma[count - 1]->pindex + 1 + i, VM_ALLOC_NORMAL | VM_ALLOC_NOWAIT | VM_ALLOC_BUSY_FLAGS); if (m == NULL) break; if (!vm_page_none_valid(m)) { ASSERT3U(m->valid, ==, VM_PAGE_BITS_ALL); vm_page_do_sunbusy(m); break; } ASSERT3U(m->dirty, ==, 0); ASSERT(!pmap_page_is_write_mapped(m)); ASSERT3U(db->db_size, >, PAGE_SIZE); bufoff = IDX_TO_OFF(m->pindex) % db->db_size; tocpy = MIN(db->db_size - bufoff, PAGESIZE); va = zfs_map_page(m, &sf); memcpy(va, (char *)db->db_data + bufoff, tocpy); if (tocpy < PAGESIZE) { ASSERT3S(i, ==, *rahead - 1); ASSERT3U((db->db_size & PAGE_MASK), !=, 0); memset(va + tocpy, 0, PAGESIZE - tocpy); } zfs_unmap_page(sf); vm_page_valid(m); dmu_page_lock(m); if ((m->busy_lock & VPB_BIT_WAITERS) != 0) vm_page_activate(m); else vm_page_deactivate(m); dmu_page_unlock(m); vm_page_do_sunbusy(m); } *rahead = i; zfs_vmobject_wunlock_12(vmobj); dmu_buf_rele_array(dbp, numbufs, FTAG); return (0); } diff --git a/module/os/freebsd/zfs/kmod_core.c b/module/os/freebsd/zfs/kmod_core.c index 00c1acf57710..2bced9ab6446 100644 --- a/module/os/freebsd/zfs/kmod_core.c +++ b/module/os/freebsd/zfs/kmod_core.c @@ -1,351 +1,348 @@ /* * Copyright (c) 2020 iXsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "zfs_comutil.h" #include "zfs_deleg.h" #include "zfs_namecheck.h" #include "zfs_prop.h" SYSCTL_DECL(_vfs_zfs); SYSCTL_DECL(_vfs_zfs_vdev); extern uint_t rrw_tsd_key; static int zfs_version_ioctl = ZFS_IOCVER_OZFS; SYSCTL_DECL(_vfs_zfs_version); SYSCTL_INT(_vfs_zfs_version, OID_AUTO, ioctl, CTLFLAG_RD, &zfs_version_ioctl, 0, "ZFS_IOCTL_VERSION"); static struct cdev *zfsdev; static struct root_hold_token *zfs_root_token; extern uint_t rrw_tsd_key; extern uint_t zfs_allow_log_key; extern uint_t zfs_geom_probe_vdev_key; static int zfs__init(void); static int zfs__fini(void); static void zfs_shutdown(void *, int); static eventhandler_tag zfs_shutdown_event_tag; #define ZFS_MIN_KSTACK_PAGES 4 static int zfsdev_ioctl(struct cdev *dev, ulong_t zcmd, caddr_t arg, int flag, struct thread *td) { uint_t len; int vecnum; zfs_iocparm_t *zp; zfs_cmd_t *zc; #ifdef ZFS_LEGACY_SUPPORT zfs_cmd_legacy_t *zcl; #endif int rc, error; void *uaddr; len = IOCPARM_LEN(zcmd); vecnum = zcmd & 0xff; zp = (void *)arg; error = 0; #ifdef ZFS_LEGACY_SUPPORT zcl = NULL; #endif if (len != sizeof (zfs_iocparm_t)) return (EINVAL); uaddr = (void *)(uintptr_t)zp->zfs_cmd; zc = vmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); #ifdef ZFS_LEGACY_SUPPORT /* * Remap ioctl code for legacy user binaries */ if (zp->zfs_ioctl_version == ZFS_IOCVER_LEGACY) { vecnum = zfs_ioctl_legacy_to_ozfs(vecnum); if (vecnum < 0) { vmem_free(zc, sizeof (zfs_cmd_t)); return (ENOTSUP); } zcl = vmem_zalloc(sizeof (zfs_cmd_legacy_t), KM_SLEEP); if (copyin(uaddr, zcl, sizeof (zfs_cmd_legacy_t))) { error = SET_ERROR(EFAULT); goto out; } zfs_cmd_legacy_to_ozfs(zcl, zc); } else #endif if (copyin(uaddr, zc, sizeof (zfs_cmd_t))) { error = SET_ERROR(EFAULT); goto out; } error = zfsdev_ioctl_common(vecnum, zc, 0); #ifdef ZFS_LEGACY_SUPPORT if (zcl) { zfs_cmd_ozfs_to_legacy(zc, zcl); rc = copyout(zcl, uaddr, sizeof (*zcl)); } else #endif { rc = copyout(zc, uaddr, sizeof (*zc)); } if (error == 0 && rc != 0) error = SET_ERROR(EFAULT); out: #ifdef ZFS_LEGACY_SUPPORT if (zcl) vmem_free(zcl, sizeof (zfs_cmd_legacy_t)); #endif vmem_free(zc, sizeof (zfs_cmd_t)); MPASS(tsd_get(rrw_tsd_key) == NULL); return (error); } static void zfsdev_close(void *data) { zfsdev_state_destroy(data); } void zfsdev_private_set_state(void *priv __unused, zfsdev_state_t *zs) { devfs_set_cdevpriv(zs, zfsdev_close); } zfsdev_state_t * zfsdev_private_get_state(void *priv) { return (priv); } static int zfsdev_open(struct cdev *devp __unused, int flag __unused, int mode __unused, struct thread *td __unused) { int error; mutex_enter(&zfsdev_state_lock); error = zfsdev_state_init(NULL); mutex_exit(&zfsdev_state_lock); return (error); } static struct cdevsw zfs_cdevsw = { .d_version = D_VERSION, .d_open = zfsdev_open, .d_ioctl = zfsdev_ioctl, .d_name = ZFS_DRIVER }; int zfsdev_attach(void) { struct make_dev_args args; make_dev_args_init(&args); args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK; args.mda_devsw = &zfs_cdevsw; args.mda_cr = NULL; args.mda_uid = UID_ROOT; args.mda_gid = GID_OPERATOR; args.mda_mode = 0666; return (make_dev_s(&args, &zfsdev, ZFS_DRIVER)); } void zfsdev_detach(void) { if (zfsdev != NULL) destroy_dev(zfsdev); } int zfs__init(void) { int error; #if KSTACK_PAGES < ZFS_MIN_KSTACK_PAGES printf("ZFS NOTICE: KSTACK_PAGES is %d which could result in stack " "overflow panic!\nPlease consider adding " "'options KSTACK_PAGES=%d' to your kernel config\n", KSTACK_PAGES, ZFS_MIN_KSTACK_PAGES); #endif zfs_root_token = root_mount_hold("ZFS"); if ((error = zfs_kmod_init()) != 0) { printf("ZFS: Failed to Load ZFS Filesystem" ", rc = %d\n", error); root_mount_rel(zfs_root_token); return (error); } tsd_create(&zfs_geom_probe_vdev_key, NULL); printf("ZFS storage pool version: features support (" SPA_VERSION_STRING ")\n"); root_mount_rel(zfs_root_token); ddi_sysevent_init(); return (0); } int zfs__fini(void) { if (zfs_busy() || zvol_busy() || zio_injection_enabled) { return (EBUSY); } zfs_kmod_fini(); tsd_destroy(&zfs_geom_probe_vdev_key); return (0); } static void zfs_shutdown(void *arg __unused, int howto __unused) { /* * ZFS fini routines can not properly work in a panic-ed system. */ if (panicstr == NULL) zfs__fini(); } static int zfs_modevent(module_t mod, int type, void *unused __unused) { int err; switch (type) { case MOD_LOAD: err = zfs__init(); if (err == 0) zfs_shutdown_event_tag = EVENTHANDLER_REGISTER( shutdown_post_sync, zfs_shutdown, NULL, SHUTDOWN_PRI_FIRST); return (err); case MOD_UNLOAD: err = zfs__fini(); if (err == 0 && zfs_shutdown_event_tag != NULL) EVENTHANDLER_DEREGISTER(shutdown_post_sync, zfs_shutdown_event_tag); return (err); case MOD_SHUTDOWN: return (0); default: break; } return (EOPNOTSUPP); } static moduledata_t zfs_mod = { "zfsctrl", zfs_modevent, 0 }; #ifdef _KERNEL EVENTHANDLER_DEFINE(mountroot, spa_boot_init, NULL, 0); #endif FEATURE(zfs, "OpenZFS support"); DECLARE_MODULE(zfsctrl, zfs_mod, SI_SUB_CLOCKS, SI_ORDER_ANY); MODULE_VERSION(zfsctrl, 1); #if __FreeBSD_version > 1300092 MODULE_DEPEND(zfsctrl, xdr, 1, 1, 1); #else MODULE_DEPEND(zfsctrl, krpc, 1, 1, 1); #endif MODULE_DEPEND(zfsctrl, acl_nfs4, 1, 1, 1); MODULE_DEPEND(zfsctrl, crypto, 1, 1, 1); MODULE_DEPEND(zfsctrl, zlib, 1, 1, 1); diff --git a/module/os/freebsd/zfs/sysctl_os.c b/module/os/freebsd/zfs/sysctl_os.c index 312d76c3e023..30983b13f7d1 100644 --- a/module/os/freebsd/zfs/sysctl_os.c +++ b/module/os/freebsd/zfs/sysctl_os.c @@ -1,890 +1,887 @@ /* * Copyright (c) 2020 iXsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SYSCTL_DECL(_vfs_zfs); SYSCTL_NODE(_vfs_zfs, OID_AUTO, arc, CTLFLAG_RW, 0, "ZFS adaptive replacement cache"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, brt, CTLFLAG_RW, 0, "ZFS Block Reference Table"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, condense, CTLFLAG_RW, 0, "ZFS condense"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, dbuf, CTLFLAG_RW, 0, "ZFS disk buf cache"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, dbuf_cache, CTLFLAG_RW, 0, "ZFS disk buf cache"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, deadman, CTLFLAG_RW, 0, "ZFS deadman"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, dedup, CTLFLAG_RW, 0, "ZFS dedup"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, l2arc, CTLFLAG_RW, 0, "ZFS l2arc"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, livelist, CTLFLAG_RW, 0, "ZFS livelist"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, lua, CTLFLAG_RW, 0, "ZFS lua"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, metaslab, CTLFLAG_RW, 0, "ZFS metaslab"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, mg, CTLFLAG_RW, 0, "ZFS metaslab group"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, multihost, CTLFLAG_RW, 0, "ZFS multihost protection"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, prefetch, CTLFLAG_RW, 0, "ZFS prefetch"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, reconstruct, CTLFLAG_RW, 0, "ZFS reconstruct"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, recv, CTLFLAG_RW, 0, "ZFS receive"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, send, CTLFLAG_RW, 0, "ZFS send"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, spa, CTLFLAG_RW, 0, "ZFS space allocation"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, trim, CTLFLAG_RW, 0, "ZFS TRIM"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, txg, CTLFLAG_RW, 0, "ZFS transaction group"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, vdev, CTLFLAG_RW, 0, "ZFS VDEV"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, vnops, CTLFLAG_RW, 0, "ZFS VNOPS"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, zevent, CTLFLAG_RW, 0, "ZFS event"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, zil, CTLFLAG_RW, 0, "ZFS ZIL"); SYSCTL_NODE(_vfs_zfs, OID_AUTO, zio, CTLFLAG_RW, 0, "ZFS ZIO"); SYSCTL_NODE(_vfs_zfs_livelist, OID_AUTO, condense, CTLFLAG_RW, 0, "ZFS livelist condense"); SYSCTL_NODE(_vfs_zfs_vdev, OID_AUTO, cache, CTLFLAG_RW, 0, "ZFS VDEV Cache"); SYSCTL_NODE(_vfs_zfs_vdev, OID_AUTO, file, CTLFLAG_RW, 0, "ZFS VDEV file"); SYSCTL_NODE(_vfs_zfs_vdev, OID_AUTO, mirror, CTLFLAG_RD, 0, "ZFS VDEV mirror"); SYSCTL_DECL(_vfs_zfs_version); SYSCTL_CONST_STRING(_vfs_zfs_version, OID_AUTO, module, CTLFLAG_RD, (ZFS_META_VERSION "-" ZFS_META_RELEASE), "OpenZFS module version"); /* arc.c */ int param_set_arc_u64(SYSCTL_HANDLER_ARGS) { int err; err = sysctl_handle_64(oidp, arg1, 0, req); if (err != 0 || req->newptr == NULL) return (err); arc_tuning_update(B_TRUE); return (0); } int param_set_arc_int(SYSCTL_HANDLER_ARGS) { int err; err = sysctl_handle_int(oidp, arg1, 0, req); if (err != 0 || req->newptr == NULL) return (err); arc_tuning_update(B_TRUE); return (0); } int param_set_arc_max(SYSCTL_HANDLER_ARGS) { unsigned long val; int err; val = zfs_arc_max; err = sysctl_handle_64(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL) return (SET_ERROR(err)); if (val != 0 && (val < MIN_ARC_MAX || val <= arc_c_min || val >= arc_all_memory())) return (SET_ERROR(EINVAL)); zfs_arc_max = val; arc_tuning_update(B_TRUE); /* Update the sysctl to the tuned value */ if (val != 0) zfs_arc_max = arc_c_max; return (0); } /* BEGIN CSTYLED */ SYSCTL_PROC(_vfs_zfs, OID_AUTO, arc_max, CTLTYPE_ULONG | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, NULL, 0, param_set_arc_max, "LU", "Maximum ARC size in bytes (LEGACY)"); /* END CSTYLED */ int param_set_arc_min(SYSCTL_HANDLER_ARGS) { unsigned long val; int err; val = zfs_arc_min; err = sysctl_handle_64(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL) return (SET_ERROR(err)); if (val != 0 && (val < 2ULL << SPA_MAXBLOCKSHIFT || val > arc_c_max)) return (SET_ERROR(EINVAL)); zfs_arc_min = val; arc_tuning_update(B_TRUE); /* Update the sysctl to the tuned value */ if (val != 0) zfs_arc_min = arc_c_min; return (0); } /* BEGIN CSTYLED */ SYSCTL_PROC(_vfs_zfs, OID_AUTO, arc_min, CTLTYPE_ULONG | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, NULL, 0, param_set_arc_min, "LU", "Minimum ARC size in bytes (LEGACY)"); /* END CSTYLED */ extern uint_t zfs_arc_free_target; int param_set_arc_free_target(SYSCTL_HANDLER_ARGS) { uint_t val; int err; val = zfs_arc_free_target; err = sysctl_handle_int(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL) return (err); if (val < minfree) return (EINVAL); if (val > vm_cnt.v_page_count) return (EINVAL); zfs_arc_free_target = val; return (0); } /* * NOTE: This sysctl is CTLFLAG_RW not CTLFLAG_RWTUN due to its dependency on * pagedaemon initialization. */ /* BEGIN CSTYLED */ SYSCTL_PROC(_vfs_zfs, OID_AUTO, arc_free_target, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0, param_set_arc_free_target, "IU", "Desired number of free pages below which ARC triggers reclaim" " (LEGACY)"); /* END CSTYLED */ int param_set_arc_no_grow_shift(SYSCTL_HANDLER_ARGS) { int err, val; val = arc_no_grow_shift; err = sysctl_handle_int(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL) return (err); if (val < 0 || val >= arc_shrink_shift) return (EINVAL); arc_no_grow_shift = val; return (0); } /* BEGIN CSTYLED */ SYSCTL_PROC(_vfs_zfs, OID_AUTO, arc_no_grow_shift, CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, NULL, 0, param_set_arc_no_grow_shift, "I", "log2(fraction of ARC which must be free to allow growing) (LEGACY)"); /* END CSTYLED */ extern uint64_t l2arc_write_max; /* BEGIN CSTYLED */ SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, l2arc_write_max, CTLFLAG_RWTUN, &l2arc_write_max, 0, "Max write bytes per interval (LEGACY)"); /* END CSTYLED */ extern uint64_t l2arc_write_boost; /* BEGIN CSTYLED */ SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, l2arc_write_boost, CTLFLAG_RWTUN, &l2arc_write_boost, 0, "Extra write bytes during device warmup (LEGACY)"); /* END CSTYLED */ extern uint64_t l2arc_headroom; /* BEGIN CSTYLED */ SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, l2arc_headroom, CTLFLAG_RWTUN, &l2arc_headroom, 0, "Number of max device writes to precache (LEGACY)"); /* END CSTYLED */ extern uint64_t l2arc_headroom_boost; /* BEGIN CSTYLED */ SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, l2arc_headroom_boost, CTLFLAG_RWTUN, &l2arc_headroom_boost, 0, "Compressed l2arc_headroom multiplier (LEGACY)"); /* END CSTYLED */ extern uint64_t l2arc_feed_secs; /* BEGIN CSTYLED */ SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, l2arc_feed_secs, CTLFLAG_RWTUN, &l2arc_feed_secs, 0, "Seconds between L2ARC writing (LEGACY)"); /* END CSTYLED */ extern uint64_t l2arc_feed_min_ms; /* BEGIN CSTYLED */ SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, l2arc_feed_min_ms, CTLFLAG_RWTUN, &l2arc_feed_min_ms, 0, "Min feed interval in milliseconds (LEGACY)"); /* END CSTYLED */ extern int l2arc_noprefetch; /* BEGIN CSTYLED */ SYSCTL_INT(_vfs_zfs, OID_AUTO, l2arc_noprefetch, CTLFLAG_RWTUN, &l2arc_noprefetch, 0, "Skip caching prefetched buffers (LEGACY)"); /* END CSTYLED */ extern int l2arc_feed_again; /* BEGIN CSTYLED */ SYSCTL_INT(_vfs_zfs, OID_AUTO, l2arc_feed_again, CTLFLAG_RWTUN, &l2arc_feed_again, 0, "Turbo L2ARC warmup (LEGACY)"); /* END CSTYLED */ extern int l2arc_norw; /* BEGIN CSTYLED */ SYSCTL_INT(_vfs_zfs, OID_AUTO, l2arc_norw, CTLFLAG_RWTUN, &l2arc_norw, 0, "No reads during writes (LEGACY)"); /* END CSTYLED */ static int param_get_arc_state_size(SYSCTL_HANDLER_ARGS) { arc_state_t *state = (arc_state_t *)arg1; int64_t val; val = zfs_refcount_count(&state->arcs_size[ARC_BUFC_DATA]) + zfs_refcount_count(&state->arcs_size[ARC_BUFC_METADATA]); return (sysctl_handle_64(oidp, &val, 0, req)); } extern arc_state_t ARC_anon; /* BEGIN CSTYLED */ SYSCTL_PROC(_vfs_zfs, OID_AUTO, anon_size, CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, &ARC_anon, 0, param_get_arc_state_size, "Q", "size of anonymous state"); SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, anon_metadata_esize, CTLFLAG_RD, &ARC_anon.arcs_esize[ARC_BUFC_METADATA].rc_count, 0, "size of evictable metadata in anonymous state"); SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, anon_data_esize, CTLFLAG_RD, &ARC_anon.arcs_esize[ARC_BUFC_DATA].rc_count, 0, "size of evictable data in anonymous state"); /* END CSTYLED */ extern arc_state_t ARC_mru; /* BEGIN CSTYLED */ SYSCTL_PROC(_vfs_zfs, OID_AUTO, mru_size, CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, &ARC_mru, 0, param_get_arc_state_size, "Q", "size of mru state"); SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, mru_metadata_esize, CTLFLAG_RD, &ARC_mru.arcs_esize[ARC_BUFC_METADATA].rc_count, 0, "size of evictable metadata in mru state"); SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, mru_data_esize, CTLFLAG_RD, &ARC_mru.arcs_esize[ARC_BUFC_DATA].rc_count, 0, "size of evictable data in mru state"); /* END CSTYLED */ extern arc_state_t ARC_mru_ghost; /* BEGIN CSTYLED */ SYSCTL_PROC(_vfs_zfs, OID_AUTO, mru_ghost_size, CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, &ARC_mru_ghost, 0, param_get_arc_state_size, "Q", "size of mru ghost state"); SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, mru_ghost_metadata_esize, CTLFLAG_RD, &ARC_mru_ghost.arcs_esize[ARC_BUFC_METADATA].rc_count, 0, "size of evictable metadata in mru ghost state"); SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, mru_ghost_data_esize, CTLFLAG_RD, &ARC_mru_ghost.arcs_esize[ARC_BUFC_DATA].rc_count, 0, "size of evictable data in mru ghost state"); /* END CSTYLED */ extern arc_state_t ARC_mfu; /* BEGIN CSTYLED */ SYSCTL_PROC(_vfs_zfs, OID_AUTO, mfu_size, CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, &ARC_mfu, 0, param_get_arc_state_size, "Q", "size of mfu state"); SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, mfu_metadata_esize, CTLFLAG_RD, &ARC_mfu.arcs_esize[ARC_BUFC_METADATA].rc_count, 0, "size of evictable metadata in mfu state"); SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, mfu_data_esize, CTLFLAG_RD, &ARC_mfu.arcs_esize[ARC_BUFC_DATA].rc_count, 0, "size of evictable data in mfu state"); /* END CSTYLED */ extern arc_state_t ARC_mfu_ghost; /* BEGIN CSTYLED */ SYSCTL_PROC(_vfs_zfs, OID_AUTO, mfu_ghost_size, CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, &ARC_mfu_ghost, 0, param_get_arc_state_size, "Q", "size of mfu ghost state"); SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, mfu_ghost_metadata_esize, CTLFLAG_RD, &ARC_mfu_ghost.arcs_esize[ARC_BUFC_METADATA].rc_count, 0, "size of evictable metadata in mfu ghost state"); SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, mfu_ghost_data_esize, CTLFLAG_RD, &ARC_mfu_ghost.arcs_esize[ARC_BUFC_DATA].rc_count, 0, "size of evictable data in mfu ghost state"); /* END CSTYLED */ extern arc_state_t ARC_uncached; /* BEGIN CSTYLED */ SYSCTL_PROC(_vfs_zfs, OID_AUTO, uncached_size, CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, &ARC_uncached, 0, param_get_arc_state_size, "Q", "size of uncached state"); SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, uncached_metadata_esize, CTLFLAG_RD, &ARC_uncached.arcs_esize[ARC_BUFC_METADATA].rc_count, 0, "size of evictable metadata in uncached state"); SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, uncached_data_esize, CTLFLAG_RD, &ARC_uncached.arcs_esize[ARC_BUFC_DATA].rc_count, 0, "size of evictable data in uncached state"); /* END CSTYLED */ extern arc_state_t ARC_l2c_only; /* BEGIN CSTYLED */ SYSCTL_PROC(_vfs_zfs, OID_AUTO, l2c_only_size, CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, &ARC_l2c_only, 0, param_get_arc_state_size, "Q", "size of l2c_only state"); /* END CSTYLED */ /* dbuf.c */ /* dmu.c */ /* dmu_zfetch.c */ SYSCTL_NODE(_vfs_zfs, OID_AUTO, zfetch, CTLFLAG_RW, 0, "ZFS ZFETCH (LEGACY)"); extern uint32_t zfetch_max_distance; /* BEGIN CSTYLED */ SYSCTL_UINT(_vfs_zfs_zfetch, OID_AUTO, max_distance, CTLFLAG_RWTUN, &zfetch_max_distance, 0, "Max bytes to prefetch per stream (LEGACY)"); /* END CSTYLED */ extern uint32_t zfetch_max_idistance; /* BEGIN CSTYLED */ SYSCTL_UINT(_vfs_zfs_zfetch, OID_AUTO, max_idistance, CTLFLAG_RWTUN, &zfetch_max_idistance, 0, "Max bytes to prefetch indirects for per stream (LEGACY)"); /* END CSTYLED */ /* dsl_pool.c */ /* dnode.c */ /* dsl_scan.c */ /* metaslab.c */ int param_set_active_allocator(SYSCTL_HANDLER_ARGS) { char buf[16]; int rc; if (req->newptr == NULL) strlcpy(buf, zfs_active_allocator, sizeof (buf)); rc = sysctl_handle_string(oidp, buf, sizeof (buf), req); if (rc || req->newptr == NULL) return (rc); if (strcmp(buf, zfs_active_allocator) == 0) return (0); return (param_set_active_allocator_common(buf)); } /* * In pools where the log space map feature is not enabled we touch * multiple metaslabs (and their respective space maps) with each * transaction group. Thus, we benefit from having a small space map * block size since it allows us to issue more I/O operations scattered * around the disk. So a sane default for the space map block size * is 8~16K. */ extern int zfs_metaslab_sm_blksz_no_log; /* BEGIN CSTYLED */ SYSCTL_INT(_vfs_zfs_metaslab, OID_AUTO, sm_blksz_no_log, CTLFLAG_RDTUN, &zfs_metaslab_sm_blksz_no_log, 0, "Block size for space map in pools with log space map disabled. " "Power of 2 greater than 4096."); /* END CSTYLED */ /* * When the log space map feature is enabled, we accumulate a lot of * changes per metaslab that are flushed once in a while so we benefit * from a bigger block size like 128K for the metaslab space maps. */ extern int zfs_metaslab_sm_blksz_with_log; /* BEGIN CSTYLED */ SYSCTL_INT(_vfs_zfs_metaslab, OID_AUTO, sm_blksz_with_log, CTLFLAG_RDTUN, &zfs_metaslab_sm_blksz_with_log, 0, "Block size for space map in pools with log space map enabled. " "Power of 2 greater than 4096."); /* END CSTYLED */ /* * The in-core space map representation is more compact than its on-disk form. * The zfs_condense_pct determines how much more compact the in-core * space map representation must be before we compact it on-disk. * Values should be greater than or equal to 100. */ extern uint_t zfs_condense_pct; /* BEGIN CSTYLED */ SYSCTL_UINT(_vfs_zfs, OID_AUTO, condense_pct, CTLFLAG_RWTUN, &zfs_condense_pct, 0, "Condense on-disk spacemap when it is more than this many percents" " of in-memory counterpart"); /* END CSTYLED */ extern uint_t zfs_remove_max_segment; /* BEGIN CSTYLED */ SYSCTL_UINT(_vfs_zfs, OID_AUTO, remove_max_segment, CTLFLAG_RWTUN, &zfs_remove_max_segment, 0, "Largest contiguous segment ZFS will attempt to allocate when removing" " a device"); /* END CSTYLED */ extern int zfs_removal_suspend_progress; /* BEGIN CSTYLED */ SYSCTL_INT(_vfs_zfs, OID_AUTO, removal_suspend_progress, CTLFLAG_RWTUN, &zfs_removal_suspend_progress, 0, "Ensures certain actions can happen while in the middle of a removal"); /* END CSTYLED */ /* * Minimum size which forces the dynamic allocator to change * it's allocation strategy. Once the space map cannot satisfy * an allocation of this size then it switches to using more * aggressive strategy (i.e search by size rather than offset). */ extern uint64_t metaslab_df_alloc_threshold; /* BEGIN CSTYLED */ SYSCTL_QUAD(_vfs_zfs_metaslab, OID_AUTO, df_alloc_threshold, CTLFLAG_RWTUN, &metaslab_df_alloc_threshold, 0, "Minimum size which forces the dynamic allocator to change its" " allocation strategy"); /* END CSTYLED */ /* * The minimum free space, in percent, which must be available * in a space map to continue allocations in a first-fit fashion. * Once the space map's free space drops below this level we dynamically * switch to using best-fit allocations. */ extern uint_t metaslab_df_free_pct; /* BEGIN CSTYLED */ SYSCTL_UINT(_vfs_zfs_metaslab, OID_AUTO, df_free_pct, CTLFLAG_RWTUN, &metaslab_df_free_pct, 0, "The minimum free space, in percent, which must be available in a" " space map to continue allocations in a first-fit fashion"); /* END CSTYLED */ /* mmp.c */ int param_set_multihost_interval(SYSCTL_HANDLER_ARGS) { int err; err = sysctl_handle_64(oidp, &zfs_multihost_interval, 0, req); if (err != 0 || req->newptr == NULL) return (err); if (spa_mode_global != SPA_MODE_UNINIT) mmp_signal_all_threads(); return (0); } /* spa.c */ extern int zfs_ccw_retry_interval; /* BEGIN CSTYLED */ SYSCTL_INT(_vfs_zfs, OID_AUTO, ccw_retry_interval, CTLFLAG_RWTUN, &zfs_ccw_retry_interval, 0, "Configuration cache file write, retry after failure, interval" " (seconds)"); /* END CSTYLED */ extern uint64_t zfs_max_missing_tvds_cachefile; /* BEGIN CSTYLED */ SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, max_missing_tvds_cachefile, CTLFLAG_RWTUN, &zfs_max_missing_tvds_cachefile, 0, "Allow importing pools with missing top-level vdevs in cache file"); /* END CSTYLED */ extern uint64_t zfs_max_missing_tvds_scan; /* BEGIN CSTYLED */ SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, max_missing_tvds_scan, CTLFLAG_RWTUN, &zfs_max_missing_tvds_scan, 0, "Allow importing pools with missing top-level vdevs during scan"); /* END CSTYLED */ /* spa_misc.c */ extern int zfs_flags; static int sysctl_vfs_zfs_debug_flags(SYSCTL_HANDLER_ARGS) { int err, val; val = zfs_flags; err = sysctl_handle_int(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL) return (err); /* * ZFS_DEBUG_MODIFY must be enabled prior to boot so all * arc buffers in the system have the necessary additional * checksum data. However, it is safe to disable at any * time. */ if (!(zfs_flags & ZFS_DEBUG_MODIFY)) val &= ~ZFS_DEBUG_MODIFY; zfs_flags = val; return (0); } /* BEGIN CSTYLED */ SYSCTL_PROC(_vfs_zfs, OID_AUTO, debugflags, CTLTYPE_UINT | CTLFLAG_MPSAFE | CTLFLAG_RWTUN, NULL, 0, sysctl_vfs_zfs_debug_flags, "IU", "Debug flags for ZFS testing."); /* END CSTYLED */ int param_set_deadman_synctime(SYSCTL_HANDLER_ARGS) { unsigned long val; int err; val = zfs_deadman_synctime_ms; err = sysctl_handle_64(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL) return (err); zfs_deadman_synctime_ms = val; spa_set_deadman_synctime(MSEC2NSEC(zfs_deadman_synctime_ms)); return (0); } int param_set_deadman_ziotime(SYSCTL_HANDLER_ARGS) { unsigned long val; int err; val = zfs_deadman_ziotime_ms; err = sysctl_handle_64(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL) return (err); zfs_deadman_ziotime_ms = val; spa_set_deadman_ziotime(MSEC2NSEC(zfs_deadman_synctime_ms)); return (0); } int param_set_deadman_failmode(SYSCTL_HANDLER_ARGS) { char buf[16]; int rc; if (req->newptr == NULL) strlcpy(buf, zfs_deadman_failmode, sizeof (buf)); rc = sysctl_handle_string(oidp, buf, sizeof (buf), req); if (rc || req->newptr == NULL) return (rc); if (strcmp(buf, zfs_deadman_failmode) == 0) return (0); if (strcmp(buf, "wait") == 0) zfs_deadman_failmode = "wait"; if (strcmp(buf, "continue") == 0) zfs_deadman_failmode = "continue"; if (strcmp(buf, "panic") == 0) zfs_deadman_failmode = "panic"; return (-param_set_deadman_failmode_common(buf)); } int param_set_slop_shift(SYSCTL_HANDLER_ARGS) { int val; int err; val = spa_slop_shift; err = sysctl_handle_int(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL) return (err); if (val < 1 || val > 31) return (EINVAL); spa_slop_shift = val; return (0); } /* spacemap.c */ extern int space_map_ibs; /* BEGIN CSTYLED */ SYSCTL_INT(_vfs_zfs, OID_AUTO, space_map_ibs, CTLFLAG_RWTUN, &space_map_ibs, 0, "Space map indirect block shift"); /* END CSTYLED */ /* vdev.c */ int param_set_min_auto_ashift(SYSCTL_HANDLER_ARGS) { int val; int err; val = zfs_vdev_min_auto_ashift; err = sysctl_handle_int(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL) return (SET_ERROR(err)); if (val < ASHIFT_MIN || val > zfs_vdev_max_auto_ashift) return (SET_ERROR(EINVAL)); zfs_vdev_min_auto_ashift = val; return (0); } /* BEGIN CSTYLED */ SYSCTL_PROC(_vfs_zfs, OID_AUTO, min_auto_ashift, CTLTYPE_UINT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, &zfs_vdev_min_auto_ashift, sizeof (zfs_vdev_min_auto_ashift), param_set_min_auto_ashift, "IU", "Min ashift used when creating new top-level vdev. (LEGACY)"); /* END CSTYLED */ int param_set_max_auto_ashift(SYSCTL_HANDLER_ARGS) { int val; int err; val = zfs_vdev_max_auto_ashift; err = sysctl_handle_int(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL) return (SET_ERROR(err)); if (val > ASHIFT_MAX || val < zfs_vdev_min_auto_ashift) return (SET_ERROR(EINVAL)); zfs_vdev_max_auto_ashift = val; return (0); } /* BEGIN CSTYLED */ SYSCTL_PROC(_vfs_zfs, OID_AUTO, max_auto_ashift, CTLTYPE_UINT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, &zfs_vdev_max_auto_ashift, sizeof (zfs_vdev_max_auto_ashift), param_set_max_auto_ashift, "IU", "Max ashift used when optimizing for logical -> physical sector size on" " new top-level vdevs. (LEGACY)"); /* END CSTYLED */ /* * Since the DTL space map of a vdev is not expected to have a lot of * entries, we default its block size to 4K. */ extern int zfs_vdev_dtl_sm_blksz; /* BEGIN CSTYLED */ SYSCTL_INT(_vfs_zfs, OID_AUTO, dtl_sm_blksz, CTLFLAG_RDTUN, &zfs_vdev_dtl_sm_blksz, 0, "Block size for DTL space map. Power of 2 greater than 4096."); /* END CSTYLED */ /* * vdev-wide space maps that have lots of entries written to them at * the end of each transaction can benefit from a higher I/O bandwidth * (e.g. vdev_obsolete_sm), thus we default their block size to 128K. */ extern int zfs_vdev_standard_sm_blksz; /* BEGIN CSTYLED */ SYSCTL_INT(_vfs_zfs, OID_AUTO, standard_sm_blksz, CTLFLAG_RDTUN, &zfs_vdev_standard_sm_blksz, 0, "Block size for standard space map. Power of 2 greater than 4096."); /* END CSTYLED */ extern int vdev_validate_skip; /* BEGIN CSTYLED */ SYSCTL_INT(_vfs_zfs, OID_AUTO, validate_skip, CTLFLAG_RDTUN, &vdev_validate_skip, 0, "Enable to bypass vdev_validate()."); /* END CSTYLED */ /* vdev_mirror.c */ /* vdev_queue.c */ extern uint_t zfs_vdev_max_active; /* BEGIN CSTYLED */ SYSCTL_UINT(_vfs_zfs, OID_AUTO, top_maxinflight, CTLFLAG_RWTUN, &zfs_vdev_max_active, 0, "The maximum number of I/Os of all types active for each device." " (LEGACY)"); /* END CSTYLED */ /* zio.c */ /* BEGIN CSTYLED */ SYSCTL_INT(_vfs_zfs_zio, OID_AUTO, exclude_metadata, CTLFLAG_RDTUN, &zio_exclude_metadata, 0, "Exclude metadata buffers from dumps as well"); /* END CSTYLED */ diff --git a/module/os/freebsd/zfs/zfs_file_os.c b/module/os/freebsd/zfs/zfs_file_os.c index 60c9ff0581e0..9d68499d82ec 100644 --- a/module/os/freebsd/zfs/zfs_file_os.c +++ b/module/os/freebsd/zfs/zfs_file_os.c @@ -1,307 +1,304 @@ /* * Copyright (c) 2020 iXsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int zfs_file_open(const char *path, int flags, int mode, zfs_file_t **fpp) { struct thread *td; int rc, fd; td = curthread; pwd_ensure_dirs(); /* 12.x doesn't take a const char * */ rc = kern_openat(td, AT_FDCWD, __DECONST(char *, path), UIO_SYSSPACE, flags, mode); if (rc) return (SET_ERROR(rc)); fd = td->td_retval[0]; td->td_retval[0] = 0; if (fget(curthread, fd, &cap_no_rights, fpp)) kern_close(td, fd); return (0); } void zfs_file_close(zfs_file_t *fp) { fo_close(fp, curthread); } static int zfs_file_write_impl(zfs_file_t *fp, const void *buf, size_t count, loff_t *offp, ssize_t *resid) { ssize_t rc; struct uio auio; struct thread *td; struct iovec aiov; td = curthread; aiov.iov_base = (void *)(uintptr_t)buf; aiov.iov_len = count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_segflg = UIO_SYSSPACE; auio.uio_resid = count; auio.uio_rw = UIO_WRITE; auio.uio_td = td; auio.uio_offset = *offp; if ((fp->f_flag & FWRITE) == 0) return (SET_ERROR(EBADF)); if (fp->f_type == DTYPE_VNODE) bwillwrite(); rc = fo_write(fp, &auio, td->td_ucred, FOF_OFFSET, td); if (rc) return (SET_ERROR(rc)); if (resid) *resid = auio.uio_resid; else if (auio.uio_resid) return (SET_ERROR(EIO)); *offp += count - auio.uio_resid; return (rc); } int zfs_file_write(zfs_file_t *fp, const void *buf, size_t count, ssize_t *resid) { loff_t off = fp->f_offset; ssize_t rc; rc = zfs_file_write_impl(fp, buf, count, &off, resid); if (rc == 0) fp->f_offset = off; return (SET_ERROR(rc)); } int zfs_file_pwrite(zfs_file_t *fp, const void *buf, size_t count, loff_t off, ssize_t *resid) { return (zfs_file_write_impl(fp, buf, count, &off, resid)); } static int zfs_file_read_impl(zfs_file_t *fp, void *buf, size_t count, loff_t *offp, ssize_t *resid) { ssize_t rc; struct uio auio; struct thread *td; struct iovec aiov; td = curthread; aiov.iov_base = (void *)(uintptr_t)buf; aiov.iov_len = count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_segflg = UIO_SYSSPACE; auio.uio_resid = count; auio.uio_rw = UIO_READ; auio.uio_td = td; auio.uio_offset = *offp; if ((fp->f_flag & FREAD) == 0) return (SET_ERROR(EBADF)); rc = fo_read(fp, &auio, td->td_ucred, FOF_OFFSET, td); if (rc) return (SET_ERROR(rc)); if (resid) *resid = auio.uio_resid; *offp += count - auio.uio_resid; return (SET_ERROR(0)); } int zfs_file_read(zfs_file_t *fp, void *buf, size_t count, ssize_t *resid) { loff_t off = fp->f_offset; ssize_t rc; rc = zfs_file_read_impl(fp, buf, count, &off, resid); if (rc == 0) fp->f_offset = off; return (rc); } int zfs_file_pread(zfs_file_t *fp, void *buf, size_t count, loff_t off, ssize_t *resid) { return (zfs_file_read_impl(fp, buf, count, &off, resid)); } int zfs_file_seek(zfs_file_t *fp, loff_t *offp, int whence) { int rc; struct thread *td; td = curthread; if ((fp->f_ops->fo_flags & DFLAG_SEEKABLE) == 0) return (SET_ERROR(ESPIPE)); rc = fo_seek(fp, *offp, whence, td); if (rc == 0) *offp = td->td_uretoff.tdu_off; return (SET_ERROR(rc)); } int zfs_file_getattr(zfs_file_t *fp, zfs_file_attr_t *zfattr) { struct thread *td; struct stat sb; int rc; td = curthread; #if __FreeBSD_version < 1400037 rc = fo_stat(fp, &sb, td->td_ucred, td); #else rc = fo_stat(fp, &sb, td->td_ucred); #endif if (rc) return (SET_ERROR(rc)); zfattr->zfa_size = sb.st_size; zfattr->zfa_mode = sb.st_mode; return (0); } static __inline int zfs_vop_fsync(vnode_t *vp) { struct mount *mp; int error; #if __FreeBSD_version < 1400068 if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) #else if ((error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH)) != 0) #endif goto drop; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); error = VOP_FSYNC(vp, MNT_WAIT, curthread); VOP_UNLOCK1(vp); vn_finished_write(mp); drop: return (SET_ERROR(error)); } int zfs_file_fsync(zfs_file_t *fp, int flags) { if (fp->f_type != DTYPE_VNODE) return (EINVAL); return (zfs_vop_fsync(fp->f_vnode)); } zfs_file_t * zfs_file_get(int fd) { struct file *fp; if (fget(curthread, fd, &cap_no_rights, &fp)) return (NULL); return (fp); } void zfs_file_put(zfs_file_t *fp) { fdrop(fp, curthread); } loff_t zfs_file_off(zfs_file_t *fp) { return (fp->f_offset); } void * zfs_file_private(zfs_file_t *fp) { file_t *tmpfp; void *data; int error; tmpfp = curthread->td_fpop; curthread->td_fpop = fp; error = devfs_get_cdevpriv(&data); curthread->td_fpop = tmpfp; if (error != 0) return (NULL); return (data); } int zfs_file_unlink(const char *fnamep) { zfs_uio_seg_t seg = UIO_SYSSPACE; int rc; #if __FreeBSD_version >= 1300018 rc = kern_funlinkat(curthread, AT_FDCWD, fnamep, FD_NONE, seg, 0, 0); #elif __FreeBSD_version >= 1202504 || defined(AT_BENEATH) rc = kern_unlinkat(curthread, AT_FDCWD, __DECONST(char *, fnamep), seg, 0, 0); #else rc = kern_unlinkat(curthread, AT_FDCWD, __DECONST(char *, fnamep), seg, 0); #endif return (SET_ERROR(rc)); } diff --git a/module/os/freebsd/zfs/zfs_ioctl_compat.c b/module/os/freebsd/zfs/zfs_ioctl_compat.c index 3ddffec91e83..4a7beb650a8d 100644 --- a/module/os/freebsd/zfs/zfs_ioctl_compat.c +++ b/module/os/freebsd/zfs/zfs_ioctl_compat.c @@ -1,365 +1,362 @@ /* * Copyright (c) 2020 iXsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #include #ifdef ZFS_LEGACY_SUPPORT enum zfs_ioc_legacy { ZFS_IOC_LEGACY_NONE = -1, ZFS_IOC_LEGACY_FIRST = 0, ZFS_LEGACY_IOC = ZFS_IOC_LEGACY_FIRST, ZFS_IOC_LEGACY_POOL_CREATE = ZFS_IOC_LEGACY_FIRST, ZFS_IOC_LEGACY_POOL_DESTROY, ZFS_IOC_LEGACY_POOL_IMPORT, ZFS_IOC_LEGACY_POOL_EXPORT, ZFS_IOC_LEGACY_POOL_CONFIGS, ZFS_IOC_LEGACY_POOL_STATS, ZFS_IOC_LEGACY_POOL_TRYIMPORT, ZFS_IOC_LEGACY_POOL_SCAN, ZFS_IOC_LEGACY_POOL_FREEZE, ZFS_IOC_LEGACY_POOL_UPGRADE, ZFS_IOC_LEGACY_POOL_GET_HISTORY, ZFS_IOC_LEGACY_VDEV_ADD, ZFS_IOC_LEGACY_VDEV_REMOVE, ZFS_IOC_LEGACY_VDEV_SET_STATE, ZFS_IOC_LEGACY_VDEV_ATTACH, ZFS_IOC_LEGACY_VDEV_DETACH, ZFS_IOC_LEGACY_VDEV_SETPATH, ZFS_IOC_LEGACY_VDEV_SETFRU, ZFS_IOC_LEGACY_OBJSET_STATS, ZFS_IOC_LEGACY_OBJSET_ZPLPROPS, ZFS_IOC_LEGACY_DATASET_LIST_NEXT, ZFS_IOC_LEGACY_SNAPSHOT_LIST_NEXT, ZFS_IOC_LEGACY_SET_PROP, ZFS_IOC_LEGACY_CREATE, ZFS_IOC_LEGACY_DESTROY, ZFS_IOC_LEGACY_ROLLBACK, ZFS_IOC_LEGACY_RENAME, ZFS_IOC_LEGACY_RECV, ZFS_IOC_LEGACY_SEND, ZFS_IOC_LEGACY_INJECT_FAULT, ZFS_IOC_LEGACY_CLEAR_FAULT, ZFS_IOC_LEGACY_INJECT_LIST_NEXT, ZFS_IOC_LEGACY_ERROR_LOG, ZFS_IOC_LEGACY_CLEAR, ZFS_IOC_LEGACY_PROMOTE, ZFS_IOC_LEGACY_DESTROY_SNAPS, ZFS_IOC_LEGACY_SNAPSHOT, ZFS_IOC_LEGACY_DSOBJ_TO_DSNAME, ZFS_IOC_LEGACY_OBJ_TO_PATH, ZFS_IOC_LEGACY_POOL_SET_PROPS, ZFS_IOC_LEGACY_POOL_GET_PROPS, ZFS_IOC_LEGACY_SET_FSACL, ZFS_IOC_LEGACY_GET_FSACL, ZFS_IOC_LEGACY_SHARE, ZFS_IOC_LEGACY_INHERIT_PROP, ZFS_IOC_LEGACY_SMB_ACL, ZFS_IOC_LEGACY_USERSPACE_ONE, ZFS_IOC_LEGACY_USERSPACE_MANY, ZFS_IOC_LEGACY_USERSPACE_UPGRADE, ZFS_IOC_LEGACY_HOLD, ZFS_IOC_LEGACY_RELEASE, ZFS_IOC_LEGACY_GET_HOLDS, ZFS_IOC_LEGACY_OBJSET_RECVD_PROPS, ZFS_IOC_LEGACY_VDEV_SPLIT, ZFS_IOC_LEGACY_NEXT_OBJ, ZFS_IOC_LEGACY_DIFF, ZFS_IOC_LEGACY_TMP_SNAPSHOT, ZFS_IOC_LEGACY_OBJ_TO_STATS, ZFS_IOC_LEGACY_JAIL, ZFS_IOC_LEGACY_UNJAIL, ZFS_IOC_LEGACY_POOL_REGUID, ZFS_IOC_LEGACY_SPACE_WRITTEN, ZFS_IOC_LEGACY_SPACE_SNAPS, ZFS_IOC_LEGACY_SEND_PROGRESS, ZFS_IOC_LEGACY_POOL_REOPEN, ZFS_IOC_LEGACY_LOG_HISTORY, ZFS_IOC_LEGACY_SEND_NEW, ZFS_IOC_LEGACY_SEND_SPACE, ZFS_IOC_LEGACY_CLONE, ZFS_IOC_LEGACY_BOOKMARK, ZFS_IOC_LEGACY_GET_BOOKMARKS, ZFS_IOC_LEGACY_DESTROY_BOOKMARKS, ZFS_IOC_LEGACY_NEXTBOOT, ZFS_IOC_LEGACY_CHANNEL_PROGRAM, ZFS_IOC_LEGACY_REMAP, ZFS_IOC_LEGACY_POOL_CHECKPOINT, ZFS_IOC_LEGACY_POOL_DISCARD_CHECKPOINT, ZFS_IOC_LEGACY_POOL_INITIALIZE, ZFS_IOC_LEGACY_POOL_SYNC, ZFS_IOC_LEGACY_LAST }; static unsigned long zfs_ioctl_legacy_to_ozfs_[] = { ZFS_IOC_POOL_CREATE, /* 0x00 */ ZFS_IOC_POOL_DESTROY, /* 0x01 */ ZFS_IOC_POOL_IMPORT, /* 0x02 */ ZFS_IOC_POOL_EXPORT, /* 0x03 */ ZFS_IOC_POOL_CONFIGS, /* 0x04 */ ZFS_IOC_POOL_STATS, /* 0x05 */ ZFS_IOC_POOL_TRYIMPORT, /* 0x06 */ ZFS_IOC_POOL_SCAN, /* 0x07 */ ZFS_IOC_POOL_FREEZE, /* 0x08 */ ZFS_IOC_POOL_UPGRADE, /* 0x09 */ ZFS_IOC_POOL_GET_HISTORY, /* 0x0a */ ZFS_IOC_VDEV_ADD, /* 0x0b */ ZFS_IOC_VDEV_REMOVE, /* 0x0c */ ZFS_IOC_VDEV_SET_STATE, /* 0x0d */ ZFS_IOC_VDEV_ATTACH, /* 0x0e */ ZFS_IOC_VDEV_DETACH, /* 0x0f */ ZFS_IOC_VDEV_SETPATH, /* 0x10 */ ZFS_IOC_VDEV_SETFRU, /* 0x11 */ ZFS_IOC_OBJSET_STATS, /* 0x12 */ ZFS_IOC_OBJSET_ZPLPROPS, /* 0x13 */ ZFS_IOC_DATASET_LIST_NEXT, /* 0x14 */ ZFS_IOC_SNAPSHOT_LIST_NEXT, /* 0x15 */ ZFS_IOC_SET_PROP, /* 0x16 */ ZFS_IOC_CREATE, /* 0x17 */ ZFS_IOC_DESTROY, /* 0x18 */ ZFS_IOC_ROLLBACK, /* 0x19 */ ZFS_IOC_RENAME, /* 0x1a */ ZFS_IOC_RECV, /* 0x1b */ ZFS_IOC_SEND, /* 0x1c */ ZFS_IOC_INJECT_FAULT, /* 0x1d */ ZFS_IOC_CLEAR_FAULT, /* 0x1e */ ZFS_IOC_INJECT_LIST_NEXT, /* 0x1f */ ZFS_IOC_ERROR_LOG, /* 0x20 */ ZFS_IOC_CLEAR, /* 0x21 */ ZFS_IOC_PROMOTE, /* 0x22 */ /* start of mismatch */ ZFS_IOC_DESTROY_SNAPS, /* 0x23:0x3b */ ZFS_IOC_SNAPSHOT, /* 0x24:0x23 */ ZFS_IOC_DSOBJ_TO_DSNAME, /* 0x25:0x24 */ ZFS_IOC_OBJ_TO_PATH, /* 0x26:0x25 */ ZFS_IOC_POOL_SET_PROPS, /* 0x27:0x26 */ ZFS_IOC_POOL_GET_PROPS, /* 0x28:0x27 */ ZFS_IOC_SET_FSACL, /* 0x29:0x28 */ ZFS_IOC_GET_FSACL, /* 0x30:0x29 */ ZFS_IOC_SHARE, /* 0x2b:0x2a */ ZFS_IOC_INHERIT_PROP, /* 0x2c:0x2b */ ZFS_IOC_SMB_ACL, /* 0x2d:0x2c */ ZFS_IOC_USERSPACE_ONE, /* 0x2e:0x2d */ ZFS_IOC_USERSPACE_MANY, /* 0x2f:0x2e */ ZFS_IOC_USERSPACE_UPGRADE, /* 0x30:0x2f */ ZFS_IOC_HOLD, /* 0x31:0x30 */ ZFS_IOC_RELEASE, /* 0x32:0x31 */ ZFS_IOC_GET_HOLDS, /* 0x33:0x32 */ ZFS_IOC_OBJSET_RECVD_PROPS, /* 0x34:0x33 */ ZFS_IOC_VDEV_SPLIT, /* 0x35:0x34 */ ZFS_IOC_NEXT_OBJ, /* 0x36:0x35 */ ZFS_IOC_DIFF, /* 0x37:0x36 */ ZFS_IOC_TMP_SNAPSHOT, /* 0x38:0x37 */ ZFS_IOC_OBJ_TO_STATS, /* 0x39:0x38 */ ZFS_IOC_JAIL, /* 0x3a:0xc2 */ ZFS_IOC_UNJAIL, /* 0x3b:0xc3 */ ZFS_IOC_POOL_REGUID, /* 0x3c:0x3c */ ZFS_IOC_SPACE_WRITTEN, /* 0x3d:0x39 */ ZFS_IOC_SPACE_SNAPS, /* 0x3e:0x3a */ ZFS_IOC_SEND_PROGRESS, /* 0x3f:0x3e */ ZFS_IOC_POOL_REOPEN, /* 0x40:0x3d */ ZFS_IOC_LOG_HISTORY, /* 0x41:0x3f */ ZFS_IOC_SEND_NEW, /* 0x42:0x40 */ ZFS_IOC_SEND_SPACE, /* 0x43:0x41 */ ZFS_IOC_CLONE, /* 0x44:0x42 */ ZFS_IOC_BOOKMARK, /* 0x45:0x43 */ ZFS_IOC_GET_BOOKMARKS, /* 0x46:0x44 */ ZFS_IOC_DESTROY_BOOKMARKS, /* 0x47:0x45 */ ZFS_IOC_NEXTBOOT, /* 0x48:0xc1 */ ZFS_IOC_CHANNEL_PROGRAM, /* 0x49:0x48 */ ZFS_IOC_REMAP, /* 0x4a:0x4c */ ZFS_IOC_POOL_CHECKPOINT, /* 0x4b:0x4d */ ZFS_IOC_POOL_DISCARD_CHECKPOINT, /* 0x4c:0x4e */ ZFS_IOC_POOL_INITIALIZE, /* 0x4d:0x4f */ }; static unsigned long zfs_ioctl_ozfs_to_legacy_common_[] = { ZFS_IOC_POOL_CREATE, /* 0x00 */ ZFS_IOC_POOL_DESTROY, /* 0x01 */ ZFS_IOC_POOL_IMPORT, /* 0x02 */ ZFS_IOC_POOL_EXPORT, /* 0x03 */ ZFS_IOC_POOL_CONFIGS, /* 0x04 */ ZFS_IOC_POOL_STATS, /* 0x05 */ ZFS_IOC_POOL_TRYIMPORT, /* 0x06 */ ZFS_IOC_POOL_SCAN, /* 0x07 */ ZFS_IOC_POOL_FREEZE, /* 0x08 */ ZFS_IOC_POOL_UPGRADE, /* 0x09 */ ZFS_IOC_POOL_GET_HISTORY, /* 0x0a */ ZFS_IOC_VDEV_ADD, /* 0x0b */ ZFS_IOC_VDEV_REMOVE, /* 0x0c */ ZFS_IOC_VDEV_SET_STATE, /* 0x0d */ ZFS_IOC_VDEV_ATTACH, /* 0x0e */ ZFS_IOC_VDEV_DETACH, /* 0x0f */ ZFS_IOC_VDEV_SETPATH, /* 0x10 */ ZFS_IOC_VDEV_SETFRU, /* 0x11 */ ZFS_IOC_OBJSET_STATS, /* 0x12 */ ZFS_IOC_OBJSET_ZPLPROPS, /* 0x13 */ ZFS_IOC_DATASET_LIST_NEXT, /* 0x14 */ ZFS_IOC_SNAPSHOT_LIST_NEXT, /* 0x15 */ ZFS_IOC_SET_PROP, /* 0x16 */ ZFS_IOC_CREATE, /* 0x17 */ ZFS_IOC_DESTROY, /* 0x18 */ ZFS_IOC_ROLLBACK, /* 0x19 */ ZFS_IOC_RENAME, /* 0x1a */ ZFS_IOC_RECV, /* 0x1b */ ZFS_IOC_SEND, /* 0x1c */ ZFS_IOC_INJECT_FAULT, /* 0x1d */ ZFS_IOC_CLEAR_FAULT, /* 0x1e */ ZFS_IOC_INJECT_LIST_NEXT, /* 0x1f */ ZFS_IOC_ERROR_LOG, /* 0x20 */ ZFS_IOC_CLEAR, /* 0x21 */ ZFS_IOC_PROMOTE, /* 0x22 */ /* start of mismatch */ ZFS_IOC_LEGACY_SNAPSHOT, /* 0x23 */ ZFS_IOC_LEGACY_DSOBJ_TO_DSNAME, /* 0x24 */ ZFS_IOC_LEGACY_OBJ_TO_PATH, /* 0x25 */ ZFS_IOC_LEGACY_POOL_SET_PROPS, /* 0x26 */ ZFS_IOC_LEGACY_POOL_GET_PROPS, /* 0x27 */ ZFS_IOC_LEGACY_SET_FSACL, /* 0x28 */ ZFS_IOC_LEGACY_GET_FSACL, /* 0x29 */ ZFS_IOC_LEGACY_SHARE, /* 0x2a */ ZFS_IOC_LEGACY_INHERIT_PROP, /* 0x2b */ ZFS_IOC_LEGACY_SMB_ACL, /* 0x2c */ ZFS_IOC_LEGACY_USERSPACE_ONE, /* 0x2d */ ZFS_IOC_LEGACY_USERSPACE_MANY, /* 0x2e */ ZFS_IOC_LEGACY_USERSPACE_UPGRADE, /* 0x2f */ ZFS_IOC_LEGACY_HOLD, /* 0x30 */ ZFS_IOC_LEGACY_RELEASE, /* 0x31 */ ZFS_IOC_LEGACY_GET_HOLDS, /* 0x32 */ ZFS_IOC_LEGACY_OBJSET_RECVD_PROPS, /* 0x33 */ ZFS_IOC_LEGACY_VDEV_SPLIT, /* 0x34 */ ZFS_IOC_LEGACY_NEXT_OBJ, /* 0x35 */ ZFS_IOC_LEGACY_DIFF, /* 0x36 */ ZFS_IOC_LEGACY_TMP_SNAPSHOT, /* 0x37 */ ZFS_IOC_LEGACY_OBJ_TO_STATS, /* 0x38 */ ZFS_IOC_LEGACY_SPACE_WRITTEN, /* 0x39 */ ZFS_IOC_LEGACY_SPACE_SNAPS, /* 0x3a */ ZFS_IOC_LEGACY_DESTROY_SNAPS, /* 0x3b */ ZFS_IOC_LEGACY_POOL_REGUID, /* 0x3c */ ZFS_IOC_LEGACY_POOL_REOPEN, /* 0x3d */ ZFS_IOC_LEGACY_SEND_PROGRESS, /* 0x3e */ ZFS_IOC_LEGACY_LOG_HISTORY, /* 0x3f */ ZFS_IOC_LEGACY_SEND_NEW, /* 0x40 */ ZFS_IOC_LEGACY_SEND_SPACE, /* 0x41 */ ZFS_IOC_LEGACY_CLONE, /* 0x42 */ ZFS_IOC_LEGACY_BOOKMARK, /* 0x43 */ ZFS_IOC_LEGACY_GET_BOOKMARKS, /* 0x44 */ ZFS_IOC_LEGACY_DESTROY_BOOKMARKS, /* 0x45 */ ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_RECV_NEW */ ZFS_IOC_LEGACY_POOL_SYNC, /* 0x47 */ ZFS_IOC_LEGACY_CHANNEL_PROGRAM, /* 0x48 */ ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_LOAD_KEY */ ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_UNLOAD_KEY */ ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_CHANGE_KEY */ ZFS_IOC_LEGACY_REMAP, /* 0x4c */ ZFS_IOC_LEGACY_POOL_CHECKPOINT, /* 0x4d */ ZFS_IOC_LEGACY_POOL_DISCARD_CHECKPOINT, /* 0x4e */ ZFS_IOC_LEGACY_POOL_INITIALIZE, /* 0x4f */ ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_POOL_TRIM */ ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_REDACT */ ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_GET_BOOKMARK_PROPS */ ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_WAIT */ ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_WAIT_FS */ }; static unsigned long zfs_ioctl_ozfs_to_legacy_platform_[] = { ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_EVENTS_NEXT */ ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_EVENTS_CLEAR */ ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_EVENTS_SEEK */ ZFS_IOC_LEGACY_NEXTBOOT, ZFS_IOC_LEGACY_JAIL, ZFS_IOC_LEGACY_UNJAIL, ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_SET_BOOTENV */ ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_GET_BOOTENV */ }; int zfs_ioctl_legacy_to_ozfs(int request) { if (request >= sizeof (zfs_ioctl_legacy_to_ozfs_)/sizeof (long)) return (-1); return (zfs_ioctl_legacy_to_ozfs_[request]); } int zfs_ioctl_ozfs_to_legacy(int request) { if (request >= ZFS_IOC_LAST) return (-1); if (request > ZFS_IOC_PLATFORM) { request -= ZFS_IOC_PLATFORM + 1; return (zfs_ioctl_ozfs_to_legacy_platform_[request]); } if (request >= sizeof (zfs_ioctl_ozfs_to_legacy_common_)/sizeof (long)) return (-1); return (zfs_ioctl_ozfs_to_legacy_common_[request]); } void zfs_cmd_legacy_to_ozfs(zfs_cmd_legacy_t *src, zfs_cmd_t *dst) { memcpy(dst, src, offsetof(zfs_cmd_t, zc_objset_stats)); *&dst->zc_objset_stats = *&src->zc_objset_stats; memcpy(&dst->zc_begin_record, &src->zc_begin_record, offsetof(zfs_cmd_t, zc_sendobj) - offsetof(zfs_cmd_t, zc_begin_record)); memcpy(&dst->zc_sendobj, &src->zc_sendobj, sizeof (zfs_cmd_t) - 8 - offsetof(zfs_cmd_t, zc_sendobj)); dst->zc_zoneid = src->zc_jailid; } void zfs_cmd_ozfs_to_legacy(zfs_cmd_t *src, zfs_cmd_legacy_t *dst) { memcpy(dst, src, offsetof(zfs_cmd_t, zc_objset_stats)); *&dst->zc_objset_stats = *&src->zc_objset_stats; *&dst->zc_begin_record.drr_u.drr_begin = *&src->zc_begin_record; dst->zc_begin_record.drr_payloadlen = 0; dst->zc_begin_record.drr_type = 0; memcpy(&dst->zc_inject_record, &src->zc_inject_record, offsetof(zfs_cmd_t, zc_sendobj) - offsetof(zfs_cmd_t, zc_inject_record)); dst->zc_resumable = B_FALSE; memcpy(&dst->zc_sendobj, &src->zc_sendobj, sizeof (zfs_cmd_t) - 8 - offsetof(zfs_cmd_t, zc_sendobj)); dst->zc_jailid = src->zc_zoneid; } #endif /* ZFS_LEGACY_SUPPORT */ diff --git a/module/os/freebsd/zfs/zfs_ioctl_os.c b/module/os/freebsd/zfs/zfs_ioctl_os.c index a835e013d630..b8f5fa4e7543 100644 --- a/module/os/freebsd/zfs/zfs_ioctl_os.c +++ b/module/os/freebsd/zfs/zfs_ioctl_os.c @@ -1,178 +1,175 @@ /* * Copyright (c) 2020 iXsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include -__FBSDID("$FreeBSD$"); - #include #include #include #include #include #include #include #include #include #if __FreeBSD_version < 1201517 #define vm_page_max_user_wired vm_page_max_wired #endif int zfs_vfs_ref(zfsvfs_t **zfvp) { int error = 0; if (*zfvp == NULL) return (SET_ERROR(ESRCH)); error = vfs_busy((*zfvp)->z_vfs, 0); if (error != 0) { *zfvp = NULL; error = SET_ERROR(ESRCH); } return (error); } boolean_t zfs_vfs_held(zfsvfs_t *zfsvfs) { return (zfsvfs->z_vfs != NULL); } void zfs_vfs_rele(zfsvfs_t *zfsvfs) { vfs_unbusy(zfsvfs->z_vfs); } static const zfs_ioc_key_t zfs_keys_nextboot[] = { {"command", DATA_TYPE_STRING, 0}, { ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64, 0}, { ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64, 0} }; static int zfs_ioc_jail(zfs_cmd_t *zc) { return (zone_dataset_attach(curthread->td_ucred, zc->zc_name, (int)zc->zc_zoneid)); } static int zfs_ioc_unjail(zfs_cmd_t *zc) { return (zone_dataset_detach(curthread->td_ucred, zc->zc_name, (int)zc->zc_zoneid)); } static int zfs_ioc_nextboot(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) { char name[MAXNAMELEN]; spa_t *spa; vdev_t *vd; const char *command; uint64_t pool_guid; uint64_t vdev_guid; int error; if (nvlist_lookup_uint64(innvl, ZPOOL_CONFIG_POOL_GUID, &pool_guid) != 0) return (EINVAL); if (nvlist_lookup_uint64(innvl, ZPOOL_CONFIG_GUID, &vdev_guid) != 0) return (EINVAL); if (nvlist_lookup_string(innvl, "command", &command) != 0) return (EINVAL); mutex_enter(&spa_namespace_lock); spa = spa_by_guid(pool_guid, vdev_guid); if (spa != NULL) strcpy(name, spa_name(spa)); mutex_exit(&spa_namespace_lock); if (spa == NULL) return (ENOENT); if ((error = spa_open(name, &spa, FTAG)) != 0) return (error); spa_vdev_state_enter(spa, SCL_ALL); vd = spa_lookup_by_guid(spa, vdev_guid, B_TRUE); if (vd == NULL) { (void) spa_vdev_state_exit(spa, NULL, ENXIO); spa_close(spa, FTAG); return (ENODEV); } error = vdev_label_write_pad2(vd, command, strlen(command)); (void) spa_vdev_state_exit(spa, NULL, 0); txg_wait_synced(spa->spa_dsl_pool, 0); spa_close(spa, FTAG); return (error); } /* Update the VFS's cache of mountpoint properties */ void zfs_ioctl_update_mount_cache(const char *dsname) { zfsvfs_t *zfsvfs; if (getzfsvfs(dsname, &zfsvfs) == 0) { struct mount *mp = zfsvfs->z_vfs; VFS_STATFS(mp, &mp->mnt_stat); zfs_vfs_rele(zfsvfs); } /* * Ignore errors; we can't do anything useful if either getzfsvfs or * VFS_STATFS fails. */ } uint64_t zfs_max_nvlist_src_size_os(void) { if (zfs_max_nvlist_src_size != 0) return (zfs_max_nvlist_src_size); return (ptob(vm_page_max_user_wired) / 4); } void zfs_ioctl_init_os(void) { zfs_ioctl_register_dataset_nolog(ZFS_IOC_JAIL, zfs_ioc_jail, zfs_secpolicy_config, POOL_CHECK_NONE); zfs_ioctl_register_dataset_nolog(ZFS_IOC_UNJAIL, zfs_ioc_unjail, zfs_secpolicy_config, POOL_CHECK_NONE); zfs_ioctl_register("fbsd_nextboot", ZFS_IOC_NEXTBOOT, zfs_ioc_nextboot, zfs_secpolicy_config, NO_NAME, POOL_CHECK_NONE, B_FALSE, B_FALSE, zfs_keys_nextboot, 3); }