Print this page
NEX-16818 Add fksmbcl development tool
NEX-17264 SMB client test tp_smbutil_013 fails after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
and: (fix ref leaks)
NEX-16783 Panic in smbfs_delmap_callback (cstyle)
NEX-16783 Panic in smbfs_delmap_callback (fix leak)
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
5404 smbfs needs mmap support
Portions contributed by: Gordon Ross <gordon.w.ross@gmail.com>
Reviewed by: C Fraire <cfraire@me.com>
Reviewed by: Toomas Soome <tsoome@me.com>
Reviewed by: Jason King <jason.brian.king@gmail.com>
Reviewed by: Andrew Stormont <andyjstormont@gmail.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
1586 mount_smbfs doesn't document noacl
Reviewed by: Jason King <jason.brian.king@gmail.com>
Reviewed by: C Fraire <cfraire@me.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
        
@@ -34,10 +34,11 @@
 
 /*
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2013, Joyent, Inc. All rights reserved.
  * Copyright (c) 2016 by Delphix. All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #include <sys/systm.h>
 #include <sys/cred.h>
 #include <sys/time.h>
@@ -49,18 +50,20 @@
 #include <sys/mkdev.h>
 #include <sys/mount.h>
 #include <sys/statvfs.h>
 #include <sys/errno.h>
 #include <sys/debug.h>
+#include <sys/disp.h>
 #include <sys/cmn_err.h>
 #include <sys/modctl.h>
 #include <sys/policy.h>
 #include <sys/atomic.h>
 #include <sys/zone.h>
 #include <sys/vfs_opreg.h>
 #include <sys/mntent.h>
 #include <sys/priv.h>
+#include <sys/taskq.h>
 #include <sys/tsol/label.h>
 #include <sys/tsol/tndb.h>
 #include <inet/ip.h>
 
 #include <netsmb/smb_osdep.h>
@@ -71,16 +74,47 @@
 
 #include <smbfs/smbfs.h>
 #include <smbfs/smbfs_node.h>
 #include <smbfs/smbfs_subr.h>
 
+#ifndef _KERNEL
+
+#include <libfksmbfs.h>
+
+#define STRUCT_DECL(s, a) struct s a
+#define STRUCT_FGET(handle, field) ((handle).field)
+#define _init(v)        fksmbfs_init(v)
+#define _fini(v)        fksmbfs_fini(v)
+
+#endif  /* !_KERNEL */
+
 /*
+ * Should smbfs mount enable "-o acl" by default?  There are good
+ * arguments for both.  The most common use case is individual users
+ * accessing files on some SMB server, for which "noacl" is the more
+ * convenient default.  A less common use case is data migration,
+ * where the "acl" option might be a desirable default.  We'll make
+ * the common use case the default.  This default can be changed via
+ * /etc/system, and/or set per-mount via the "acl" mount option.
+ */
+int smbfs_default_opt_acl = 0;
+
+/*
+ * How many taskq threads per-mount should we use.
+ * Just one is fine (until we do more async work).
+ */
+int smbfs_tq_nthread = 1;
+
+/*
  * Local functions definitions.
  */
 int             smbfsinit(int fstyp, char *name);
 void            smbfsfini();
+
+#ifdef  _KERNEL
 static int      smbfs_mount_label_policy(vfs_t *, void *, int, cred_t *);
+#endif  /* _KERNEL */
 
 /*
  * SMBFS Mount options table for MS_OPTIONSTR
  * Note: These are not all the options.
  * Some options come in via MS_DATA.
@@ -98,14 +132,18 @@
  *      option name             cancel option   default arg     flags
  *              ufs arg flag
  */
         { MNTOPT_INTR,          intr_cancel,    NULL,   MO_DEFAULT, 0 },
         { MNTOPT_NOINTR,        nointr_cancel,  NULL,   0,      0 },
-        { MNTOPT_ACL,           acl_cancel,     NULL,   MO_DEFAULT, 0 },
+        { MNTOPT_ACL,           acl_cancel,     NULL,   0,      0 },
         { MNTOPT_NOACL,         noacl_cancel,   NULL,   0,      0 },
         { MNTOPT_XATTR,         xattr_cancel,   NULL,   MO_DEFAULT, 0 },
-        { MNTOPT_NOXATTR,       noxattr_cancel, NULL,   0,      0 }
+        { MNTOPT_NOXATTR,       noxattr_cancel, NULL,   0,      0 },
+#ifndef _KERNEL
+        /* See vfs_optionisset MNTOPT_NOAC below. */
+        { MNTOPT_NOAC,          NULL,           NULL,   0,      0 },
+#endif  /* !_KERNEL */
 };
 
 static mntopts_t smbfs_mntopts = {
         sizeof (mntopts) / sizeof (mntopt_t),
         mntopts
@@ -119,19 +157,21 @@
         smbfsinit,              /* init routine */
         VSW_HASPROTO|VSW_NOTZONESAFE,   /* flags */
         &smbfs_mntopts                  /* mount options table prototype */
 };
 
+#ifdef  _KERNEL
 static struct modlfs modlfs = {
         &mod_fsops,
         "SMBFS filesystem",
         &vfw
 };
 
 static struct modlinkage modlinkage = {
         MODREV_1, (void *)&modlfs, NULL
 };
+#endif  /* _KERNEL */
 
 /*
  * Mutex to protect the following variables:
  *        smbfs_major
  *        smbfs_minor
@@ -200,11 +240,16 @@
                 smbfs_vfsfini();
                 smbfs_subrfini();
                 return (error);
         }
 
+#ifdef  _KERNEL
         error = mod_install((struct modlinkage *)&modlinkage);
+#else   /* _KERNEL */
+        error = fake_installfs(&vfw);
+#endif  /* _KERNEL */
+
         return (error);
 }
 
 /*
  * Free kernel module resources that were allocated in _init
@@ -222,11 +267,15 @@
          * into VFS_FREEVFS().
          */
         if (smbfs_mountcount)
                 return (EBUSY);
 
+#ifdef  _KERNEL
         error = mod_remove(&modlinkage);
+#else   /* _KERNEL */
+        error = fake_removefs(&vfw);
+#endif  /* _KERNEL */
         if (error)
                 return (error);
 
         /*
          * Free the allocated smbnodes, etc.
@@ -245,21 +294,23 @@
 }
 
 /*
  * Return information about the module
  */
+#ifdef  _KERNEL
 int
 _info(struct modinfo *modinfop)
 {
         return (mod_info((struct modlinkage *)&modlinkage, modinfop));
 }
+#endif  /* _KERNEL */
 
 /*
  * Initialize the vfs structure
  */
 
-int smbfsfstyp;
+int smbfs_fstyp;
 vfsops_t *smbfs_vfsops = NULL;
 
 static const fs_operation_def_t smbfs_vfsops_template[] = {
         { VFSNAME_MOUNT, { .vfs_mount = smbfs_mount } },
         { VFSNAME_UNMOUNT, { .vfs_unmount = smbfs_unmount } },
@@ -270,40 +321,44 @@
         { VFSNAME_MOUNTROOT, { .error = fs_nosys } },
         { VFSNAME_FREEVFS, { .vfs_freevfs = smbfs_freevfs } },
         { NULL, NULL }
 };
 
+/*
+ * This is the VFS switch initialization routine, normally called
+ * via vfssw[x].vsw_init by vfsinit() or mod_install
+ */
 int
 smbfsinit(int fstyp, char *name)
 {
         int             error;
 
         error = vfs_setfsops(fstyp, smbfs_vfsops_template, &smbfs_vfsops);
         if (error != 0) {
-                zcmn_err(GLOBAL_ZONEID, CE_WARN,
+                cmn_err(CE_WARN,
                     "smbfsinit: bad vfs ops template");
                 return (error);
         }
 
         error = vn_make_ops(name, smbfs_vnodeops_template, &smbfs_vnodeops);
         if (error != 0) {
                 (void) vfs_freevfsops_by_type(fstyp);
-                zcmn_err(GLOBAL_ZONEID, CE_WARN,
+                cmn_err(CE_WARN,
                     "smbfsinit: bad vnode ops template");
                 return (error);
         }
 
-        smbfsfstyp = fstyp;
+        smbfs_fstyp = fstyp;
 
         return (0);
 }
 
 void
 smbfsfini()
 {
         if (smbfs_vfsops) {
-                (void) vfs_freevfsops_by_type(smbfsfstyp);
+                (void) vfs_freevfsops_by_type(smbfs_fstyp);
                 smbfs_vfsops = NULL;
         }
         if (smbfs_vnodeops) {
                 vn_freevnodeops(smbfs_vnodeops);
                 smbfs_vnodeops = NULL;
@@ -314,12 +369,14 @@
 smbfs_free_smi(smbmntinfo_t *smi)
 {
         if (smi == NULL)
                 return;
 
+#ifdef  _KERNEL
         if (smi->smi_zone_ref.zref_zone != NULL)
                 zone_rele_ref(&smi->smi_zone_ref, ZONE_REF_SMBFS);
+#endif  /* _KERNEL */
 
         if (smi->smi_share != NULL)
                 smb_share_rele(smi->smi_share);
 
         avl_destroy(&smi->smi_hash_avl);
@@ -342,20 +399,25 @@
         smbnode_t       *rtnp = NULL;   /* root of this fs */
         smbmntinfo_t    *smi = NULL;
         dev_t           smbfs_dev;
         int             version;
         int             devfd;
-        zone_t          *zone = curproc->p_zone;
+        zone_t          *zone = curzone;
+#ifdef  _KERNEL
         zone_t          *mntzone = NULL;
+#else   /* _KERNEL */
+        short           minclsyspri = MINCLSYSPRI;
+#endif  /* _KERNEL */
         smb_share_t     *ssp = NULL;
         smb_cred_t      scred;
         int             flags, sec;
-
         STRUCT_DECL(smbfs_args, args);          /* smbfs mount arguments */
 
+#ifdef  _KERNEL
         if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
                 return (error);
+#endif  /* _KERNEL */
 
         if (mvp->v_type != VDIR)
                 return (ENOTDIR);
 
         /*
@@ -362,15 +424,21 @@
          * get arguments
          *
          * uap->datalen might be different from sizeof (args)
          * in a compatible situation.
          */
+#ifdef  _KERNEL
         STRUCT_INIT(args, get_udatamodel());
         bzero(STRUCT_BUF(args), SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE));
         if (copyin(data, STRUCT_BUF(args), MIN(uap->datalen,
             SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE))))
                 return (EFAULT);
+#else   /* _KERNEL */
+        bzero(&args, sizeof (args));
+        if (copyin(data, &args, MIN(uap->datalen, sizeof (args))))
+                return (EFAULT);
+#endif  /* _KERNEL */
 
         /*
          * Check mount program version
          */
         version = STRUCT_FGET(args, version);
@@ -417,10 +485,11 @@
         /*
          * Use "goto errout" from here on.
          * See: ssp, smi, rtnp, mntzone
          */
 
+#ifdef  _KERNEL
         /*
          * Determine the zone we're being mounted into.
          */
         zone_hold(mntzone = zone);              /* start with this assumption */
         if (getzoneid() == GLOBAL_ZONEID) {
@@ -460,10 +529,11 @@
                 if (error == -1) {
                         /* change mount to read-only to prevent write-down */
                         vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
                 }
         }
+#endif  /* _KERNEL */
 
         /* Prevent unload. */
         atomic_inc_32(&smbfs_mountcount);
 
         /*
@@ -482,49 +552,81 @@
         smbfs_init_hash_avl(&smi->smi_hash_avl);
 
         smi->smi_share = ssp;
         ssp = NULL;
 
+#ifdef  _KERNEL
         /*
          * Convert the anonymous zone hold acquired via zone_hold() above
          * into a zone reference.
          */
         zone_init_ref(&smi->smi_zone_ref);
         zone_hold_ref(mntzone, &smi->smi_zone_ref, ZONE_REF_SMBFS);
         zone_rele(mntzone);
         mntzone = NULL;
+#else   /* _KERNEL */
+        smi->smi_zone_ref.zref_zone = curzone;
+#endif  /* _KERNEL */
 
         /*
          * Initialize option defaults
          */
-        smi->smi_flags  = SMI_LLOCK;
         smi->smi_acregmin = SEC2HR(SMBFS_ACREGMIN);
         smi->smi_acregmax = SEC2HR(SMBFS_ACREGMAX);
         smi->smi_acdirmin = SEC2HR(SMBFS_ACDIRMIN);
         smi->smi_acdirmax = SEC2HR(SMBFS_ACDIRMAX);
+        smi->smi_flags  = SMI_LLOCK;
+#ifndef _KERNEL
+        /* Always direct IO with fakekernel */
+        smi->smi_flags  |= SMI_DIRECTIO;
+#endif  /* _KERNEL */
 
         /*
          * All "generic" mount options have already been
          * handled in vfs.c:domount() - see mntopts stuff.
          * Query generic options using vfs_optionisset().
+         * Give ACL an adjustable system-wide default.
          */
+        if (smbfs_default_opt_acl ||
+            vfs_optionisset(vfsp, MNTOPT_ACL, NULL))
+                smi->smi_flags |= SMI_ACL;
+        if (vfs_optionisset(vfsp, MNTOPT_NOACL, NULL))
+                smi->smi_flags &= ~SMI_ACL;
         if (vfs_optionisset(vfsp, MNTOPT_INTR, NULL))
                 smi->smi_flags |= SMI_INT;
-        if (vfs_optionisset(vfsp, MNTOPT_ACL, NULL))
-                smi->smi_flags |= SMI_ACL;
 
         /*
          * Get the mount options that come in as smbfs_args,
          * starting with args.flags (SMBFS_MF_xxx)
          */
         flags = STRUCT_FGET(args, flags);
-        smi->smi_uid    = STRUCT_FGET(args, uid);
-        smi->smi_gid    = STRUCT_FGET(args, gid);
         smi->smi_fmode  = STRUCT_FGET(args, file_mode) & 0777;
         smi->smi_dmode  = STRUCT_FGET(args, dir_mode) & 0777;
+#ifdef  _KERNEL
+        smi->smi_uid    = STRUCT_FGET(args, uid);
+        smi->smi_gid    = STRUCT_FGET(args, gid);
+#else   /* _KERNEL */
+        /*
+         * Need uid/gid to match our fake cred we'll fail in
+         * smbfs_access_rwx later.
+         */
+        smi->smi_uid    = crgetuid(cr);
+        smi->smi_gid    = crgetgid(cr);
 
         /*
+         * Our user-level do_mount() passes the mount options sting
+         * as-is, where the real mount program would convert some
+         * of those options to bits set in smbfs_args.flags.
+         * To avoid replicating all that conversion code, this
+         * uses the generic vfs option support to handle those
+         * option flag bits we need, i.e.: "noac"
+         */
+        if (vfs_optionisset(vfsp, MNTOPT_NOAC, NULL))
+                flags |= SMBFS_MF_NOAC;
+#endif  /* _KERNEL */
+
+        /*
          * Hande the SMBFS_MF_xxx flags.
          */
         if (flags & SMBFS_MF_NOAC)
                 smi->smi_flags |= SMI_NOAC;
         if (flags & SMBFS_MF_ACREGMIN) {
@@ -589,13 +691,13 @@
                 smbfs_dev = makedevice(smbfs_major, smbfs_minor);
         } while (vfs_devismounted(smbfs_dev));
         mutex_exit(&smbfs_minor_lock);
 
         vfsp->vfs_dev   = smbfs_dev;
-        vfs_make_fsid(&vfsp->vfs_fsid, smbfs_dev, smbfsfstyp);
+        vfs_make_fsid(&vfsp->vfs_fsid, smbfs_dev, smbfs_fstyp);
         vfsp->vfs_data  = (caddr_t)smi;
-        vfsp->vfs_fstype = smbfsfstyp;
+        vfsp->vfs_fstype = smbfs_fstyp;
         vfsp->vfs_bsize = MAXBSIZE;
         vfsp->vfs_bcount = 0;
 
         smi->smi_vfsp   = vfsp;
         smbfs_zonelist_add(smi);        /* undo in smbfs_freevfs */
@@ -615,18 +717,27 @@
         rtnp->r_vnode->v_type = VDIR;
         rtnp->r_vnode->v_flag |= VROOT;
         smi->smi_root = rtnp;
 
         /*
+         * Create a taskq for async work (i.e. putpage)
+         */
+        smi->smi_taskq = taskq_create_proc("smbfs",
+            smbfs_tq_nthread, minclsyspri,
+            smbfs_tq_nthread, smbfs_tq_nthread * 2,
+            zone->zone_zsched, TASKQ_PREPOPULATE);
+
+        /*
          * NFS does other stuff here too:
          *   async worker threads
          *   init kstats
          *
          * End of code from NFS nfsrootvp()
          */
         return (0);
 
+#ifdef  _KERNEL
 errout:
         vfsp->vfs_data = NULL;
         if (smi != NULL)
                 smbfs_free_smi(smi);
 
@@ -635,10 +746,11 @@
 
         if (ssp != NULL)
                 smb_share_rele(ssp);
 
         return (error);
+#endif  /* _KERNEL */
 }
 
 /*
  * vfs operations
  */
@@ -648,12 +760,14 @@
         smbmntinfo_t    *smi;
         smbnode_t       *rtnp;
 
         smi = VFTOSMI(vfsp);
 
+#ifdef  _KERNEL
         if (secpolicy_fs_unmount(cr, vfsp) != 0)
                 return (EPERM);
+#endif  /* _KERNEL */
 
         if ((flag & MS_FORCE) == 0) {
                 smbfs_rflush(vfsp, cr);
 
                 /*
@@ -683,19 +797,10 @@
          * but not for long.
          */
         vfsp->vfs_flag |= VFS_UNMOUNTED;
 
         /*
-         * Shutdown any outstanding I/O requests on this share,
-         * and force a tree disconnect.  The share object will
-         * continue to hang around until smb_share_rele().
-         * This should also cause most active nodes to be
-         * released as their operations fail with EIO.
-         */
-        smb_share_kill(smi->smi_share);
-
-        /*
          * If we hold the root VP (and we normally do)
          * then it's safe to release it now.
          */
         if (smi->smi_root) {
                 rtnp = smi->smi_root;
@@ -714,10 +819,25 @@
          * after their last vn_rele.
          */
         smbfs_destroy_table(vfsp);
 
         /*
+         * Shutdown any outstanding I/O requests on this share,
+         * and force a tree disconnect.  The share object will
+         * continue to hang around until smb_share_rele().
+         * This should also cause most active nodes to be
+         * released as their operations fail with EIO.
+         */
+        smb_share_kill(smi->smi_share);
+
+        /*
+         * Any async taskq work should be giving up.
+         * Wait for those to exit.
+         */
+        taskq_destroy(smi->smi_taskq);
+
+        /*
          * Delete our kstats...
          *
          * Doing it here, rather than waiting until
          * smbfs_freevfs so these are not visible
          * after the unmount.
@@ -863,49 +983,56 @@
                 *sbp = smi->smi_statvfsbuf;
         mutex_exit(&smi->smi_lock);
         return (error);
 }
 
-static kmutex_t smbfs_syncbusy;
-
 /*
  * Flush dirty smbfs files for file system vfsp.
  * If vfsp == NULL, all smbfs files are flushed.
  */
 /*ARGSUSED*/
 static int
 smbfs_sync(vfs_t *vfsp, short flag, cred_t *cr)
 {
+
         /*
-         * Cross-zone calls are OK here, since this translates to a
-         * VOP_PUTPAGE(B_ASYNC), which gets picked up by the right zone.
+         * SYNC_ATTR is used by fsflush() to force old filesystems like UFS
+         * to sync metadata, which they would otherwise cache indefinitely.
+         * Semantically, the only requirement is that the sync be initiated.
+         * Assume the server-side takes care of attribute sync.
          */
-        if (!(flag & SYNC_ATTR) && mutex_tryenter(&smbfs_syncbusy) != 0) {
-                smbfs_rflush(vfsp, cr);
-                mutex_exit(&smbfs_syncbusy);
+        if (flag & SYNC_ATTR)
+                return (0);
+
+        if (vfsp == NULL) {
+                /*
+                 * Flush ALL smbfs mounts in this zone.
+                 */
+                smbfs_flushall(cr);
+                return (0);
         }
 
+        smbfs_rflush(vfsp, cr);
+
         return (0);
 }
 
 /*
  * Initialization routine for VFS routines.  Should only be called once
  */
 int
 smbfs_vfsinit(void)
 {
-        mutex_init(&smbfs_syncbusy, NULL, MUTEX_DEFAULT, NULL);
         return (0);
 }
 
 /*
  * Shutdown routine for VFS routines.  Should only be called once
  */
 void
 smbfs_vfsfini(void)
 {
-        mutex_destroy(&smbfs_syncbusy);
 }
 
 void
 smbfs_freevfs(vfs_t *vfsp)
 {
@@ -929,10 +1056,11 @@
          * Allow _fini() to succeed now, if so desired.
          */
         atomic_dec_32(&smbfs_mountcount);
 }
 
+#ifdef  _KERNEL
 /*
  * smbfs_mount_label_policy:
  *      Determine whether the mount is allowed according to MAC check,
  *      by comparing (where appropriate) label of the remote server
  *      against the label of the zone being mounted into.
@@ -1019,5 +1147,6 @@
         if (mntzone)
                 zone_rele(mntzone);
         label_rele(zlabel);
         return (retv);
 }
+#endif  /* _KERNEL */