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 */