diff options
Diffstat (limited to 'usr/src/uts/common/fs/smbclnt/smbfs')
-rw-r--r-- | usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h | 55 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbclnt/smbfs/smbfs_acl.c | 130 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c | 448 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c | 392 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h | 247 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c | 1449 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c | 205 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h | 88 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c | 912 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c | 208 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c | 653 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c | 36 |
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); } |