summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/fs/smbclnt/smbfs
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/fs/smbclnt/smbfs')
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h55
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_acl.c130
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c448
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c392
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h247
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c1449
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c205
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h88
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c912
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c208
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c653
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c36
12 files changed, 2362 insertions, 2461 deletions
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h
index 9eb6cdbed3..68c626094a 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h
@@ -50,7 +50,9 @@
#include <sys/param.h>
#include <sys/fstyp.h>
+#include <sys/avl.h>
#include <sys/list.h>
+#include <sys/t_lock.h>
#include <sys/vfs.h>
#include <sys/fs/smbfs_mount.h>
@@ -85,11 +87,14 @@ struct smbnode;
struct smb_share;
/*
- * The values for smi_flags.
+ * The values for smi_flags (from nfs_clnt.h)
*/
-#define SMI_INT 0x01 /* interrupts allowed */
-#define SMI_DEAD 0x02 /* zone shutting down */
+#define SMI_INT 0x04 /* interrupts allowed */
+#define SMI_NOAC 0x10 /* don't cache attributes */
#define SMI_LLOCK 0x80 /* local locking only */
+#define SMI_ACL 0x2000 /* share supports ACLs */
+#define SMI_EXTATTR 0x80000 /* share supports ext. attrs */
+#define SMI_DEAD 0x200000 /* mount has been terminated */
/*
* Stuff returned by smbfs_smb_qfsattr
@@ -118,6 +123,14 @@ typedef struct smbmntinfo {
#define smi_fsattr smi_fsa.fsa_aflags
/*
+ * The smbfs node cache for this mount.
+ * Named "hash" for historical reasons.
+ * See smbfs_node.h for details.
+ */
+ avl_tree_t smi_hash_avl;
+ krwlock_t smi_hash_lk;
+
+ /*
* Kstat statistics
*/
struct kstat *smi_io_kstats;
@@ -131,20 +144,34 @@ typedef struct smbmntinfo {
/* Lock for the list is: smi_globals_t -> smg_lock */
/*
- * Copy of the args from mount.
+ * Stuff copied or derived from the mount args
*/
- struct smbfs_args smi_args;
+ uid_t smi_uid; /* user id */
+ gid_t smi_gid; /* group id */
+ mode_t smi_fmode; /* mode for files */
+ mode_t smi_dmode; /* mode for dirs */
+
+ hrtime_t smi_acregmin; /* min time to hold cached file attr */
+ hrtime_t smi_acregmax; /* max time to hold cached file attr */
+ hrtime_t smi_acdirmin; /* min time to hold cached dir attr */
+ hrtime_t smi_acdirmax; /* max time to hold cached dir attr */
} smbmntinfo_t;
-typedef struct smbfattr {
- int fa_attr;
- len_t fa_size;
- struct timespec fa_atime;
- struct timespec fa_ctime;
- struct timespec fa_mtime;
- ino64_t fa_ino;
- struct timespec fa_reqtime;
-} smbfattr_t;
+/*
+ * Attribute cache timeout defaults (in seconds).
+ */
+#define SMBFS_ACREGMIN 3 /* min secs to hold cached file attr */
+#define SMBFS_ACREGMAX 60 /* max secs to hold cached file attr */
+#define SMBFS_ACDIRMIN 30 /* min secs to hold cached dir attr */
+#define SMBFS_ACDIRMAX 60 /* max secs to hold cached dir attr */
+/* and limits for the mount options */
+#define SMBFS_ACMINMAX 600 /* 10 min. is longest min timeout */
+#define SMBFS_ACMAXMAX 3600 /* 1 hr is longest max timeout */
+
+/*
+ * High-res time is nanoseconds.
+ */
+#define SEC2HR(sec) ((sec) * (hrtime_t)NANOSEC)
/*
* vnode pointer to mount info
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_acl.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_acl.c
index e50e3b2389..81493f5783 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_acl.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_acl.c
@@ -39,11 +39,11 @@
#include <sys/vfs.h>
#include <sys/byteorder.h>
-#include <netsmb/smb_osdep.h>
+#include <netsmb/mchain.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
+#include <netsmb/smb_osdep.h>
#include <netsmb/smb_subr.h>
-#include <netsmb/mchain.h>
#include <smbfs/smbfs.h>
#include <smbfs/smbfs_node.h>
@@ -51,17 +51,15 @@
#include <sys/fs/smbfs_ioctl.h>
#include <fs/fs_subr.h>
+#include "smbfs_ntacl.h"
/* Sanity check SD sizes */
#define MAX_RAW_SD_SIZE 32768
#define SMALL_SD_SIZE 1024
-#undef ACL_SUPPORT /* not yet */
-
-
/*
- * smbfs_getsd(), smbfs_setsd() are common functions used by
- * both ioctl get/set ACL and VOP_GETSECATTR, VOP_SETSECATTR.
+ * smbfs_getsd() is a common function used by both
+ * smbfs_ioctl SMBFSIO_GETSD and VOP_GETSECATTR.
* Handles required rights, tmpopen/tmpclose.
*
* Note: smbfs_getsd allocates and returns an mblk chain,
@@ -119,7 +117,7 @@ again:
cerror = smbfs_smb_tmpclose(np, fid, &scred);
if (cerror)
- SMBERROR("error %d closing file %s\n",
+ SMBVDEBUG("error %d closing file %s\n",
cerror, np->n_rpath);
out:
@@ -174,7 +172,7 @@ smbfs_setsd(vnode_t *vp, uint32_t selector, mblk_t **mp, cred_t *cr)
cerror = smbfs_smb_tmpclose(np, fid, &scred);
if (cerror)
- SMBERROR("error %d closing file %s\n",
+ SMBVDEBUG("error %d closing file %s\n",
cerror, np->n_rpath);
out:
@@ -185,7 +183,7 @@ out:
}
/*
- * Entry points from VOP_IOCTL
+ * Helper for VOP_IOCTL: SMBFSIO_GETSD
*/
int
smbfs_ioc_getsd(vnode_t *vp, intptr_t arg, int flag, cred_t *cr)
@@ -245,6 +243,9 @@ out:
return (error);
}
+/*
+ * Helper for VOP_IOCTL: SMBFSIO_SETSD
+ */
int
smbfs_ioc_setsd(vnode_t *vp, intptr_t arg, int flag, cred_t *cr)
{
@@ -285,62 +286,26 @@ out:
}
-#ifdef ACL_SUPPORT
-/*
- * Conversion functions for VOP_GETSECATTR, VOP_SETSECATTR
- *
- * XXX: We may or may not add conversion code here, or we
- * may add that to usr/src/common (TBD). For now all the
- * ACL conversion code is in libsmbfs.
- */
-
-/*
- * Convert a Windows SD (in the mdchain mdp) into a
- * ZFS-style vsecattr_t and possibly uid, gid.
- */
-/* ARGSUSED */
-static int
-smb_ntsd2vsec(mdchain_t *mdp, vsecattr_t *vsa,
- int *uidp, int *gidp, cred_t *cr)
-{
- /* XXX NOT_YET */
- return (ENOSYS);
-}
-
-/*
- * Convert a ZFS-style vsecattr_t (and possibly uid, gid)
- * into a Windows SD (built in the mbchain mbp).
- */
-/* ARGSUSED */
-static int
-smb_vsec2ntsd(vsecattr_t *vsa, int uid, int gid,
- mbchain_t *mbp, cred_t *cr)
-{
- /* XXX NOT_YET */
- return (ENOSYS);
-}
-#endif /* ACL_SUPPORT */
/*
- * Entry points from VOP_GETSECATTR, VOP_SETSECATTR
- *
- * Disabled the real _getacl functionality for now,
- * because we have no way to return the owner and
- * primary group until we replace our fake uid/gid
- * in getattr with something derived from _getsd.
+ * Helper for VOP_GETSECATTR
+ * Call smbfs_getsd, convert NT to ZFS form.
*/
/* ARGSUSED */
int
smbfs_getacl(vnode_t *vp, vsecattr_t *vsa,
- int *uidp, int *gidp, int flag, cred_t *cr)
+ uid_t *uidp, gid_t *gidp, int flag, cred_t *cr)
{
-#ifdef ACL_SUPPORT
mdchain_t *mdp, md_store;
- mblk_t *m;
+ mblk_t *m = NULL;
+ i_ntsd_t *sd = NULL;
uint32_t selector;
int error;
+ bzero(&md_store, sizeof (md_store));
+ mdp = &md_store;
+
/*
* Which parts of the SD we request.
* XXX: We need a way to let the caller specify
@@ -365,41 +330,51 @@ smbfs_getacl(vnode_t *vp, vsecattr_t *vsa,
*/
error = smbfs_getsd(vp, selector, &m, cr);
if (error)
- return (error);
+ goto out;
+ /* Note: allocated *m */
+ md_initm(mdp, m);
/*
- * Have m. Must free it before return.
+ * Parse the OtW security descriptor,
+ * storing in our internal form.
*/
- mdp = &md_store;
- md_initm(mdp, m);
+ error = md_get_ntsd(mdp, &sd);
+ if (error)
+ goto out;
/*
* Convert the Windows security descriptor to a
* ZFS ACL (and owner ID, primary group ID).
- * This is the difficult part. (todo)
*/
- error = smb_ntsd2vsec(mdp, vsa, uidp, gidp, cr);
+ error = smbfs_acl_sd2zfs(sd, vsa, uidp, gidp);
+out:
+ if (sd != NULL)
+ smbfs_acl_free_sd(sd);
/* Note: m_freem(m) is done by... */
md_done(mdp);
return (error);
-#else /* ACL_SUPPORT */
- return (ENOSYS);
-#endif /* ACL_SUPPORT */
}
+/*
+ * Helper for VOP_SETSECATTR
+ * Convert ZFS to NT form, call smbfs_setsd.
+ */
/* ARGSUSED */
int
smbfs_setacl(vnode_t *vp, vsecattr_t *vsa,
- int uid, int gid, int flag, cred_t *cr)
+ uid_t uid, gid_t gid, int flag, cred_t *cr)
{
-#ifdef ACL_SUPPORT
mbchain_t *mbp, mb_store;
+ i_ntsd_t *sd = NULL;
uint32_t selector;
int error;
+ bzero(&mb_store, sizeof (mb_store));
+ mbp = &mb_store;
+
/*
* Which parts of the SD we'll modify.
* Ditto comments above re. SACL
@@ -407,25 +382,27 @@ smbfs_setacl(vnode_t *vp, vsecattr_t *vsa,
selector = 0;
if (vsa)
selector |= DACL_SECURITY_INFORMATION;
- if (uid != -1)
+ if (uid != (uid_t)-1)
selector |= OWNER_SECURITY_INFORMATION;
- if (gid != -1)
+ if (gid != (gid_t)-1)
selector |= GROUP_SECURITY_INFORMATION;
if (selector == 0)
return (0);
/*
- * Setup buffer for SD data.
+ * Convert a ZFS ACL (and owner ID, group ID)
+ * into an NT SD, internal form.
*/
- mbp = &mb_store;
- mb_init(mbp);
+ error = smbfs_acl_zfs2sd(vsa, uid, gid, &sd);
+ if (error)
+ goto out;
/*
- * Convert a ZFS ACL (and owner ID, group ID)
- * to a Windows security descriptor.
- * This is the difficult part. (todo)
+ * Marshall the internal form SD into an
+ * OtW security descriptor.
*/
- error = smb_vsec2ntsd(vsa, uid, gid, mbp, cr);
+ mb_init(mbp);
+ error = mb_put_ntsd(mbp, sd);
if (error)
goto out;
@@ -436,9 +413,8 @@ smbfs_setacl(vnode_t *vp, vsecattr_t *vsa,
error = smbfs_setsd(vp, selector, &mbp->mb_top, cr);
out:
+ if (sd != NULL)
+ smbfs_acl_free_sd(sd);
mb_done(mbp);
return (error);
-#else /* ACL_SUPPORT */
- return (ENOSYS);
-#endif /* ACL_SUPPORT */
}
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c
index fc089d4c33..c6d23011fe 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c
@@ -40,7 +40,6 @@
#include <sys/cred.h>
#include <sys/kmem.h>
#include <sys/debug.h>
-#include <sys/dnlc.h>
#include <sys/vmsystm.h>
#include <sys/flock.h>
#include <sys/share.h>
@@ -54,7 +53,9 @@
#include <sys/list.h>
#include <sys/zone.h>
+#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
#include <smbfs/smbfs.h>
#include <smbfs/smbfs_node.h>
@@ -68,6 +69,10 @@
#include <vm/seg_map.h>
#include <vm/seg_vn.h>
+static int smbfs_getattr_cache(vnode_t *, struct smbfattr *);
+static int smbfattr_to_vattr(vnode_t *, struct smbfattr *,
+ struct vattr *);
+
/*
* The following code provide zone support in order to perform an action
* for each smbfs mount in a zone. This is also where we would add
@@ -84,6 +89,437 @@ typedef struct smi_globals smi_globals_t;
static zone_key_t smi_list_key;
+/*
+ * Attributes caching:
+ *
+ * Attributes are cached in the smbnode in struct vattr form.
+ * There is a time associated with the cached attributes (r_attrtime)
+ * which tells whether the attributes are valid. The time is initialized
+ * to the difference between current time and the modify time of the vnode
+ * when new attributes are cached. This allows the attributes for
+ * files that have changed recently to be timed out sooner than for files
+ * that have not changed for a long time. There are minimum and maximum
+ * timeout values that can be set per mount point.
+ */
+
+/*
+ * Validate caches by checking cached attributes. If they have timed out
+ * get the attributes from the server and compare mtimes. If mtimes are
+ * different purge all caches for this vnode.
+ */
+int
+smbfs_validate_caches(
+ struct vnode *vp,
+ cred_t *cr)
+{
+ struct vattr va;
+
+ va.va_mask = AT_SIZE;
+ return (smbfsgetattr(vp, &va, cr));
+}
+
+/*
+ * Purge all of the various data caches.
+ */
+/*ARGSUSED*/
+void
+smbfs_purge_caches(struct vnode *vp)
+{
+#if 0 /* not yet: mmap support */
+ /*
+ * NFS: Purge the DNLC for this vp,
+ * Clear any readdir state bits,
+ * the readlink response cache, ...
+ */
+ smbnode_t *np = VTOSMB(vp);
+
+ /*
+ * Flush the page cache.
+ */
+ if (vn_has_cached_data(vp)) {
+ (void) VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_INVAL, cr, NULL);
+ }
+#endif /* not yet */
+}
+
+/*
+ * Check the attribute cache to see if the new attributes match
+ * those cached. If they do, the various `data' caches are
+ * considered to be good. Otherwise, purge the cached data.
+ */
+void
+smbfs_cache_check(
+ struct vnode *vp,
+ struct smbfattr *fap)
+{
+ smbnode_t *np;
+ int purge_data = 0;
+#if 0 /* not yet: ACL support */
+ int purge_acl = 0;
+ vsecattr_t *vsp = NULL;
+#endif /* not yet */
+
+ np = VTOSMB(vp);
+ mutex_enter(&np->r_statelock);
+
+ /*
+ * Compare with NFS macro: CACHE_VALID
+ * If the mtime or size has changed,
+ * purge cached data.
+ */
+ if (np->r_attr.fa_mtime.tv_sec != fap->fa_mtime.tv_sec ||
+ np->r_attr.fa_mtime.tv_nsec != fap->fa_mtime.tv_nsec)
+ purge_data = 1;
+ if (np->r_attr.fa_size != fap->fa_size)
+ purge_data = 1;
+
+#if 0 /* not yet: ACL support */
+ if (np->r_attr.fa_ctime.tv_sec != fap->fa_ctime.tv_sec ||
+ np->r_attr.fa_ctime.tv_nsec != fap->fa_ctime.tv_nsec)
+ purge_acl = 1;
+#endif /* not yet */
+
+ mutex_exit(&np->r_statelock);
+
+ if (purge_data)
+ smbfs_purge_caches(vp);
+
+#if 0 /* not yet: ACL support */
+ if (purge_acl) {
+ vsecattr_t *vsp;
+
+ if (np->r_secattr != NULL) {
+ mutex_enter(&np->r_statelock);
+ vsp = np->r_secattr;
+ np->r_secattr = NULL;
+ mutex_exit(&np->r_statelock);
+ if (vsp != NULL)
+ smbfs_acl_free(vsp);
+ }
+ }
+#endif /* not yet */
+}
+
+/*
+ * Set attributes cache for given vnode using vnode attributes.
+ * From NFS: nfs_attrcache_va
+ */
+#if 0 /* not yet (not sure if we need this) */
+void
+smbfs_attrcache_va(vnode_t *vp, struct vattr *vap)
+{
+ smbfattr_t fa;
+ smbnode_t *np;
+
+ vattr_to_fattr(vp, vap, &fa);
+ smbfs_attrcache_fa(vp, &fa);
+}
+#endif /* not yet */
+
+/*
+ * Set attributes cache for given vnode using SMB fattr
+ * and update the attribute cache timeout.
+ *
+ * From NFS: nfs_attrcache, nfs_attrcache_va
+ */
+void
+smbfs_attrcache_fa(vnode_t *vp, struct smbfattr *fap)
+{
+ smbnode_t *np;
+ smbmntinfo_t *smi;
+ hrtime_t delta, now;
+ u_offset_t newsize;
+ vtype_t vtype, oldvt;
+ mode_t mode;
+
+ np = VTOSMB(vp);
+ smi = VTOSMI(vp);
+
+ /*
+ * We allow v_type to change, so set that here
+ * (and the mode, which is derived from it).
+ */
+ if (fap->fa_attr & SMB_FA_DIR) {
+ vtype = VDIR;
+ mode = S_IFDIR | smi->smi_dmode;
+ } else {
+ vtype = VREG;
+ mode = S_IFREG | smi->smi_fmode;
+ }
+
+ /*
+ * For now, n_uid/n_gid never change after they are
+ * set by: smbfs_node_findcreate / make_smbnode.
+ * Later, they will change in getsecattr.
+ */
+
+ mutex_enter(&np->r_statelock);
+
+ now = gethrtime();
+
+ /*
+ * Delta is the number of nanoseconds that we will
+ * cache the attributes of the file. It is based on
+ * the number of nanoseconds since the last time that
+ * we detected a change. The assumption is that files
+ * that changed recently are likely to change again.
+ * There is a minimum and a maximum for regular files
+ * and for directories which is enforced though.
+ *
+ * Using the time since last change was detected
+ * eliminates direct comparison or calculation
+ * using mixed client and server times. SMBFS
+ * does not make any assumptions regarding the
+ * client and server clocks being synchronized.
+ */
+ if (fap->fa_mtime.tv_sec != np->r_attr.fa_mtime.tv_sec ||
+ fap->fa_mtime.tv_nsec != np->r_attr.fa_mtime.tv_nsec ||
+ fap->fa_size != np->r_attr.fa_size)
+ np->r_mtime = now;
+
+ if ((smi->smi_flags & SMI_NOAC) || (vp->v_flag & VNOCACHE))
+ delta = 0;
+ else {
+ delta = now - np->r_mtime;
+ if (vtype == VDIR) {
+ if (delta < smi->smi_acdirmin)
+ delta = smi->smi_acdirmin;
+ else if (delta > smi->smi_acdirmax)
+ delta = smi->smi_acdirmax;
+ } else {
+ if (delta < smi->smi_acregmin)
+ delta = smi->smi_acregmin;
+ else if (delta > smi->smi_acregmax)
+ delta = smi->smi_acregmax;
+ }
+ }
+
+ np->r_attrtime = now + delta;
+ np->r_attr = *fap;
+ np->n_mode = mode;
+ oldvt = vp->v_type;
+ vp->v_type = vtype;
+
+ /*
+ * Shall we update r_size? (local notion of size)
+ *
+ * The real criteria for updating r_size should be:
+ * if the file has grown on the server, or if
+ * the client has not modified the file.
+ *
+ * Also deal with the fact that SMB presents
+ * directories as having size=0. Doing that
+ * here and leaving fa_size as returned OtW
+ * avoids fixing the size lots of places.
+ */
+ newsize = fap->fa_size;
+ if (vtype == VDIR && newsize < DEV_BSIZE)
+ newsize = DEV_BSIZE;
+
+ if (np->r_size != newsize) {
+#if 0 /* not yet: mmap support */
+ if (!vn_has_cached_data(vp) || ...)
+ /* XXX: See NFS page cache code. */
+#endif /* not yet */
+ /* OK to set the size. */
+ np->r_size = newsize;
+ }
+
+ /* NFS: np->r_flags &= ~RWRITEATTR; */
+ np->n_flag &= ~NATTRCHANGED;
+
+ mutex_exit(&np->r_statelock);
+
+ if (oldvt != vtype) {
+ SMBVDEBUG("vtype change %d to %d\n", oldvt, vtype);
+ }
+}
+
+/*
+ * Fill in attribute from the cache.
+ *
+ * If valid, copy to *fap and return zero,
+ * otherwise return an error.
+ *
+ * From NFS: nfs_getattr_cache()
+ */
+int
+smbfs_getattr_cache(vnode_t *vp, struct smbfattr *fap)
+{
+ smbnode_t *np;
+ int error;
+
+ np = VTOSMB(vp);
+
+ mutex_enter(&np->r_statelock);
+ if (gethrtime() >= np->r_attrtime) {
+ /* cache expired */
+ error = ENOENT;
+ } else {
+ /* cache is valid */
+ *fap = np->r_attr;
+ error = 0;
+ }
+ mutex_exit(&np->r_statelock);
+
+ return (error);
+}
+
+/*
+ * Get attributes over-the-wire and update attributes cache
+ * if no error occurred in the over-the-wire operation.
+ * Return 0 if successful, otherwise error.
+ * From NFS: nfs_getattr_otw
+ */
+int
+smbfs_getattr_otw(vnode_t *vp, struct smbfattr *fap, cred_t *cr)
+{
+ struct smbnode *np;
+ struct smb_cred scred;
+ int error;
+
+ np = VTOSMB(vp);
+
+ /*
+ * NFS uses the ACL rpc here
+ * (if smi_flags & SMI_ACL)
+ */
+
+ /* Shared lock for (possible) n_fid use. */
+ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
+ return (EINTR);
+ smb_credinit(&scred, cr);
+
+ bzero(fap, sizeof (*fap));
+ error = smbfs_smb_getfattr(np, fap, &scred);
+
+ smb_credrele(&scred);
+ smbfs_rw_exit(&np->r_lkserlock);
+
+ if (error) {
+ /* NFS had: PURGE_STALE_FH(error, vp, cr) */
+ smbfs_attrcache_remove(np);
+ if (error == ENOENT || error == ENOTDIR) {
+ /*
+ * Getattr failed because the object was
+ * removed or renamed by another client.
+ * Remove any cached attributes under it.
+ */
+ smbfs_attrcache_prune(np);
+ }
+ return (error);
+ }
+
+ /*
+ * NFS: smbfs_cache_fattr(vap, fa, vap, t, cr);
+ * which did: fattr_to_vattr, nfs_attr_cache.
+ * We cache the fattr form, so just do the
+ * cache check and store the attributes.
+ */
+ smbfs_cache_check(vp, fap);
+ smbfs_attrcache_fa(vp, fap);
+
+ return (0);
+}
+
+/*
+ * Return either cached or remote attributes. If get remote attr
+ * use them to check and invalidate caches, then cache the new attributes.
+ *
+ * From NFS: nfsgetattr()
+ */
+int
+smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr)
+{
+ struct smbfattr fa;
+ int error;
+
+ ASSERT(curproc->p_zone == VTOSMI(vp)->smi_zone);
+
+ /*
+ * If we've got cached attributes, just use them;
+ * otherwise go to the server to get attributes,
+ * which will update the cache in the process.
+ */
+ error = smbfs_getattr_cache(vp, &fa);
+ if (error)
+ error = smbfs_getattr_otw(vp, &fa, cr);
+ if (error)
+ return (error);
+
+ /*
+ * Re. client's view of the file size, see:
+ * smbfs_attrcache_fa, smbfs_getattr_otw
+ */
+
+ error = smbfattr_to_vattr(vp, &fa, vap);
+ return (error);
+}
+
+
+/*
+ * Convert SMB over the wire attributes to vnode form.
+ * Returns 0 for success, error if failed (overflow, etc).
+ * From NFS: nattr_to_vattr()
+ */
+int
+smbfattr_to_vattr(vnode_t *vp, struct smbfattr *fa, struct vattr *vap)
+{
+ struct smbnode *np = VTOSMB(vp);
+
+ vap->va_mask = AT_ALL;
+
+ /*
+ * Take type, mode, uid, gid from the smbfs node,
+ * which has have been updated by _getattr_otw.
+ */
+ vap->va_type = vp->v_type;
+ vap->va_mode = np->n_mode;
+
+ vap->va_uid = np->n_uid;
+ vap->va_gid = np->n_gid;
+
+ vap->va_fsid = vp->v_vfsp->vfs_dev;
+ vap->va_nodeid = np->n_ino;
+ vap->va_nlink = 1;
+
+ /*
+ * Difference from NFS here: We cache attributes as
+ * reported by the server, so r_attr.fa_size is the
+ * server's idea of the file size. This is called
+ * for getattr, so we want to return the client's
+ * idea of the file size. NFS deals with that in
+ * nfsgetattr(), the equivalent of our caller.
+ */
+ vap->va_size = np->r_size;
+
+ /*
+ * Times. Note, already converted from NT to
+ * Unix form (in the unmarshalling code).
+ */
+ vap->va_atime = fa->fa_atime;
+ vap->va_mtime = fa->fa_mtime;
+ vap->va_ctime = fa->fa_ctime;
+
+ /*
+ * rdev, blksize, seq are made up.
+ * va_nblocks is 512 byte blocks.
+ */
+ vap->va_rdev = vp->v_rdev;
+ vap->va_blksize = MAXBSIZE;
+ vap->va_nblocks = (fsblkcnt64_t)btod(np->r_attr.fa_allocsz);
+ vap->va_seq = 0;
+
+ return (0);
+}
+
+
+/*
+ * SMB Client initialization and cleanup.
+ * Much of it is per-zone now.
+ */
+
+
/* ARGSUSED */
static void *
smbfs_zone_init(zoneid_t zoneid)
@@ -127,11 +563,6 @@ again:
*/
VFS_HOLD(smi->smi_vfsp);
- /*
- * purge the DNLC for this filesystem
- */
- (void) dnlc_purge_vfsp(smi->smi_vfsp, 0);
-
mutex_enter(&smi->smi_lock);
smi->smi_flags |= SMI_DEAD;
mutex_exit(&smi->smi_lock);
@@ -252,11 +683,7 @@ smb_fscb_t smbfs_cb = {
int
smbfs_clntinit(void)
{
- int error;
- error = smbfs_subrinit();
- if (error)
- return (error);
zone_key_create(&smi_list_key, smbfs_zone_init, smbfs_zone_shutdown,
smbfs_zone_destroy);
#ifdef NEED_SMBFS_CALLBACKS
@@ -276,5 +703,4 @@ smbfs_clntfini(void)
smb_fscb_set(NULL);
#endif /* NEED_SMBFS_CALLBACKS */
(void) zone_key_delete(smi_list_key);
- smbfs_subrfini();
}
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c
index 594dedecc0..6c8eda5a87 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c
@@ -33,15 +33,14 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/cred.h>
+#include <sys/time.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/kmem.h>
@@ -51,11 +50,7 @@
#include <sys/sysmacros.h>
#include <sys/bitmap.h>
-#ifdef APPLE
-#include <sys/smb_apple.h>
-#else
#include <netsmb/smb_osdep.h>
-#endif
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
@@ -65,10 +60,6 @@
#include <smbfs/smbfs_node.h>
#include <smbfs/smbfs_subr.h>
-#if defined(DEBUG) || defined(lint)
-#define SMBFS_NAME_DEBUG
-#endif
-
/*
* Lack of inode numbers leads us to the problem of generating them.
* Partially this problem can be solved by having a dir/file cache
@@ -83,8 +74,8 @@
#define FNV_32_PRIME ((uint32_t)0x01000193UL)
#define FNV1_32_INIT ((uint32_t)33554467UL)
-uint32_t
-smbfs_hash3(uint32_t ival, const char *name, int nmlen)
+static inline uint32_t
+smbfs_hash(uint32_t ival, const char *name, int nmlen)
{
uint32_t v;
@@ -95,166 +86,104 @@ smbfs_hash3(uint32_t ival, const char *name, int nmlen)
return (v);
}
+/*
+ * Compute the hash of the full (remote) path name
+ * using the three parts supplied separately.
+ */
uint32_t
-smbfs_hash(const char *name, int nmlen)
+smbfs_gethash(const char *rpath, int rplen)
{
uint32_t v;
- v = smbfs_hash3(FNV1_32_INIT, name, nmlen);
+ v = smbfs_hash(FNV1_32_INIT, rpath, rplen);
return (v);
}
/*
- * This is basically a hash of the full path name, but
- * computed without having the full path contiguously.
- * The path building logic needs to match what
- * smbfs_fullpath does.
- *
- * Note that smbfs_make_node computes inode numbers by
- * calling smbfs_hash on the full path name. This will
- * compute the same result given the directory path and
- * the last component separately.
+ * Like smbfs_gethash, but optimized a little by
+ * starting with the directory hash.
*/
uint32_t
smbfs_getino(struct smbnode *dnp, const char *name, int nmlen)
{
uint32_t ino;
+ char sep;
/* Start with directory hash */
ino = (uint32_t)dnp->n_ino;
- /*
- * If not the root, hash a slash.
- */
- if (dnp->n_rplen > 1)
- ino = smbfs_hash3(ino, "\\", 1);
+ /* separator (maybe) */
+ sep = SMBFS_DNP_SEP(dnp);
+ if (sep)
+ ino = smbfs_hash(ino, &sep, 1);
/* Now hash this component. */
- ino = smbfs_hash3(ino, name, nmlen);
+ ino = smbfs_hash(ino, name, nmlen);
return (ino);
}
-#define CHAR_FC '\374' /* 0xFC */
-#define CHAR_FE '\376' /* 0xFE */
+/*
+ * Allocate and copy a string of passed length.
+ * The passed length does NOT include the null.
+ */
char *
smbfs_name_alloc(const char *name, int nmlen)
{
char *cp;
- size_t alen;
-#ifdef SMBFS_NAME_DEBUG
- /*
- * Note: The passed length is strlen(name),
- * and does NOT include the terminating nul.
- * Allocated space holds: (in order)
- * (int)strlen
- * char 0xFC (1st marker)
- * copy of string
- * terminating null
- * char 0xFE (2nd marker)
- */
- alen = sizeof (int) + 1 + nmlen + 1 + 1;
- cp = kmem_alloc(alen, KM_SLEEP);
- /*LINTED*/
- *(int *)cp = nmlen;
- cp += sizeof (int);
- cp[0] = CHAR_FC;
- cp++;
- bcopy(name, cp, nmlen);
- cp[nmlen] = 0;
- cp[nmlen + 1] = CHAR_FE;
-#else
- alen = nmlen + 1; /* Passed length does NOT include the nul. */
- cp = kmem_alloc(alen, KM_SLEEP);
+ cp = kmem_alloc(nmlen + 1, KM_SLEEP);
bcopy(name, cp, nmlen);
cp[nmlen] = 0;
-#endif
+
return (cp);
}
/*
- * Note: Passed length does NOT include the nul,
- * the same as with smbfs_name_alloc().
+ * Free string from smbfs_name_alloc(). Again,
+ * the passed length does NOT include the null.
*/
void
smbfs_name_free(const char *name, int nmlen)
{
- size_t alen;
-#ifdef SMBFS_NAME_DEBUG
- int lnmlen;
- char *cp;
-
- /*
- * See comment in smbfs_name_alloc
- * about the layout of this memory.
- */
- alen = sizeof (int) + 1 + nmlen + 1 + 1;
- cp = (char *)name;
- cp--;
- if (*cp != CHAR_FC) {
- debug_enter("smbfs_name_free: name[-1] != 0xFC");
- }
- cp -= sizeof (int);
- /*LINTED*/
- lnmlen = *(int *)cp;
- if (lnmlen != nmlen) {
- debug_enter("smbfs_name_free: name[-5] != nmlen");
- }
- if (name[nmlen + 1] != CHAR_FE) {
- debug_enter("smbfs_name_free: name[nmlen+1] != 0xFE");
- }
- kmem_free(cp, alen);
-#else
- alen = nmlen + 1;
- kmem_free((char *)name, alen);
-#endif
+ kmem_free((char *)name, nmlen + 1);
}
/*
* smbfs_nget()
*
- * NOTES:
- *
- * It would be nice to be able to pass in a flag when the caller is sure
- * that the node does not exist and should just be allocated.
+ * Find or create a node under some directory node.
*/
int
smbfs_nget(vnode_t *dvp, const char *name, int nmlen,
- struct smbfattr *fap, vnode_t **vpp)
+ struct smbfattr *fap, vnode_t **vpp)
{
struct smbnode *dnp = VTOSMB(dvp);
struct smbnode *np;
vnode_t *vp;
char sep;
+ ASSERT(fap != NULL);
*vpp = NULL;
- /* Don't expect "." or ".." here anymore. */
- if ((nmlen == 1 && name[0] == '.') ||
+ /* Don't expect "" or "." or ".." here anymore. */
+ if (nmlen == 0 || (nmlen == 1 && name[0] == '.') ||
(nmlen == 2 && name[0] == '.' && name[1] == '.')) {
- DEBUG_ENTER("smbfs_nget: name is '.' or '..'");
return (EINVAL);
}
-
- /*
- * See the comment near the top of smbfs_xattr.c about
- * the logic for what separators to use where.
- */
- sep = (dnp->n_flag & N_XATTR) ? 0 : '\\';
+ sep = SMBFS_DNP_SEP(dnp);
/* Find or create the node. */
- vp = smbfs_make_node(dvp->v_vfsp,
+ np = smbfs_node_findcreate(dnp->n_mount,
dnp->n_rpath, dnp->n_rplen,
name, nmlen, sep, fap);
/*
- * We always have a vp now, because
- * smbfs_make_node / make_smbnode
- * calls kmem_alloc with KM_SLEEP.
+ * We should have np now, because we passed
+ * fap != NULL to smbfs_node_findcreate.
*/
- ASSERT(vp);
- np = VTOSMB(vp);
+ ASSERT(np != NULL);
+ vp = SMBTOV(np);
/*
* Files in an XATTR dir are also XATTR.
@@ -265,248 +194,57 @@ smbfs_nget(vnode_t *dvp, const char *name, int nmlen,
mutex_exit(&np->r_statelock);
}
-#ifdef NOT_YET
- /* update the attr_cache info if the file is clean */
- if (fap && !(VTOSMB(vp)->n_flag & NFLUSHWIRE))
- smbfs_attr_cacheenter(vp, fap);
- if (dvp && makeentry) {
- /* add entry to DNLC */
- cache_enter(dvp, vp, &cn);
- }
-#endif /* NOT_YET */
-
/* BSD symlink hack removed (smb_symmagic) */
-#ifdef NOT_YET
- smbfs_attr_cacheenter(vp, fap); /* update the attr_cache info */
-#endif /* NOT_YET */
-
*vpp = vp;
return (0);
}
/*
- * routines to maintain vnode attributes cache
- * smbfs_attr_cacheenter: unpack np.i to vnode_vattr structure
- *
- * Note that some SMB servers do not exhibit POSIX behaviour
- * with regard to the mtime on directories. To work around
- * this, we never allow the mtime on a directory to go backwards,
- * and bump it forwards elsewhere to simulate the correct
- * behaviour.
+ * smbfs_attrcache_enter, smbfs_attrcache_lookup replaced by
+ * code more closely resembling NFS. See smbfs_client.c
*/
-void
-smbfs_attr_cacheenter(vnode_t *vp, struct smbfattr *fap)
-{
- struct smbnode *np = VTOSMB(vp);
- int vtype;
- struct timespec ts;
-
- mutex_enter(&np->r_statelock);
-
- vtype = (fap->fa_attr & SMB_FA_DIR) ? VDIR : VREG;
- if (vp->v_type != vtype)
- SMBVDEBUG("vtype change %d to %d\n",
- vp->v_type, vtype);
- vp->v_type = vtype;
-
- if (vtype == VREG) {
- if (np->n_size != fap->fa_size) {
- /*
- * Had Darwin ubc_sync_range call here,
- * invalidating the truncated range.
- * XXX: Solaris equivalent?
- */
- SMBVDEBUG("Update size?\n");
- }
- np->n_size = fap->fa_size;
- } else if (vtype == VDIR) {
- np->n_size = 16384; /* XXX should be a better way ... */
- /*
- * Don't allow mtime to go backwards.
- * Yes this has its flaws. Better ideas are welcome!
- */
- /*CSTYLED*/
- if (timespeccmp(&fap->fa_mtime, &np->n_mtime, <))
- fap->fa_mtime = np->n_mtime;
- } else if (vtype != VLNK)
- goto out;
-
- np->n_atime = fap->fa_atime;
- np->n_ctime = fap->fa_ctime;
- np->n_mtime = fap->fa_mtime;
- np->n_dosattr = fap->fa_attr;
-
- np->n_flag &= ~NATTRCHANGED;
- gethrestime(&ts);
- np->n_attrage = ts.tv_sec;
-
-out:
- mutex_exit(&np->r_statelock);
-}
-
-int
-smbfs_attr_cachelookup(vnode_t *vp, struct vattr *vap)
-{
- struct smbnode *np = VTOSMB(vp);
- struct smbmntinfo *smi = VTOSMI(vp);
- time_t attrtimeo;
- struct timespec ts, *stime;
- mode_t type;
-
- /*
- * Determine attrtimeo. It will be something between SMB_MINATTRTIMO and
- * SMB_MAXATTRTIMO where recently modified files have a short timeout
- * and files that haven't been modified in a long time have a long
- * timeout. This is the same algorithm used by NFS.
- */
- gethrestime(&ts);
- stime = &np->r_mtime;
- attrtimeo = (ts.tv_sec - stime->tv_sec) / 10;
- if (attrtimeo < SMB_MINATTRTIMO) {
- attrtimeo = SMB_MINATTRTIMO;
- } else if (attrtimeo > SMB_MAXATTRTIMO)
- attrtimeo = SMB_MAXATTRTIMO;
- /* has too much time passed? */
- stime = (struct timespec *)&np->r_attrtime;
- if ((ts.tv_sec - stime->tv_sec) > attrtimeo)
- return (ENOENT);
-
- if (!vap)
- return (0);
-
- switch (vp->v_type) {
- case VREG:
- type = S_IFREG;
- break;
- case VLNK:
- type = S_IFLNK;
- break;
- case VDIR:
- type = S_IFDIR;
- break;
- default:
- SMBSDEBUG("unknown vnode_vtype %d\n", vp->v_type);
- return (EINVAL);
- }
-
- mutex_enter(&np->r_statelock);
-
- if (!(np->n_flag & NGOTIDS)) {
- np->n_mode = type;
-#ifdef APPLE
- if (smi->smi_fsattr & FILE_PERSISTENT_ACLS) {
- /* XXX: Can this block? Drop r_statelock? */
- if (!smbfs_getids(np, scredp)) {
- np->n_flag |= NGOTIDS;
- np->n_mode |= ACCESSPERMS; /* 0777 */
- }
- }
-#endif /* APPLE */
- if (!(np->n_flag & NGOTIDS)) {
- np->n_flag |= NGOTIDS;
- np->n_uid = smi->smi_args.uid;
- np->n_gid = smi->smi_args.gid;
- }
- }
-
- if (vap->va_mask & AT_TYPE)
- vap->va_type = vp->v_type;
- if (vap->va_mask & AT_MODE) {
- np->n_mode = 0;
- if (vp->v_type == VDIR)
- np->n_mode |= smi->smi_args.dir_mode;
- else /* symlink and regular file */
- np->n_mode |= smi->smi_args.file_mode;
- vap->va_mode = np->n_mode;
- }
- if (vap->va_mask & AT_SIZE)
- vap->va_size = np->n_size;
- if (vap->va_mask & AT_NODEID)
- vap->va_nodeid = np->n_ino;
- if (vap->va_mask & AT_ATIME)
- vap->va_atime = np->n_atime;
- if (vap->va_mask & AT_CTIME)
- vap->va_ctime = np->n_ctime;
- if (vap->va_mask & AT_MTIME)
- vap->va_mtime = np->n_mtime;
- vap->va_nlink = 1;
- vap->va_uid = np->n_uid;
- vap->va_gid = np->n_gid;
- vap->va_fsid = vp->v_vfsp->vfs_dev;
- vap->va_rdev = 0;
- vap->va_blksize = MAXBSIZE;
- vap->va_nblocks = (fsblkcnt64_t)btod(np->n_size);
- vap->va_seq = 0;
-
- mutex_exit(&np->r_statelock);
-
- return (0);
-}
/*
- * Some SMB servers don't exhibit POSIX behaviour with regard to
- * updating the directory mtime when the directory's contents
- * change.
- *
- * We force the issue here by updating our cached copy of the mtime
- * whenever we perform such an action ourselves, and then mark the
- * cache invalid. Subsequently when the invalidated cache entry is
- * updated, we disallow an update that would move the mtime backwards.
- *
- * This preserves correct or near-correct behaviour with a
- * compliant server, and gives near-correct behaviour with
- * a non-compliant server in the most common case (we are the
- * only client changing the directory).
- *
- * There are also complications if a server's time is ahead
- * of our own. We must 'touch' a directory when it is first
- * created, to ensure that the timestamp starts out sane,
- * however it may have a timestamp well ahead of the 'touch'
- * point which will be returned and cached the first time the
- * directory's attributes are fetched. Subsequently, the
- * directory's mtime will not appear to us to change at all
- * until our local time catches up to the server.
- *
- * Thus, any time a directory is 'touched', the saved timestamp
- * must advance at least far enough forwards to be visible to
- * the stat(2) interface.
- *
- * XXX note that better behaviour with non-compliant servers
- * could be obtained by bumping the mtime forwards when
- * an update for an invalidated entry returns a nonsensical
- * mtime.
+ * Update the local notion of the mtime of some directory.
+ * See comments re. r_mtime in smbfs_node.h
*/
-
void
smbfs_attr_touchdir(struct smbnode *dnp)
{
- struct timespec ts, ta;
mutex_enter(&dnp->r_statelock);
/*
- * XXX - not sure about this...
- * Creep the saved time forwards far enough that
- * layers above the kernel will notice.
- */
- ta.tv_sec = 1;
- ta.tv_nsec = 0;
- timespecadd(&dnp->n_mtime, &ta);
- /*
- * If the current time is later than the updated
- * saved time, apply it instead.
+ * Now that we keep the client's notion of mtime
+ * separately from the server, this is easy.
*/
- gethrestime(&ts);
- /*CSTYLED*/
- if (timespeccmp(&dnp->n_mtime, &ts, <))
- dnp->n_mtime = ts;
+ dnp->r_mtime = gethrtime();
+
/*
* Invalidate the cache, so that we go to the wire
* to check that the server doesn't have a better
* timestamp next time we care.
*/
- smbfs_attr_cacheremove(dnp);
+ smbfs_attrcache_rm_locked(dnp);
mutex_exit(&dnp->r_statelock);
}
+
+void
+smbfs_attrcache_remove(struct smbnode *np)
+{
+ mutex_enter(&np->r_statelock);
+ /* smbfs_attrcache_rm_locked(np); */
+ np->r_attrtime = gethrtime();
+ mutex_exit(&np->r_statelock);
+}
+
+/* See smbfs_node.h */
+#undef smbfs_attrcache_rm_locked
+void
+smbfs_attrcache_rm_locked(struct smbnode *np)
+{
+ ASSERT(MUTEX_HELD(&np->r_statelock));
+ np->r_attrtime = gethrtime();
+}
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h
index 08e31e0d4d..a85ceb5f60 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h
@@ -42,6 +42,7 @@
/*
* Much code copied into here from Sun NFS.
+ * Compare with nfs_clnt.h
*/
#include <sys/avl.h>
@@ -51,6 +52,32 @@
extern "C" {
#endif
+/*
+ * These are the attributes we can get from the server via
+ * SMB commands such as TRANS2_QUERY_FILE_INFORMATION
+ * with info level SMB_QFILEINFO_ALL_INFO, and directory
+ * FindFirst/FindNext info. levels FIND_DIRECTORY_INFO
+ * and FIND_BOTH_DIRECTORY_INFO, etc.
+ *
+ * Values in this struct are always native endian,
+ * and times are converted converted to Unix form.
+ * Note: zero in any of the times means "unknown".
+ *
+ * XXX: Later, move this to nsmb
+ */
+typedef struct smbfattr {
+ timespec_t fa_createtime; /* Note, != ctime */
+ timespec_t fa_atime; /* these 3 are like unix */
+ timespec_t fa_mtime;
+ timespec_t fa_ctime;
+ u_offset_t fa_size; /* EOF position */
+ u_offset_t fa_allocsz; /* Allocated size. */
+ uint32_t fa_attr; /* Ext. file (DOS) attr */
+} smbfattr_t;
+
+/*
+ * Cache whole directories (not yet)
+ */
typedef struct rddir_cache {
lloff_t _cookie; /* cookie used to find this cache entry */
lloff_t _ncookie; /* cookie used to find the next cache entry */
@@ -93,32 +120,59 @@ typedef struct smbfs_rwlock {
} smbfs_rwlock_t;
/*
- * The format of the hash bucket used to lookup smbnodes from a file handle.
+ * The format of the smbfs node header, which contains the
+ * fields used to link nodes in the AVL tree, and those
+ * fields needed by the AVL node comparison functions.
+ * It's a separate struct so we can call avl_find with
+ * this relatively small struct as a stack local.
+ *
+ * The AVL tree is mntinfo.smi_hash_avl,
+ * and its lock is mntinfo.smi_hash_lk.
*/
-typedef struct rhashq {
- struct smbnode *r_hashf;
- struct smbnode *r_hashb;
- krwlock_t r_lock;
-} rhashq_t;
+typedef struct smbfs_node_hdr {
+ /*
+ * Our linkage in the node cache AVL tree.
+ */
+ avl_node_t hdr_avl_node;
+
+ /*
+ * Identity of this node: The full path name,
+ * in server form, relative to the share root.
+ */
+ char *hdr_n_rpath;
+ int hdr_n_rplen;
+} smbfs_node_hdr_t;
/*
- * Remote file information structure.
+ * Below is the SMBFS-specific representation of a "node".
+ * This struct is a mixture of Sun NFS and Darwin code.
+ * Fields starting with "r_" came from NFS struct "rnode"
+ * and fields starting with "n_" came from Darwin, or
+ * were added during the Solaris port. We have avoided
+ * renaming fields so we would not cause excessive
+ * changes in the code using this struct.
+ *
+ * Now using an AVL tree instead of hash lists, but kept the
+ * "hash" in some member names and functions to reduce churn.
+ * One AVL tree per mount replaces the global hash buckets.
+ *
+ * Notes carried over from the NFS code:
*
* The smbnode is the "inode" for remote files. It contains all the
* information necessary to handle remote file on the client side.
*
* Note on file sizes: we keep two file sizes in the smbnode: the size
* according to the client (r_size) and the size according to the server
- * (r_attr.va_size). They can differ because we modify r_size during a
+ * (r_attr.fa_size). They can differ because we modify r_size during a
* write system call (smbfs_rdwr), before the write request goes over the
* wire (before the file is actually modified on the server). If an OTW
* request occurs before the cached data is written to the server the file
- * size returned from the server (r_attr.va_size) may not match r_size.
- * r_size is the one we use, in general. r_attr.va_size is only used to
+ * size returned from the server (r_attr.fa_size) may not match r_size.
+ * r_size is the one we use, in general. r_attr.fa_size is only used to
* determine whether or not our cached data is valid.
*
* Each smbnode has 3 locks associated with it (not including the smbnode
- * hash table and free list locks):
+ * "hash" AVL tree and free list locks):
*
* r_rwlock: Serializes smbfs_write and smbfs_setattr requests
* and allows smbfs_read requests to proceed in parallel.
@@ -133,13 +187,12 @@ typedef struct rhashq {
* time (not accross entire putpage operations,
* for example).
*
- * The following members are protected by the mutex rpfreelist_lock:
+ * The following members are protected by the mutex smbfreelist_lock:
* r_freef
* r_freeb
*
- * The following members are protected by the hash bucket rwlock:
- * r_hashf
- * r_hashb
+ * The following members are protected by the AVL tree rwlock:
+ * r_avl_node (r__hdr.hdr_avl_node)
*
* Note: r_modaddr is only accessed when the r_statelock mutex is held.
* Its value is also controlled via r_rwlock. It is assumed that
@@ -156,99 +209,98 @@ typedef struct rhashq {
* Lock ordering:
* r_rwlock > r_lkserlock > r_statelock
*/
-struct exportinfo; /* defined in smbfs/export.h */
-struct failinfo; /* defined in smbfs/smbfs_clnt.h */
-struct mntinfo; /* defined in smbfs/smbfs_clnt.h */
-
-#ifdef _KERNEL
-/* Bits for smbnode.n_flag */
-#define NFLUSHINPROG 0x00001
-#define NFLUSHWANT 0x00002 /* they should gone ... */
-#define NMODIFIED 0x00004 /* bogus, until async IO implemented */
-#define NREFPARENT 0x00010 /* node holds parent from recycling */
-#define NGOTIDS 0x00020
-#define NRDIRSERIAL 0x00080 /* serialize readdir operation */
-#define NISMAPPED 0x00800
-#define NFLUSHWIRE 0x01000
-#define NATTRCHANGED 0x02000 /* use smbfs_attr_cacheremove at close */
-#define NALLOC 0x04000 /* being created */
-#define NWALLOC 0x08000 /* awaiting creation */
-#define N_XATTR 0x10000 /* extended attribute (dir or file) */
typedef struct smbnode {
- /* from Sun NFS struct rnode (XXX: cleanup needed) */
- /* the hash fields must be first to match the rhashq_t */
- /* Lock for the hash queue is: np->r_hashq->r_lock */
- struct smbnode *r_hashf; /* hash queue forward pointer */
- struct smbnode *r_hashb; /* hash queue back pointer */
- /* Lock for the free list is: smbfreelist_lock */
+ /* Our linkage in the node cache AVL tree (see above). */
+ smbfs_node_hdr_t r__hdr;
+
+ /* short-hand names for r__hdr members */
+#define r_avl_node r__hdr.hdr_avl_node
+#define n_rpath r__hdr.hdr_n_rpath
+#define n_rplen r__hdr.hdr_n_rplen
+
+ smbmntinfo_t *n_mount; /* VFS data */
+ vnode_t *r_vnode; /* associated vnode */
+
+ /*
+ * Linkage in smbfreelist, for reclaiming nodes.
+ * Lock for the free list is: smbfreelist_lock
+ */
struct smbnode *r_freef; /* free list forward pointer */
struct smbnode *r_freeb; /* free list back pointer */
- rhashq_t *r_hashq; /* pointer to the hash bucket */
- vnode_t *r_vnode; /* vnode for remote file */
- smbfs_rwlock_t r_rwlock; /* serializes write/setattr requests */
+
+ smbfs_rwlock_t r_rwlock; /* serialize write/setattr requests */
smbfs_rwlock_t r_lkserlock; /* serialize lock with other ops */
- kmutex_t r_statelock; /* protects (most of) smbnode fields */
- u_offset_t r_nextr; /* next byte read offset (read-ahead) */
+ kmutex_t r_statelock; /* protect (most) smbnode fields */
+
+ /*
+ * File handle, directory search handle,
+ * and reference counts for them, etc.
+ * Lock for these is: r_lkserlock
+ */
+ int n_dirrefs;
+ struct smbfs_fctx *n_dirseq; /* ff context */
+ int n_dirofs; /* last ff offset */
+ int n_fidrefs;
+ uint16_t n_fid; /* file handle */
+ enum vtype n_ovtype; /* vnode type opened */
+ uint32_t n_rights; /* granted rights */
+ int n_vcgenid; /* gereration no. (reconnect) */
+
+ /*
+ * Misc. bookkeeping
+ */
cred_t *r_cred; /* current credentials */
- len_t r_size; /* client's view of file size */
- struct vattr r_attr; /* cached vnode attributes */
- hrtime_t r_attrtime; /* time attributes become invalid */
+ u_offset_t r_nextr; /* next read offset (read-ahead) */
long r_mapcnt; /* count of mmapped pages */
uint_t r_count; /* # of refs not reflect in v_count */
uint_t r_awcount; /* # of outstanding async write */
uint_t r_gcount; /* getattrs waiting to flush pages */
- ushort_t r_flags; /* flags, see below */
- short r_error; /* async write error */
+ uint_t r_flags; /* flags, see below */
+ uint32_t n_flag; /* NXXX flags below */
+ uint_t r_error; /* async write error */
kcondvar_t r_cv; /* condvar for blocked threads */
avl_tree_t r_dir; /* cache of readdir responses */
rddir_cache *r_direof; /* pointer to the EOF entry */
kthread_t *r_serial; /* id of purging thread */
list_t r_indelmap; /* list of delmap callers */
+
/*
- * Members derived from Darwin struct smbnode.
- * Note: n_parent node pointer removed because it
- * caused unwanted "holds" on nodes in our cache.
- * Now keeping just the full remote path instead,
- * in server form, relative to the share root.
+ * Attributes: local, and as last seen on the server.
+ * See notes above re: r_size vs r_attr.fa_size, etc.
*/
- char *n_rpath;
- int n_rplen;
- uint32_t n_flag;
- smbmntinfo_t *n_mount;
- ino64_t n_ino;
- /* Lock for the next 7 is r_lkserlock */
- enum vtype n_ovtype; /* vnode type opened */
- int n_dirrefs;
- struct smbfs_fctx *n_dirseq; /* ff context */
- int n_dirofs; /* last ff offset */
- int n_vcgenid; /* gereration no. (reconnect) */
- int n_fidrefs;
- uint16_t n_fid; /* file handle */
- uint32_t n_rights; /* granted rights */
- /* Lock for the rest is r_statelock */
+ smbfattr_t r_attr; /* attributes from the server */
+ hrtime_t r_attrtime; /* time attributes become invalid */
+ hrtime_t r_mtime; /* client time file last modified */
+ len_t r_size; /* client's view of file size */
+
+ /*
+ * Other attributes, not carried in smbfattr_t
+ */
+ u_longlong_t n_ino;
uid_t n_uid;
gid_t n_gid;
mode_t n_mode;
- timestruc_t r_atime;
- timestruc_t r_ctime;
- timestruc_t r_mtime;
- int n_dosattr;
- /*
- * XXX: Maybe use this instead:
- * #define n_atime r_attr.va_atime
- * etc.
- */
-#define n_size r_size
-#define n_atime r_atime
-#define n_ctime r_ctime
-#define n_mtime r_mtime
-#define n_attrage r_attrtime
} smbnode_t;
-#endif /* _KERNEL */
/*
- * Flags
+ * Flag bits in: smbnode_t .n_flag
+ */
+#define NFLUSHINPROG 0x00001
+#define NFLUSHWANT 0x00002 /* they should gone ... */
+#define NMODIFIED 0x00004 /* bogus, until async IO implemented */
+#define NREFPARENT 0x00010 /* node holds parent from recycling */
+#define NGOTIDS 0x00020
+#define NRDIRSERIAL 0x00080 /* serialize readdir operation */
+#define NISMAPPED 0x00800
+#define NFLUSHWIRE 0x01000
+#define NATTRCHANGED 0x02000 /* kill cached attributes at close */
+#define NALLOC 0x04000 /* being created */
+#define NWALLOC 0x08000 /* awaiting creation */
+#define N_XATTR 0x10000 /* extended attribute (dir or file) */
+
+/*
+ * Flag bits in: smbnode_t .r_flags
*/
#define RREADDIRPLUS 0x1 /* issue a READDIRPLUS instead of READDIR */
#define RDIRTY 0x2 /* dirty pages from write operation */
@@ -258,7 +310,7 @@ typedef struct smbnode {
#define RHAVEVERF 0x20 /* have a write verifier to compare against */
#define RCOMMIT 0x40 /* commit in progress */
#define RCOMMITWAIT 0x80 /* someone is waiting to do a commit */
-#define RHASHED 0x100 /* smbnode is in hash queues */
+#define RHASHED 0x100 /* smbnode is in the "hash" AVL tree */
#define ROUTOFSPACE 0x200 /* an out of space error has happened */
#define RDIRECTIO 0x400 /* bypass the buffer cache */
#define RLOOKUP 0x800 /* a lookup has been performed */
@@ -272,27 +324,12 @@ typedef struct smbnode {
#define VTOSMB(vp) ((smbnode_t *)((vp)->v_data))
#define SMBTOV(np) ((np)->r_vnode)
-/* Attribute cache timeouts in seconds */
-#define SMB_MINATTRTIMO 2
-#define SMB_MAXATTRTIMO 30
-
/*
- * Function definitions.
+ * A macro to compute the separator that should be used for
+ * names under some directory. See smbfs_fullpath().
*/
-struct smb_cred;
-int smbfs_nget(vnode_t *dvp, const char *name, int nmlen,
- struct smbfattr *fap, vnode_t **vpp);
-void smbfs_attr_cacheenter(vnode_t *vp, struct smbfattr *fap);
-int smbfs_attr_cachelookup(vnode_t *vp, struct vattr *va);
-void smbfs_attr_touchdir(struct smbnode *dnp);
-char *smbfs_name_alloc(const char *name, int nmlen);
-void smbfs_name_free(const char *name, int nmlen);
-uint32_t smbfs_hash(const char *name, int nmlen);
-uint32_t smbfs_hash3(uint32_t ival, const char *name, int nmlen);
-uint32_t smbfs_getino(struct smbnode *dnp, const char *name, int nmlen);
-int smb_check_table(struct vfs *vfsp, smbnode_t *srp);
-
-#define smbfs_attr_cacheremove(np) (np)->n_attrage = 0
+#define SMBFS_DNP_SEP(dnp) \
+ (((dnp->n_flag & N_XATTR) == 0 && dnp->n_rplen > 1) ? '\\' : '\0')
#ifdef __cplusplus
}
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c
index 63c34c26d0..aa27feac67 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c
@@ -44,12 +44,7 @@
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
-#ifdef APPLE
-#include <sys/smb_apple.h>
-#include <sys/utfconv.h>
-#else
#include <netsmb/smb_osdep.h>
-#endif
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
@@ -61,38 +56,37 @@
#include <smbfs/smbfs_subr.h>
/*
+ * Jan 1 1980 as 64 bit NT time.
+ * (tenths of microseconds since 1601)
+ */
+const uint64_t NT1980 = 11960035200ULL*10000000ULL;
+
+/*
* Local functions.
* Not static, to aid debugging.
*/
-int smbfs_smb_qfileinfo(struct smbnode *np, struct smbfattr *fap,
- struct smb_cred *scrp, short infolevel);
-int smbfs_smb_qpathinfo(struct smbnode *np, struct smbfattr *fap,
- struct smb_cred *scrp, short infolevel);
int smbfs_smb_query_info(struct smbnode *np, const char *name, int nmlen,
struct smbfattr *fap, struct smb_cred *scrp);
+int smbfs_smb_trans2_query(struct smbnode *np, struct smbfattr *fap,
+ struct smb_cred *scrp, uint16_t infolevel);
int smbfs_smb_statfsLM1(struct smb_share *ssp,
statvfs64_t *sbp, struct smb_cred *scrp);
int smbfs_smb_statfsLM2(struct smb_share *ssp,
statvfs64_t *sbp, struct smb_cred *scrp);
+int smbfs_smb_setfattrNT(struct smbnode *np, int fid,
+ uint32_t attr, struct timespec *mtime, struct timespec *atime,
+ struct smb_cred *scrp);
+
int smbfs_smb_setftime1(struct smbnode *np, uint16_t fid,
struct timespec *mtime, struct timespec *atime,
struct smb_cred *scrp);
-int smbfs_smb_setfattrNT(struct smbnode *np, uint16_t fid,
- uint32_t attr, struct timespec *mtime, struct timespec *atime,
- struct smb_cred *scrp);
int smbfs_smb_setpattr1(struct smbnode *np,
const char *name, int len, uint32_t attr,
struct timespec *mtime, struct smb_cred *scrp);
-int smbfs_smb_setpattr2(struct smbnode *np, uint32_t attr,
- struct timespec *mtime, struct timespec *atime,
- struct smb_cred *scrp);
-int smbfs_smb_setpattrNT(struct smbnode *np, uint32_t attr,
- struct timespec *mtime, struct timespec *atime,
- struct smb_cred *scrp);
/*
@@ -120,6 +114,7 @@ smbfs_smb_lockandx(struct smbnode *np, int op, uint32_t pid,
if (op == SMB_LOCK_SHARED)
ltype |= SMB_LOCKING_ANDX_SHARED_LOCK;
+ /* XXX: if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES)? */
if (largelock)
ltype |= SMB_LOCKING_ANDX_LARGE_FILES;
error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_LOCKING_ANDX, scrp);
@@ -177,9 +172,13 @@ smbfs_smb_lock(struct smbnode *np, int op, caddr_t id,
* TODO: use LOCK_BYTE_RANGE here.
*/
return (EINVAL);
- else
- return (smbfs_smb_lockandx(np, op, (uint32_t)id, start, len,
- largelock, scrp, timeout));
+
+ /*
+ * XXX: compute largelock via:
+ * (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES)?
+ */
+ return (smbfs_smb_lockandx(np, op, (uint32_t)id, start, len,
+ largelock, scrp, timeout));
}
#endif /* APPLE */
@@ -197,8 +196,7 @@ smbfs_smb_getfattr(
int error;
/*
- * This lock is really only necessary for qfileinfo,
- * but hopefully we use that most of the time.
+ * This lock is necessary for FID-based calls.
* Lock may be writer (via open) or reader.
*/
ASSERT(np->r_lkserlock.count != 0);
@@ -211,35 +209,22 @@ smbfs_smb_getfattr(
return (error);
}
- if (np->n_fidrefs)
- error = smbfs_smb_qfileinfo(np, fap, scrp, 0);
- else
- error = smbfs_smb_qpathinfo(np, fap, scrp, 0);
+ error = smbfs_smb_trans2_query(np, fap, scrp, 0);
+ if (error != EINVAL)
+ return (error);
- if (error == EINVAL) {
- /* fallback */
- error = smbfs_smb_query_info(np, NULL, 0, fap, scrp);
- }
-
- /*
- * Note directory size is not provided by
- * windows servers (they leave it as zero)
- */
- if ((fap->fa_attr & SMB_FA_DIR) &&
- (fap->fa_size < DEV_BSIZE))
- fap->fa_size = DEV_BSIZE;
+ /* fallback */
+ error = smbfs_smb_query_info(np, NULL, 0, fap, scrp);
return (error);
}
-
/*
- * Nearly identical to smbfs_smb_qfileinfo (below).
- * Please keep them in sync.
+ * Common function for QueryFileInfo, QueryPathInfo.
*/
int
-smbfs_smb_qpathinfo(struct smbnode *np, struct smbfattr *fap,
- struct smb_cred *scrp, short infolevel)
+smbfs_smb_trans2_query(struct smbnode *np, struct smbfattr *fap,
+ struct smb_cred *scrp, uint16_t infolevel)
{
struct smb_share *ssp = np->n_mount->smi_share;
struct smb_vc *vcp = SSTOVC(ssp);
@@ -247,139 +232,7 @@ smbfs_smb_qpathinfo(struct smbnode *np, struct smbfattr *fap,
int error, svtz, timesok = 1;
struct mbchain *mbp;
struct mdchain *mdp;
- uint16_t date, time, wattr;
- uint64_t llongint, lsize;
- uint32_t size, dattr;
-
-top:
- error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_PATH_INFORMATION,
- scrp, &t2p);
- if (error)
- return (error);
- mbp = &t2p->t2_tparam;
- mb_init(mbp);
- if (!infolevel) {
- if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12)
- infolevel = SMB_QFILEINFO_STANDARD;
- else
- infolevel = SMB_QFILEINFO_ALL_INFO;
- }
- mb_put_uint16le(mbp, infolevel);
- mb_put_uint32le(mbp, 0);
- /* mb_put_uint8(mbp, SMB_DT_ASCII); specs are wrong */
- error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\');
- if (error) {
- smb_t2_done(t2p);
- return (error);
- }
- t2p->t2_maxpcount = 2;
- t2p->t2_maxdcount = vcp->vc_txmax;
- error = smb_t2_request(t2p);
- if (error) {
- smb_t2_done(t2p);
- /* Invalid info level? Try fallback. */
- if (error == EINVAL &&
- infolevel == SMB_QFILEINFO_ALL_INFO) {
- infolevel = SMB_QFILEINFO_STANDARD;
- goto top;
- }
- return (error);
- }
- mdp = &t2p->t2_rdata;
- svtz = vcp->vc_sopt.sv_tz;
- switch (infolevel) {
- case SMB_QFILEINFO_STANDARD:
- timesok = 0;
- md_get_uint16le(mdp, NULL);
- md_get_uint16le(mdp, NULL); /* creation time */
- md_get_uint16le(mdp, &date);
- md_get_uint16le(mdp, &time); /* access time */
- if (date || time) {
- timesok++;
- smb_dos2unixtime(date, time, 0, svtz, &fap->fa_atime);
- }
- md_get_uint16le(mdp, &date);
- md_get_uint16le(mdp, &time); /* modify time */
- if (date || time) {
- timesok++;
- smb_dos2unixtime(date, time, 0, svtz, &fap->fa_mtime);
- }
- md_get_uint32le(mdp, &size);
- fap->fa_size = size;
- md_get_uint32le(mdp, NULL); /* allocation size */
- md_get_uint16le(mdp, &wattr);
- fap->fa_attr = wattr;
- break;
- case SMB_QFILEINFO_ALL_INFO:
- timesok = 0;
- /* creation time (discard) */
- md_get_uint64le(mdp, NULL);
- /* last access time */
- md_get_uint64le(mdp, &llongint);
- if (llongint) {
- timesok++;
- smb_time_NT2local(llongint, svtz, &fap->fa_atime);
- }
- /* last write time */
- md_get_uint64le(mdp, &llongint);
- if (llongint) {
- timesok++;
- smb_time_NT2local(llongint, svtz, &fap->fa_mtime);
- }
- /* last change time */
- md_get_uint64le(mdp, &llongint);
- if (llongint) {
- timesok++;
- smb_time_NT2local(llongint, svtz, &fap->fa_ctime);
- }
- /* attributes */
- md_get_uint32le(mdp, &dattr);
- fap->fa_attr = dattr;
- /*
- * 4-Byte alignment - discard
- * Specs doesn't talk about this.
- */
- md_get_uint32le(mdp, NULL);
- /* allocation size (discard) */
- md_get_uint64le(mdp, NULL);
- /* File size */
- md_get_uint64le(mdp, &lsize);
- fap->fa_size = lsize;
- break;
- default:
- SMBVDEBUG("unexpected info level %d\n", infolevel);
- error = EINVAL;
- }
- smb_t2_done(t2p);
- /*
- * if all times are zero (observed with FAT on NT4SP6)
- * then fall back to older info level
- */
- if (!timesok) {
- if (infolevel == SMB_QFILEINFO_ALL_INFO) {
- infolevel = SMB_QFILEINFO_STANDARD;
- goto top;
- }
- error = EINVAL;
- }
- return (error);
-}
-
-/*
- * Nearly identical to smbfs_smb_qpathinfo (above).
- * Please keep them in sync.
- */
-int
-smbfs_smb_qfileinfo(struct smbnode *np, struct smbfattr *fap,
- struct smb_cred *scrp, short infolevel)
-{
- struct smb_share *ssp = np->n_mount->smi_share;
- struct smb_vc *vcp = SSTOVC(ssp);
- struct smb_t2rq *t2p;
- int error, svtz, timesok = 1;
- struct mbchain *mbp;
- struct mdchain *mdp;
- uint16_t date, time, wattr;
+ uint16_t cmd, date, time, wattr;
uint64_t llongint, lsize;
uint32_t size, dattr;
@@ -389,16 +242,18 @@ smbfs_smb_qfileinfo(struct smbnode *np, struct smbfattr *fap,
*/
ASSERT(np->r_lkserlock.count != 0);
- /* After reconnect, n_fid is invalid */
- if (np->n_vcgenid != ssp->ss_vcgenid)
- return (ESTALE);
-
- if (np->n_fid == SMB_FID_UNUSED)
- return (EBADF);
+ /*
+ * If we have a valid open FID, use it.
+ */
+ if ((np->n_fidrefs > 0) &&
+ (np->n_fid != SMB_FID_UNUSED) &&
+ (np->n_vcgenid == ssp->ss_vcgenid))
+ cmd = SMB_TRANS2_QUERY_FILE_INFORMATION;
+ else
+ cmd = SMB_TRANS2_QUERY_PATH_INFORMATION;
top:
- error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_FILE_INFORMATION,
- scrp, &t2p);
+ error = smb_t2_alloc(SSTOCP(ssp), cmd, scrp, &t2p);
if (error)
return (error);
mbp = &t2p->t2_tparam;
@@ -409,8 +264,22 @@ top:
else
infolevel = SMB_QFILEINFO_ALL_INFO;
}
- mb_put_uint16le(mbp, np->n_fid);
+
+ if (cmd == SMB_TRANS2_QUERY_FILE_INFORMATION)
+ mb_put_uint16le(mbp, np->n_fid);
+
mb_put_uint16le(mbp, infolevel);
+
+ if (cmd == SMB_TRANS2_QUERY_PATH_INFORMATION) {
+ mb_put_uint32le(mbp, 0);
+ /* mb_put_uint8(mbp, SMB_DT_ASCII); specs are wrong */
+ error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\');
+ if (error) {
+ smb_t2_done(t2p);
+ return (error);
+ }
+ }
+
t2p->t2_maxpcount = 2;
t2p->t2_maxdcount = vcp->vc_txmax;
error = smb_t2_request(t2p);
@@ -428,61 +297,63 @@ top:
svtz = vcp->vc_sopt.sv_tz;
switch (infolevel) {
case SMB_QFILEINFO_STANDARD:
- timesok = 0;
- md_get_uint16le(mdp, NULL);
- md_get_uint16le(mdp, NULL); /* creation time */
+ md_get_uint16le(mdp, &date);
+ md_get_uint16le(mdp, &time); /* creation time */
+ smb_dos2unixtime(date, time, 0, svtz, &fap->fa_createtime);
md_get_uint16le(mdp, &date);
md_get_uint16le(mdp, &time); /* access time */
- if (date || time) {
- timesok++;
- smb_dos2unixtime(date, time, 0, svtz, &fap->fa_atime);
- }
+ smb_dos2unixtime(date, time, 0, svtz, &fap->fa_atime);
md_get_uint16le(mdp, &date);
md_get_uint16le(mdp, &time); /* modify time */
- if (date || time) {
- timesok++;
- smb_dos2unixtime(date, time, 0, svtz, &fap->fa_mtime);
- }
- md_get_uint32le(mdp, &size);
+ smb_dos2unixtime(date, time, 0, svtz, &fap->fa_mtime);
+ md_get_uint32le(mdp, &size); /* EOF position */
fap->fa_size = size;
- md_get_uint32le(mdp, NULL); /* allocation size */
- md_get_uint16le(mdp, &wattr);
+ md_get_uint32le(mdp, &size); /* allocation size */
+ fap->fa_allocsz = size;
+ error = md_get_uint16le(mdp, &wattr);
fap->fa_attr = wattr;
+ timesok = 1;
break;
case SMB_QFILEINFO_ALL_INFO:
timesok = 0;
- /* creation time (discard) */
- md_get_uint64le(mdp, NULL);
+ /* creation time */
+ md_get_uint64le(mdp, &llongint);
+ if (llongint)
+ timesok++;
+ smb_time_NT2local(llongint, &fap->fa_createtime);
+
/* last access time */
md_get_uint64le(mdp, &llongint);
- if (llongint) {
+ if (llongint)
timesok++;
- smb_time_NT2local(llongint, svtz, &fap->fa_atime);
- }
+ smb_time_NT2local(llongint, &fap->fa_atime);
+
/* last write time */
md_get_uint64le(mdp, &llongint);
- if (llongint) {
+ if (llongint)
timesok++;
- smb_time_NT2local(llongint, svtz, &fap->fa_mtime);
- }
+ smb_time_NT2local(llongint, &fap->fa_mtime);
+
/* last change time */
md_get_uint64le(mdp, &llongint);
- if (llongint) {
+ if (llongint)
timesok++;
- smb_time_NT2local(llongint, svtz, &fap->fa_ctime);
- }
+ smb_time_NT2local(llongint, &fap->fa_ctime);
+
/* attributes */
md_get_uint32le(mdp, &dattr);
fap->fa_attr = dattr;
+
/*
* 4-Byte alignment - discard
- * Specs doesn't talk about this.
+ * Specs don't talk about this.
*/
md_get_uint32le(mdp, NULL);
- /* allocation size (discard) */
- md_get_uint64le(mdp, NULL);
- /* File size */
+ /* allocation size */
md_get_uint64le(mdp, &lsize);
+ fap->fa_allocsz = lsize;
+ /* File size */
+ error = md_get_uint64le(mdp, &lsize);
fap->fa_size = lsize;
break;
default:
@@ -529,14 +400,15 @@ smbfs_smb_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *fsa,
t2p->t2_maxpcount = 4;
t2p->t2_maxdcount = 4 * 3 + 512;
error = smb_t2_request(t2p);
- if (error) {
- smb_t2_done(t2p);
- return (error);
- }
+ if (error)
+ goto out;
+
mdp = &t2p->t2_rdata;
md_get_uint32le(mdp, &fsa->fsa_aflags);
md_get_uint32le(mdp, &fsa->fsa_maxname);
- md_get_uint32le(mdp, &nlen); /* fs name length */
+ error = md_get_uint32le(mdp, &nlen); /* fs name length */
+ if (error)
+ goto out;
/*
* Get the FS type name.
@@ -548,7 +420,7 @@ smbfs_smb_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *fsa,
if (nlen > sizeof (tmpbuf))
nlen = sizeof (tmpbuf);
- md_get_mem(mdp, tmpbuf, nlen, MB_MSYSTEM);
+ error = md_get_mem(mdp, tmpbuf, nlen, MB_MSYSTEM);
tmplen = nlen / 2; /* UCS-2 chars */
outlen = FSTYPSZ - 1;
(void) uconv_u16tou8(tmpbuf, &tmplen,
@@ -557,9 +429,19 @@ smbfs_smb_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *fsa,
} else {
if (nlen > (FSTYPSZ - 1))
nlen = FSTYPSZ - 1;
- md_get_mem(mdp, fsa->fsa_tname, nlen, MB_MSYSTEM);
+ error = md_get_mem(mdp, fsa->fsa_tname, nlen, MB_MSYSTEM);
+ }
+
+ /*
+ * If fs_name starts with FAT, we can't set dates before 1980
+ */
+ if (0 == strncmp(fsa->fsa_tname, "FAT", 3)) {
+ SMB_SS_LOCK(ssp);
+ ssp->ss_flags |= SMBS_FST_FAT;
+ SMB_SS_UNLOCK(ssp);
}
+out:
smb_t2_done(t2p);
return (0);
}
@@ -600,16 +482,17 @@ smbfs_smb_statfsLM2(struct smb_share *ssp, statvfs64_t *sbp,
t2p->t2_maxpcount = 4;
t2p->t2_maxdcount = 4 * 4 + 2;
error = smb_t2_request(t2p);
- if (error) {
- smb_t2_done(t2p);
- return (error);
- }
+ if (error)
+ goto out;
+
mdp = &t2p->t2_rdata;
md_get_uint32le(mdp, NULL); /* fs id */
md_get_uint32le(mdp, &bpu);
md_get_uint32le(mdp, &units);
md_get_uint32le(mdp, &funits);
- md_get_uint16le(mdp, &bsize);
+ error = md_get_uint16le(mdp, &bsize);
+ if (error)
+ goto out;
s = bsize;
s *= bpu;
t = units;
@@ -638,6 +521,8 @@ smbfs_smb_statfsLM2(struct smb_share *ssp, statvfs64_t *sbp,
sbp->f_bavail = f; /* free blocks avail to non-superuser */
sbp->f_files = (-1); /* total file nodes in file system */
sbp->f_ffree = (-1); /* free file nodes in fs */
+
+out:
smb_t2_done(t2p);
return (0);
}
@@ -661,15 +546,16 @@ smbfs_smb_statfsLM1(struct smb_share *ssp, statvfs64_t *sbp,
smb_rq_bstart(rqp);
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
- if (error) {
- smb_rq_done(rqp);
- return (error);
- }
+ if (error)
+ goto out;
+
smb_rq_getreply(rqp, &mdp);
md_get_uint16le(mdp, &units);
md_get_uint16le(mdp, &bpu);
md_get_uint16le(mdp, &bsize);
- md_get_uint16le(mdp, &funits);
+ error = md_get_uint16le(mdp, &funits);
+ if (error)
+ goto out;
s = bsize;
s *= bpu;
t = units;
@@ -698,6 +584,8 @@ smbfs_smb_statfsLM1(struct smb_share *ssp, statvfs64_t *sbp,
sbp->f_bavail = f; /* free blocks avail to non-superuser */
sbp->f_files = (-1); /* total file nodes in file system */
sbp->f_ffree = (-1); /* free file nodes in fs */
+
+out:
smb_rq_done(rqp);
return (0);
}
@@ -722,12 +610,10 @@ smbfs_smb_seteof(struct smb_share *ssp, uint16_t fid, uint64_t newsize,
mb_put_uint16le(mbp, SMB_SFILEINFO_END_OF_FILE_INFORMATION);
else
mb_put_uint16le(mbp, SMB_SFILEINFO_END_OF_FILE_INFO);
- mb_put_uint32le(mbp, 0); /* XXX should be 16 not 32(?) */
+ mb_put_uint16le(mbp, 0); /* pad */
mbp = &t2p->t2_tdata;
mb_init(mbp);
mb_put_uint64le(mbp, newsize);
- mb_put_uint32le(mbp, 0); /* padding */
- mb_put_uint16le(mbp, 0);
t2p->t2_maxpcount = 2;
t2p->t2_maxdcount = 0;
error = smb_t2_request(t2p);
@@ -791,7 +677,8 @@ exit:
if (fid) {
cerror = smbfs_smb_tmpclose(tdnp, fid, scrp);
if (cerror)
- SMBERROR("error %d closing fid %d\n", cerror, fid);
+ SMBVDEBUG("error %d closing %s\n",
+ cerror, tdnp->n_rpath);
}
smb_t2_done(t2p);
return (error);
@@ -845,24 +732,17 @@ smbfs_smb_setfsize(struct smbnode *np, uint16_t fid, uint64_t newsize,
struct mbchain *mbp;
int error;
- /*
- * This call knows about 64-bit offsets.
- */
- error = smbfs_smb_seteof(ssp, fid, newsize, scrp);
- if (!error) {
- mutex_enter(&np->r_statelock);
- np->n_flag |= (NFLUSHWIRE | NATTRCHANGED);
- mutex_exit(&np->r_statelock);
- return (0);
- }
-
- /*
- * If we have SMB_CAP_LARGE_FILES, the above
- * should have worked. XXX: Don't fallback?
- * XXX: Or fallback on specific errors?
- */
- if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES) {
- SMBVDEBUG("Have CAP_LARGE but _seteof error=%d\n", error);
+ if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) {
+ /*
+ * This call knows about 64-bit offsets.
+ */
+ error = smbfs_smb_seteof(ssp, fid, newsize, scrp);
+ if (!error) {
+ mutex_enter(&np->r_statelock);
+ np->n_flag |= (NFLUSHWIRE | NATTRCHANGED);
+ mutex_exit(&np->r_statelock);
+ return (0);
+ }
}
/*
@@ -894,6 +774,9 @@ smbfs_smb_setfsize(struct smbnode *np, uint16_t fid, uint64_t newsize,
return (error);
}
+/*
+ * Old method for getting file attributes.
+ */
int
smbfs_smb_query_info(struct smbnode *np, const char *name, int nmlen,
struct smbfattr *fap, struct smb_cred *scrp)
@@ -915,79 +798,39 @@ smbfs_smb_query_info(struct smbnode *np, const char *name, int nmlen,
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
mb_put_uint8(mbp, SMB_DT_ASCII);
- do {
- error = smbfs_fullpath(mbp, SSTOVC(ssp), np,
- name, &nmlen, '\\');
- if (error)
- break;
- smb_rq_bend(rqp);
- error = smb_rq_simple(rqp);
- if (error)
- break;
- smb_rq_getreply(rqp, &mdp);
- if (md_get_uint8(mdp, &wc) != 0 || wc != 10) {
- error = EBADRPC;
- break;
- }
- md_get_uint16le(mdp, &wattr);
- fap->fa_attr = wattr;
- /*
- * Be careful using the time returned here, as
- * with FAT on NT4SP6, at least, the time returned is low
- * 32 bits of 100s of nanoseconds (since 1601) so it rolls
- * over about every seven minutes!
- */
- md_get_uint32le(mdp, &longint); /* specs: secs since 1970 */
- if (longint) /* avoid bogus zero returns */
- smb_time_server2local(longint,
- SSTOVC(ssp)->vc_sopt.sv_tz, &fap->fa_mtime);
- md_get_uint32le(mdp, &longint);
- fap->fa_size = longint;
- /*LINTED*/
- } while (0);
- smb_rq_done(rqp);
- return (error);
-}
-
-int
-smbfs_smb_setpattr(struct smbnode *np, uint32_t attr,
- struct timespec *mtime, struct timespec *atime,
- struct smb_cred *scrp)
-{
- struct smb_share *ssp = np->n_mount->smi_share;
- struct smb_vc *vcp = SSTOVC(ssp);
- int error;
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), np,
+ name, &nmlen, '\\');
+ if (error)
+ goto out;
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ if (error)
+ goto out;
+ smb_rq_getreply(rqp, &mdp);
+ error = md_get_uint8(mdp, &wc);
+ if (error)
+ goto out;
+ if (wc != 10) {
+ error = EBADRPC;
+ goto out;
+ }
+ md_get_uint16le(mdp, &wattr);
+ fap->fa_attr = wattr;
/*
- * This is the logic that was in smbfs_vnops.c
+ * Be careful using the time returned here, as
+ * with FAT on NT4SP6, at least, the time returned is low
+ * 32 bits of 100s of nanoseconds (since 1601) so it rolls
+ * over about every seven minutes!
*/
- if ((vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) &&
- (vcp->vc_flags & SMBV_NT4) == 0) {
- /*
- * NT4 doesn't understand "NT" style SMBs;
- * for NT4 we use the old SET_PATH_INFO
- * XXX Actually, I think the issue is that
- * NT requires an open handle for this.
- */
- error = smbfs_smb_setpattrNT(np,
- attr, mtime, atime, scrp);
- if (error != EBADRPC)
- return (error);
-
- /* NT4 response, remember */
- SMB_VC_LOCK(vcp);
- vcp->vc_flags |= SMBV_NT4;
- SMB_VC_UNLOCK(vcp);
- }
-
- if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) {
- error = smbfs_smb_setpattr2(np,
- attr, mtime, atime, scrp);
- } else {
- error = smbfs_smb_setpattr1(np, NULL, 0,
- attr, mtime, scrp);
- }
+ md_get_uint32le(mdp, &longint); /* specs: secs since 1970 */
+ smb_time_server2local(longint,
+ SSTOVC(ssp)->vc_sopt.sv_tz, &fap->fa_mtime);
+ error = md_get_uint32le(mdp, &longint);
+ fap->fa_size = longint;
+out:
+ smb_rq_done(rqp);
return (error);
}
@@ -1021,22 +864,20 @@ smbfs_smb_setpattr1(struct smbnode *np, const char *name, int len,
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
mb_put_uint8(mbp, SMB_DT_ASCII);
- do {
- error = smbfs_fullpath(mbp, SSTOVC(ssp), np, name, &len, '\\');
- if (error)
- break;
- mb_put_uint8(mbp, SMB_DT_ASCII);
- if (SMB_UNICODE_STRINGS(SSTOVC(ssp))) {
- mb_put_padbyte(mbp);
- mb_put_uint8(mbp, 0); /* 1st byte NULL Unicode char */
- }
- mb_put_uint8(mbp, 0);
- smb_rq_bend(rqp);
- error = smb_rq_simple(rqp);
- if (error)
- break;
- /*LINTED*/
- } while (0);
+
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), np, name, &len, '\\');
+ if (error)
+ goto out;
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ if (SMB_UNICODE_STRINGS(SSTOVC(ssp))) {
+ mb_put_padbyte(mbp);
+ mb_put_uint8(mbp, 0); /* 1st byte NULL Unicode char */
+ }
+ mb_put_uint8(mbp, 0);
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+
+out:
smb_rq_done(rqp);
return (error);
}
@@ -1077,188 +918,41 @@ smbfs_smb_unhideit(struct smbnode *np, const char *name, int len,
}
/*
- * Note, win95 doesn't support this call.
+ * Set file attributes (optionally: DOS attr, atime, mtime)
+ * either by open FID or by path name (FID == -1).
*/
int
-smbfs_smb_setpattr2(struct smbnode *np, uint32_t attr,
- struct timespec *mtime, struct timespec *atime,
- struct smb_cred *scrp)
-{
- struct smb_t2rq *t2p;
- struct smb_share *ssp = np->n_mount->smi_share;
- struct smb_vc *vcp = SSTOVC(ssp);
- struct mbchain *mbp;
- uint16_t date, time;
- int error, tzoff;
-
- error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION,
- scrp, &t2p);
- if (error)
- return (error);
- mbp = &t2p->t2_tparam;
- mb_init(mbp);
- mb_put_uint16le(mbp, SMB_SFILEINFO_STANDARD);
- mb_put_uint32le(mbp, 0); /* MBZ */
- /* mb_put_uint8(mbp, SMB_DT_ASCII); specs incorrect */
- error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\');
- if (error) {
- smb_t2_done(t2p);
- return (error);
- }
- tzoff = vcp->vc_sopt.sv_tz;
- mbp = &t2p->t2_tdata;
- mb_init(mbp);
- mb_put_uint32le(mbp, 0); /* creation time */
- if (atime)
- smb_time_unix2dos(atime, tzoff, &date, &time, NULL);
- else
- time = date = 0;
- mb_put_uint16le(mbp, date);
- mb_put_uint16le(mbp, time);
- if (mtime)
- smb_time_unix2dos(mtime, tzoff, &date, &time, NULL);
- else
- time = date = 0;
- mb_put_uint16le(mbp, date);
- mb_put_uint16le(mbp, time);
- mb_put_uint32le(mbp, 0); /* file size */
- mb_put_uint32le(mbp, 0); /* allocation unit size */
- mb_put_uint16le(mbp, attr); /* DOS attr */
- mb_put_uint32le(mbp, 0); /* EA size */
- t2p->t2_maxpcount = 5 * 2;
- t2p->t2_maxdcount = vcp->vc_txmax;
- error = smb_t2_request(t2p);
- smb_t2_done(t2p);
- return (error);
-}
-
-/*
- * *BASIC_INFO works with Samba, but Win2K servers say it is an
- * invalid information level on a SET_PATH_INFO. Note Win2K does
- * support *BASIC_INFO on a SET_FILE_INFO, and they support the
- * equivalent *BASIC_INFORMATION on SET_PATH_INFO. Go figure.
- */
-int
-smbfs_smb_setpattrNT(struct smbnode *np, uint32_t attr,
- struct timespec *mtime, struct timespec *atime,
+smbfs_smb_setfattr(
+ struct smbnode *np,
+ int fid,
+ uint32_t attr,
+ struct timespec *mtime,
+ struct timespec *atime,
struct smb_cred *scrp)
{
- struct smb_t2rq *t2p;
struct smb_share *ssp = np->n_mount->smi_share;
struct smb_vc *vcp = SSTOVC(ssp);
- struct mbchain *mbp;
- uint64_t tm;
- int error, tzoff;
- /* 64 bit value for Jan 1 1980 */
- PRIVSYM uint64_t DIFF1980TO1601 = 11960035200ULL*10000000ULL;
-
- error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION,
- scrp, &t2p);
- if (error)
- return (error);
- mbp = &t2p->t2_tparam;
- mb_init(mbp);
- if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU)
- mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFORMATION);
- else
- mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFO);
- mb_put_uint32le(mbp, 0); /* MBZ */
- /* mb_put_uint8(mbp, SMB_DT_ASCII); specs incorrect */
- error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\');
- if (error) {
- smb_t2_done(t2p);
- return (error);
- }
- tzoff = vcp->vc_sopt.sv_tz;
+ int error;
- /* do we know it won't support dates < 1980? */
- if (!(ssp->ss_flags & SMBS_1980)) {
- mbp = &t2p->t2_tdata;
- mb_init(mbp);
- mb_put_uint64le(mbp, 0); /* creation time */
- if (atime) {
- smb_time_local2NT(atime, tzoff, &tm);
- } else
- tm = 0;
- mb_put_uint64le(mbp, tm); /* access time */
- if (mtime) {
- smb_time_local2NT(mtime, tzoff, &tm);
- } else
- tm = 0;
- mb_put_uint64le(mbp, tm); /* last write time */
- mb_put_uint64le(mbp, tm); /* change time */
- mb_put_uint32le(mbp, attr); /* attr */
- mb_put_uint32le(mbp, 0); /* undocumented padding */
- t2p->t2_maxpcount = 24;
- t2p->t2_maxdcount = 56;
- error = smb_t2_request(t2p);
- }
/*
- * "invalid argument" error probably means it's a
- * FAT drive that doesn't accept dates earlier
- * than 1980, so adjust dates and retry. If the
- * 1980 flag is on we fell thru the if {} above
+ * Normally can use the trans2 call.
*/
- if ((ssp->ss_flags & SMBS_1980) || (error == EINVAL)) {
- mbp = &t2p->t2_tdata;
- mb_init(mbp);
- mb_put_uint64le(mbp, 0); /* creation time */
- if (atime) {
- smb_time_local2NT(atime, tzoff, &tm);
- if (tm < DIFF1980TO1601)
- tm = DIFF1980TO1601;
- } else
- tm = 0;
- mb_put_uint64le(mbp, tm); /* access time */
- if (mtime) {
- smb_time_local2NT(mtime, tzoff, &tm);
- if (tm < DIFF1980TO1601)
- tm = DIFF1980TO1601;
- } else
- tm = 0;
- mb_put_uint64le(mbp, tm); /* last write time */
- mb_put_uint64le(mbp, tm); /* change time */
- mb_put_uint32le(mbp, attr); /* attr */
- mb_put_uint32le(mbp, 0); /* undocumented padding */
- t2p->t2_maxpcount = 24;
- t2p->t2_maxdcount = 56;
- error = smb_t2_request(t2p);
-
- /* if this worked set flag to do the right thing next time */
- if (!(error)) {
- SMB_SS_LOCK(ssp);
- ssp->ss_flags |= SMBS_1980;
- SMB_SS_UNLOCK(ssp);
- }
+ if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) {
+ error = smbfs_smb_setfattrNT(np, fid,
+ attr, mtime, atime, scrp);
+ return (error);
}
- smb_t2_done(t2p);
- return (error);
-}
-
-int
-smbfs_smb_setfattr(struct smbnode *np, uint16_t fid,
- uint32_t attr, struct timespec *mtime,
- struct timespec *atime, struct smb_cred *scrp)
-{
- struct smb_share *ssp = np->n_mount->smi_share;
- struct smb_vc *vcp = SSTOVC(ssp);
- int error;
/*
- * This is the logic that was in smbfs_vnops.c
- * Might not be quite right for older dialects.
- * (XXX: What about the DOS attributes?)
+ * Fall-back for older protocols.
*/
- if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS)
- error = smbfs_smb_setfattrNT(np, fid,
- np->n_dosattr, mtime, atime, scrp);
- else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN1_0)
+ if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN1_0) {
error = smbfs_smb_setftime1(np, fid,
mtime, atime, scrp);
- else
- error = smbfs_smb_setpattr1(np, NULL, 0,
- attr, mtime, scrp);
-
+ return (error);
+ }
+ error = smbfs_smb_setpattr1(np, NULL, 0,
+ attr, mtime, scrp);
return (error);
}
@@ -1282,6 +976,7 @@ smbfs_smb_setftime1(
error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION2, scrp);
if (error)
return (error);
+
tzoff = SSTOVC(ssp)->vc_sopt.sv_tz;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
@@ -1310,53 +1005,91 @@ smbfs_smb_setftime1(
}
/*
- * Set DOS file attributes.
+ * Set DOS file attributes, either via open FID or by path name.
* Looks like this call can be used only if CAP_NT_SMBS bit is on.
+ *
+ * When setting via path (fid == -1):
+ * *BASIC_INFO works with Samba, but Win2K servers say it is an
+ * invalid information level on a SET_PATH_INFO. Note Win2K does
+ * support *BASIC_INFO on a SET_FILE_INFO, and they support the
+ * equivalent *BASIC_INFORMATION on SET_PATH_INFO. Go figure.
*/
int
-smbfs_smb_setfattrNT(struct smbnode *np, uint16_t fid,
- uint32_t attr, struct timespec *mtime,
- struct timespec *atime, struct smb_cred *scrp)
+smbfs_smb_setfattrNT(
+ struct smbnode *np,
+ int fid, /* if fid == -1, set by path */
+ uint32_t attr,
+ struct timespec *mtime,
+ struct timespec *atime,
+ struct smb_cred *scrp)
{
struct smb_t2rq *t2p;
struct smb_share *ssp = np->n_mount->smi_share;
struct smb_vc *vcp = SSTOVC(ssp);
struct mbchain *mbp;
uint64_t tm;
- int error, svtz;
+ int error;
+ uint16_t cmd, level;
- error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION,
- scrp, &t2p);
+ if (fid == -1) {
+ cmd = SMB_TRANS2_SET_PATH_INFORMATION;
+ } else {
+ if (fid > UINT16_MAX)
+ return (EINVAL);
+ cmd = SMB_TRANS2_SET_FILE_INFORMATION;
+ }
+ if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU)
+ level = SMB_SFILEINFO_BASIC_INFORMATION;
+ else
+ level = SMB_SFILEINFO_BASIC_INFO;
+
+ error = smb_t2_alloc(SSTOCP(ssp), cmd, scrp, &t2p);
if (error)
return (error);
- svtz = SSTOVC(ssp)->vc_sopt.sv_tz;
+
mbp = &t2p->t2_tparam;
mb_init(mbp);
- mb_put_uint16le(mbp, fid);
- if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU)
- mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFORMATION);
- else
- mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFO);
- mb_put_uint32le(mbp, 0); /* XXX should be 16 not 32(?) */
+
+ if (cmd == SMB_TRANS2_SET_FILE_INFORMATION)
+ mb_put_uint16le(mbp, fid);
+
+ mb_put_uint16le(mbp, level);
+ mb_put_uint32le(mbp, 0); /* MBZ */
+
+ if (cmd == SMB_TRANS2_SET_PATH_INFORMATION) {
+ error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\');
+ if (error != 0)
+ goto out;
+ }
+
+ /* FAT file systems don't support dates earlier than 1980. */
+
mbp = &t2p->t2_tdata;
mb_init(mbp);
mb_put_uint64le(mbp, 0); /* creation time */
if (atime) {
- smb_time_local2NT(atime, svtz, &tm);
+ smb_time_local2NT(atime, &tm);
+ if (tm != 0 && (ssp->ss_flags & SMBS_FST_FAT) &&
+ tm < NT1980)
+ tm = NT1980;
} else
tm = 0;
mb_put_uint64le(mbp, tm); /* access time */
if (mtime) {
- smb_time_local2NT(mtime, svtz, &tm);
+ smb_time_local2NT(mtime, &tm);
+ if (tm != 0 && (ssp->ss_flags & SMBS_FST_FAT) &&
+ tm < NT1980)
+ tm = NT1980;
} else
tm = 0;
mb_put_uint64le(mbp, tm); /* last write time */
- mb_put_uint64le(mbp, tm); /* change time */
+ mb_put_uint64le(mbp, 0); /* ctime (no change) */
mb_put_uint32le(mbp, attr);
- mb_put_uint32le(mbp, 0); /* padding */
+ mb_put_uint32le(mbp, 0); /* padding */
t2p->t2_maxpcount = 2;
t2p->t2_maxdcount = 0;
error = smb_t2_request(t2p);
+out:
smb_t2_done(t2p);
return (error);
}
@@ -1380,38 +1113,34 @@ smbfs_smb_setfattrNT(struct smbnode *np, uint16_t fid,
* now too, which may or may not create a new object.
*/
int
-smbfs_smb_ntcreatex(struct smbnode *np, uint32_t rights,
- struct smb_cred *scrp, enum vtype vt,
- int *attrcacheupdated, uint16_t *fidp,
- const char *name, int nmlen, uint32_t disp, int xattr,
- len_t *sizep, uint32_t *rightsp)
+smbfs_smb_ntcreatex(
+ struct smbnode *np,
+ const char *name,
+ int nmlen,
+ int xattr, /* is named stream? */
+ uint32_t req_acc, /* requested access */
+ uint32_t efa, /* ext. file attrs (DOS attr +) */
+ uint32_t share_acc,
+ uint32_t disp, /* open disposition */
+ uint32_t createopt, /* NTCREATEX_OPTIONS_ */
+ struct smb_cred *scrp,
+ uint16_t *fidp,
+ uint32_t *cr_act_p, /* create action */
+ struct smbfattr *fap) /* optional */
{
struct smb_rq rq, *rqp = &rq;
struct smb_share *ssp = np->n_mount->smi_share;
struct smb_vc *vcp = SSTOVC(ssp);
struct mbchain *mbp;
struct mdchain *mdp;
- struct smbfattr fap;
+ struct smbfattr fa;
uint8_t wc;
- uint32_t longint, createact, createopt, efa;
+ uint32_t longint, createact;
uint64_t llongint;
int error;
uint16_t fid, *namelenp;
- /*
- * Set the File attributes and Create options.
- * WinXP uses EFA_NORMAL in all of these cases.
- */
- createopt = (vt == VDIR) ?
- NTCREATEX_OPTIONS_DIRECTORY :
- NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
- efa = SMB_EFA_NORMAL;
- if (disp != NTCREATEX_DISP_OPEN && !xattr) {
- if (name && *name == '.')
- efa = SMB_EFA_HIDDEN;
- }
-
- gethrestime(&fap.fa_reqtime);
+ bzero(&fa, sizeof (fa));
error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_NT_CREATE_ANDX, scrp);
if (error)
return (error);
@@ -1426,127 +1155,87 @@ smbfs_smb_ntcreatex(struct smbnode *np, uint32_t rights,
* XP to a W2K Server does not use NTCREATEX_FLAGS_OPEN_DIRECTORY
* for creating nor for opening a directory. Samba ignores the bit.
*/
-#if 0 /* causes sharing violation when making dir on W2K! */
- mb_put_uint32le(mbp, vt == VDIR ? NTCREATEX_FLAGS_OPEN_DIRECTORY : 0);
-#else
mb_put_uint32le(mbp, 0); /* NTCREATEX_FLAGS_* */
-#endif
mb_put_uint32le(mbp, 0); /* FID - basis for path if not root */
- mb_put_uint32le(mbp, rights);
+ mb_put_uint32le(mbp, req_acc);
mb_put_uint64le(mbp, 0); /* "initial allocation size" */
mb_put_uint32le(mbp, efa);
- mb_put_uint32le(mbp, NTCREATEX_SHARE_ACCESS_ALL);
+ mb_put_uint32le(mbp, share_acc);
mb_put_uint32le(mbp, disp);
mb_put_uint32le(mbp, createopt);
mb_put_uint32le(mbp, NTCREATEX_IMPERSONATION_IMPERSONATION); /* (?) */
mb_put_uint8(mbp, 0); /* security flags (?) */
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
- do {
- if (name == NULL)
- nmlen = 0;
- error = smbfs_fullpath(mbp, vcp, np, name, &nmlen,
- xattr ? ':' : '\\');
- if (error)
- break;
- *namelenp = htoles(nmlen); /* includes null */
- smb_rq_bend(rqp);
- /*
- * Don't want to risk missing a successful
- * open response, or we could "leak" FIDs.
- */
- rqp->sr_flags |= SMBR_NOINTR_RECV;
- error = smb_rq_simple_timed(rqp, smb_timo_open);
- if (error)
- break;
- smb_rq_getreply(rqp, &mdp);
- /*
- * spec says 26 for word count, but 34 words are defined
- * and observed from win2000
- */
- if (md_get_uint8(mdp, &wc) != 0 ||
- (wc != 26 && wc != 34 && wc != 42)) {
- error = EBADRPC;
- break;
- }
- md_get_uint8(mdp, NULL); /* secondary cmd */
- md_get_uint8(mdp, NULL); /* mbz */
- md_get_uint16le(mdp, NULL); /* andxoffset */
- md_get_uint8(mdp, NULL); /* oplock lvl granted */
- md_get_uint16le(mdp, &fid); /* file ID */
- md_get_uint32le(mdp, &createact); /* create_action */
- md_get_uint64le(mdp, &llongint); /* creation time */
- md_get_uint64le(mdp, &llongint); /* access time */
- if (llongint) /* avoid bogus 0 time (on FAT roots) */
- smb_time_NT2local(llongint, vcp->vc_sopt.sv_tz,
- &fap.fa_atime);
- md_get_uint64le(mdp, &llongint); /* write time */
- if (llongint) /* avoid bogus 0 time (on FAT roots) */
- smb_time_NT2local(llongint, vcp->vc_sopt.sv_tz,
- &fap.fa_mtime);
- md_get_uint64le(mdp, &llongint); /* change time */
- if (llongint) /* avoid bogus 0 time (on FAT roots) */
- smb_time_NT2local(llongint, vcp->vc_sopt.sv_tz,
- &fap.fa_ctime);
- md_get_uint32le(mdp, &longint); /* attributes */
- fap.fa_attr = longint;
- md_get_uint64le(mdp, NULL); /* allocation size */
- md_get_uint64le(mdp, &llongint); /* EOF */
- fap.fa_size = llongint;
- if (sizep)
- *sizep = fap.fa_size;
- md_get_uint16le(mdp, NULL); /* file type */
- md_get_uint16le(mdp, NULL); /* device state */
- md_get_uint8(mdp, NULL); /* directory (boolean) */
- /*LINTED*/
- } while (0);
- smb_rq_done(rqp);
+
+ if (name == NULL)
+ nmlen = 0;
+ error = smbfs_fullpath(mbp, vcp, np, name, &nmlen,
+ xattr ? ':' : '\\');
if (error)
- return (error);
- if (fidp)
- *fidp = fid;
- if (rightsp)
- *rightsp = rights;
+ goto done;
+ *namelenp = htoles(nmlen); /* includes null */
+ smb_rq_bend(rqp);
/*
- * Is it possible that we have cached attributes?
- * Assume "not cached" if we created the object.
+ * Don't want to risk missing a successful
+ * open response, or we could "leak" FIDs.
*/
- if (createact == NTCREATEX_ACTION_CREATED || xattr)
- goto uncached;
- if (attrcacheupdated)
- *attrcacheupdated = 0;
+ rqp->sr_flags |= SMBR_NOINTR_RECV;
+ error = smb_rq_simple_timed(rqp, smb_timo_open);
+ if (error)
+ goto done;
+ smb_rq_getreply(rqp, &mdp);
/*
- * Update the cached attributes if they are still valid
- * in the cache and if nothing has changed.
+ * spec says 26 for word count, but 34 words are defined
+ * and observed from win2000
*/
- if (np->r_vnode == NULL)
- goto uncached;
- if (smbfs_attr_cachelookup(np->r_vnode, NULL) != 0)
- goto uncached; /* the cached attributes are not valid */
- if (fap.fa_size != np->n_size)
- goto uncached; /* the size is different */
- if (fap.fa_attr != np->n_dosattr)
- goto uncached; /* the attrs are different */
- /*
- * fap.fa_mtime is in two second increments while np->n_mtime
- * may be in one second increments, so comparing the times is
- * somewhat sloppy.
- *
- * XXX: true fap.fa_mtime resolution must depend upon server's
- * local filesystem and is thus indeterminate... XXX ...TBD how that
- * affects this code... note wire resolution here is 100ns versus
- * 1sec down in smbfs_smb_oldopen(SMB_COM_OPEN)
- */
- if (fap.fa_mtime.tv_sec != np->n_mtime.tv_sec &&
- fap.fa_mtime.tv_sec != np->n_mtime.tv_sec - 1 &&
- fap.fa_mtime.tv_sec != np->n_mtime.tv_sec + 1)
- goto uncached; /* the mod time is different */
-
- fap.fa_mtime.tv_sec = np->n_mtime.tv_sec; /* keep higher res time */
- smbfs_attr_cacheenter(np->r_vnode, &fap);
- if (attrcacheupdated)
- *attrcacheupdated = 1;
-uncached:
+ error = md_get_uint8(mdp, &wc);
+ if (error)
+ goto done;
+ if (wc != 26 && wc != 34 && wc != 42) {
+ error = EBADRPC;
+ goto done;
+ }
+ md_get_uint8(mdp, NULL); /* secondary cmd */
+ md_get_uint8(mdp, NULL); /* mbz */
+ md_get_uint16le(mdp, NULL); /* andxoffset */
+ md_get_uint8(mdp, NULL); /* oplock lvl granted */
+ md_get_uint16le(mdp, &fid); /* file ID */
+ md_get_uint32le(mdp, &createact); /* create_action */
+
+ md_get_uint64le(mdp, &llongint); /* creation time */
+ smb_time_NT2local(llongint, &fa.fa_createtime);
+ md_get_uint64le(mdp, &llongint); /* access time */
+ smb_time_NT2local(llongint, &fa.fa_atime);
+ md_get_uint64le(mdp, &llongint); /* write time */
+ smb_time_NT2local(llongint, &fa.fa_mtime);
+ md_get_uint64le(mdp, &llongint); /* change time */
+ smb_time_NT2local(llongint, &fa.fa_ctime);
+
+ md_get_uint32le(mdp, &longint); /* attributes */
+ fa.fa_attr = longint;
+
+ md_get_uint64le(mdp, &llongint); /* allocation size */
+ fa.fa_allocsz = llongint;
+
+ md_get_uint64le(mdp, &llongint); /* EOF position */
+ fa.fa_size = llongint;
+
+ error = md_get_uint16le(mdp, NULL); /* file type */
+ /* other stuff we don't care about */
+
+done:
+ smb_rq_done(rqp);
+ if (error)
+ return (error);
+
+ if (fidp)
+ *fidp = fid;
+ if (cr_act_p)
+ *cr_act_p = createact;
+ if (fap)
+ *fap = fa; /* struct copy */
+
return (0);
}
@@ -1554,18 +1243,32 @@ static uint32_t
smb_mode2rights(int mode)
{
mode = mode & SMB_AM_OPENMODE;
+ uint32_t rights =
+ STD_RIGHT_SYNCHRONIZE_ACCESS |
+ STD_RIGHT_READ_CONTROL_ACCESS;
- switch (mode) {
- case SMB_AM_OPENREAD:
- return (GENERIC_RIGHT_READ_ACCESS);
- case SMB_AM_OPENWRITE:
- return (GENERIC_RIGHT_WRITE_ACCESS);
- case SMB_AM_OPENRW:
- return (GENERIC_RIGHT_ALL_ACCESS);
- case SMB_AM_OPENEXEC:
- return (GENERIC_RIGHT_EXECUTE_ACCESS);
+ if ((mode == SMB_AM_OPENREAD) ||
+ (mode == SMB_AM_OPENRW)) {
+ rights |=
+ SA_RIGHT_FILE_READ_ATTRIBUTES |
+ SA_RIGHT_FILE_READ_DATA;
}
- return (0);
+
+ if ((mode == SMB_AM_OPENWRITE) ||
+ (mode == SMB_AM_OPENRW)) {
+ rights |=
+ SA_RIGHT_FILE_WRITE_ATTRIBUTES |
+ SA_RIGHT_FILE_APPEND_DATA |
+ SA_RIGHT_FILE_WRITE_DATA;
+ }
+
+ if (mode == SMB_AM_OPENEXEC) {
+ rights |=
+ SA_RIGHT_FILE_READ_ATTRIBUTES |
+ SA_RIGHT_FILE_EXECUTE;
+ }
+
+ return (rights);
}
static int
@@ -1576,34 +1279,43 @@ smb_rights2mode(uint32_t rights)
if (rights & (SA_RIGHT_FILE_APPEND_DATA | SA_RIGHT_FILE_DELETE_CHILD |
SA_RIGHT_FILE_WRITE_EA | SA_RIGHT_FILE_WRITE_ATTRIBUTES |
SA_RIGHT_FILE_WRITE_DATA | STD_RIGHT_WRITE_OWNER_ACCESS |
- STD_RIGHT_DELETE_ACCESS | STD_RIGHT_WRITE_DAC_ACCESS |
- GENERIC_RIGHT_ALL_ACCESS | GENERIC_RIGHT_WRITE_ACCESS))
+ STD_RIGHT_DELETE_ACCESS | STD_RIGHT_WRITE_DAC_ACCESS))
accmode = SMB_AM_OPENWRITE;
if (rights & (SA_RIGHT_FILE_READ_DATA | SA_RIGHT_FILE_READ_ATTRIBUTES |
- SA_RIGHT_FILE_READ_EA | STD_RIGHT_READ_CONTROL_ACCESS |
- GENERIC_RIGHT_ALL_ACCESS | GENERIC_RIGHT_READ_ACCESS))
+ SA_RIGHT_FILE_READ_EA | STD_RIGHT_READ_CONTROL_ACCESS))
accmode = (accmode == SMB_AM_OPENEXEC) ? SMB_AM_OPENREAD
: SMB_AM_OPENRW;
return (accmode);
}
static int
-smbfs_smb_oldopen(struct smbnode *np, int accmode, struct smb_cred *scrp,
- int *attrcacheupdated, uint16_t *fidp, const char *name,
- int nmlen, int xattr, len_t *sizep, uint32_t *rightsp)
+smbfs_smb_oldopen(
+ struct smbnode *np,
+ const char *name,
+ int nmlen,
+ int xattr,
+ int accmode,
+ struct smb_cred *scrp,
+ uint16_t *fidp,
+ uint16_t *granted_mode_p,
+ smbfattr_t *fap)
{
struct smb_rq rq, *rqp = &rq;
struct smb_share *ssp = np->n_mount->smi_share;
struct smb_vc *vcp = SSTOVC(ssp);
struct mbchain *mbp;
struct mdchain *mdp;
- struct smbfattr fap;
+ struct smbfattr fa;
uint8_t wc;
- uint16_t fid, wattr, grantedmode;
+ uint16_t wattr;
uint32_t longint;
int error;
+ bzero(&fa, sizeof (fa));
+
/*
+ * XXX: move to callers...
+ *
* Use DENYNONE to give unixy semantics of permitting
* everything not forbidden by permissions. Ie denial
* is up to server with clients/openers needing to use
@@ -1611,7 +1323,6 @@ smbfs_smb_oldopen(struct smbnode *np, int accmode, struct smb_cred *scrp,
*/
accmode |= SMB_SM_DENYNONE;
- gethrestime(&fap.fa_reqtime);
error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_OPEN, scrp);
if (error)
return (error);
@@ -1623,91 +1334,55 @@ smbfs_smb_oldopen(struct smbnode *np, int accmode, struct smb_cred *scrp,
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
mb_put_uint8(mbp, SMB_DT_ASCII);
- do {
- error = smbfs_fullpath(mbp, vcp, np, name, &nmlen,
- xattr ? ':' : '\\');
- if (error)
- break;
- smb_rq_bend(rqp);
- /*
- * Don't want to risk missing a successful
- * open response, or we could "leak" FIDs.
- */
- rqp->sr_flags |= SMBR_NOINTR_RECV;
- error = smb_rq_simple_timed(rqp, smb_timo_open);
- if (error)
- break;
- smb_rq_getreply(rqp, &mdp);
- /*
- * 8/2002 a DAVE server returned wc of 15 so we ignore that.
- * (the actual packet length and data was correct)
- */
- if (md_get_uint8(mdp, &wc) != 0 || (wc != 7 && wc != 15)) {
- error = EBADRPC;
- break;
- }
- md_get_uint16le(mdp, &fid);
- md_get_uint16le(mdp, &wattr);
- fap.fa_attr = wattr;
- /*
- * Be careful using the time returned here, as
- * with FAT on NT4SP6, at least, the time returned is low
- * 32 bits of 100s of nanoseconds (since 1601) so it rolls
- * over about every seven minutes!
- */
- md_get_uint32le(mdp, &longint); /* specs: secs since 1970 */
- if (longint) /* avoid bogus zero returns */
- smb_time_server2local(longint, vcp->vc_sopt.sv_tz,
- &fap.fa_mtime);
- md_get_uint32le(mdp, &longint);
- fap.fa_size = longint;
- if (sizep)
- *sizep = fap.fa_size;
- md_get_uint16le(mdp, &grantedmode);
- /*LINTED*/
- } while (0);
- smb_rq_done(rqp);
+
+ error = smbfs_fullpath(mbp, vcp, np, name, &nmlen,
+ xattr ? ':' : '\\');
if (error)
- return (error);
- if (fidp)
- *fidp = fid;
- if (xattr)
- goto uncached;
- if (rightsp)
- *rightsp = smb_mode2rights(grantedmode);
- if (attrcacheupdated)
- *attrcacheupdated = 0;
+ goto done;
+ smb_rq_bend(rqp);
/*
- * Update the cached attributes if they are still valid
- * in the cache and if nothing has changed.
- * Note that this won't ever update if the file size is
- * greater than the 32-bits returned by SMB_COM_OPEN.
- * For 64-bit file sizes, SMB_COM_NT_CREATE_ANDX must
- * be used instead of SMB_COM_OPEN.
+ * Don't want to risk missing a successful
+ * open response, or we could "leak" FIDs.
*/
- if (np->r_vnode == NULL)
- goto uncached;
- if (smbfs_attr_cachelookup(np->r_vnode, NULL) != 0)
- goto uncached; /* the cached attributes are not valid */
- if (fap.fa_size != np->n_size)
- goto uncached; /* the size is different */
- if (fap.fa_attr != np->n_dosattr)
- goto uncached; /* the attrs are different */
+ rqp->sr_flags |= SMBR_NOINTR_RECV;
+ error = smb_rq_simple_timed(rqp, smb_timo_open);
+ if (error)
+ goto done;
+ smb_rq_getreply(rqp, &mdp);
/*
- * fap.fa_mtime is in two second increments while np->n_mtime
- * may be in one second increments, so comparing the times is
- * somewhat sloppy.
+ * 8/2002 a DAVE server returned wc of 15 so we ignore that.
+ * (the actual packet length and data was correct)
*/
- if (fap.fa_mtime.tv_sec != np->n_mtime.tv_sec &&
- fap.fa_mtime.tv_sec != np->n_mtime.tv_sec - 1 &&
- fap.fa_mtime.tv_sec != np->n_mtime.tv_sec + 1)
- goto uncached; /* the mod time is different */
-
- fap.fa_mtime.tv_sec = np->n_mtime.tv_sec; /* keep higher res time */
- smbfs_attr_cacheenter(np->r_vnode, &fap);
- if (attrcacheupdated)
- *attrcacheupdated = 1;
-uncached:
+ error = md_get_uint8(mdp, &wc);
+ if (error)
+ goto done;
+ if (wc != 7 && wc != 15) {
+ error = EBADRPC;
+ goto done;
+ }
+ md_get_uint16le(mdp, fidp);
+ md_get_uint16le(mdp, &wattr);
+ fa.fa_attr = wattr;
+ /*
+ * Be careful using the time returned here, as
+ * with FAT on NT4SP6, at least, the time returned is low
+ * 32 bits of 100s of nanoseconds (since 1601) so it rolls
+ * over about every seven minutes!
+ */
+ md_get_uint32le(mdp, &longint); /* specs: secs since 1970 */
+ smb_time_server2local(longint, vcp->vc_sopt.sv_tz, &fa.fa_mtime);
+ md_get_uint32le(mdp, &longint);
+ fa.fa_size = longint;
+ error = md_get_uint16le(mdp, granted_mode_p);
+
+done:
+ smb_rq_done(rqp);
+ if (error)
+ return (error);
+
+ if (fap)
+ *fap = fa; /* struct copy */
+
return (0);
}
@@ -1717,8 +1392,7 @@ smbfs_smb_tmpopen(struct smbnode *np, uint32_t rights, struct smb_cred *scrp,
{
struct smb_share *ssp = np->n_mount->smi_share;
struct smb_vc *vcp = SSTOVC(ssp);
- enum vtype vt = VREG;
- int error;
+ int accmode, error;
/* Shared lock for n_fid use below. */
ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER));
@@ -1735,18 +1409,27 @@ smbfs_smb_tmpopen(struct smbnode *np, uint32_t rights, struct smb_cred *scrp,
}
mutex_exit(&np->r_statelock);
- if (!(vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS)) {
- int mode = smb_rights2mode(rights);
- error = smbfs_smb_oldopen(np, mode, scrp,
- NULL, fidp, NULL, 0, 0, NULL, NULL);
- } else {
- if (SMBTOV(np))
- vt = SMBTOV(np)->v_type;
- error = smbfs_smb_ntcreatex(np, rights, scrp, vt,
- NULL, fidp, NULL, 0, NTCREATEX_DISP_OPEN, 0,
- NULL, NULL);
+ /* re-open an existing file. */
+ if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) {
+ error = smbfs_smb_ntcreatex(np,
+ NULL, 0, 0, /* name nmlen xattr */
+ rights, SMB_EFA_NORMAL,
+ NTCREATEX_SHARE_ACCESS_ALL,
+ NTCREATEX_DISP_OPEN,
+ 0, /* create options */
+ scrp, fidp,
+ NULL, NULL); /* cr_act_p fa_p */
+ return (error);
}
+ accmode = smb_rights2mode(rights);
+ error = smbfs_smb_oldopen(np,
+ NULL, 0, 0, /* name nmlen xattr */
+ accmode, scrp,
+ fidp,
+ NULL, /* granted mode p */
+ NULL); /* fa p */
+
return (error);
}
@@ -1786,27 +1469,48 @@ smbfs_smb_tmpclose(struct smbnode *np, uint16_t fid, struct smb_cred *scrp)
}
int
-smbfs_smb_open(struct smbnode *np, uint32_t rights, struct smb_cred *scrp,
- int *attrcacheupdated, uint16_t *fidp, const char *name,
- int nmlen, int xattr, len_t *sizep, uint32_t *rightsp)
+smbfs_smb_open(
+ struct smbnode *np,
+ const char *name,
+ int nmlen,
+ int xattr,
+ uint32_t rights,
+ struct smb_cred *scrp,
+ uint16_t *fidp,
+ uint32_t *rightsp,
+ smbfattr_t *fap)
{
- int error;
struct smb_share *ssp = np->n_mount->smi_share;
struct smb_vc *vcp = SSTOVC(ssp);
- enum vtype vt = VREG;
+ int accmode, error;
+ uint16_t grantedmode;
+ /* open an existing file */
if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) {
- if (SMBTOV(np))
- vt = SMBTOV(np)->v_type;
- error = smbfs_smb_ntcreatex(np, rights, scrp, vt,
- attrcacheupdated, fidp, name, nmlen,
- NTCREATEX_DISP_OPEN, xattr, sizep, rightsp);
- } else {
- error = smbfs_smb_oldopen(np, smb_rights2mode(rights), scrp,
- attrcacheupdated, fidp, name, nmlen, xattr, sizep, rightsp);
+ error = smbfs_smb_ntcreatex(np,
+ name, nmlen, xattr,
+ rights, SMB_EFA_NORMAL,
+ NTCREATEX_SHARE_ACCESS_ALL,
+ NTCREATEX_DISP_OPEN,
+ 0, /* create options */
+ scrp, fidp,
+ NULL, fap); /* cr_act_p fa_p */
+ if (error != 0)
+ return (error);
+ *rightsp = rights;
+ return (0);
}
- return (error);
+ accmode = smb_rights2mode(rights);
+ error = smbfs_smb_oldopen(np,
+ name, nmlen, xattr, accmode, scrp,
+ fidp, &grantedmode, fap);
+ if (error != 0)
+ return (error);
+ *rightsp = smb_mode2rights(grantedmode);
+ (void) smbfs_smb_getfattr(np, fap, scrp);
+
+ return (0);
}
int
@@ -1825,7 +1529,8 @@ smbfs_smb_close(struct smb_share *ssp, uint16_t fid, struct timespec *mtime,
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, fid);
if (mtime) {
- smb_time_local2server(mtime, SSTOVC(ssp)->vc_sopt.sv_tz, &time);
+ int sv_tz = SSTOVC(ssp)->vc_sopt.sv_tz;
+ smb_time_local2server(mtime, sv_tz, &time);
} else
time = 0;
mb_put_uint32le(mbp, time);
@@ -1865,7 +1570,7 @@ smbfs_smb_close(struct smb_share *ssp, uint16_t fid, struct timespec *mtime,
static int
smbfs_smb_oldcreate(struct smbnode *dnp, const char *name, int nmlen,
- struct smb_cred *scrp, uint16_t *fidp, int xattr)
+ int xattr, struct smb_cred *scrp, uint16_t *fidp)
{
struct smb_rq rq, *rqp = &rq;
struct smb_share *ssp = dnp->n_mount->smi_share;
@@ -1893,32 +1598,45 @@ smbfs_smb_oldcreate(struct smbnode *dnp, const char *name, int nmlen,
mb_put_uint8(mbp, SMB_DT_ASCII);
error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, &nmlen,
xattr ? ':' : '\\');
- if (!error) {
- smb_rq_bend(rqp);
- /*
- * Don't want to risk missing a successful
- * open response, or we could "leak" FIDs.
- */
- rqp->sr_flags |= SMBR_NOINTR_RECV;
- error = smb_rq_simple_timed(rqp, smb_timo_open);
- if (!error) {
- smb_rq_getreply(rqp, &mdp);
- md_get_uint8(mdp, &wc);
- if (wc == 1)
- md_get_uint16le(mdp, fidp);
- else
- error = EBADRPC;
- }
+ if (error)
+ goto out;
+ smb_rq_bend(rqp);
+ /*
+ * Don't want to risk missing a successful
+ * open response, or we could "leak" FIDs.
+ */
+ rqp->sr_flags |= SMBR_NOINTR_RECV;
+ error = smb_rq_simple_timed(rqp, smb_timo_open);
+ if (error)
+ goto out;
+
+ smb_rq_getreply(rqp, &mdp);
+ md_get_uint8(mdp, &wc);
+ if (wc != 1) {
+ error = EBADRPC;
+ goto out;
}
+ error = md_get_uint16le(mdp, fidp);
+
+out:
smb_rq_done(rqp);
return (error);
}
int
-smbfs_smb_create(struct smbnode *dnp, const char *name, int nmlen,
- struct smb_cred *scrp, uint16_t *fidp, uint32_t disp, int xattr)
+smbfs_smb_create(
+ struct smbnode *dnp,
+ const char *name,
+ int nmlen,
+ int xattr,
+ uint32_t disp,
+ struct smb_cred *scrp,
+ uint16_t *fidp)
{
- struct smb_vc *vcp = SSTOVC(dnp->n_mount->smi_share);
+ struct smb_share *ssp = dnp->n_mount->smi_share;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ uint32_t efa, rights;
+ int error;
/*
* At present the only access we might need is to WRITE data,
@@ -1927,12 +1645,21 @@ smbfs_smb_create(struct smbnode *dnp, const char *name, int nmlen,
* and be set upstream.
*/
if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) {
- return (smbfs_smb_ntcreatex(dnp, SA_RIGHT_FILE_WRITE_DATA,
- scrp, VREG, NULL, fidp, name, nmlen, disp, xattr,
- NULL, NULL));
- } else
- return (smbfs_smb_oldcreate(dnp, name, nmlen, scrp, fidp,
- xattr));
+ rights = SA_RIGHT_FILE_WRITE_DATA;
+ efa = SMB_EFA_NORMAL;
+ if (!xattr && name && *name == '.')
+ efa = SMB_EFA_HIDDEN;
+ error = smbfs_smb_ntcreatex(dnp,
+ name, nmlen, xattr, rights, efa,
+ NTCREATEX_SHARE_ACCESS_ALL,
+ disp, /* != NTCREATEX_DISP_OPEN */
+ NTCREATEX_OPTIONS_NON_DIRECTORY_FILE,
+ scrp, fidp, NULL, NULL); /* cr_act_p fa_p */
+ return (error);
+ }
+
+ error = smbfs_smb_oldcreate(dnp, name, nmlen, xattr, scrp, fidp);
+ return (error);
}
int
@@ -2031,19 +1758,18 @@ smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp,
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
mb_put_uint8(mbp, SMB_DT_ASCII);
- do {
- error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, NULL, '\\');
- if (error)
- break;
- mb_put_uint8(mbp, SMB_DT_ASCII);
- error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, &tnmlen,
- '\\');
- if (error)
- break;
- smb_rq_bend(rqp);
- error = smb_rq_simple(rqp);
- /*LINTED*/
- } while (0);
+
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, NULL, '\\');
+ if (error)
+ goto out;
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, &tnmlen, '\\');
+ if (error)
+ goto out;
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+
+out:
smb_rq_done(rqp);
return (error);
}
@@ -2075,10 +1801,12 @@ smbfs_smb_oldmkdir(struct smbnode *dnp, const char *name, int len,
}
int
-smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len,
+smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int nmlen,
struct smb_cred *scrp)
{
struct smb_share *ssp = dnp->n_mount->smi_share;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ uint32_t rights;
uint16_t fid;
int error;
@@ -2087,18 +1815,23 @@ smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len,
* just to be asking for something. The rights==0 case could
* easily be broken on some old or unusual servers.
*/
- if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) {
- error = smbfs_smb_ntcreatex(dnp, SA_RIGHT_FILE_READ_DATA,
- scrp, VDIR, NULL, &fid, name, len,
- NTCREATEX_DISP_CREATE, 0, NULL, NULL);
+ if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) {
+ rights = SA_RIGHT_FILE_READ_DATA;
+ error = smbfs_smb_ntcreatex(dnp,
+ name, nmlen, 0, /* xattr */
+ rights, SMB_EFA_DIRECTORY,
+ NTCREATEX_SHARE_ACCESS_ALL,
+ NTCREATEX_DISP_CREATE,
+ NTCREATEX_OPTIONS_DIRECTORY,
+ scrp, &fid, NULL, NULL); /* cr_act_p fa_p */
if (error)
return (error);
- error = smbfs_smb_close(ssp, fid, NULL, scrp);
- if (error)
- SMBERROR("error %d closing fid %d\n", error, fid);
+ (void) smbfs_smb_close(ssp, fid, NULL, scrp);
return (0);
- } else
- return (smbfs_smb_oldmkdir(dnp, name, len, scrp));
+ }
+
+ error = smbfs_smb_oldmkdir(dnp, name, nmlen, scrp);
+ return (error);
}
int
@@ -2183,21 +1916,25 @@ smbfs_smb_search(struct smbfs_fctx *ctx)
} else if (error)
return (error);
smb_rq_getreply(rqp, &mdp);
- md_get_uint8(mdp, &wc);
+ error = md_get_uint8(mdp, &wc);
+ if (error)
+ return (error);
if (wc != 1)
return (iseof ? ENOENT : EBADRPC);
md_get_uint16le(mdp, &ec);
+ md_get_uint16le(mdp, &bc);
+ md_get_uint8(mdp, &bt);
+ error = md_get_uint16le(mdp, &dlen);
+ if (error)
+ return (error);
if (ec == 0)
return (ENOENT);
ctx->f_ecnt = ec;
- md_get_uint16le(mdp, &bc);
if (bc < 3)
return (EBADRPC);
bc -= 3;
- md_get_uint8(mdp, &bt);
if (bt != SMB_DT_VARIABLE)
return (EBADRPC);
- md_get_uint16le(mdp, &dlen);
if (dlen != bc || dlen % SMB_DENTRYLEN != 0)
return (EBADRPC);
return (0);
@@ -2249,7 +1986,6 @@ smbfs_smb_findnextLM1(struct smbfs_fctx *ctx, uint16_t limit)
error = smbfs_smb_search(ctx);
if (error)
return (error);
- ctx->f_attr.fa_reqtime = ts;
}
rqp = ctx->f_rq;
smb_rq_getreply(rqp, &mdp);
@@ -2259,7 +1995,7 @@ smbfs_smb_findnextLM1(struct smbfs_fctx *ctx, uint16_t limit)
md_get_uint16le(mdp, &date);
md_get_uint32le(mdp, &size);
cp = ctx->f_name;
- md_get_mem(mdp, cp, sizeof (ctx->f_fname), MB_MSYSTEM);
+ error = md_get_mem(mdp, cp, sizeof (ctx->f_fname), MB_MSYSTEM);
cp[sizeof (ctx->f_fname) - 1] = 0;
cp += strlen(cp) - 1;
while (*cp == ' ' && cp >= ctx->f_name)
@@ -2473,7 +2209,6 @@ smbfs_smb_findnextLM2(struct smbfs_fctx *ctx, uint16_t limit)
error = smbfs_smb_trans2find2(ctx);
if (error)
return (error);
- ctx->f_attr.fa_reqtime = ts;
ctx->f_otws++;
}
t2p = ctx->f_t2;
@@ -2485,6 +2220,8 @@ smbfs_smb_findnextLM2(struct smbfs_fctx *ctx, uint16_t limit)
fxsz = 0;
md_get_uint16le(mdp, &date);
md_get_uint16le(mdp, &time); /* creation time */
+ smb_dos2unixtime(date, time, 0, svtz,
+ &ctx->f_attr.fa_createtime);
md_get_uint16le(mdp, &date);
md_get_uint16le(mdp, &time); /* access time */
smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_atime);
@@ -2493,7 +2230,8 @@ smbfs_smb_findnextLM2(struct smbfs_fctx *ctx, uint16_t limit)
smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_mtime);
md_get_uint32le(mdp, &size);
ctx->f_attr.fa_size = size;
- md_get_uint32le(mdp, NULL); /* allocation size */
+ md_get_uint32le(mdp, &size); /* allocation size */
+ ctx->f_attr.fa_allocsz = size;
md_get_uint16le(mdp, &wattr);
ctx->f_attr.fa_attr = wattr;
error = md_get_uint8(mdp, &tb);
@@ -2507,18 +2245,19 @@ smbfs_smb_findnextLM2(struct smbfs_fctx *ctx, uint16_t limit)
case SMB_FIND_BOTH_DIRECTORY_INFO:
md_get_uint32le(mdp, &next);
md_get_uint32le(mdp, &resumekey); /* file index (resume key) */
- md_get_uint64le(mdp, NULL); /* creation time */
+ md_get_uint64le(mdp, &llongint); /* creation time */
+ smb_time_NT2local(llongint, &ctx->f_attr.fa_createtime);
md_get_uint64le(mdp, &llongint);
- smb_time_NT2local(llongint, svtz, &ctx->f_attr.fa_atime);
+ smb_time_NT2local(llongint, &ctx->f_attr.fa_atime);
md_get_uint64le(mdp, &llongint);
- smb_time_NT2local(llongint, svtz, &ctx->f_attr.fa_mtime);
+ smb_time_NT2local(llongint, &ctx->f_attr.fa_mtime);
md_get_uint64le(mdp, &llongint);
- smb_time_NT2local(llongint, svtz, &ctx->f_attr.fa_ctime);
+ smb_time_NT2local(llongint, &ctx->f_attr.fa_ctime);
md_get_uint64le(mdp, &llongint); /* file size */
ctx->f_attr.fa_size = llongint;
- md_get_uint64le(mdp, NULL); /* real size (should use) */
- /* freebsd bug: fa_attr endian bug */
- md_get_uint32le(mdp, &dattr); /* extended file attributes */
+ md_get_uint64le(mdp, &llongint); /* alloc. size */
+ ctx->f_attr.fa_allocsz = llongint;
+ md_get_uint32le(mdp, &dattr); /* ext. file attributes */
ctx->f_attr.fa_attr = dattr;
error = md_get_uint32le(mdp, &size); /* name len */
if (error)
@@ -2541,6 +2280,7 @@ smbfs_smb_findnextLM2(struct smbfs_fctx *ctx, uint16_t limit)
SMBVDEBUG("unexpected info level %d\n", ctx->f_infolevel);
return (EINVAL);
}
+
if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp)))
nmlen = min(size, SMB_MAXFNAMELEN * 2);
else
@@ -2631,7 +2371,12 @@ smbfs_smb_findcloseLM2(struct smbfs_fctx *ctx)
kmem_free(ctx->f_name, ctx->f_namesz);
if (ctx->f_t2)
smb_t2_done(ctx->f_t2);
- if ((ctx->f_flags & SMBFS_RDD_NOCLOSE) == 0)
+ /*
+ * If SMBFS_RDD_FINDFIRST is still set, we were opened
+ * but never saw a findfirst, so we don't have any
+ * search handle to close.
+ */
+ if ((ctx->f_flags & (SMBFS_RDD_FINDFIRST | SMBFS_RDD_NOCLOSE)) == 0)
error = smbfs_smb_findclose2(ctx);
return (error);
}
@@ -2656,8 +2401,7 @@ smbfs_smb_findopen(struct smbnode *dnp, const char *wild, int wlen,
goto out;
}
- if (SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_LANMAN2_0 ||
- (dnp->n_mount->smi_args.flags & SMBFS_MOUNT_NO_LONG)) {
+ if (SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_LANMAN2_0) {
error = smbfs_smb_findopenLM1(ctx, dnp, wild, wlen, attr);
} else {
error = smbfs_smb_findopenLM2(ctx, dnp, wild, wlen, attr);
@@ -2718,8 +2462,7 @@ smbfs_smb_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scrp)
* the ..._findnext functions above.
*/
- ctx->f_attr.fa_ino = smbfs_getino(ctx->f_dnp, ctx->f_name,
- ctx->f_nmlen);
+ ctx->f_inum = smbfs_getino(ctx->f_dnp, ctx->f_name, ctx->f_nmlen);
return (0);
}
@@ -2780,6 +2523,11 @@ smbfs_smb_lookup(struct smbnode *dnp, const char **namep, int *nmlenp,
}
/*
+ * XXX: Should use _qpathinfo here instead.
+ * (if SMB_CAP_NT_SMBS)
+ */
+
+ /*
* Shared lock for n_fid use (smb_flush).
*/
intr = dnp->n_mount->smi_flags & SMI_INT;
@@ -2802,8 +2550,11 @@ smbfs_smb_lookup(struct smbnode *dnp, const char **namep, int *nmlenp,
error = smbfs_smb_findnext(ctx, 1, scrp);
if (error == 0) {
*fap = ctx->f_attr;
- if (name == NULL)
- fap->fa_ino = dnp->n_ino;
+ /*
+ * Solaris smbfattr doesn't have fa_ino,
+ * and we don't allow name==NULL in this
+ * function anymore.
+ */
if (namep)
*namep = (const char *)smbfs_name_alloc(
ctx->f_name, ctx->f_nmlen);
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c
index 508dcbdede..90549cbbc7 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c
@@ -43,13 +43,7 @@
#include <sys/vnode.h>
#include <sys/sunddi.h>
-#ifdef APPLE
-#include <sys/smb_apple.h>
-#include <sys/utfconv.h>
-#include <sys/smb_iconv.h>
-#else /* APPLE */
#include <netsmb/smb_osdep.h>
-#endif /* APPLE */
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
@@ -60,203 +54,6 @@
#include <smbfs/smbfs_node.h>
#include <smbfs/smbfs_subr.h>
-#ifdef APPLE
-MALLOC_DEFINE(M_SMBFSDATA, "SMBFS data", "SMBFS private data");
-#endif /* APPLE */
-
-/*
- * Time & date conversion routines taken from msdosfs. Although leap
- * year calculation is bogus, it's sufficient before 2100 :)
- */
-/*
- * This is the format of the contents of the deTime field in the direntry
- * structure.
- * We don't use bitfields because we don't know how compilers for
- * arbitrary machines will lay them out.
- */
-#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */
-#define DT_2SECONDS_SHIFT 0
-#define DT_MINUTES_MASK 0x7E0 /* minutes */
-#define DT_MINUTES_SHIFT 5
-#define DT_HOURS_MASK 0xF800 /* hours */
-#define DT_HOURS_SHIFT 11
-
-/*
- * This is the format of the contents of the deDate field in the direntry
- * structure.
- */
-#define DD_DAY_MASK 0x1F /* day of month */
-#define DD_DAY_SHIFT 0
-#define DD_MONTH_MASK 0x1E0 /* month */
-#define DD_MONTH_SHIFT 5
-#define DD_YEAR_MASK 0xFE00 /* year - 1980 */
-#define DD_YEAR_SHIFT 9
-/*
- * Total number of days that have passed for each month in a regular year.
- */
-static ushort_t regyear[] = {
- 31, 59, 90, 120, 151, 181,
- 212, 243, 273, 304, 334, 365
-};
-
-/*
- * Total number of days that have passed for each month in a leap year.
- */
-static ushort_t leapyear[] = {
- 31, 60, 91, 121, 152, 182,
- 213, 244, 274, 305, 335, 366
-};
-
-/*
- * Variables used to remember parts of the last time conversion. Maybe we
- * can avoid a full conversion.
- */
-static ulong_t lasttime;
-static ulong_t lastday;
-static ushort_t lastddate;
-static ushort_t lastdtime;
-
-#ifdef APPLE
-PRIVSYM int wall_cmos_clock = 0; /* XXX */
-PRIVSYM int adjkerntz = 0; /* XXX */
-#endif /* APPLE */
-
-void
-smb_time_unix2dos(struct timespec *tsp, int tzoff, u_int16_t *ddp,
- u_int16_t *dtp, u_int8_t *dhp)
-{
- long t;
- ulong_t days, year, month, inc;
- ushort_t *months;
-
- /*
- * If the time from the last conversion is the same as now, then
- * skip the computations and use the saved result.
- */
- smb_time_local2server(tsp, tzoff, &t);
- t &= ~1;
- if (lasttime != t) {
- lasttime = t;
- if (t < 0) {
- /*
- * This is before 1970, so it's before 1980,
- * and can't be represented as a DOS time.
- * Just represent it as the DOS epoch.
- */
- lastdtime = 0;
- lastddate = (1 << DD_DAY_SHIFT)
- + (1 << DD_MONTH_SHIFT)
- + ((1980 - 1980) << DD_YEAR_SHIFT);
- } else {
- lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT)
- + (((t / 60) % 60) << DT_MINUTES_SHIFT)
- + (((t / 3600) % 24) << DT_HOURS_SHIFT);
-
- /*
- * If the number of days since 1970 is the same as
- * the last time we did the computation then skip
- * all this leap year and month stuff.
- */
- days = t / (24 * 60 * 60);
- if (days != lastday) {
- lastday = days;
- for (year = 1970; ; year++) {
- /*
- * XXX - works in 2000, but won't
- * work in 2100.
- */
- inc = year & 0x03 ? 365 : 366;
- if (days < inc)
- break;
- days -= inc;
- }
- /*
- * XXX - works in 2000, but won't work in 2100.
- */
- months = year & 0x03 ? regyear : leapyear;
- for (month = 0; days >= months[month]; month++)
- ;
- if (month > 0)
- days -= months[month - 1];
- lastddate = ((days + 1) << DD_DAY_SHIFT)
- + ((month + 1) << DD_MONTH_SHIFT);
- /*
- * Remember DOS's idea of time is relative
- * to 1980, but UN*X's is relative to 1970.
- * If somehow we get a time before 1980 then
- * don't give totally crazy results.
- */
- if (year > 1980)
- lastddate += (year - 1980) <<
- DD_YEAR_SHIFT;
- }
- }
- }
- if (dtp)
- *dtp = lastdtime;
- if (dhp)
- *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000;
-
- *ddp = lastddate;
-}
-
-/*
- * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that
- * interval there were 8 regular years and 2 leap years.
- */
-#define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60))
-
-static ushort_t lastdosdate;
-static ulong_t lastseconds;
-
-void
-smb_dos2unixtime(uint_t dd, uint_t dt, uint_t dh, int tzoff,
- struct timespec *tsp)
-{
- ulong_t seconds;
- ulong_t month;
- ulong_t year;
- ulong_t days;
- ushort_t *months;
-
- if (dd == 0) {
- tsp->tv_sec = 0;
- tsp->tv_nsec = 0;
- return;
- }
- seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1)
- + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60
- + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600
- + dh / 100;
- /*
- * If the year, month, and day from the last conversion are the
- * same then use the saved value.
- */
- if (lastdosdate != dd) {
- lastdosdate = (ushort_t)dd;
- days = 0;
- year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT;
- days = year * 365;
- days += year / 4 + 1; /* add in leap days */
- /*
- * XXX - works in 2000, but won't work in 2100.
- */
- if ((year & 0x03) == 0)
- days--; /* if year is a leap year */
- months = year & 0x03 ? regyear : leapyear;
- month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT;
- if (month < 1 || month > 12) {
- month = 1;
- }
- if (month > 1)
- days += months[month - 2];
- days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1;
- lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980;
- }
- smb_time_server2local(seconds + lastseconds, tzoff, tsp);
- tsp->tv_nsec = (dh % 100) * 10000000;
-}
-
/*
* In the Darwin code, this function used to compute the full path
* by following the chain of n_parent pointers back to the root.
@@ -388,7 +185,7 @@ smbfs_fname_tolocal(struct smbfs_fctx *ctx)
errout:
/*
* Conversion failed, but our caller does not
- * deal with errors here, so... (hack).
+ * deal with errors here, so just put a "?".
* Don't expect to ever see this.
*/
(void) strlcpy(ctx->f_name, "?", ctx->f_namesz);
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h
index 7c1107378b..78a50077d9 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h
@@ -110,6 +110,7 @@ struct smbfs_fctx {
* Return values
*/
struct smbfattr f_attr; /* current attributes */
+ u_longlong_t f_inum; /* current I number */
char *f_name; /* current file name */
int f_nmlen; /* name len */
int f_namesz; /* memory allocated */
@@ -148,7 +149,7 @@ typedef struct smbfs_fctx smbfs_fctx_t;
#define f_t2 f_urq.uf_t2
/*
- * smb level
+ * smb level (smbfs_smb.c)
*/
int smbfs_smb_lock(struct smbnode *np, int op, caddr_t id,
offset_t start, uint64_t len, int largelock,
@@ -163,25 +164,21 @@ int smbfs_smb_setfsize(struct smbnode *np, uint16_t fid, uint64_t newsize,
int smbfs_smb_getfattr(struct smbnode *np, struct smbfattr *fap,
struct smb_cred *scrp);
-int smbfs_smb_setfattr(struct smbnode *np, uint16_t fid,
+int smbfs_smb_setfattr(struct smbnode *np, int fid,
uint32_t attr, struct timespec *mtime, struct timespec *atime,
struct smb_cred *scrp);
-int smbfs_smb_setpattr(struct smbnode *np,
- uint32_t attr, struct timespec *mtime, struct timespec *atime,
- struct smb_cred *scrp);
-
-int smbfs_smb_open(struct smbnode *np, uint32_t rights, struct smb_cred *scrp,
- int *attrcacheupdated, uint16_t *fidp, const char *name, int nmlen,
- int xattr, len_t *sizep, uint32_t *rightsp);
+int smbfs_smb_open(struct smbnode *np, const char *name, int nmlen,
+ int xattr, uint32_t rights, struct smb_cred *scrp,
+ uint16_t *fidp, uint32_t *rightsp, struct smbfattr *fap);
int smbfs_smb_tmpopen(struct smbnode *np, uint32_t rights,
struct smb_cred *scrp, uint16_t *fidp);
int smbfs_smb_close(struct smb_share *ssp, uint16_t fid,
struct timespec *mtime, struct smb_cred *scrp);
int smbfs_smb_tmpclose(struct smbnode *ssp, uint16_t fid,
struct smb_cred *scrp);
-int smbfs_smb_create(struct smbnode *dnp, const char *name, int len,
- struct smb_cred *scrp, uint16_t *fidp, uint32_t disp, int xattr);
+int smbfs_smb_create(struct smbnode *dnp, const char *name, int nmlen,
+ int xattr, uint32_t disp, struct smb_cred *scrp, uint16_t *fidp);
int smbfs_smb_delete(struct smbnode *np, struct smb_cred *scrp,
const char *name, int len, int xattr);
int smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp,
@@ -218,9 +215,9 @@ int smbfs_smb_setsec_m(struct smb_share *ssp, uint16_t fid,
struct smb_cred *scrp, uint32_t selector, mblk_t **mp);
int smbfs_getacl(vnode_t *vp, vsecattr_t *vsecattr,
- int *uidp, int *gidp, int flag, cred_t *cr);
+ uid_t *uidp, gid_t *gidp, int flag, cred_t *cr);
int smbfs_setacl(vnode_t *vp, vsecattr_t *vsecattr,
- int uid, int gid, int flag, cred_t *cr);
+ uid_t uid, gid_t gid, int flag, cred_t *cr);
int smbfs_getsd(vnode_t *vp, uint32_t sel, mblk_t **mp, cred_t *cr);
int smbfs_setsd(vnode_t *vp, uint32_t sel, mblk_t **mp, cred_t *cr);
@@ -238,33 +235,62 @@ int smbfs_smb_qstreaminfo(struct smbnode *np, struct smb_cred *scrp,
uio_t uio, size_t *sizep);
#endif /* NOT_YET */
-void smbfs_fname_tolocal(struct smbfs_fctx *ctx);
+/*
+ * VFS-level init, fini stuff
+ */
-void smb_time_local2server(struct timespec *tsp, int tzoff, long *seconds);
-void smb_time_server2local(ulong_t seconds, int tzoff, struct timespec *tsp);
-void smb_time_NT2local(uint64_t nsec, int tzoff, struct timespec *tsp);
-void smb_time_local2NT(struct timespec *tsp, int tzoff, uint64_t *nsec);
-void smb_time_unix2dos(struct timespec *tsp, int tzoff, uint16_t *ddp,
- uint16_t *dtp, uint8_t *dhp);
-void smb_dos2unixtime(uint_t dd, uint_t dt, uint_t dh, int tzoff,
- struct timespec *tsp);
-
-/* Stuff borrowed from NFS (and then hacked) */
-vnode_t *smbfs_make_node(vfs_t *vfsp,
- const char *dir, int dirlen,
- const char *name, int nmlen,
- char sep, struct smbfattr *fap);
-void smb_addfree(smbnode_t *sp);
-void smb_addhash(smbnode_t *sp);
-void smb_rmhash(smbnode_t *);
+int smbfs_vfsinit(void);
+void smbfs_vfsfini(void);
int smbfs_subrinit(void);
void smbfs_subrfini(void);
int smbfs_clntinit(void);
void smbfs_clntfini(void);
+
void smbfs_zonelist_add(smbmntinfo_t *smi);
void smbfs_zonelist_remove(smbmntinfo_t *smi);
+
+int smbfs_check_table(struct vfs *vfsp, struct smbnode *srp);
void smbfs_destroy_table(struct vfs *vfsp);
+void smbfs_rflush(struct vfs *vfsp, cred_t *cr);
+
+/*
+ * Function definitions - those having to do with
+ * smbfs nodes, vnodes, etc
+ */
+
+void smbfs_attrcache_prune(struct smbnode *np);
+void smbfs_attrcache_remove(struct smbnode *np);
+void smbfs_attrcache_rm_locked(struct smbnode *np);
+#ifndef DEBUG
+#define smbfs_attrcache_rm_locked(np) (np)->r_attrtime = gethrtime()
+#endif
+void smbfs_attr_touchdir(struct smbnode *dnp);
+void smbfs_attrcache_fa(vnode_t *vp, struct smbfattr *fap);
+void smbfs_cache_check(struct vnode *vp, struct smbfattr *fap);
+
+void smbfs_addfree(struct smbnode *sp);
+void smbfs_rmhash(struct smbnode *);
+
+/* See avl_create in smbfs_vfsops.c */
+void smbfs_init_hash_avl(avl_tree_t *);
+
+uint32_t smbfs_gethash(const char *rpath, int prlen);
+uint32_t smbfs_getino(struct smbnode *dnp, const char *name, int nmlen);
+
+extern struct smbfattr smbfs_fattr0;
+smbnode_t *smbfs_node_findcreate(smbmntinfo_t *mi,
+ const char *dir, int dirlen,
+ const char *name, int nmlen,
+ char sep, struct smbfattr *fap);
+
+int smbfs_nget(vnode_t *dvp, const char *name, int nmlen,
+ struct smbfattr *fap, vnode_t **vpp);
+
+void smbfs_fname_tolocal(struct smbfs_fctx *ctx);
+char *smbfs_name_alloc(const char *name, int nmlen);
+void smbfs_name_free(const char *name, int nmlen);
+
int smbfs_readvnode(vnode_t *, uio_t *, cred_t *, struct vattr *);
int smbfs_writevnode(vnode_t *vp, uio_t *uiop, cred_t *cr,
int ioflag, int timo);
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c
index a1c65b236f..0e787c0d2c 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c
@@ -27,8 +27,9 @@
*/
/*
- * Node hash implementation borrowed from NFS.
- * See: uts/common/fs/nfs/nfs_subr.c
+ * Node hash implementation initially borrowed from NFS (nfs_subr.c)
+ * but then heavily modified. It's no longer an array of hash lists,
+ * but an AVL tree per mount point. More on this below.
*/
#include <sys/param.h>
@@ -39,14 +40,9 @@
#include <sys/dnlc.h>
#include <sys/kmem.h>
#include <sys/sunddi.h>
+#include <sys/sysmacros.h>
-#ifdef APPLE
-#include <sys/smb_apple.h>
-#include <sys/utfconv.h>
-#include <sys/smb_iconv.h>
-#else
#include <netsmb/smb_osdep.h>
-#endif
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
@@ -58,62 +54,63 @@
#include <smbfs/smbfs_subr.h>
/*
- * The hash queues for the access to active and cached smbnodes
- * are organized as doubly linked lists. A reader/writer lock
- * for each hash bucket is used to control access and to synchronize
- * lookups, additions, and deletions from the hash queue.
+ * The AVL trees (now per-mount) allow finding an smbfs node by its
+ * full remote path name. It also allows easy traversal of all nodes
+ * below (path wise) any given node. A reader/writer lock for each
+ * (per mount) AVL tree is used to control access and to synchronize
+ * lookups, additions, and deletions from that AVL tree.
+ *
+ * Previously, this code use a global array of hash chains, each with
+ * its own rwlock. A few struct members, functions, and comments may
+ * still refer to a "hash", and those should all now be considered to
+ * refer to the per-mount AVL tree that replaced the old hash chains.
+ * (i.e. member smi_hash_lk, function sn_hashfind, etc.)
*
* The smbnode freelist is organized as a doubly linked list with
* a head pointer. Additions and deletions are synchronized via
* a single mutex.
*
- * In order to add an smbnode to the free list, it must be hashed into
- * a hash queue and the exclusive lock to the hash queue be held.
- * If an smbnode is not hashed into a hash queue, then it is destroyed
+ * In order to add an smbnode to the free list, it must be linked into
+ * the mount's AVL tree and the exclusive lock for the AVL must be held.
+ * If an smbnode is not linked into the AVL tree, then it is destroyed
* because it represents no valuable information that can be reused
- * about the file. The exclusive lock to the hash queue must be
- * held in order to prevent a lookup in the hash queue from finding
- * the smbnode and using it and assuming that the smbnode is not on the
- * freelist. The lookup in the hash queue will have the hash queue
- * locked, either exclusive or shared.
+ * about the file. The exclusive lock for the AVL tree must be held
+ * in order to prevent a lookup in the AVL tree from finding the
+ * smbnode and using it and assuming that the smbnode is not on the
+ * freelist. The lookup in the AVL tree will have the AVL tree lock
+ * held, either exclusive or shared.
*
* The vnode reference count for each smbnode is not allowed to drop
* below 1. This prevents external entities, such as the VM
* subsystem, from acquiring references to vnodes already on the
* freelist and then trying to place them back on the freelist
* when their reference is released. This means that the when an
- * smbnode is looked up in the hash queues, then either the smbnode
+ * smbnode is looked up in the AVL tree, then either the smbnode
* is removed from the freelist and that reference is tranfered to
* the new reference or the vnode reference count must be incremented
* accordingly. The mutex for the freelist must be held in order to
* accurately test to see if the smbnode is on the freelist or not.
- * The hash queue lock might be held shared and it is possible that
+ * The AVL tree lock might be held shared and it is possible that
* two different threads may race to remove the smbnode from the
* freelist. This race can be resolved by holding the mutex for the
* freelist. Please note that the mutex for the freelist does not
* need to held if the smbnode is not on the freelist. It can not be
* placed on the freelist due to the requirement that the thread
* putting the smbnode on the freelist must hold the exclusive lock
- * to the hash queue and the thread doing the lookup in the hash
- * queue is holding either a shared or exclusive lock to the hash
- * queue.
+ * for the AVL tree and the thread doing the lookup in the AVL tree
+ * is holding either a shared or exclusive lock for the AVL tree.
*
* The lock ordering is:
*
- * hash bucket lock -> vnode lock
- * hash bucket lock -> freelist lock
+ * AVL tree lock -> vnode lock
+ * AVL tree lock -> freelist lock
*/
-static rhashq_t *smbtable;
static kmutex_t smbfreelist_lock;
static smbnode_t *smbfreelist = NULL;
static ulong_t smbnodenew = 0;
long nsmbnode = 0;
-static int smbtablesize;
-static int smbtablemask;
-static int smbhashlen = 4;
-
static struct kmem_cache *smbnode_cache;
/*
@@ -125,19 +122,25 @@ kmutex_t smbfs_minor_lock;
int smbfs_major;
int smbfs_minor;
+/* See smbfs_node_findcreate() */
+struct smbfattr smbfs_fattr0;
+
/*
* Local functions.
- * Not static, to aid debugging.
+ * SN for Smb Node
*/
-void smb_rmfree(smbnode_t *);
-void smbinactive(smbnode_t *);
-void smb_rmhash_locked(smbnode_t *);
-void smb_destroy_node(smbnode_t *);
+static void sn_rmfree(smbnode_t *);
+static void sn_inactive(smbnode_t *);
+static void sn_addhash_locked(smbnode_t *, avl_index_t);
+static void sn_rmhash_locked(smbnode_t *);
+static void sn_destroy_node(smbnode_t *);
void smbfs_kmem_reclaim(void *cdrarg);
-smbnode_t *smbhashfind(struct vfs *, const char *, int, rhashq_t *);
-static vnode_t *make_smbnode(vfs_t *, char *, int, rhashq_t *, int *);
+static smbnode_t *
+sn_hashfind(smbmntinfo_t *, const char *, int, avl_index_t *);
+static smbnode_t *
+make_smbnode(smbmntinfo_t *, const char *, int, int *);
/*
* Free the resources associated with an smbnode.
@@ -145,135 +148,147 @@ static vnode_t *make_smbnode(vfs_t *, char *, int, rhashq_t *, int *);
*
* NFS: nfs_subr.c:rinactive
*/
-void
-smbinactive(smbnode_t *np)
+static void
+sn_inactive(smbnode_t *np)
{
+ cred_t *oldcr;
+ char *orpath;
+ int orplen;
- if (np->n_rpath) {
- kmem_free(np->n_rpath, np->n_rplen + 1);
- np->n_rpath = NULL;
- }
+ /*
+ * Flush and invalidate all pages (todo)
+ * Free any held credentials and caches...
+ * etc. (See NFS code)
+ */
+ mutex_enter(&np->r_statelock);
+
+ oldcr = np->r_cred;
+ np->r_cred = NULL;
+
+ orpath = np->n_rpath;
+ orplen = np->n_rplen;
+ np->n_rpath = NULL;
+ np->n_rplen = 0;
+
+ mutex_exit(&np->r_statelock);
+
+ if (oldcr != NULL)
+ crfree(oldcr);
+
+ if (orpath != NULL)
+ kmem_free(orpath, orplen + 1);
}
/*
- * Return a vnode for the given CIFS directory and filename.
- * If no smbnode exists for this fhandle, create one and put it
- * into the hash queues. If the smbnode for this fhandle
- * already exists, return it.
+ * Find and optionally create an smbnode for the passed
+ * mountinfo, directory, separator, and name. If the
+ * desired smbnode already exists, return a reference.
+ * If the file attributes pointer is non-null, the node
+ * is created if necessary and linked into the AVL tree.
+ *
+ * Callers that need a node created but don't have the
+ * real attributes pass smbfs_fattr0 to force creation.
*
- * Note: make_smbnode() may upgrade the hash bucket lock to exclusive.
+ * Note: make_smbnode() may upgrade the "hash" lock to exclusive.
*
* NFS: nfs_subr.c:makenfsnode
*/
-vnode_t *
-smbfs_make_node(
- vfs_t *vfsp,
- const char *dir,
+smbnode_t *
+smbfs_node_findcreate(
+ smbmntinfo_t *mi,
+ const char *dirnm,
int dirlen,
const char *name,
int nmlen,
char sep,
struct smbfattr *fap)
{
- char *rpath;
- int rplen, idx;
- uint32_t hash;
- rhashq_t *rhtp;
+ char tmpbuf[256];
+ size_t rpalloc;
+ char *p, *rpath;
+ int rplen;
smbnode_t *np;
vnode_t *vp;
-#ifdef NOT_YET
- vattr_t va;
-#endif
int newnode;
/*
- * Build the full path name in allocated memory
- * so we have it for lookup, etc. Note the
- * special case at the root (dir=="\\", dirlen==1)
- * where this does not add a slash separator.
- * To do that would make a double slash, which
- * has special meaning in CIFS.
- *
- * ToDo: Would prefer to allocate a remote path
- * only when we will create a new node.
+ * Build the search string, either in tmpbuf or
+ * in allocated memory if larger than tmpbuf.
*/
- if (dirlen <= 1 && sep == '\\')
- sep = '\0'; /* no slash */
-
- /* Compute the length of rpath and allocate. */
rplen = dirlen;
- if (sep)
+ if (sep != '\0')
rplen++;
- if (name)
- rplen += nmlen;
-
- rpath = kmem_alloc(rplen + 1, KM_SLEEP);
-
- /* Fill in rpath */
- bcopy(dir, rpath, dirlen);
- if (sep)
- rpath[dirlen++] = sep;
- if (name)
- bcopy(name, &rpath[dirlen], nmlen);
- rpath[rplen] = 0;
-
- hash = smbfs_hash(rpath, rplen);
- idx = hash & smbtablemask;
- rhtp = &smbtable[idx];
- rw_enter(&rhtp->r_lock, RW_READER);
-
- vp = make_smbnode(vfsp, rpath, rplen, rhtp, &newnode);
- np = VTOSMB(vp);
- np->n_ino = hash; /* Equivalent to: smbfs_getino() */
+ rplen += nmlen;
+ if (rplen < sizeof (tmpbuf)) {
+ /* use tmpbuf */
+ rpalloc = 0;
+ rpath = tmpbuf;
+ } else {
+ rpalloc = rplen + 1;
+ rpath = kmem_alloc(rpalloc, KM_SLEEP);
+ }
+ p = rpath;
+ bcopy(dirnm, p, dirlen);
+ p += dirlen;
+ if (sep != '\0')
+ *p++ = sep;
+ if (name != NULL) {
+ bcopy(name, p, nmlen);
+ p += nmlen;
+ }
+ ASSERT(p == rpath + rplen);
/*
- * Note: make_smbnode keeps a reference to rpath in
- * new nodes it creates, so only free when we found
- * an existing node.
+ * Find or create a node with this path.
*/
- if (!newnode) {
- kmem_free(rpath, rplen + 1);
- rpath = NULL;
- }
+ rw_enter(&mi->smi_hash_lk, RW_READER);
+ if (fap == NULL)
+ np = sn_hashfind(mi, rpath, rplen, NULL);
+ else
+ np = make_smbnode(mi, rpath, rplen, &newnode);
+ rw_exit(&mi->smi_hash_lk);
+
+ if (rpalloc)
+ kmem_free(rpath, rpalloc);
if (fap == NULL) {
-#ifdef NOT_YET
- if (newnode) {
- PURGE_ATTRCACHE(vp);
- }
-#endif
- rw_exit(&rhtp->r_lock);
- return (vp);
+ /*
+ * Caller is "just looking" (no create)
+ * so np may or may not be NULL here.
+ * Either way, we're done.
+ */
+ return (np);
}
- /* Have SMB attributes. */
- vp->v_type = (fap->fa_attr & SMB_FA_DIR) ? VDIR : VREG;
- /* XXX: np->n_ino = fap->fa_ino; see above */
- np->r_size = fap->fa_size;
- /* XXX: np->r_attr = *fap here instead? */
- np->r_atime = fap->fa_atime;
- np->r_ctime = fap->fa_ctime;
- np->r_mtime = fap->fa_mtime;
+ /*
+ * We should have a node, possibly created.
+ * Do we have (real) attributes to apply?
+ */
+ ASSERT(np != NULL);
+ if (fap == &smbfs_fattr0)
+ return (np);
-#ifdef NOT_YET
+ /*
+ * Apply the given attributes to this node,
+ * dealing with any cache impact, etc.
+ */
+ vp = SMBTOV(np);
if (!newnode) {
- rw_exit(&rhtp->r_lock);
- (void) nfs_cache_fattr(vp, attr, &va, t, cr);
- } else {
- if (attr->na_type < NFNON || attr->na_type > NFSOC)
- vp->v_type = VBAD;
- else
- vp->v_type = n2v_type(attr);
- vp->v_rdev = makedevice(attr->rdev.specdata1,
- attr->rdev.specdata2);
- nfs_attrcache(vp, attr, t);
- rw_exit(&rhtp->r_lock);
+ /*
+ * Found an existing node.
+ * Maybe purge caches...
+ */
+ smbfs_cache_check(vp, fap);
}
-#else
- rw_exit(&rhtp->r_lock);
-#endif
+ smbfs_attrcache_fa(vp, fap);
- return (vp);
+ /*
+ * Note NFS sets vp->v_type here, assuming it
+ * can never change for the life of a node.
+ * We allow v_type to change, and set it in
+ * smbfs_attrcache(). Also: mode, uid, gid
+ */
+ return (np);
}
/*
@@ -285,33 +300,32 @@ smbfs_make_node(
* Find or create an smbnode.
* NFS: nfs_subr.c:make_rnode
*/
-static vnode_t *
+static smbnode_t *
make_smbnode(
- vfs_t *vfsp,
- char *rpath,
+ smbmntinfo_t *mi,
+ const char *rpath,
int rplen,
- rhashq_t *rhtp,
int *newnode)
{
smbnode_t *np;
smbnode_t *tnp;
vnode_t *vp;
- smbmntinfo_t *mi;
+ vfs_t *vfsp;
+ avl_index_t where;
+ char *new_rpath = NULL;
- ASSERT(RW_READ_HELD(&rhtp->r_lock));
-
- mi = VFTOSMI(vfsp);
+ ASSERT(RW_READ_HELD(&mi->smi_hash_lk));
+ vfsp = mi->smi_vfsp;
start:
- np = smbhashfind(vfsp, rpath, rplen, rhtp);
+ np = sn_hashfind(mi, rpath, rplen, NULL);
if (np != NULL) {
- vp = SMBTOV(np);
*newnode = 0;
- return (vp);
+ return (np);
}
/* Note: will retake this lock below. */
- rw_exit(&rhtp->r_lock);
+ rw_exit(&mi->smi_hash_lk);
/*
* see if we can find something on the freelist
@@ -319,33 +333,36 @@ start:
mutex_enter(&smbfreelist_lock);
if (smbfreelist != NULL && smbnodenew >= nsmbnode) {
np = smbfreelist;
- smb_rmfree(np);
+ sn_rmfree(np);
mutex_exit(&smbfreelist_lock);
vp = SMBTOV(np);
if (np->r_flags & RHASHED) {
- rw_enter(&np->r_hashq->r_lock, RW_WRITER);
+ smbmntinfo_t *tmp_mi = np->n_mount;
+ ASSERT(tmp_mi != NULL);
+ rw_enter(&tmp_mi->smi_hash_lk, RW_WRITER);
mutex_enter(&vp->v_lock);
if (vp->v_count > 1) {
vp->v_count--;
mutex_exit(&vp->v_lock);
- rw_exit(&np->r_hashq->r_lock);
- rw_enter(&rhtp->r_lock, RW_READER);
+ rw_exit(&tmp_mi->smi_hash_lk);
+ /* start over */
+ rw_enter(&mi->smi_hash_lk, RW_READER);
goto start;
}
mutex_exit(&vp->v_lock);
- smb_rmhash_locked(np);
- rw_exit(&np->r_hashq->r_lock);
+ sn_rmhash_locked(np);
+ rw_exit(&tmp_mi->smi_hash_lk);
}
- smbinactive(np);
+ sn_inactive(np);
mutex_enter(&vp->v_lock);
if (vp->v_count > 1) {
vp->v_count--;
mutex_exit(&vp->v_lock);
- rw_enter(&rhtp->r_lock, RW_READER);
+ rw_enter(&mi->smi_hash_lk, RW_READER);
goto start;
}
mutex_exit(&vp->v_lock);
@@ -380,6 +397,13 @@ start:
vp = new_vp;
}
+ /*
+ * Allocate and copy the rpath we'll need below.
+ */
+ new_rpath = kmem_alloc(rplen + 1, KM_SLEEP);
+ bcopy(rpath, new_rpath, rplen);
+ new_rpath[rplen] = '\0';
+
/* Initialize smbnode_t */
bzero(np, sizeof (*np));
@@ -391,11 +415,11 @@ start:
np->r_vnode = vp;
np->n_mount = mi;
- np->r_hashq = rhtp;
+
np->n_fid = SMB_FID_UNUSED;
- np->n_uid = UID_NOBODY;
- np->n_gid = GID_NOBODY;
- /* XXX: make attributes stale? */
+ np->n_uid = mi->smi_uid;
+ np->n_gid = mi->smi_gid;
+ /* Leave attributes "stale." */
#if 0 /* XXX dircache */
/*
@@ -414,77 +438,94 @@ start:
vp->v_type = VNON;
/*
- * There is a race condition if someone else
- * alloc's the smbnode while no locks are held, so we
- * check again and recover if found.
+ * We entered with mi->smi_hash_lk held (reader).
+ * Retake it now, (as the writer).
+ * Will return with it held.
*/
- rw_enter(&rhtp->r_lock, RW_WRITER);
- tnp = smbhashfind(vfsp, rpath, rplen, rhtp);
+ rw_enter(&mi->smi_hash_lk, RW_WRITER);
+
+ /*
+ * There is a race condition where someone else
+ * may alloc the smbnode while no locks are held,
+ * so check again and recover if found.
+ */
+ tnp = sn_hashfind(mi, rpath, rplen, &where);
if (tnp != NULL) {
- vp = SMBTOV(tnp);
+ /*
+ * Lost the race. Put the node we were building
+ * on the free list and return the one we found.
+ */
+ rw_exit(&mi->smi_hash_lk);
+ kmem_free(new_rpath, rplen + 1);
+ smbfs_addfree(np);
+ rw_enter(&mi->smi_hash_lk, RW_READER);
*newnode = 0;
- rw_exit(&rhtp->r_lock);
- /* The node we were building goes on the free list. */
- smb_addfree(np);
- rw_enter(&rhtp->r_lock, RW_READER);
- return (vp);
+ return (tnp);
}
/*
- * Hash search identifies nodes by the full pathname,
- * so store that before linking in the hash list.
- * Note: caller allocates the rpath, and knows
- * about this reference when *newnode is set.
+ * Hash search identifies nodes by the remote path
+ * (n_rpath) so fill that in now, before linking
+ * this node into the node cache (AVL tree).
*/
- np->n_rpath = rpath;
+ np->n_rpath = new_rpath;
np->n_rplen = rplen;
+ np->n_ino = smbfs_gethash(new_rpath, rplen);
- smb_addhash(np);
+ sn_addhash_locked(np, where);
*newnode = 1;
- return (vp);
+ return (np);
}
/*
- * smb_addfree
- * Put a smbnode on the free list.
+ * smbfs_addfree
+ * Put an smbnode on the free list, or destroy it immediately
+ * if it offers no value were it to be reclaimed later. Also
+ * destroy immediately when we have too many smbnodes, etc.
*
* Normally called by smbfs_inactive, but also
* called in here during cleanup operations.
*
- * Smbnodes which were allocated above and beyond the normal limit
- * are immediately freed.
- *
* NFS: nfs_subr.c:rp_addfree
*/
void
-smb_addfree(smbnode_t *np)
+smbfs_addfree(smbnode_t *np)
{
vnode_t *vp;
struct vfs *vfsp;
+ smbmntinfo_t *mi;
+
+ ASSERT(np->r_freef == NULL && np->r_freeb == NULL);
vp = SMBTOV(np);
ASSERT(vp->v_count >= 1);
- ASSERT(np->r_freef == NULL && np->r_freeb == NULL);
+
+ vfsp = vp->v_vfsp;
+ mi = VFTOSMI(vfsp);
/*
- * If we have too many smbnodes allocated and there are no
- * references to this smbnode, or if the smbnode is no longer
- * accessible by it does not reside in the hash queues,
- * or if an i/o error occurred while writing to the file,
- * then just free it instead of putting it on the smbnode
- * freelist.
+ * If there are no more references to this smbnode and:
+ * we have too many smbnodes allocated, or if the node
+ * is no longer accessible via the AVL tree (!RHASHED),
+ * or an i/o error occurred while writing to the file,
+ * or it's part of an unmounted FS, then try to destroy
+ * it instead of putting it on the smbnode freelist.
*/
- vfsp = vp->v_vfsp;
- if (((smbnodenew > nsmbnode || !(np->r_flags & RHASHED) ||
- np->r_error || (vfsp->vfs_flag & VFS_UNMOUNTED)) &&
- np->r_count == 0)) {
+ if (np->r_count == 0 && (
+ (np->r_flags & RHASHED) == 0 ||
+ (np->r_error != 0) ||
+ (vfsp->vfs_flag & VFS_UNMOUNTED) ||
+ (smbnodenew > nsmbnode))) {
+
+ /* Try to destroy this node. */
+
if (np->r_flags & RHASHED) {
- rw_enter(&np->r_hashq->r_lock, RW_WRITER);
+ rw_enter(&mi->smi_hash_lk, RW_WRITER);
mutex_enter(&vp->v_lock);
if (vp->v_count > 1) {
vp->v_count--;
mutex_exit(&vp->v_lock);
- rw_exit(&np->r_hashq->r_lock);
+ rw_exit(&mi->smi_hash_lk);
return;
/*
* Will get another call later,
@@ -492,23 +533,23 @@ smb_addfree(smbnode_t *np)
*/
}
mutex_exit(&vp->v_lock);
- smb_rmhash_locked(np);
- rw_exit(&np->r_hashq->r_lock);
+ sn_rmhash_locked(np);
+ rw_exit(&mi->smi_hash_lk);
}
- smbinactive(np);
+ sn_inactive(np);
/*
* Recheck the vnode reference count. We need to
* make sure that another reference has not been
* acquired while we were not holding v_lock. The
- * smbnode is not in the smbnode hash queues, so the
- * only way for a reference to have been acquired
+ * smbnode is not in the smbnode "hash" AVL tree, so
+ * the only way for a reference to have been acquired
* is for a VOP_PUTPAGE because the smbnode was marked
- * with RDIRTY or for a modified page. This
+ * with RDIRTY or for a modified page. This vnode
* reference may have been acquired before our call
- * to smbinactive. The i/o may have been completed,
- * thus allowing smbinactive to complete, but the
+ * to sn_inactive. The i/o may have been completed,
+ * thus allowing sn_inactive to complete, but the
* reference to the vnode may not have been released
* yet. In any case, the smbnode can not be destroyed
* until the other references to this vnode have been
@@ -525,33 +566,31 @@ smb_addfree(smbnode_t *np)
}
mutex_exit(&vp->v_lock);
- smb_destroy_node(np);
+ sn_destroy_node(np);
return;
}
+
/*
- * Lock the hash queue and then recheck the reference count
+ * Lock the AVL tree and then recheck the reference count
* to ensure that no other threads have acquired a reference
* to indicate that the smbnode should not be placed on the
* freelist. If another reference has been acquired, then
* just release this one and let the other thread complete
* the processing of adding this smbnode to the freelist.
*/
- rw_enter(&np->r_hashq->r_lock, RW_WRITER);
+ rw_enter(&mi->smi_hash_lk, RW_WRITER);
mutex_enter(&vp->v_lock);
if (vp->v_count > 1) {
vp->v_count--;
mutex_exit(&vp->v_lock);
- rw_exit(&np->r_hashq->r_lock);
+ rw_exit(&mi->smi_hash_lk);
return;
}
mutex_exit(&vp->v_lock);
/*
- * If there is no cached data or metadata for this file, then
- * put the smbnode on the front of the freelist so that it will
- * be reused before other smbnodes which may have cached data or
- * metadata associated with them.
+ * Put this node on the free list.
*/
mutex_enter(&smbfreelist_lock);
if (smbfreelist == NULL) {
@@ -566,7 +605,7 @@ smb_addfree(smbnode_t *np)
}
mutex_exit(&smbfreelist_lock);
- rw_exit(&np->r_hashq->r_lock);
+ rw_exit(&mi->smi_hash_lk);
}
/*
@@ -577,8 +616,8 @@ smb_addfree(smbnode_t *np)
*
* NFS: nfs_subr.c:rp_rmfree
*/
-void
-smb_rmfree(smbnode_t *np)
+static void
+sn_rmfree(smbnode_t *np)
{
ASSERT(MUTEX_HELD(&smbfreelist_lock));
@@ -597,23 +636,21 @@ smb_rmfree(smbnode_t *np)
}
/*
- * Put a smbnode in the hash table.
+ * Put an smbnode in the "hash" AVL tree.
*
- * The caller must be holding the exclusive hash queue lock.
+ * The caller must be hold the rwlock as writer.
*
* NFS: nfs_subr.c:rp_addhash
*/
-void
-smb_addhash(smbnode_t *np)
+static void
+sn_addhash_locked(smbnode_t *np, avl_index_t where)
{
+ smbmntinfo_t *mi = np->n_mount;
- ASSERT(RW_WRITE_HELD(&np->r_hashq->r_lock));
+ ASSERT(RW_WRITE_HELD(&mi->smi_hash_lk));
ASSERT(!(np->r_flags & RHASHED));
- np->r_hashf = np->r_hashq->r_hashf;
- np->r_hashq->r_hashf = np;
- np->r_hashb = (smbnode_t *)np->r_hashq;
- np->r_hashf->r_hashb = np;
+ avl_insert(&mi->smi_hash_avl, np, where);
mutex_enter(&np->r_statelock);
np->r_flags |= RHASHED;
@@ -621,21 +658,21 @@ smb_addhash(smbnode_t *np)
}
/*
- * Remove a smbnode from the hash table.
+ * Remove an smbnode from the "hash" AVL tree.
*
- * The caller must be holding the hash queue lock.
+ * The caller must hold the rwlock as writer.
*
* NFS: nfs_subr.c:rp_rmhash_locked
*/
-void
-smb_rmhash_locked(smbnode_t *np)
+static void
+sn_rmhash_locked(smbnode_t *np)
{
+ smbmntinfo_t *mi = np->n_mount;
- ASSERT(RW_WRITE_HELD(&np->r_hashq->r_lock));
+ ASSERT(RW_WRITE_HELD(&mi->smi_hash_lk));
ASSERT(np->r_flags & RHASHED);
- np->r_hashb->r_hashf = np->r_hashf;
- np->r_hashf->r_hashb = np->r_hashb;
+ avl_remove(&mi->smi_hash_avl, np);
mutex_enter(&np->r_statelock);
np->r_flags &= ~RHASHED;
@@ -643,83 +680,177 @@ smb_rmhash_locked(smbnode_t *np)
}
/*
- * Remove a smbnode from the hash table.
+ * Remove an smbnode from the "hash" AVL tree.
*
- * The caller must not be holding the hash queue lock.
+ * The caller must not be holding the rwlock.
*/
void
-smb_rmhash(smbnode_t *np)
+smbfs_rmhash(smbnode_t *np)
{
+ smbmntinfo_t *mi = np->n_mount;
- rw_enter(&np->r_hashq->r_lock, RW_WRITER);
- smb_rmhash_locked(np);
- rw_exit(&np->r_hashq->r_lock);
+ rw_enter(&mi->smi_hash_lk, RW_WRITER);
+ sn_rmhash_locked(np);
+ rw_exit(&mi->smi_hash_lk);
}
/*
- * Lookup a smbnode by fhandle.
+ * Lookup an smbnode by remote pathname
*
- * The caller must be holding the hash queue lock, either shared or exclusive.
- * XXX: make static?
+ * The caller must be holding the AVL rwlock, either shared or exclusive.
*
* NFS: nfs_subr.c:rfind
*/
-smbnode_t *
-smbhashfind(
- struct vfs *vfsp,
+static smbnode_t *
+sn_hashfind(
+ smbmntinfo_t *mi,
const char *rpath,
int rplen,
- rhashq_t *rhtp)
+ avl_index_t *pwhere) /* optional */
{
+ smbfs_node_hdr_t nhdr;
smbnode_t *np;
vnode_t *vp;
- ASSERT(RW_LOCK_HELD(&rhtp->r_lock));
+ ASSERT(RW_LOCK_HELD(&mi->smi_hash_lk));
- for (np = rhtp->r_hashf; np != (smbnode_t *)rhtp; np = np->r_hashf) {
- vp = SMBTOV(np);
- if (vp->v_vfsp == vfsp &&
- np->n_rplen == rplen &&
- bcmp(np->n_rpath, rpath, rplen) == 0) {
- /*
- * remove smbnode from free list, if necessary.
- */
- if (np->r_freef != NULL) {
- mutex_enter(&smbfreelist_lock);
- /*
- * If the smbnode is on the freelist,
- * then remove it and use that reference
- * as the new reference. Otherwise,
- * need to increment the reference count.
- */
- if (np->r_freef != NULL) {
- smb_rmfree(np);
- mutex_exit(&smbfreelist_lock);
- } else {
- mutex_exit(&smbfreelist_lock);
- VN_HOLD(vp);
- }
- } else
- VN_HOLD(vp);
- return (np);
+ bzero(&nhdr, sizeof (nhdr));
+ nhdr.hdr_n_rpath = (char *)rpath;
+ nhdr.hdr_n_rplen = rplen;
+
+ /* See smbfs_node_cmp below. */
+ np = avl_find(&mi->smi_hash_avl, &nhdr, pwhere);
+
+ if (np == NULL)
+ return (NULL);
+
+ /*
+ * Found it in the "hash" AVL tree.
+ * Remove from free list, if necessary.
+ */
+ vp = SMBTOV(np);
+ if (np->r_freef != NULL) {
+ mutex_enter(&smbfreelist_lock);
+ /*
+ * If the smbnode is on the freelist,
+ * then remove it and use that reference
+ * as the new reference. Otherwise,
+ * need to increment the reference count.
+ */
+ if (np->r_freef != NULL) {
+ sn_rmfree(np);
+ mutex_exit(&smbfreelist_lock);
+ } else {
+ mutex_exit(&smbfreelist_lock);
+ VN_HOLD(vp);
}
+ } else
+ VN_HOLD(vp);
+
+ return (np);
+}
+
+static int
+smbfs_node_cmp(const void *va, const void *vb)
+{
+ const smbfs_node_hdr_t *a = va;
+ const smbfs_node_hdr_t *b = vb;
+ int clen, diff;
+
+ /*
+ * Same semantics as strcmp, but does not
+ * assume the strings are null terminated.
+ */
+ clen = (a->hdr_n_rplen < b->hdr_n_rplen) ?
+ a->hdr_n_rplen : b->hdr_n_rplen;
+ diff = strncmp(a->hdr_n_rpath, b->hdr_n_rpath, clen);
+ if (diff < 0)
+ return (-1);
+ if (diff > 0)
+ return (1);
+ /* they match through clen */
+ if (b->hdr_n_rplen > clen)
+ return (-1);
+ if (a->hdr_n_rplen > clen)
+ return (1);
+ return (0);
+}
+
+/*
+ * Setup the "hash" AVL tree used for our node cache.
+ * See: smbfs_mount, smbfs_destroy_table.
+ */
+void
+smbfs_init_hash_avl(avl_tree_t *avl)
+{
+ avl_create(avl, smbfs_node_cmp, sizeof (smbnode_t),
+ offsetof(smbnode_t, r_avl_node));
+}
+
+/*
+ * Invalidate the cached attributes for all nodes "under" the
+ * passed-in node. Note: the passed-in node is NOT affected by
+ * this call. This is used both for files under some directory
+ * after the directory is deleted or renamed, and for extended
+ * attribute files (named streams) under a plain file after that
+ * file is renamed or deleted.
+ *
+ * Do this by walking the AVL tree starting at the passed in node,
+ * and continuing while the visited nodes have a path prefix matching
+ * the entire path of the passed-in node, and a separator just after
+ * that matching path prefix. Watch out for cases where the AVL tree
+ * order may not exactly match the order of an FS walk, i.e.
+ * consider this sequence:
+ * "foo" (directory)
+ * "foo bar" (name containing a space)
+ * "foo/bar"
+ * The walk needs to skip "foo bar" and keep going until it finds
+ * something that doesn't match the "foo" name prefix.
+ */
+void
+smbfs_attrcache_prune(smbnode_t *top_np)
+{
+ smbmntinfo_t *mi;
+ smbnode_t *np;
+ char *rpath;
+ int rplen;
+
+ mi = top_np->n_mount;
+ rw_enter(&mi->smi_hash_lk, RW_READER);
+
+ np = top_np;
+ rpath = top_np->n_rpath;
+ rplen = top_np->n_rplen;
+ for (;;) {
+ np = avl_walk(&mi->smi_hash_avl, np, AVL_AFTER);
+ if (np == NULL)
+ break;
+ if (np->n_rplen < rplen)
+ break;
+ if (0 != strncmp(np->n_rpath, rpath, rplen))
+ break;
+ if (np->n_rplen > rplen && (
+ np->n_rpath[rplen] == ':' ||
+ np->n_rpath[rplen] == '\\'))
+ smbfs_attrcache_remove(np);
}
- return (NULL);
+
+ rw_exit(&mi->smi_hash_lk);
}
#ifdef SMB_VNODE_DEBUG
-int smb_check_table_debug = 1;
+int smbfs_check_table_debug = 1;
#else /* SMB_VNODE_DEBUG */
-int smb_check_table_debug = 0;
+int smbfs_check_table_debug = 0;
#endif /* SMB_VNODE_DEBUG */
/*
* Return 1 if there is a active vnode belonging to this vfs in the
- * smbtable cache.
+ * smbnode cache.
*
* Several of these checks are done without holding the usual
- * locks. This is safe because destroy_smbtable(), smb_addfree(),
+ * locks. This is safe because destroy_smbtable(), smbfs_addfree(),
* etc. will redo the necessary checks before actually destroying
* any smbnodes.
*
@@ -729,117 +860,148 @@ int smb_check_table_debug = 0;
* Relatively harmless, so left 'em in.
*/
int
-smb_check_table(struct vfs *vfsp, smbnode_t *rtnp)
+smbfs_check_table(struct vfs *vfsp, smbnode_t *rtnp)
{
+ smbmntinfo_t *mi;
smbnode_t *np;
vnode_t *vp;
- int index;
int busycnt = 0;
- for (index = 0; index < smbtablesize; index++) {
- rw_enter(&smbtable[index].r_lock, RW_READER);
- for (np = smbtable[index].r_hashf;
- np != (smbnode_t *)(&smbtable[index]);
- np = np->r_hashf) {
- if (np == rtnp)
- continue; /* skip the root */
- vp = SMBTOV(np);
- if (vp->v_vfsp != vfsp)
- continue; /* skip other mount */
-
- /* Now the 'busy' checks: */
- /* Not on the free list? */
- if (np->r_freef == NULL) {
- SMBVDEBUG("!r_freef: node=0x%p, v_path=%s\n",
- (void *)np, vp->v_path);
- busycnt++;
- }
+ mi = VFTOSMI(vfsp);
+ rw_enter(&mi->smi_hash_lk, RW_READER);
+ for (np = avl_first(&mi->smi_hash_avl); np != NULL;
+ np = avl_walk(&mi->smi_hash_avl, np, AVL_AFTER)) {
- /* Has dirty pages? */
- if (vn_has_cached_data(vp) &&
- (np->r_flags & RDIRTY)) {
- SMBVDEBUG("is dirty: node=0x%p, v_path=%s\n",
- (void *)np, vp->v_path);
- busycnt++;
- }
+ if (np == rtnp)
+ continue; /* skip the root */
+ vp = SMBTOV(np);
- /* Other refs? (not reflected in v_count) */
- if (np->r_count > 0) {
- SMBVDEBUG("+r_count: node=0x%p, v_path=%s\n",
- (void *)np, vp->v_path);
- busycnt++;
- }
+ /* Now the 'busy' checks: */
+ /* Not on the free list? */
+ if (np->r_freef == NULL) {
+ SMBVDEBUG("!r_freef: node=0x%p, rpath=%s\n",
+ (void *)np, np->n_rpath);
+ busycnt++;
+ }
- if (busycnt && !smb_check_table_debug)
- break;
+ /* Has dirty pages? */
+ if (vn_has_cached_data(vp) &&
+ (np->r_flags & RDIRTY)) {
+ SMBVDEBUG("is dirty: node=0x%p, rpath=%s\n",
+ (void *)np, np->n_rpath);
+ busycnt++;
+ }
+ /* Other refs? (not reflected in v_count) */
+ if (np->r_count > 0) {
+ SMBVDEBUG("+r_count: node=0x%p, rpath=%s\n",
+ (void *)np, np->n_rpath);
+ busycnt++;
}
- rw_exit(&smbtable[index].r_lock);
+
+ if (busycnt && !smbfs_check_table_debug)
+ break;
+
}
+ rw_exit(&mi->smi_hash_lk);
+
return (busycnt);
}
/*
- * Destroy inactive vnodes from the hash queues which belong to this
+ * Destroy inactive vnodes from the AVL tree which belong to this
* vfs. It is essential that we destroy all inactive vnodes during a
* forced unmount as well as during a normal unmount.
*
* NFS: nfs_subr.c:destroy_rtable
+ *
+ * In here, we're normally destrying all or most of the AVL tree,
+ * so the natural choice is to use avl_destroy_nodes. However,
+ * there may be a few busy nodes that should remain in the AVL
+ * tree when we're done. The solution: use a temporary tree to
+ * hold the busy nodes until we're done destroying the old tree,
+ * then copy the temporary tree over the (now emtpy) real tree.
*/
void
smbfs_destroy_table(struct vfs *vfsp)
{
- int index;
+ avl_tree_t tmp_avl;
+ smbmntinfo_t *mi;
smbnode_t *np;
smbnode_t *rlist;
- smbnode_t *r_hashf;
- vnode_t *vp;
+ void *v;
+ mi = VFTOSMI(vfsp);
rlist = NULL;
+ smbfs_init_hash_avl(&tmp_avl);
- for (index = 0; index < smbtablesize; index++) {
- rw_enter(&smbtable[index].r_lock, RW_WRITER);
- for (np = smbtable[index].r_hashf;
- np != (smbnode_t *)(&smbtable[index]);
- np = r_hashf) {
- /* save the hash pointer before destroying */
- r_hashf = np->r_hashf;
- vp = SMBTOV(np);
- if (vp->v_vfsp == vfsp) {
- mutex_enter(&smbfreelist_lock);
- if (np->r_freef != NULL) {
- smb_rmfree(np);
- mutex_exit(&smbfreelist_lock);
- smb_rmhash_locked(np);
- np->r_hashf = rlist;
- rlist = np;
- } else
- mutex_exit(&smbfreelist_lock);
- }
+ rw_enter(&mi->smi_hash_lk, RW_WRITER);
+ v = NULL;
+ while ((np = avl_destroy_nodes(&mi->smi_hash_avl, &v)) != NULL) {
+
+ mutex_enter(&smbfreelist_lock);
+ if (np->r_freef == NULL) {
+ /*
+ * Busy node (not on the free list).
+ * Will keep in the final AVL tree.
+ */
+ mutex_exit(&smbfreelist_lock);
+ avl_add(&tmp_avl, np);
+ } else {
+ /*
+ * It's on the free list. Remove and
+ * arrange for it to be destroyed.
+ */
+ sn_rmfree(np);
+ mutex_exit(&smbfreelist_lock);
+
+ /*
+ * Last part of sn_rmhash_locked().
+ * NB: avl_destroy_nodes has already
+ * removed this from the "hash" AVL.
+ */
+ mutex_enter(&np->r_statelock);
+ np->r_flags &= ~RHASHED;
+ mutex_exit(&np->r_statelock);
+
+ /*
+ * Add to the list of nodes to destroy.
+ * Borrowing avl_child[0] for this list.
+ */
+ np->r_avl_node.avl_child[0] =
+ (struct avl_node *)rlist;
+ rlist = np;
}
- rw_exit(&smbtable[index].r_lock);
}
+ avl_destroy(&mi->smi_hash_avl);
- for (np = rlist; np != NULL; np = rlist) {
- rlist = np->r_hashf;
- /*
- * This call to smb_addfree will end up destroying the
- * smbnode, but in a safe way with the appropriate set
- * of checks done.
- */
- smb_addfree(np);
- }
+ /*
+ * Replace the (now destroyed) "hash" AVL with the
+ * temporary AVL, which restores the busy nodes.
+ */
+ mi->smi_hash_avl = tmp_avl;
+ rw_exit(&mi->smi_hash_lk);
+ /*
+ * Now destroy the nodes on our temporary list (rlist).
+ * This call to smbfs_addfree will end up destroying the
+ * smbnode, but in a safe way with the appropriate set
+ * of checks done.
+ */
+ while ((np = rlist) != NULL) {
+ rlist = (smbnode_t *)np->r_avl_node.avl_child[0];
+ smbfs_addfree(np);
+ }
}
/*
* This routine destroys all the resources associated with the smbnode
- * and then the smbnode itself.
+ * and then the smbnode itself. Note: sn_inactive has been called.
*
* NFS: nfs_subr.c:destroy_rnode
*/
-void
-smb_destroy_node(smbnode_t *np)
+static void
+sn_destroy_node(smbnode_t *np)
{
vnode_t *vp;
vfs_t *vfsp;
@@ -850,6 +1012,8 @@ smb_destroy_node(smbnode_t *np)
ASSERT(vp->v_count == 1);
ASSERT(np->r_count == 0);
ASSERT(np->r_mapcnt == 0);
+ ASSERT(np->r_cred == NULL);
+ ASSERT(np->n_rpath == NULL);
ASSERT(!(np->r_flags & RHASHED));
ASSERT(np->r_freef == NULL && np->r_freeb == NULL);
atomic_add_long((ulong_t *)&smbnodenew, -1);
@@ -859,7 +1023,17 @@ smb_destroy_node(smbnode_t *np)
VFS_RELE(vfsp);
}
-/* rflush? */
+/*
+ * Flush all vnodes in this (or every) vfs.
+ * Used by nfs_sync and by nfs_unmount.
+ */
+/*ARGSUSED*/
+void
+smbfs_rflush(struct vfs *vfsp, cred_t *cr)
+{
+ /* Todo: mmap support. */
+}
+
/* access cache */
/* client handles */
@@ -867,17 +1041,15 @@ smb_destroy_node(smbnode_t *np)
* initialize resources that are used by smbfs_subr.c
* this is called from the _init() routine (by the way of smbfs_clntinit())
*
- * allocate and initialze smbfs hash table
* NFS: nfs_subr.c:nfs_subrinit
*/
int
smbfs_subrinit(void)
{
- int i;
ulong_t nsmbnode_max;
/*
- * Allocate and initialize the smbnode hash queues
+ * Allocate and initialize the smbnode cache
*/
if (nsmbnode <= 0)
nsmbnode = ncsize; /* dnlc.h */
@@ -889,14 +1061,6 @@ smbfs_subrinit(void)
nsmbnode = nsmbnode_max;
}
- smbtablesize = 1 << highbit(nsmbnode / smbhashlen);
- smbtablemask = smbtablesize - 1;
- smbtable = kmem_alloc(smbtablesize * sizeof (*smbtable), KM_SLEEP);
- for (i = 0; i < smbtablesize; i++) {
- smbtable[i].r_hashf = (smbnode_t *)(&smbtable[i]);
- smbtable[i].r_hashb = (smbnode_t *)(&smbtable[i]);
- rw_init(&smbtable[i].r_lock, NULL, RW_DEFAULT, NULL);
- }
smbnode_cache = kmem_cache_create("smbnode_cache", sizeof (smbnode_t),
0, NULL, NULL, smbfs_kmem_reclaim, NULL, NULL, 0);
@@ -926,17 +1090,12 @@ smbfs_subrinit(void)
void
smbfs_subrfini(void)
{
- int i;
/*
- * Deallocate the smbnode hash queues
+ * Destroy the smbnode cache
*/
kmem_cache_destroy(smbnode_cache);
- for (i = 0; i < smbtablesize; i++)
- rw_destroy(&smbtable[i].r_lock);
- kmem_free(smbtable, smbtablesize * sizeof (*smbtable));
-
/*
* Destroy the various mutexes and reader/writer locks
*/
@@ -950,43 +1109,42 @@ smbfs_subrfini(void)
* Support functions for smbfs_kmem_reclaim
*/
-static int
+static void
smbfs_node_reclaim(void)
{
- int freed;
+ smbmntinfo_t *mi;
smbnode_t *np;
vnode_t *vp;
- freed = 0;
mutex_enter(&smbfreelist_lock);
while ((np = smbfreelist) != NULL) {
- smb_rmfree(np);
+ sn_rmfree(np);
mutex_exit(&smbfreelist_lock);
if (np->r_flags & RHASHED) {
vp = SMBTOV(np);
- rw_enter(&np->r_hashq->r_lock, RW_WRITER);
+ mi = np->n_mount;
+ rw_enter(&mi->smi_hash_lk, RW_WRITER);
mutex_enter(&vp->v_lock);
if (vp->v_count > 1) {
vp->v_count--;
mutex_exit(&vp->v_lock);
- rw_exit(&np->r_hashq->r_lock);
+ rw_exit(&mi->smi_hash_lk);
mutex_enter(&smbfreelist_lock);
continue;
}
mutex_exit(&vp->v_lock);
- smb_rmhash_locked(np);
- rw_exit(&np->r_hashq->r_lock);
+ sn_rmhash_locked(np);
+ rw_exit(&mi->smi_hash_lk);
}
/*
- * This call to smb_addfree will end up destroying the
+ * This call to smbfs_addfree will end up destroying the
* smbnode, but in a safe way with the appropriate set
* of checks done.
*/
- smb_addfree(np);
+ smbfs_addfree(np);
mutex_enter(&smbfreelist_lock);
}
mutex_exit(&smbfreelist_lock);
- return (freed);
}
/*
@@ -999,7 +1157,7 @@ smbfs_node_reclaim(void)
void
smbfs_kmem_reclaim(void *cdrarg)
{
- (void) smbfs_node_reclaim();
+ smbfs_node_reclaim();
}
/* nfs failover stuff */
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c
index b616543a92..d33b0ee0a1 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c
@@ -39,6 +39,7 @@
#include <sys/systm.h>
#include <sys/cred.h>
+#include <sys/time.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <fs/fs_subr.h>
@@ -173,7 +174,7 @@ static void smbfs_freevfs(vfs_t *);
int
_init(void)
{
- int status;
+ int error;
/*
* Check compiled-in version of "nsmb"
@@ -186,13 +187,30 @@ _init(void)
smbfs_mountcount = 0;
- if ((status = smbfs_clntinit()) != 0) {
+ /*
+ * NFS calls these two in _clntinit
+ * Easier to follow this way.
+ */
+ if ((error = smbfs_subrinit()) != 0) {
+ cmn_err(CE_WARN, "_init: smbfs_subrinit failed");
+ return (error);
+ }
+
+ if ((error = smbfs_vfsinit()) != 0) {
+ cmn_err(CE_WARN, "_init: smbfs_vfsinit failed");
+ smbfs_subrfini();
+ return (error);
+ }
+
+ if ((error = smbfs_clntinit()) != 0) {
cmn_err(CE_WARN, "_init: smbfs_clntinit failed");
- return (status);
+ smbfs_vfsfini();
+ smbfs_subrfini();
+ return (error);
}
- status = mod_install((struct modlinkage *)&modlinkage);
- return (status);
+ error = mod_install((struct modlinkage *)&modlinkage);
+ return (error);
}
/*
@@ -222,6 +240,10 @@ _fini(void)
*/
smbfs_clntfini();
+ /* NFS calls these two in _clntfini */
+ smbfs_vfsfini();
+ smbfs_subrfini();
+
/*
* Free the ops vectors
*/
@@ -298,10 +320,21 @@ smbfsfini()
void
smbfs_free_smi(smbmntinfo_t *smi)
{
- if (smi) {
- smbfs_zonelist_remove(smi);
- kmem_free(smi, sizeof (smbmntinfo_t));
- }
+ if (smi == NULL)
+ return;
+
+ if (smi->smi_zone != NULL)
+ zone_rele(smi->smi_zone);
+
+ if (smi->smi_share != NULL)
+ smb_share_rele(smi->smi_share);
+
+ avl_destroy(&smi->smi_hash_avl);
+ rw_destroy(&smi->smi_hash_lk);
+ cv_destroy(&smi->smi_statvfs_cv);
+ mutex_destroy(&smi->smi_lock);
+
+ kmem_free(smi, sizeof (smbmntinfo_t));
}
/*
@@ -313,7 +346,7 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
{
char *data = uap->dataptr;
int error;
- vnode_t *rtvp = NULL; /* root of this fs */
+ smbnode_t *rtnp = NULL; /* root of this fs */
smbmntinfo_t *smi = NULL;
dev_t smbfs_dev;
int version;
@@ -322,6 +355,7 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
zone_t *mntzone = NULL;
smb_share_t *ssp = NULL;
smb_cred_t scred;
+ int flags, sec;
STRUCT_DECL(smbfs_args, args); /* smbfs mount arguments */
@@ -336,8 +370,6 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
*
* uap->datalen might be different from sizeof (args)
* in a compatible situation.
- *
- * XXX - todo: handle mount options string
*/
STRUCT_INIT(args, get_udatamodel());
bzero(STRUCT_BUF(args), SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE));
@@ -356,6 +388,9 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
return (EINVAL);
}
+ /*
+ * Deal with re-mount requests.
+ */
if (uap->flags & MS_REMOUNT) {
cmn_err(CE_WARN, "MS_REMOUNT not implemented");
return (ENOTSUP);
@@ -386,11 +421,9 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
return (error);
}
- smb_credinit(&scred, cr);
-
/*
* Use "goto errout" from here on.
- * See: ssp, smi, rtvp, mntzone
+ * See: ssp, smi, rtnp, mntzone
*/
/*
@@ -437,41 +470,93 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
}
}
- /*
- * Get root vnode.
- */
-proceed:
+ /* Prevent unload. */
+ atomic_inc_32(&smbfs_mountcount);
/*
* Create a mount record and link it to the vfs struct.
+ * No more possiblities for errors from here on.
+ * Tear-down of this stuff is in smbfs_free_smi()
+ *
* Compare with NFS: nfsrootvp()
*/
- smi = kmem_zalloc(sizeof (smbmntinfo_t), KM_SLEEP);
+ smi = kmem_zalloc(sizeof (*smi), KM_SLEEP);
+
+ mutex_init(&smi->smi_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&smi->smi_statvfs_cv, NULL, CV_DEFAULT, NULL);
- smi->smi_share = ssp;
- smi->smi_zone = mntzone;
+ rw_init(&smi->smi_hash_lk, NULL, RW_DEFAULT, NULL);
+ smbfs_init_hash_avl(&smi->smi_hash_avl);
+
+ smi->smi_share = ssp;
+ ssp = NULL;
+ smi->smi_zone = mntzone;
+ mntzone = NULL;
+
+ /*
+ * 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);
/*
- * Handle mount options. See also XATTR below.
- * XXX: forcedirectio, largefiles (later)
+ * All "generic" mount options have already been
+ * handled in vfs.c:domount() - see mntopts stuff.
+ * Query generic options using vfs_optionisset().
*/
if (vfs_optionisset(vfsp, MNTOPT_INTR, NULL))
smi->smi_flags |= SMI_INT;
/*
- * XXX If not root, get uid/gid from the covered vnode.
+ * 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;
+
+ /*
+ * Hande the SMBFS_MF_xxx flags.
*/
- smi->smi_args.dir_mode = STRUCT_FGET(args, dir_mode);
- smi->smi_args.file_mode = STRUCT_FGET(args, file_mode);
- smi->smi_args.uid = STRUCT_FGET(args, uid);
- smi->smi_args.gid = STRUCT_FGET(args, gid);
+ if (flags & SMBFS_MF_NOAC)
+ smi->smi_flags |= SMI_NOAC;
+ if (flags & SMBFS_MF_ACREGMIN) {
+ sec = STRUCT_FGET(args, acregmin);
+ if (sec < 0 || sec > SMBFS_ACMINMAX)
+ sec = SMBFS_ACMINMAX;
+ smi->smi_acregmin = SEC2HR(sec);
+ }
+ if (flags & SMBFS_MF_ACREGMAX) {
+ sec = STRUCT_FGET(args, acregmax);
+ if (sec < 0 || sec > SMBFS_ACMAXMAX)
+ sec = SMBFS_ACMAXMAX;
+ smi->smi_acregmax = SEC2HR(sec);
+ }
+ if (flags & SMBFS_MF_ACDIRMIN) {
+ sec = STRUCT_FGET(args, acdirmin);
+ if (sec < 0 || sec > SMBFS_ACMINMAX)
+ sec = SMBFS_ACMINMAX;
+ smi->smi_acdirmin = SEC2HR(sec);
+ }
+ if (flags & SMBFS_MF_ACDIRMAX) {
+ sec = STRUCT_FGET(args, acdirmax);
+ if (sec < 0 || sec > SMBFS_ACMAXMAX)
+ sec = SMBFS_ACMAXMAX;
+ smi->smi_acdirmax = SEC2HR(sec);
+ }
/*
* Get attributes of the remote file system,
* i.e. ACL support, named streams, etc.
*/
- error = smbfs_smb_qfsattr(ssp, &smi->smi_fsa, &scred);
+ smb_credinit(&scred, cr);
+ error = smbfs_smb_qfsattr(smi->smi_share, &smi->smi_fsa, &scred);
+ smb_credrele(&scred);
if (error) {
SMBVDEBUG("smbfs_smb_qfsattr error %d\n", error);
}
@@ -503,57 +588,40 @@ proceed:
vfsp->vfs_bcount = 0;
smi->smi_vfsp = vfsp;
- smbfs_zonelist_add(smi);
+ smbfs_zonelist_add(smi); /* undo in smbfs_freevfs */
/*
* Create the root vnode, which we need in unmount
- * for the call to smb_check_table(), etc.
+ * for the call to smbfs_check_table(), etc.
+ * Release this hold in smbfs_unmount.
*/
- rtvp = smbfs_make_node(vfsp, "\\", 1, NULL, 0, 0, NULL);
- if (!rtvp) {
- cmn_err(CE_WARN, "smbfs_mount: make_node failed\n");
- return (ENOENT);
- }
- rtvp->v_type = VDIR;
- rtvp->v_flag |= VROOT;
+ rtnp = smbfs_node_findcreate(smi, "\\", 1, NULL, 0, 0,
+ &smbfs_fattr0);
+ ASSERT(rtnp != NULL);
+ rtnp->r_vnode->v_type = VDIR;
+ rtnp->r_vnode->v_flag |= VROOT;
+ smi->smi_root = rtnp;
/*
- * Could get attributes here, but that can wait
- * until someone does a getattr call.
- *
* NFS does other stuff here too:
* async worker threads
* init kstats
*
* End of code from NFS nfsrootvp()
*/
-
- smb_credrele(&scred);
-
- smi->smi_root = VTOSMB(rtvp);
-
- atomic_inc_32(&smbfs_mountcount);
-
return (0);
errout:
-
- ASSERT(rtvp == NULL);
-
vfsp->vfs_data = NULL;
- if (smi)
+ if (smi != NULL)
smbfs_free_smi(smi);
if (mntzone != NULL)
zone_rele(mntzone);
- if (ssp)
+ if (ssp != NULL)
smb_share_rele(ssp);
- smb_credrele(&scred);
-
- /* args, if we allocated */
-
return (error);
}
@@ -572,16 +640,14 @@ smbfs_unmount(vfs_t *vfsp, int flag, cred_t *cr)
return (EPERM);
if ((flag & MS_FORCE) == 0) {
-#ifdef APPLE
smbfs_rflush(vfsp, cr);
-#endif
/*
* If there are any active vnodes on this file system,
* (other than the root vnode) then the file system is
* busy and can't be umounted.
*/
- if (smb_check_table(vfsp, smi->smi_root))
+ if (smbfs_check_table(vfsp, smi->smi_root))
return (EBUSY);
/*
@@ -625,7 +691,7 @@ smbfs_unmount(vfs_t *vfsp, int flag, cred_t *cr)
/*
* Remove all nodes from the node hash tables.
- * This (indirectly) calls: smb_addfree, smbinactive,
+ * This (indirectly) calls: smbfs_addfree, smbinactive,
* which will try to flush dirty pages, etc. so
* don't destroy the underlying share just yet.
*
@@ -652,10 +718,8 @@ smbfs_unmount(vfs_t *vfsp, int flag, cred_t *cr)
}
/*
- * Note: the smb_share_rele()
- * happens in smbfs_freevfs()
+ * The rest happens in smbfs_freevfs()
*/
-
return (0);
}
@@ -801,12 +865,11 @@ 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.
*/
-#ifdef APPLE
if (!(flag & SYNC_ATTR) && mutex_tryenter(&smbfs_syncbusy) != 0) {
smbfs_rflush(vfsp, cr);
mutex_exit(&smbfs_syncbusy);
}
-#endif /* APPLE */
+
return (0);
}
@@ -833,7 +896,6 @@ void
smbfs_freevfs(vfs_t *vfsp)
{
smbmntinfo_t *smi;
- smb_share_t *ssp;
/* free up the resources */
smi = VFTOSMI(vfsp);
@@ -845,15 +907,7 @@ smbfs_freevfs(vfs_t *vfsp)
*/
ASSERT(smi->smi_io_kstats == NULL);
- /*
- * Drop our reference to the share.
- * This usually leads to VC close.
- */
- ssp = smi->smi_share;
- smi->smi_share = NULL;
- smb_share_rele(ssp);
-
- zone_rele(smi->smi_zone);
+ smbfs_zonelist_remove(smi);
smbfs_free_smi(smi);
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c
index a2ba2f9a5e..1ce01a3112 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c
@@ -49,7 +49,6 @@
#include <sys/sysmacros.h>
#include <sys/kmem.h>
#include <sys/cmn_err.h>
-#include <sys/dnlc.h>
#include <sys/vfs_opreg.h>
#include <sys/policy.h>
@@ -98,18 +97,17 @@ static const char illegal_chars[] = {
/*
* Turning this on causes nodes to be created in the cache
- * during directory listings. The "fast" claim is debatable,
- * and the effects on the cache can be undesirable.
+ * during directory listings, normally avoiding a second
+ * OtW attribute fetch just after a readdir.
*/
+int smbfs_fastlookup = 1;
/* local static function defines */
-#ifdef USE_DNLC
-static int smbfslookup_dnlc(vnode_t *dvp, char *nm, vnode_t **vpp,
- cred_t *cr);
-#endif
+static int smbfslookup_cache(vnode_t *, char *, int, vnode_t **,
+ cred_t *);
static int smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr,
- int dnlc, caller_context_t *);
+ int cache_ok, caller_context_t *);
static int smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm,
cred_t *cr, caller_context_t *);
static int smbfssetattr(vnode_t *, struct vattr *, int, cred_t *);
@@ -240,9 +238,9 @@ const fs_operation_def_t smbfs_vnodeops_template[] = {
static int
smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
{
- struct vattr va;
smbnode_t *np;
vnode_t *vp;
+ smbfattr_t fa;
u_int32_t rights, rightsrcvd;
u_int16_t fid, oldfid;
int oldgenid;
@@ -250,7 +248,6 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
smbmntinfo_t *smi;
smb_share_t *ssp;
cred_t *oldcr;
- int attrcacheupdated = 0;
int tmperror;
int error = 0;
@@ -328,14 +325,10 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
int upgrade = 0;
if ((flag & FWRITE) &&
- !(np->n_rights & (SA_RIGHT_FILE_WRITE_DATA |
- GENERIC_RIGHT_ALL_ACCESS |
- GENERIC_RIGHT_WRITE_ACCESS)))
+ !(np->n_rights & SA_RIGHT_FILE_WRITE_DATA))
upgrade = 1;
if ((flag & FREAD) &&
- !(np->n_rights & (SA_RIGHT_FILE_READ_DATA |
- GENERIC_RIGHT_ALL_ACCESS |
- GENERIC_RIGHT_READ_ACCESS)))
+ !(np->n_rights & SA_RIGHT_FILE_READ_DATA))
upgrade = 1;
if (!upgrade) {
/*
@@ -350,20 +343,24 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
/*
* we always ask for READ_CONTROL so we can always get the
* owner/group IDs to satisfy a stat. Ditto attributes.
- * XXX: verify that works with "drop boxes"
*/
rights |= (STD_RIGHT_READ_CONTROL_ACCESS |
SA_RIGHT_FILE_READ_ATTRIBUTES);
if ((flag & FREAD))
rights |= SA_RIGHT_FILE_READ_DATA;
if ((flag & FWRITE))
- rights |= SA_RIGHT_FILE_APPEND_DATA | SA_RIGHT_FILE_WRITE_DATA;
-
- /* XXX: open gets the current size, but we don't use it. */
- error = smbfs_smb_open(np, rights, &scred, &attrcacheupdated, &fid,
- NULL, 0, 0, NULL, &rightsrcvd);
+ rights |= SA_RIGHT_FILE_WRITE_DATA |
+ SA_RIGHT_FILE_APPEND_DATA |
+ SA_RIGHT_FILE_WRITE_ATTRIBUTES;
+
+ bzero(&fa, sizeof (fa));
+ error = smbfs_smb_open(np,
+ NULL, 0, 0, /* name nmlen xattr */
+ rights, &scred,
+ &fid, &rightsrcvd, &fa);
if (error)
goto out;
+ smbfs_attrcache_fa(vp, &fa);
/*
* We have a new FID and access rights.
@@ -382,7 +379,7 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
* Close old wire-open.
*/
tmperror = smbfs_smb_close(ssp,
- oldfid, &np->n_mtime, &scred);
+ oldfid, NULL, &scred);
if (tmperror)
SMBVDEBUG("error %d closing %s\n",
tmperror, np->n_rpath);
@@ -408,25 +405,6 @@ have_fid:
if (np->n_ovtype == VNON)
np->n_ovtype = vp->v_type;
- /* Get attributes (maybe). */
-
- /* Darwin (derived) code. */
-
- va.va_mask = AT_MTIME;
- if (np->n_flag & NMODIFIED)
- smbfs_attr_cacheremove(np);
-
- /*
- * Try to get attributes, but don't bail on error.
- * We already hold r_lkserlock/reader so note:
- * this call will recursively take r_lkserlock.
- */
- tmperror = smbfsgetattr(vp, &va, cr);
- if (tmperror)
- SMBERROR("getattr failed, error=%d", tmperror);
- else
- np->n_mtime.tv_sec = va.va_mtime.tv_sec;
-
out:
smb_credrele(&scred);
smbfs_rw_exit(&np->r_lkserlock);
@@ -441,6 +419,7 @@ smbfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
smbnode_t *np;
smbmntinfo_t *smi;
smb_share_t *ssp;
+ cred_t *oldcr;
int error = 0;
struct smb_cred scred;
@@ -492,12 +471,13 @@ smbfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
cleanshares(vp, pid);
}
- if (count > 1)
- return (0);
/*
- * OK, do "last close" stuff.
+ * This (passed in) count is the ref. count from the
+ * user's file_t before the closef call (fio.c).
+ * We only care when the reference goes away.
*/
-
+ if (count > 1)
+ return (0);
/*
* Do the CIFS close.
@@ -545,15 +525,24 @@ smbfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
}
}
if (error) {
- SMBERROR("error %d closing %s\n",
+ SMBVDEBUG("error %d closing %s\n",
error, np->n_rpath);
}
/* Allow next open to use any v_type. */
np->n_ovtype = VNON;
+ /*
+ * Other "last close" stuff.
+ */
+ mutex_enter(&np->r_statelock);
if (np->n_flag & NATTRCHANGED)
- smbfs_attr_cacheremove(np);
+ smbfs_attrcache_rm_locked(np);
+ oldcr = np->r_cred;
+ np->r_cred = NULL;
+ mutex_exit(&np->r_statelock);
+ if (oldcr != NULL)
+ crfree(oldcr);
out:
smb_credrele(&scred);
@@ -685,7 +674,7 @@ smbfs_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr,
*/
if (ioflag & (FAPPEND | FSYNC)) {
if (np->n_flag & NMODIFIED) {
- smbfs_attr_cacheremove(np);
+ smbfs_attrcache_remove(np);
/* XXX: smbfs_vinvalbuf? */
}
}
@@ -730,7 +719,7 @@ smbfs_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr,
/* Timeout: longer for append. */
timo = smb_timo_write;
- if (endoff > np->n_size)
+ if (endoff > np->r_size)
timo = smb_timo_append;
/* Shared lock for n_fid use in smb_rwuio */
@@ -748,8 +737,8 @@ smbfs_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr,
if (error == 0) {
mutex_enter(&np->r_statelock);
np->n_flag |= (NFLUSHWIRE | NATTRCHANGED);
- if (uiop->uio_loffset > (offset_t)np->n_size)
- np->n_size = (len_t)uiop->uio_loffset;
+ if (uiop->uio_loffset > (offset_t)np->r_size)
+ np->r_size = (len_t)uiop->uio_loffset;
mutex_exit(&np->r_statelock);
if (ioflag & (FSYNC|FDSYNC)) {
/* Don't error the I/O if this fails. */
@@ -866,62 +855,18 @@ smbfs_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
if (vap->va_mask | AT_SIZE)
vap->va_size = np->r_size;
if (vap->va_mask | AT_FSID)
- vap->va_fsid = np->r_attr.va_fsid;
+ vap->va_fsid = vp->v_vfsp->vfs_dev;
if (vap->va_mask | AT_RDEV)
- vap->va_rdev = np->r_attr.va_rdev;
+ vap->va_rdev = vp->v_rdev;
mutex_exit(&np->r_statelock);
return (0);
}
}
-
return (smbfsgetattr(vp, vap, cr));
}
-/*
- * Mostly from Darwin smbfs_getattr()
- */
-int
-smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr)
-{
- int error;
- smbnode_t *np;
- struct smb_cred scred;
- struct smbfattr fattr;
-
- ASSERT(curproc->p_zone == VTOSMI(vp)->smi_zone);
-
- np = VTOSMB(vp);
-
- /*
- * If we've got cached attributes, we're done, otherwise go
- * to the server to get attributes, which will update the cache
- * in the process.
- *
- * This section from Darwin smbfs_getattr,
- * but then modified a lot.
- */
- error = smbfs_attr_cachelookup(vp, vap);
- if (error != ENOENT)
- return (error);
-
- /* Shared lock for (possible) n_fid use. */
- if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
- return (EINTR);
- smb_credinit(&scred, cr);
-
- bzero(&fattr, sizeof (fattr));
- error = smbfs_smb_getfattr(np, &fattr, &scred);
-
- smb_credrele(&scred);
- smbfs_rw_exit(&np->r_lkserlock);
-
- if (!error) {
- smbfs_attr_cacheenter(vp, &fattr);
- error = smbfs_attr_cachelookup(vp, vap);
- }
- return (error);
-}
+/* smbfsgetattr() in smbfs_client.c */
/*
* XXX
@@ -933,23 +878,29 @@ static int
smbfs_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
caller_context_t *ct)
{
+ vfs_t *vfsp;
+ smbmntinfo_t *smi;
int error;
uint_t mask;
struct vattr oldva;
- smbmntinfo_t *smi;
- smi = VTOSMI(vp);
+ vfsp = vp->v_vfsp;
+ smi = VFTOSMI(vfsp);
if (curproc->p_zone != smi->smi_zone)
return (EIO);
- if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ if (smi->smi_flags & SMI_DEAD || vfsp->vfs_flag & VFS_UNMOUNTED)
return (EIO);
mask = vap->va_mask;
if (mask & AT_NOSET)
return (EINVAL);
+ if (vfsp->vfs_flag & VFS_RDONLY)
+ return (EROFS);
+
+ bzero(&oldva, sizeof (oldva));
oldva.va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID;
error = smbfsgetattr(vp, &oldva, cr);
if (error)
@@ -973,7 +924,6 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr)
{
int error = 0;
smbnode_t *np = VTOSMB(vp);
- smbmntinfo_t *smi = VTOSMI(vp);
uint_t mask = vap->va_mask;
struct timespec *mtime, *atime;
struct smb_cred scred;
@@ -982,7 +932,7 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr)
int have_fid = 0;
uint32_t rights = 0;
- ASSERT(curproc->p_zone == smi->smi_zone);
+ ASSERT(curproc->p_zone == VTOSMI(vp)->smi_zone);
/*
* There are no settable attributes on the XATTR dir,
@@ -1016,20 +966,21 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr)
*/
if (mask & (AT_ATIME | AT_MTIME)) {
rights |=
- SA_RIGHT_FILE_WRITE_ATTRIBUTES |
- GENERIC_RIGHT_ALL_ACCESS |
- GENERIC_RIGHT_WRITE_ACCESS;
+ SA_RIGHT_FILE_WRITE_ATTRIBUTES;
}
if (mask & AT_SIZE) {
rights |=
SA_RIGHT_FILE_WRITE_DATA |
SA_RIGHT_FILE_APPEND_DATA;
- /*
- * Only SIZE requires a handle.
- * XXX May be more reliable to just
- * always get the file handle here.
- * The tmpopen checks n_vcgenid.
- */
+ }
+
+ /*
+ * Only SIZE really requires a handle, but it's
+ * simpler and more reliable to set via a handle.
+ * Some servers like NT4 won't set times by path.
+ * Also, we're usually setting everything anyway.
+ */
+ if (mask & (AT_SIZE | AT_ATIME | AT_MTIME)) {
error = smbfs_smb_tmpopen(np, rights, &scred, &fid);
if (error) {
SMBVDEBUG("error %d opening %s\n",
@@ -1039,7 +990,6 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr)
have_fid = 1;
}
-
/*
* If the server supports the UNIX extensions, right here is where
* we'd support changes to uid, gid, mode, and possibly va_flags.
@@ -1089,21 +1039,16 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr)
if (mtime || atime) {
/*
- * If file is opened with write-attributes capability,
- * we use handle-based calls. If not, we use path-based ones.
+ * Always use the handle-based set attr call now.
+ * Not trying to set DOS attributes here so pass zero.
*/
- if (have_fid) {
- error = smbfs_smb_setfattr(np, fid,
- np->n_dosattr, mtime, atime, &scred);
- } else {
- error = smbfs_smb_setpattr(np,
- np->n_dosattr, mtime, atime, &scred);
- }
+ ASSERT(have_fid);
+ error = smbfs_smb_setfattr(np, fid,
+ 0, mtime, atime, &scred);
if (error) {
SMBVDEBUG("set times error %d file %s\n",
error, np->n_rpath);
} else {
- /* XXX: set np->n_mtime, etc? */
modified = 1;
}
}
@@ -1111,20 +1056,16 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr)
out:
if (modified) {
/*
- * Invalidate attribute cache in case if server doesn't set
- * required attributes.
- */
- smbfs_attr_cacheremove(np);
- /*
- * XXX Darwin called _getattr here to
- * update the mtime. Should we?
+ * Invalidate attribute cache in case the server
+ * doesn't set exactly the attributes we asked.
*/
+ smbfs_attrcache_remove(np);
}
if (have_fid) {
cerror = smbfs_smb_tmpclose(np, fid, &scred);
if (cerror)
- SMBERROR("error %d closing %s\n",
+ SMBVDEBUG("error %d closing %s\n",
cerror, np->n_rpath);
}
@@ -1188,10 +1129,9 @@ smbfs_access_rwx(vfs_t *vfsp, int vtype, int mode, cred_t *cr)
va.va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID;
va.va_type = vtype;
va.va_mode = (vtype == VDIR) ?
- smi->smi_args.dir_mode :
- smi->smi_args.file_mode;
- va.va_uid = smi->smi_args.uid;
- va.va_gid = smi->smi_args.gid;
+ smi->smi_dmode : smi->smi_fmode;
+ va.va_uid = smi->smi_uid;
+ va.va_gid = smi->smi_gid;
/*
* Disallow write attempts on read-only file systems,
@@ -1367,7 +1307,7 @@ smbfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
np->n_dirrefs, fid, np->n_rpath);
}
- smb_addfree(np);
+ smbfs_addfree(np);
}
/*
@@ -1408,37 +1348,29 @@ smbfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
if ((vfs->vfs_flag & VFS_XATTR) == 0)
return (EINVAL);
- /*
- * We don't allow recursive attributes.
- */
- if (dnp->n_flag & N_XATTR)
- return (EINVAL);
-
error = smbfs_get_xattrdir(dvp, vpp, cr, flags);
return (error);
}
- if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_READER, SMBINTR(dvp))) {
- error = EINTR;
- goto out;
- }
+ if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_READER, SMBINTR(dvp)))
+ return (EINTR);
error = smbfslookup(dvp, nm, vpp, cr, 1, ct);
smbfs_rw_exit(&dnp->r_rwlock);
-out:
return (error);
}
/* ARGSUSED */
static int
-smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
- caller_context_t *ct)
+smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr,
+ int cache_ok, caller_context_t *ct)
{
int error;
int supplen; /* supported length */
vnode_t *vp;
+ smbnode_t *np;
smbnode_t *dnp;
smbmntinfo_t *smi;
/* struct smb_vc *vcp; */
@@ -1471,9 +1403,8 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
ASSERT(dnp->r_rwlock.count != 0);
/*
- * If lookup is for "", just return dvp. Don't need
- * to send it over the wire, look it up in the dnlc,
- * or perform any access checks.
+ * If lookup is for "", just return dvp.
+ * No need to perform any access checks.
*/
if (nmlen == 0) {
VN_HOLD(dvp);
@@ -1495,9 +1426,8 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
return (error);
/*
- * If lookup is for ".", just return dvp. Don't need
- * to send it over the wire or look it up in the dnlc,
- * just need to check access (done above).
+ * If lookup is for ".", just return dvp.
+ * Access check was done above.
*/
if (nmlen == 1 && name[0] == '.') {
VN_HOLD(dvp);
@@ -1523,21 +1453,8 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
if (strpbrk(nm, ill))
return (EINVAL);
-#ifdef USE_DNLC
- if (dnlc) {
- /*
- * Lookup this name in the DNLC. If there was a valid entry,
- * then return the results of the lookup.
- */
- error = smbfslookup_dnlc(dvp, nm, vpp, cr);
- if (error || *vpp != NULL)
- return (error);
- }
-#endif /* USE_DNLC */
-
/*
- * Handle lookup of ".." which is quite tricky,
- * because the protocol gives us little help.
+ * Special handling for lookup of ".."
*
* We keep full pathnames (as seen on the server)
* so we can just trim off the last component to
@@ -1570,7 +1487,6 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
*/
if (dvp->v_flag & V_XATTRDIR) {
error = smbfs_xa_parent(dvp, vpp);
- /* Intentionally no dnlc_update */
return (error);
}
@@ -1583,21 +1499,19 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
if (dnp->n_rpath[rplen] == '\\')
break;
}
- if (rplen == 0) {
+ if (rplen <= 0) {
/* Found our way to the root. */
vp = SMBTOV(smi->smi_root);
VN_HOLD(vp);
*vpp = vp;
return (0);
}
- vp = smbfs_make_node(dvp->v_vfsp,
- dnp->n_rpath, rplen,
- NULL, 0, 0, NULL);
- ASSERT(vp);
+ np = smbfs_node_findcreate(smi,
+ dnp->n_rpath, rplen, NULL, 0, 0,
+ &smbfs_fattr0); /* force create */
+ ASSERT(np != NULL);
+ vp = SMBTOV(np);
vp->v_type = VDIR;
-#ifdef USE_DNLC
- dnlc_update(dvp, nm, vp);
-#endif
/* Success! */
*vpp = vp;
@@ -1605,34 +1519,49 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
}
/*
- * Normal lookup of a child node.
- * Note we handled "." and ".." above.
- *
- * First, go over-the-wire to get the
- * node type (and attributes).
+ * Normal lookup of a name under this directory.
+ * Note we handled "", ".", ".." above.
+ */
+ if (cache_ok) {
+ /*
+ * The caller indicated that it's OK to use a
+ * cached result for this lookup, so try to
+ * reclaim a node from the smbfs node cache.
+ */
+ error = smbfslookup_cache(dvp, nm, nmlen, &vp, cr);
+ if (error)
+ return (error);
+ if (vp != NULL) {
+ /* hold taken in lookup_cache */
+ *vpp = vp;
+ return (0);
+ }
+ }
+
+ /*
+ * OK, go over-the-wire to get the attributes,
+ * then create the node.
*/
smb_credinit(&scred, cr);
/* Note: this can allocate a new "name" */
error = smbfs_smb_lookup(dnp, &name, &nmlen, &fa, &scred);
smb_credrele(&scred);
-#ifdef USE_DNLC
- if (error == ENOENT)
- dnlc_enter(dvp, nm, DNLC_NO_VNODE);
-#endif
+ if (error == ENOTDIR) {
+ /*
+ * Lookup failed because this directory was
+ * removed or renamed by another client.
+ * Remove any cached attributes under it.
+ */
+ smbfs_attrcache_remove(dnp);
+ smbfs_attrcache_prune(dnp);
+ }
if (error)
goto out;
- /*
- * Find or create the node.
- */
error = smbfs_nget(dvp, name, nmlen, &fa, &vp);
if (error)
goto out;
-#ifdef USE_DNLC
- dnlc_update(dvp, nm, vp);
-#endif
-
/* Success! */
*vpp = vp;
@@ -1644,83 +1573,98 @@ out:
return (error);
}
-#ifdef USE_DNLC
+/*
+ * smbfslookup_cache
+ *
+ * Try to reclaim a node from the smbfs node cache.
+ * Some statistics for DEBUG.
+ *
+ * This mechanism lets us avoid many of the five (or more)
+ * OtW lookup calls per file seen with "ls -l" if we search
+ * the smbfs node cache for recently inactive(ated) nodes.
+ */
#ifdef DEBUG
-static int smbfs_lookup_dnlc_hits = 0;
-static int smbfs_lookup_dnlc_misses = 0;
-static int smbfs_lookup_dnlc_neg_hits = 0;
-static int smbfs_lookup_dnlc_disappears = 0;
-static int smbfs_lookup_dnlc_lookups = 0;
-#endif
+int smbfs_lookup_cache_calls = 0;
+int smbfs_lookup_cache_error = 0;
+int smbfs_lookup_cache_miss = 0;
+int smbfs_lookup_cache_stale = 0;
+int smbfs_lookup_cache_hits = 0;
+#endif /* DEBUG */
/* ARGSUSED */
static int
-smbfslookup_dnlc(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr)
+smbfslookup_cache(vnode_t *dvp, char *nm, int nmlen,
+ vnode_t **vpp, cred_t *cr)
{
- int error;
- vnode_t *vp;
struct vattr va;
smbnode_t *dnp;
+ smbnode_t *np;
+ vnode_t *vp;
+ int error;
+ char sep;
dnp = VTOSMB(dvp);
+ *vpp = NULL;
- ASSERT(*nm != '\0');
- ASSERT(curproc->p_zone == VTOSMI(dvp)->smi_zone);
+#ifdef DEBUG
+ smbfs_lookup_cache_calls++;
+#endif
/*
- * Lookup this name in the DNLC. If successful, then validate
- * the caches and then recheck the DNLC. The DNLC is rechecked
- * just in case this entry got invalidated during the call
- * to smbfsgetattr().
- * An assumption is being made that it is safe to say that a
- * file exists which may not on the server. Any operations to
- * the server will fail with ESTALE.
+ * First make sure we can get attributes for the
+ * directory. Cached attributes are OK here.
+ * If we removed or renamed the directory, this
+ * will return ENOENT. If someone else removed
+ * this directory or file, we'll find out when we
+ * try to open or get attributes.
*/
-
-#ifdef DEBUG
- smbfs_lookup_dnlc_lookups++;
-#endif
- vp = dnlc_lookup(dvp, nm);
- if (vp != NULL) {
- if (vp == DNLC_NO_VNODE && !vn_is_readonly(dvp))
- smbfs_attr_cacheremove(dnp);
- VN_RELE(vp);
- error = smbfsgetattr(dvp, &va, cr);
- if (error)
- return (error);
- vp = dnlc_lookup(dvp, nm);
- if (vp != NULL) {
- /*
- * NFS checks VEXEC access here,
- * but we've already done that
- * in the caller.
- */
- if (vp == DNLC_NO_VNODE) {
- VN_RELE(vp);
+ va.va_mask = AT_TYPE | AT_MODE;
+ error = smbfsgetattr(dvp, &va, cr);
+ if (error) {
#ifdef DEBUG
- smbfs_lookup_dnlc_neg_hits++;
+ smbfs_lookup_cache_error++;
#endif
- return (ENOENT);
- }
- *vpp = vp;
+ return (error);
+ }
+
+ /*
+ * Passing NULL smbfattr here so we will
+ * just look, not create.
+ */
+ sep = SMBFS_DNP_SEP(dnp);
+ np = smbfs_node_findcreate(dnp->n_mount,
+ dnp->n_rpath, dnp->n_rplen,
+ nm, nmlen, sep, NULL);
+ if (np == NULL) {
#ifdef DEBUG
- smbfs_lookup_dnlc_hits++;
+ smbfs_lookup_cache_miss++;
#endif
- return (0);
- }
+ return (0);
+ }
+
+ /*
+ * Found it. Attributes still valid?
+ */
+ vp = SMBTOV(np);
+ if (np->r_attrtime <= gethrtime()) {
+ /* stale */
#ifdef DEBUG
- smbfs_lookup_dnlc_disappears++;
+ smbfs_lookup_cache_stale++;
#endif
+ VN_RELE(vp);
+ return (0);
}
+
+ /*
+ * Success!
+ * Caller gets hold from smbfs_node_findcreate
+ */
#ifdef DEBUG
- else
- smbfs_lookup_dnlc_misses++;
+ smbfs_lookup_cache_hits++;
#endif
- *vpp = NULL;
-
+ *vpp = vp;
return (0);
}
-#endif /* USE_DNLC */
/*
* XXX
@@ -1805,7 +1749,7 @@ smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
/*
* NFS needs to go over the wire, just to be sure whether the
- * file exists or not. Using the DNLC can be dangerous in
+ * file exists or not. Using a cached result is dangerous in
* this case when making a decision regarding existence.
*
* The SMB protocol does NOT really need to go OTW here
@@ -1870,9 +1814,6 @@ smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
if (error)
goto out;
- /* remove possible negative entry from the dnlc */
- dnlc_remove(dvp, nm);
-
/*
* Now the code derived from Darwin,
* but with greater use of NT_CREATE
@@ -1894,7 +1835,9 @@ smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
disp = NTCREATEX_DISP_OPEN_IF;
}
xattr = (dnp->n_flag & N_XATTR) ? 1 : 0;
- error = smbfs_smb_create(dnp, name, nmlen, &scred, &fid, disp, xattr);
+ error = smbfs_smb_create(dnp,
+ name, nmlen, xattr,
+ disp, &scred, &fid);
if (error)
goto out;
@@ -1935,7 +1878,7 @@ smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
cerror = smbfs_smb_close(smi->smi_share, fid, NULL, &scred);
if (cerror)
- SMBERROR("error %d closing %s\\%s\n",
+ SMBVDEBUG("error %d closing %s\\%s\n",
cerror, dnp->n_rpath, name);
/*
@@ -1958,10 +1901,6 @@ smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
if (error)
goto out;
-#ifdef USE_DNLC
- dnlc_update(dvp, nm, vp);
-#endif
-
/* XXX invalidate pages if we truncated? */
/* Success! */
@@ -1970,9 +1909,9 @@ smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
out:
smb_credrele(&scred);
+ smbfs_rw_exit(&dnp->r_rwlock);
if (name != nm)
smbfs_name_free(name, nmlen);
- smbfs_rw_exit(&dnp->r_rwlock);
return (error);
}
@@ -2005,6 +1944,7 @@ smbfs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
dnp = VTOSMB(dvp);
if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp)))
return (EINTR);
+ smb_credinit(&scred, cr);
/*
* Verify access to the dirctory.
@@ -2031,48 +1971,46 @@ smbfs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
}
/*
- * First just remove the entry from the name cache, as it
- * is most likely the only entry for this vp.
- */
- dnlc_remove(dvp, nm);
-
- /*
- * If the file has a v_count > 1 then there may be more than one
- * entry in the name cache due multiple links or an open file,
- * but we don't have the real reference count so flush all
- * possible entries.
- */
- if (vp->v_count > 1)
- dnlc_purge_vp(vp);
-
- /*
* Now we have the real reference count on the vnode
+ * Do we have the file open?
*/
np = VTOSMB(vp);
mutex_enter(&np->r_statelock);
- if (vp->v_count > 1) {
+ if ((vp->v_count > 1) && (np->n_fidrefs > 0)) {
/*
* NFS does a rename on remove here.
* Probably not applicable for SMB.
* Like Darwin, just return EBUSY.
*
- * XXX: Todo - Ask the server to set the
+ * XXX: Todo - Use Trans2rename, and
+ * if that fails, ask the server to
* set the delete-on-close flag.
*/
mutex_exit(&np->r_statelock);
error = EBUSY;
} else {
+ smbfs_attrcache_rm_locked(np);
mutex_exit(&np->r_statelock);
- smb_credinit(&scred, cr);
error = smbfs_smb_delete(np, &scred, NULL, 0, 0);
- smb_credrele(&scred);
+ /*
+ * If the file should no longer exist, discard
+ * any cached attributes under this node.
+ */
+ switch (error) {
+ case 0:
+ case ENOENT:
+ case ENOTDIR:
+ smbfs_attrcache_prune(np);
+ break;
+ }
}
VN_RELE(vp);
out:
+ smb_credrele(&scred);
smbfs_rw_exit(&dnp->r_rwlock);
return (error);
@@ -2164,6 +2102,7 @@ smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
return (EINTR);
}
}
+ smb_credinit(&scred, cr);
/*
* No returns after this point (goto out)
*/
@@ -2238,35 +2177,6 @@ smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
}
/*
- * Purge the name cache of all references to this vnode
- * so that we can check the reference count to infer
- * whether it is active or not.
- */
- /*
- * First just remove the entry from the name cache, as it
- * is most likely the only entry for this vp.
- */
- dnlc_remove(ndvp, nnm);
- /*
- * If the file has a v_count > 1 then there may be more
- * than one entry in the name cache due multiple links
- * or an open file, but we don't have the real reference
- * count so flush all possible entries.
- */
- if (nvp->v_count > 1)
- dnlc_purge_vp(nvp);
- /*
- * when renaming directories to be a subdirectory of a
- * different parent, the dnlc entry for ".." will no
- * longer be valid, so it must be removed
- */
- if (ndvp != odvp) {
- if (ovp->v_type == VDIR) {
- dnlc_remove(ovp, "..");
- }
- }
-
- /*
* CIFS gives a SHARING_VIOLATION error when
* trying to rename onto an exising object,
* so try to remove the target first.
@@ -2285,7 +2195,7 @@ smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
*/
nnp = VTOSMB(nvp);
mutex_enter(&nnp->r_statelock);
- if (nvp->v_count > 2) {
+ if ((nvp->v_count > 2) && (nnp->n_fidrefs > 0)) {
/*
* The target file exists, is not the same as
* the source file, and is active. Other FS
@@ -2297,14 +2207,26 @@ smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
error = EBUSY;
goto out;
}
- mutex_exit(&nnp->r_statelock);
/*
* Target file is not active. Try to remove it.
*/
- smb_credinit(&scred, cr);
+ smbfs_attrcache_rm_locked(nnp);
+ mutex_exit(&nnp->r_statelock);
+
error = smbfs_smb_delete(nnp, &scred, NULL, 0, 0);
- smb_credrele(&scred);
+
+ /*
+ * Similar to smbfs_remove
+ */
+ switch (error) {
+ case 0:
+ case ENOENT:
+ case ENOTDIR:
+ smbfs_attrcache_prune(nnp);
+ break;
+ }
+
if (error)
goto out;
/*
@@ -2317,14 +2239,17 @@ smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
nvp = NULL;
} /* nvp */
- dnlc_remove(odvp, onm);
- dnlc_remove(ndvp, nnm);
-
onp = VTOSMB(ovp);
- smb_credinit(&scred, cr);
+ smbfs_attrcache_remove(onp);
+
error = smbfs_smb_rename(onp, ndnp, nnm, strlen(nnm), &scred);
- smb_credrele(&scred);
+ /*
+ * If the old name should no longer exist,
+ * discard any cached attributes under it.
+ */
+ if (error == 0)
+ smbfs_attrcache_prune(onp);
out:
if (nvp) {
@@ -2335,6 +2260,7 @@ out:
if (ovp)
VN_RELE(ovp);
+ smb_credrele(&scred);
smbfs_rw_exit(&odnp->r_rwlock);
smbfs_rw_exit(&ndnp->r_rwlock);
@@ -2393,9 +2319,6 @@ smbfs_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp,
if (error)
goto out;
- /* remove possible negative entry from the dnlc */
- dnlc_remove(dvp, nm);
-
error = smbfs_smb_mkdir(dnp, name, nmlen, &scred);
if (error)
goto out;
@@ -2410,10 +2333,6 @@ smbfs_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp,
if (error)
goto out;
-#ifdef USE_DNLC
- dnlc_update(dvp, nm, vp);
-#endif
-
if (name[0] == '.')
if ((hiderr = smbfs_smb_hideit(VTOSMB(vp), NULL, 0, &scred)))
SMBVDEBUG("hide failure %d\n", hiderr);
@@ -2498,28 +2417,20 @@ smbfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
goto out;
}
- /*
- * First just remove the entry from the name cache, as it
- * is most likely an entry for this vp.
- */
- dnlc_remove(dvp, nm);
+ smbfs_attrcache_remove(np);
+ error = smbfs_smb_rmdir(np, &scred);
/*
- * If there vnode reference count is greater than one, then
- * there may be additional references in the DNLC which will
- * need to be purged. First, trying removing the entry for
- * the parent directory and see if that removes the additional
- * reference(s). If that doesn't do it, then use dnlc_purge_vp
- * to completely remove any references to the directory which
- * might still exist in the DNLC.
+ * Similar to smbfs_remove
*/
- if (vp->v_count > 1) {
- dnlc_remove(vp, "..");
- if (vp->v_count > 1)
- dnlc_purge_vp(vp);
+ switch (error) {
+ case 0:
+ case ENOENT:
+ case ENOTDIR:
+ smbfs_attrcache_prune(np);
+ break;
}
- error = smbfs_smb_rmdir(np, &scred);
if (error)
goto out;
@@ -2527,7 +2438,7 @@ smbfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
dnp->n_flag |= NMODIFIED;
mutex_exit(&np->r_statelock);
smbfs_attr_touchdir(dnp);
- smb_rmhash(np);
+ smbfs_rmhash(np);
out:
if (vp) {
@@ -2636,15 +2547,6 @@ smbfs_readvdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp,
if (uio->uio_resid < dbufsiz)
return (EINVAL);
-#ifdef USE_DNLC
- /*
- * This dnlc_purge_vp ensures that name cache for this dir will be
- * current - it'll only have the items for which the smbfs_nget
- * MAKEENTRY happened.
- */
- if (smbfs_fastlookup)
- dnlc_purge_vp(vp);
-#endif
SMBVDEBUG("dirname='%s'\n", np->n_rpath);
smb_credinit(&scred, cr);
dp = kmem_alloc(dbufsiz, KM_SLEEP);
@@ -2757,20 +2659,19 @@ smbfs_readvdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp,
nmlen = SMB_MAXFNAMELEN;
SMBVDEBUG("Truncating name: %s\n", ctx->f_name);
}
-#ifdef NOT_YET
if (smbfs_fastlookup) {
+ /* See comment at smbfs_fastlookup above. */
if (smbfs_nget(vp, ctx->f_name, nmlen,
&ctx->f_attr, &newvp) == 0)
VN_RELE(newvp);
}
-#endif /* NOT_YET */
reclen = DIRENT64_RECLEN(nmlen);
bzero(dp, reclen);
dp->d_reclen = reclen;
bcopy(ctx->f_name, dp->d_name, nmlen);
dp->d_name[nmlen] = '\0';
- dp->d_ino = ctx->f_attr.fa_ino;
+ dp->d_ino = ctx->f_inum;
dp->d_off = offset + 1; /* See d_off comment above */
error = uiomove(dp, reclen, UIO_READ, uio);
if (error)
@@ -3000,9 +2901,10 @@ smbfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
case _PC_ACL_ENABLED:
/*
- * Always say "yes" here. Our _getsecattr
- * will build a trivial ACL when needed,
- * i.e. when server does not have ACLs.
+ * Always indicate that ACLs are enabled and
+ * that we support ACE_T format, otherwise
+ * libsec will ask for ACLENT_T format data
+ * which we don't support.
*/
*valp = _ACL_ACE_ENABLED;
break;
@@ -3019,8 +2921,11 @@ smbfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
return (EINVAL);
case _PC_TIMESTAMP_RESOLUTION:
- /* nanosecond timestamp resolution */
- *valp = 1L;
+ /*
+ * Windows times are tenths of microseconds
+ * (multiples of 100 nanoseconds).
+ */
+ *valp = 100L;
break;
default:
@@ -3036,7 +2941,9 @@ smbfs_getsecattr(vnode_t *vp, vsecattr_t *vsa, int flag, cred_t *cr,
{
vfs_t *vfsp;
smbmntinfo_t *smi;
- int error, uid, gid;
+ uid_t uid;
+ gid_t gid;
+ int error;
uint_t mask;
vfsp = vp->v_vfsp;
@@ -3059,12 +2966,9 @@ smbfs_getsecattr(vnode_t *vp, vsecattr_t *vsa, int flag, cred_t *cr,
if (mask == 0)
return (ENOSYS);
- /* XXX - access check ACE_READ_ACL? */
-
- if (smi->smi_fsattr & FILE_PERSISTENT_ACLS) {
+ if (smi->smi_flags & SMI_ACL)
error = smbfs_getacl(vp, vsa, &uid, &gid, flag, cr);
- /* XXX: Save uid/gid somewhere? */
- } else
+ else
error = ENOSYS;
if (error == ENOSYS)
@@ -3100,21 +3004,20 @@ smbfs_setsecattr(vnode_t *vp, vsecattr_t *vsa, int flag, cred_t *cr,
if (mask == 0)
return (ENOSYS);
- /*
- * If and when smbfs_access is extended, we can
- * check ACE_WRITE_ACL here instead. (XXX todo)
- * For now, in-line parts of smbfs_access,
- * i.e. only allow _setacl by the owner,
- * and check for read-only FS.
- */
if (vfsp->vfs_flag & VFS_RDONLY)
return (EROFS);
- if (crgetuid(cr) != smi->smi_args.uid)
- return (EACCES);
- if (smi->smi_fsattr & FILE_PERSISTENT_ACLS) {
+ /*
+ * Allow only the mount owner to do this.
+ * See comments at smbfs_access_rwx.
+ */
+ error = secpolicy_vnode_setdac(cr, smi->smi_uid);
+ if (error != 0)
+ return (error);
+
+ if (smi->smi_flags & SMI_ACL)
error = smbfs_setacl(vp, vsa, -1, -1, flag, cr);
- } else
+ else
error = ENOSYS;
return (error);
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c
index ad68648d12..83c91fecc0 100644
--- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c
@@ -41,7 +41,6 @@
#include <sys/kmem.h>
#include <sys/stat.h>
#include <sys/cmn_err.h>
-#include <sys/dnlc.h>
#include <sys/u8_textprep.h>
#include <netsmb/smb_osdep.h>
@@ -86,12 +85,20 @@ smbfs_get_xattrdir(vnode_t *pvp, vnode_t **vpp, cred_t *cr, int flags)
pnp = VTOSMB(pvp);
- xvp = smbfs_make_node(pvp->v_vfsp,
- pnp->n_rpath, pnp->n_rplen,
- NULL, 0, ':', NULL);
- ASSERT(xvp);
+ /*
+ * We don't allow recursive extended attributes
+ * (xattr under xattr dir.) so the "parent" node
+ * (pnp) must NOT be an XATTR directory or file.
+ */
+ if (pnp->n_flag & N_XATTR)
+ return (EINVAL);
+
+ xnp = smbfs_node_findcreate(pnp->n_mount,
+ pnp->n_rpath, pnp->n_rplen, NULL, 0, ':',
+ &smbfs_fattr0); /* force create */
+ ASSERT(xnp != NULL);
+ xvp = SMBTOV(xnp);
/* Note: xvp has a VN_HOLD, which our caller expects. */
- xnp = VTOSMB(xvp);
/* If it's a new node, initialize. */
if (xvp->v_type == VNON) {
@@ -122,9 +129,11 @@ int
smbfs_xa_parent(vnode_t *vp, vnode_t **vpp)
{
smbnode_t *np = VTOSMB(vp);
- vnode_t *pvp;
+ smbnode_t *pnp;
int rplen;
+ *vpp = NULL;
+
if ((np->n_flag & N_XATTR) == 0)
return (EINVAL);
@@ -163,13 +172,12 @@ smbfs_xa_parent(vnode_t *vp, vnode_t **vpp)
}
}
- pvp = smbfs_make_node(vp->v_vfsp,
- np->n_rpath, rplen,
- NULL, 0, 0, NULL);
- ASSERT(pvp);
-
- /* Note: pvp has a VN_HOLD from _make_node */
- *vpp = pvp;
+ pnp = smbfs_node_findcreate(np->n_mount,
+ np->n_rpath, rplen, NULL, 0, 0,
+ &smbfs_fattr0); /* force create */
+ ASSERT(pnp != NULL);
+ /* Note: have VN_HOLD from smbfs_node_findcreate */
+ *vpp = SMBTOV(pnp);
return (0);
}