diff options
author | Gordon Ross <Gordon.Ross@Sun.COM> | 2009-12-16 15:03:38 -0500 |
---|---|---|
committer | Gordon Ross <Gordon.Ross@Sun.COM> | 2009-12-16 15:03:38 -0500 |
commit | 02d09e03eb27f3a2dc299de704e45dae5173f43f (patch) | |
tree | d01a29c58a8a455627b355abb83addad11802bd6 /usr/src/uts/common | |
parent | aab2fe4104e428e5213f84aee65b9905ec97cf9a (diff) | |
download | illumos-joyent-02d09e03eb27f3a2dc299de704e45dae5173f43f.tar.gz |
6650611 Attribute cache logic needs improvement
6876185 common I/F for net message build/parse in kernel vs user code
6891728 syslog shows: smbfs_close: error 9 closing /dirname
6906037 smbfs_mount() doesn't ASSERT the return value of smbfs_make_node()
6607536 the size of a dir on smbfs is not correct
6648146 smbfs should implement reclaim from node cache
--HG--
rename : usr/src/lib/libsmbfs/smb/acl_conv.c => usr/src/common/smbclnt/smbfs_ntacl.c
rename : usr/src/lib/libsmbfs/smb/acl_nt.h => usr/src/common/smbclnt/smbfs_ntacl.h
Diffstat (limited to 'usr/src/uts/common')
31 files changed, 3161 insertions, 2930 deletions
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 1feed57654..8681c65e96 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -1262,15 +1262,17 @@ UFS_OBJS += ufs_alloc.o ufs_bmap.o ufs_dir.o ufs_xattr.o \ lufs_log.o lufs_map.o lufs_top.o lufs_debug.o VSCAN_OBJS += vscan_drv.o vscan_svc.o vscan_door.o -NSMB_OBJS += smb_conn.o smb_dev.o smb_iod.o smb_rq.o \ - smb_sign.o smb_smb.o smb_tran.o smb_trantcp.o \ - smb_usr.o smb_subrs.o subr_mchain.o smb_pass.o +NSMB_OBJS += smb_conn.o smb_dev.o smb_iod.o smb_pass.o \ + smb_rq.o smb_sign.o smb_smb.o smb_subrs.o \ + smb_time.o smb_tran.o smb_trantcp.o smb_usr.o \ + subr_mchain.o +SMBFS_COMMON_OBJS += smbfs_ntacl.o SMBFS_OBJS += smbfs_vfsops.o smbfs_vnops.o smbfs_node.o \ smbfs_acl.o smbfs_client.o smbfs_smb.o \ smbfs_subr.o smbfs_subr2.o \ - smbfs_rwlock.o smbfs_xattr.o - + smbfs_rwlock.o smbfs_xattr.o \ + $(SMBFS_COMMON_OBJS) # # LVM modules diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules index d87dd585e2..e1a301b569 100644 --- a/usr/src/uts/common/Makefile.rules +++ b/usr/src/uts/common/Makefile.rules @@ -284,6 +284,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/sharefs/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(COMMONBASE)/smbclnt/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/smbclnt/netsmb/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -1707,6 +1711,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/proc/%.c $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/sharefs/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(COMMONBASE)/smbclnt/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/smbclnt/netsmb/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c index f1dcd57ea3..d1e7efd60a 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c @@ -58,12 +58,7 @@ #include <sys/atomic.h> #include <sys/u8_textprep.h> -#ifdef APPLE -#include <sys/smb_apple.h> -#include <sys/smb_iconv.h> -#else #include <netsmb/smb_osdep.h> -#endif #include <netsmb/smb.h> #include <netsmb/smb_conn.h> @@ -620,7 +615,7 @@ smb_share_gone(struct smb_connobj *cp) smb_credinit(&scred, NULL); smb_iod_shutdown_share(ssp); - smb_smb_treedisconnect(ssp, &scred); + (void) smb_smb_treedisconnect(ssp, &scred); smb_credrele(&scred); } diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h index df85a77d11..83138776e4 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h @@ -81,13 +81,7 @@ typedef struct smb_cred { #define SMBS_RECONNECTING 0x0002 #define SMBS_CONNECTED 0x0004 #define SMBS_TCON_WAIT 0x0008 -#define SMBS_1980 0x0010 -/* - * ^ This partition can't handle dates before 1980. It's probably a FAT - * partition but could be some other ancient FS type - */ -#define SMBS_RESUMEKEYS 0x0020 /* must use resume keys */ -#define SMBS_LONGNAMES 0x0040 /* share can use long names */ +#define SMBS_FST_FAT 0x0010 /* share FS Type is FAT */ /* * Note: the common "obj" level uses this GONE flag by * the name SMBO_GONE. Keep this alias as a reminder. @@ -274,7 +268,7 @@ typedef struct smb_fscb { void (*fscb_up)(smb_share_t *); } smb_fscb_t; /* Install the above vector, or pass NULL to clear it. */ -int smb_fscb_set(smb_fscb_t *); +void smb_fscb_set(smb_fscb_t *); /* * The driver per open instance object. @@ -331,11 +325,11 @@ int smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags); int smb_iod_create(smb_vc_t *vcp); int smb_iod_destroy(smb_vc_t *vcp); int smb_iod_connect(smb_vc_t *vcp); -int smb_iod_disconnect(smb_vc_t *vcp); +void smb_iod_disconnect(smb_vc_t *vcp); int smb_iod_addrq(struct smb_rq *rqp); int smb_iod_multirq(struct smb_rq *rqp); int smb_iod_waitrq(struct smb_rq *rqp); -int smb_iod_removerq(struct smb_rq *rqp); +void smb_iod_removerq(struct smb_rq *rqp); void smb_iod_shutdown_share(smb_share_t *ssp); void smb_iod_sendall(smb_vc_t *); diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c index 8ec7daef1c..61b4088927 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c @@ -66,12 +66,7 @@ #include <sys/types.h> #include <sys/zone.h> -#ifdef APPLE -#include <sys/smb_apple.h> -#else #include <netsmb/smb_osdep.h> -#endif - #include <netsmb/mchain.h> /* for "htoles()" */ #include <netsmb/smb.h> @@ -182,7 +177,7 @@ _init(void) { int error; - ddi_soft_state_init(&statep, sizeof (smb_dev_t), 1); + (void) ddi_soft_state_init(&statep, sizeof (smb_dev_t), 1); /* Can initialize some mutexes also. */ mutex_init(&dev_lck, NULL, MUTEX_DRIVER, NULL); @@ -198,6 +193,9 @@ _init(void) /* Initialize password Key chain DB. */ smb_pkey_init(); + /* Time conversion stuff. */ + smb_time_init(); + /* Initialize crypto mechanisms. */ smb_crypto_mech_init(); @@ -247,6 +245,9 @@ _fini(void) (void) zone_key_delete(nsmb_zone_key); + /* Time conversion stuff. */ + smb_time_fini(); + /* Destroy password Key chain DB. */ smb_pkey_fini(); @@ -384,7 +385,7 @@ nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int flags, /* model.h */ err = 0; switch (cmd) { case SMBIOC_GETVERS: - ddi_copyout(&nsmb_version, (void *)arg, + (void) ddi_copyout(&nsmb_version, (void *)arg, sizeof (nsmb_version), flags); break; diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c index f6bba8380a..e7cac1aa75 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c @@ -64,11 +64,7 @@ #include <sys/zone.h> #include <sys/sdt.h> -#ifdef APPLE -#include <sys/smb_apple.h> -#else #include <netsmb/smb_osdep.h> -#endif #include <netsmb/smb.h> #include <netsmb/smb_conn.h> @@ -85,11 +81,10 @@ int smb_iod_send_echo(smb_vc_t *); * can't unload until all the mounts are gone. */ static smb_fscb_t *fscb; -int +void smb_fscb_set(smb_fscb_t *cb) { fscb = cb; - return (0); } static void @@ -161,7 +156,7 @@ smb_iod_invrq(struct smb_vc *vcp) * * Forcibly kill the connection and IOD. */ -int +void smb_iod_disconnect(struct smb_vc *vcp) { @@ -194,8 +189,6 @@ smb_iod_disconnect(struct smb_vc *vcp) vcp->iod_thr != curthread) { tsignal(vcp->iod_thr, SIGKILL); } - - return (0); } /* @@ -700,7 +693,7 @@ smb_iod_multirq(struct smb_rq *rqp) } -int +void smb_iod_removerq(struct smb_rq *rqp) { struct smb_vc *vcp = rqp->sr_vc; @@ -716,8 +709,6 @@ smb_iod_removerq(struct smb_rq *rqp) #endif TAILQ_REMOVE(&vcp->iod_rqlist, rqp, sr_link); rw_exit(&vcp->iod_rqlock); - - return (0); } @@ -789,7 +780,6 @@ smb_iod_waitrq(struct smb_rq *rqp) tmo1 = SEC_TO_TICK(smb_timo_notice); else tmo1 = 0; - tmo2 = ddi_get_lbolt() + SEC_TO_TICK(rqp->sr_timo); /* @@ -914,14 +904,13 @@ void smb_iod_sendall(smb_vc_t *vcp) { struct smb_rq *rqp; - int error, save_newrq, muxcnt; + int error, muxcnt; /* * Clear "newrq" to make sure threads adding * new requests will run this function again. */ rw_enter(&vcp->iod_rqlock, RW_WRITER); - save_newrq = vcp->iod_newrq; vcp->iod_newrq = 0; /* @@ -931,9 +920,6 @@ smb_iod_sendall(smb_vc_t *vcp) */ rw_downgrade(&vcp->iod_rqlock); - /* Expect to find about this many requests. */ - SMBIODEBUG("top, save_newrq=%d\n", save_newrq); - /* * Serialize to prevent multiple senders. * Note lock order: iod_rqlock, vc_sendlock diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.c index 4079482538..c26d80e48c 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.c @@ -166,7 +166,7 @@ smb_pkey_init() void smb_pkey_fini() { - smb_pkey_deluid((uid_t)-1, kcred); + (void) smb_pkey_deluid((uid_t)-1, kcred); avl_destroy(&smb_ptd); mutex_destroy(&smb_ptd_lock); } @@ -187,15 +187,14 @@ smb_pkey_idle() return ((n) ? EBUSY : 0); } -int -smb_node_delete(smb_passid_t *tmp) +static void +smb_pkey_delete(smb_passid_t *tmp) { ASSERT(MUTEX_HELD(&smb_ptd_lock)); avl_remove(&smb_ptd, tmp); strfree(tmp->srvdom); strfree(tmp->username); kmem_free(tmp, sizeof (*tmp)); - return (0); } @@ -225,7 +224,7 @@ smb_pkey_del(smbioc_pk_t *pk, cred_t *cr) mutex_enter(&smb_ptd_lock); if ((cpid = (smb_passid_t *)avl_find(&smb_ptd, tmp, &where)) != NULL) { - smb_node_delete(cpid); + smb_pkey_delete(cpid); } mutex_exit(&smb_ptd_lock); @@ -259,7 +258,7 @@ smb_pkey_deluid(uid_t ioc_uid, cred_t *cr) /* * Delete the node. */ - smb_node_delete(tmp); + smb_pkey_delete(tmp); } } mutex_exit(&smb_ptd_lock); @@ -306,7 +305,7 @@ smb_pkey_add(smbioc_pk_t *pk, cred_t *cr) /* If it already exists, delete it. */ ret = smb_pkey_check(pk, cr); if (ret == 0) { - smb_pkey_del(pk, cr); + (void) smb_pkey_del(pk, cr); } mutex_enter(&smb_ptd_lock); diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c index 0985d58ff4..0ce241b0ce 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c @@ -39,6 +39,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/time.h> #include <sys/kmem.h> #include <sys/proc.h> #include <sys/lock.h> @@ -261,17 +262,14 @@ smb_rq_simple_timed(struct smb_rq *rqp, int timeout) break; SMBRQ_LOCK(rqp); if (rqp->sr_share) { - cv_reltimedwait(&rqp->sr_cond, &(rqp)->sr_lock, - (hz * SMB_RCNDELAY), TR_CLOCK_TICK); + (void) cv_reltimedwait(&rqp->sr_cond, &(rqp)->sr_lock, + SEC_TO_TICK(SMB_RCNDELAY), TR_CLOCK_TICK); } else { - delay(ddi_get_lbolt() + (hz * SMB_RCNDELAY)); + delay(SEC_TO_TICK(SMB_RCNDELAY)); } SMBRQ_UNLOCK(rqp); rqp->sr_rexmit--; -#ifdef XXX - timeout *= 2; -#endif } return (error); } @@ -469,8 +467,10 @@ smb_rq_reply(struct smb_rq *rqp) u_int8_t tb; int error, rperror = 0; - if (rqp->sr_timo == SMBNOREPLYWAIT) - return (smb_iod_removerq(rqp)); + if (rqp->sr_timo == SMBNOREPLYWAIT) { + smb_iod_removerq(rqp); + return (0); + } error = smb_iod_waitrq(rqp); if (error) @@ -534,10 +534,6 @@ smb_rq_reply(struct smb_rq *rqp) error = md_get_uint16le(mdp, &rqp->sr_rpuid); error = md_get_uint16le(mdp, &rqp->sr_rpmid); - SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x, E: %d:%d\n", - rqp->sr_rpmid, rqp->sr_rppid, rqp->sr_rpuid, rqp->sr_rptid, - rqp->sr_errclass, rqp->sr_serror); - return ((error) ? error : rperror); } @@ -969,7 +965,7 @@ smb_t2_request_int(struct smb_t2rq *t2p) m = t2p->t2_tdata.mb_top; if (m) { md_initm(&mbdata, m); /* do not free it! */ - totdcount = m_fixhdr(m); + totdcount = m_fixhdr(m); if (totdcount > 0xffff) return (EINVAL); } else @@ -1045,12 +1041,14 @@ smb_t2_request_int(struct smb_t2rq *t2p) smb_rq_bstart(rqp); if (t2p->t_name) { /* Put the string and terminating null. */ - smb_put_dmem(mbp, vcp, t2p->t_name, nmlen + 1, + error = smb_put_dmem(mbp, vcp, t2p->t_name, nmlen + 1, SMB_CS_NONE, NULL); } else { /* nmsize accounts for padding, char size. */ - mb_put_mem(mbp, NULL, nmsize, MB_MZERO); + error = mb_put_mem(mbp, NULL, nmsize, MB_MZERO); } + if (error) + goto freerq; len = mb_fixhdr(mbp); if (txpcount) { mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); @@ -1153,12 +1151,10 @@ smb_t2_request_int(struct smb_t2rq *t2p) goto bad; mdp = &t2p->t2_rdata; if (mdp->md_top) { - m_fixhdr(mdp->md_top); md_initm(mdp, mdp->md_top); } mdp = &t2p->t2_rparam; if (mdp->md_top) { - m_fixhdr(mdp->md_top); md_initm(mdp, mdp->md_top); } bad: @@ -1366,12 +1362,10 @@ smb_nt_request_int(struct smb_ntrq *ntp) goto bad; mdp = &ntp->nt_rdata; if (mdp->md_top) { - m_fixhdr(mdp->md_top); md_initm(mdp, mdp->md_top); } mdp = &ntp->nt_rparam; if (mdp->md_top) { - m_fixhdr(mdp->md_top); md_initm(mdp, mdp->md_top); } bad: @@ -1409,10 +1403,10 @@ smb_t2_request(struct smb_t2rq *t2p) break; mutex_enter(&(t2p)->t2_lock); if (t2p->t2_share) { - cv_reltimedwait(&t2p->t2_cond, &(t2p)->t2_lock, - (hz * SMB_RCNDELAY), TR_CLOCK_TICK); + (void) cv_reltimedwait(&t2p->t2_cond, &(t2p)->t2_lock, + SEC_TO_TICK(SMB_RCNDELAY), TR_CLOCK_TICK); } else { - delay(ddi_get_lbolt() + (hz * SMB_RCNDELAY)); + delay(SEC_TO_TICK(SMB_RCNDELAY)); } mutex_exit(&(t2p)->t2_lock); } @@ -1442,11 +1436,11 @@ smb_nt_request(struct smb_ntrq *ntp) break; mutex_enter(&(ntp)->nt_lock); if (ntp->nt_share) { - cv_reltimedwait(&ntp->nt_cond, &(ntp)->nt_lock, - (hz * SMB_RCNDELAY), TR_CLOCK_TICK); + (void) cv_reltimedwait(&ntp->nt_cond, &(ntp)->nt_lock, + SEC_TO_TICK(SMB_RCNDELAY), TR_CLOCK_TICK); } else { - delay(ddi_get_lbolt() + (hz * SMB_RCNDELAY)); + delay(SEC_TO_TICK(SMB_RCNDELAY)); } mutex_exit(&(ntp)->nt_lock); } diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c index 91c450bb23..140d390670 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c @@ -189,7 +189,7 @@ smb_compute_MAC(struct smb_vc *vcp, mblk_t *mp, /* * Sign a request with HMAC-MD5. */ -int +void smb_rq_sign(struct smb_rq *rqp) { struct smb_vc *vcp = rqp->sr_vc; @@ -203,7 +203,7 @@ smb_rq_sign(struct smb_rq *rqp) */ if (MBLKL(mp) < SMB_HDRLEN) { if (!pullupmsg(mp, SMB_HDRLEN)) - return (0); + return; } sigloc = mp->b_rptr + SMBSIGOFF; @@ -214,7 +214,7 @@ smb_rq_sign(struct smb_rq *rqp) * This happens with SPNEGO, NTLMSSP, ... */ bcopy("BSRSPLY", sigloc, 8); - return (0); + return; } /* @@ -225,9 +225,7 @@ smb_rq_sign(struct smb_rq *rqp) if (status != CRYPTO_SUCCESS) { SMBSDEBUG("Crypto error %d", status); bzero(sigloc, SMBSIGLEN); - return (ENOTSUP); } - return (0); } /* @@ -240,8 +238,7 @@ smb_rq_verify(struct smb_rq *rqp) mblk_t *mp = rqp->sr_rp.md_top; uint8_t sigbuf[SMBSIGLEN]; uint8_t *sigloc; - int status; - int fudge; + int fudge, rsn, status; /* * Note vc_mackey and vc_mackeylen gets filled in by @@ -266,9 +263,11 @@ smb_rq_verify(struct smb_rq *rqp) } sigloc = mp->b_rptr + SMBSIGOFF; - SMBSDEBUG("sr_rseqno = 0x%x\n", rqp->sr_rseqno); - - status = smb_compute_MAC(vcp, mp, rqp->sr_rseqno, sigbuf); + /* + * Compute the expected signature in sigbuf. + */ + rsn = rqp->sr_rseqno; + status = smb_compute_MAC(vcp, mp, rsn, sigbuf); if (status != CRYPTO_SUCCESS) { SMBSDEBUG("Crypto error %d", status); /* @@ -293,10 +292,10 @@ smb_rq_verify(struct smb_rq *rqp) * of the sequence # has gotten a bit out of sync. */ for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) { - smb_compute_MAC(vcp, mp, rqp->sr_rseqno + fudge, sigbuf); + (void) smb_compute_MAC(vcp, mp, rsn + fudge, sigbuf); if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) break; - smb_compute_MAC(vcp, mp, rqp->sr_rseqno - fudge, sigbuf); + (void) smb_compute_MAC(vcp, mp, rsn - fudge, sigbuf); if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) { fudge = -fudge; break; @@ -304,7 +303,7 @@ smb_rq_verify(struct smb_rq *rqp) } if (fudge <= nsmb_signing_fudge) { SMBSDEBUG("sr_rseqno=%d, but %d would have worked\n", - rqp->sr_rseqno, rqp->sr_rseqno + fudge); + rsn, rsn + fudge); } #endif return (EBADRPC); diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c index fce18f74ff..5fa43ece4b 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c @@ -51,12 +51,7 @@ #include <sys/note.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> @@ -84,95 +79,16 @@ int smb_timo_read = 45; int smb_timo_write = 60; /* was SMBWRTTIMO */ int smb_timo_append = 90; -/* - * Debug/test feature to disable NTMLv2. - * Set this to zero to skip NTLMv2 - */ -int nsmb_enable_ntlmv2 = 1; - -static int smb_smb_read(struct smb_share *ssp, u_int16_t fid, +static int smb_smb_read(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo); -static int smb_smb_write(struct smb_share *ssp, u_int16_t fid, +static int smb_smb_write(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo); -static int smb_smb_readx(struct smb_share *ssp, u_int16_t fid, +static int smb_smb_readx(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo); -static int smb_smb_writex(struct smb_share *ssp, u_int16_t fid, +static int smb_smb_writex(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo); -struct smb_dialect { - int d_id; - const char *d_name; -}; - - -/* - * Number of seconds between 1970 and 1601 year - */ -const u_int64_t DIFF1970TO1601 = 11644473600ULL; - -void -smb_time_local2server(struct timespec *tsp, int tzoff, long *seconds) -{ - /* - * XXX - what if we connected to the server when it was in - * daylight savings/summer time and we've subsequently switched - * to standard time, or vice versa, so that the time zone - * offset we got from the server is now wrong? - */ - *seconds = tsp->tv_sec - tzoff * 60; - /* - tz.tz_minuteswest * 60 - (wall_cmos_clock ? adjkerntz : 0) */ -} - -void -smb_time_server2local(ulong_t seconds, int tzoff, struct timespec *tsp) -{ - /* - * XXX - what if we connected to the server when it was in - * daylight savings/summer time and we've subsequently switched - * to standard time, or vice versa, so that the time zone - * offset we got from the server is now wrong? - */ - tsp->tv_sec = seconds + tzoff * 60; - /* + tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0); */ - tsp->tv_nsec = 0; -} - -/* - * Time from server comes as UTC, so no need to use tz - */ -/*ARGSUSED*/ -void -smb_time_NT2local(u_int64_t nsec, int tzoff, struct timespec *tsp) -{ - smb_time_server2local(nsec / 10000000 - DIFF1970TO1601, 0, tsp); -} - -/*ARGSUSED*/ -void -smb_time_local2NT(struct timespec *tsp, int tzoff, u_int64_t *nsec) -{ - long seconds; - - smb_time_local2server(tsp, 0, &seconds); - *nsec = (((u_int64_t)(seconds) & ~1) + DIFF1970TO1601) * - (u_int64_t)10000000; -} - -#if defined(NOICONVSUPPORT) || defined(lint) -extern int iconv_open(const char *to, const char *from, void **handle); -extern int iconv_close(void *handle); -#endif - -/* - * Moved to user space helper: - * smb_smb_negotiate() - * smb_smb_ssnsetup() - * smb_smb_ssnclose() - * smb_share_typename() - */ - - int smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred) { @@ -208,7 +124,7 @@ smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred) */ unc_len = 4 + strlen(vcp->vc_srvname) + strlen(ssp->ss_name); unc_name = kmem_alloc(unc_len, KM_SLEEP); - snprintf(unc_name, unc_len, "\\\\%s\\%s", + (void) snprintf(unc_name, unc_len, "\\\\%s\\%s", vcp->vc_srvname, ssp->ss_name); /* @@ -274,14 +190,17 @@ smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred) error = EBADRPC; goto out; } - md_get_uint16le(mdp, NULL); /* AndX cmd */ - md_get_uint16le(mdp, NULL); /* AndX off */ - md_get_uint16le(mdp, &options); /* option bits (DFS, search) */ - md_get_uint16le(mdp, &bcnt); /* byte count */ + md_get_uint16le(mdp, NULL); /* AndX cmd */ + md_get_uint16le(mdp, NULL); /* AndX off */ + md_get_uint16le(mdp, &options); /* option bits (DFS, search) */ + error = md_get_uint16le(mdp, &bcnt); /* byte count */ + if (error) + goto out; /* * Get the returned share type string, - * i.e. "IPC" or whatever. + * i.e. "IPC" or whatever. Don't care + * if we get an error reading the type. */ tlen = sizeof (ssp->ss_type_ret); bzero(ssp->ss_type_ret, tlen--); @@ -476,53 +395,61 @@ smb_smb_readx(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, smb_rq_wend(rqp); smb_rq_bstart(rqp); smb_rq_bend(rqp); - do { - if (timo == 0) - timo = smb_timo_read; - error = smb_rq_simple_timed(rqp, timo); - if (error) - break; - smb_rq_getreply(rqp, &mdp); - md_get_uint8(mdp, &wc); - if (wc != 12) { - error = EBADRPC; - break; - } - md_get_uint8(mdp, NULL); - md_get_uint8(mdp, NULL); - md_get_uint16le(mdp, NULL); - md_get_uint16le(mdp, NULL); - md_get_uint16le(mdp, NULL); /* data compaction mode */ - md_get_uint16le(mdp, NULL); - md_get_uint16le(mdp, &lenlo); /* data len ret. */ - md_get_uint16le(mdp, &doff); /* data offset */ - md_get_uint16le(mdp, &lenhi); - rlen = (lenhi << 16) | lenlo; - md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM); - md_get_uint16le(mdp, NULL); /* ByteCount */ - /* - * Does the data offset indicate padding? - * Add up the gets above, we have: - */ - off = SMB_HDRLEN + 3 + (12 * 2); /* =59 */ - if (doff > off) /* pad byte(s)? */ - md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM); - if (rlen == 0) { - *lenp = rlen; - break; - } - /* paranoid */ - if (rlen > *lenp) { - SMBSDEBUG("bad server! rlen %d, len %d\n", - rlen, *lenp); - rlen = *lenp; - } - error = md_get_uio(mdp, uiop, rlen); - if (error) - break; + + if (timo == 0) + timo = smb_timo_read; + error = smb_rq_simple_timed(rqp, timo); + if (error) + goto out; + + smb_rq_getreply(rqp, &mdp); + error = md_get_uint8(mdp, &wc); + if (error) + goto out; + if (wc != 12) { + error = EBADRPC; + goto out; + } + md_get_uint8(mdp, NULL); + md_get_uint8(mdp, NULL); + md_get_uint16le(mdp, NULL); + md_get_uint16le(mdp, NULL); + md_get_uint16le(mdp, NULL); /* data compaction mode */ + md_get_uint16le(mdp, NULL); + md_get_uint16le(mdp, &lenlo); /* data len ret. */ + md_get_uint16le(mdp, &doff); /* data offset */ + md_get_uint16le(mdp, &lenhi); + rlen = (lenhi << 16) | lenlo; + md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM); + error = md_get_uint16le(mdp, NULL); /* ByteCount */ + if (error) + goto out; + /* + * Does the data offset indicate padding? + * The current offset is a constant, found + * by counting the md_get_ calls above. + */ + off = SMB_HDRLEN + 3 + (12 * 2); /* =59 */ + if (doff > off) /* pad byte(s)? */ + md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM); + if (rlen == 0) { *lenp = rlen; - /*LINTED*/ - } while (0); + goto out; + } + /* paranoid */ + if (rlen > *lenp) { + SMBSDEBUG("bad server! rlen %d, len %d\n", + rlen, *lenp); + rlen = *lenp; + } + error = md_get_uio(mdp, uiop, rlen); + if (error) + goto out; + + /* Success */ + *lenp = rlen; + +out: smb_rq_done(rqp); return (error); } @@ -563,34 +490,39 @@ smb_smb_writex(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, mb_put_uint32le(mbp, offhi); /* offset (high part) */ smb_rq_wend(rqp); smb_rq_bstart(rqp); - do { - mb_put_uint8(mbp, 0); /* pad byte */ - error = mb_put_uio(mbp, uiop, *lenp); - if (error) - break; - smb_rq_bend(rqp); - if (timo == 0) - timo = smb_timo_write; - error = smb_rq_simple_timed(rqp, timo); - if (error) - break; - smb_rq_getreply(rqp, &mdp); - md_get_uint8(mdp, &wc); - if (wc != 6) { - error = EBADRPC; - break; - } - md_get_uint8(mdp, NULL); /* andx cmd */ - md_get_uint8(mdp, NULL); /* reserved */ - md_get_uint16le(mdp, NULL); /* andx offset */ - md_get_uint16le(mdp, &lenlo); /* data len ret. */ - md_get_uint16le(mdp, NULL); /* remaining */ - md_get_uint16le(mdp, &lenhi); - rlen = (lenhi << 16) | lenlo; - *lenp = rlen; - /*LINTED*/ - } while (0); + mb_put_uint8(mbp, 0); /* pad byte */ + error = mb_put_uio(mbp, uiop, *lenp); + if (error) + goto out; + smb_rq_bend(rqp); + if (timo == 0) + timo = smb_timo_write; + error = smb_rq_simple_timed(rqp, timo); + if (error) + goto out; + smb_rq_getreply(rqp, &mdp); + error = md_get_uint8(mdp, &wc); + if (error) + goto out; + if (wc != 6) { + error = EBADRPC; + goto out; + } + md_get_uint8(mdp, NULL); /* andx cmd */ + md_get_uint8(mdp, NULL); /* reserved */ + md_get_uint16le(mdp, NULL); /* andx offset */ + md_get_uint16le(mdp, &lenlo); /* data len ret. */ + md_get_uint16le(mdp, NULL); /* remaining */ + error = md_get_uint16le(mdp, &lenhi); + if (error) + goto out; + + /* Success */ + rlen = (lenhi << 16) | lenlo; + *lenp = rlen; + +out: smb_rq_done(rqp); return (error); } @@ -626,44 +558,50 @@ smb_smb_read(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, smb_rq_wend(rqp); smb_rq_bstart(rqp); smb_rq_bend(rqp); - do { - if (timo == 0) - timo = smb_timo_read; - error = smb_rq_simple_timed(rqp, timo); - if (error) - break; - smb_rq_getreply(rqp, &mdp); - md_get_uint8(mdp, &wc); - if (wc != 5) { - error = EBADRPC; - break; - } - md_get_uint16le(mdp, &rcnt); /* ret. count */ - md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM); /* res. */ - md_get_uint16le(mdp, &bc); /* byte count */ - md_get_uint8(mdp, NULL); /* buffer format */ - md_get_uint16le(mdp, &dlen); /* data len */ - if (dlen < rcnt) { - SMBSDEBUG("oops: dlen=%d rcnt=%d\n", - (int)dlen, (int)rcnt); - rcnt = dlen; - } - if (rcnt == 0) { - *lenp = 0; - break; - } - /* paranoid */ - if (rcnt > cnt) { - SMBSDEBUG("bad server! rcnt %d, cnt %d\n", - (int)rcnt, (int)cnt); - rcnt = cnt; - } - error = md_get_uio(mdp, uiop, (int)rcnt); - if (error) - break; - *lenp = (int)rcnt; - /*LINTED*/ - } while (0); + + if (timo == 0) + timo = smb_timo_read; + error = smb_rq_simple_timed(rqp, timo); + if (error) + goto out; + smb_rq_getreply(rqp, &mdp); + error = md_get_uint8(mdp, &wc); + if (error) + goto out; + if (wc != 5) { + error = EBADRPC; + goto out; + } + md_get_uint16le(mdp, &rcnt); /* ret. count */ + md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM); /* res. */ + md_get_uint16le(mdp, &bc); /* byte count */ + md_get_uint8(mdp, NULL); /* buffer format */ + error = md_get_uint16le(mdp, &dlen); /* data len */ + if (error) + goto out; + if (dlen < rcnt) { + SMBSDEBUG("oops: dlen=%d rcnt=%d\n", + (int)dlen, (int)rcnt); + rcnt = dlen; + } + if (rcnt == 0) { + *lenp = 0; + goto out; + } + /* paranoid */ + if (rcnt > cnt) { + SMBSDEBUG("bad server! rcnt %d, cnt %d\n", + (int)rcnt, (int)cnt); + rcnt = cnt; + } + error = md_get_uio(mdp, uiop, (int)rcnt); + if (error) + goto out; + + /* success */ + *lenp = (int)rcnt; + +out: smb_rq_done(rqp); return (error); } @@ -700,26 +638,30 @@ smb_smb_write(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, smb_rq_bstart(rqp); mb_put_uint8(mbp, SMB_DT_DATA); mb_put_uint16le(mbp, cnt); - do { - error = mb_put_uio(mbp, uiop, *lenp); - if (error) - break; - smb_rq_bend(rqp); - if (timo == 0) - timo = smb_timo_write; - error = smb_rq_simple_timed(rqp, timo); - if (error) - break; - smb_rq_getreply(rqp, &mdp); - md_get_uint8(mdp, &wc); - if (wc != 1) { - error = EBADRPC; - break; - } - md_get_uint16le(mdp, &rcnt); - *lenp = rcnt; - /*LINTED*/ - } while (0); + + error = mb_put_uio(mbp, uiop, *lenp); + if (error) + goto out; + smb_rq_bend(rqp); + if (timo == 0) + timo = smb_timo_write; + error = smb_rq_simple_timed(rqp, timo); + if (error) + goto out; + smb_rq_getreply(rqp, &mdp); + error = md_get_uint8(mdp, &wc); + if (error) + goto out; + if (wc != 1) { + error = EBADRPC; + goto out; + } + error = md_get_uint16le(mdp, &rcnt); + if (error) + goto out; + *lenp = rcnt; + +out: smb_rq_done(rqp); return (error); } diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h index 3edf4c4d9d..675fa62b8d 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h @@ -122,7 +122,7 @@ struct sockaddr *smb_dup_sockaddr(struct sockaddr *sa); void smb_free_sockaddr(struct sockaddr *sa); int smb_toupper(const char *, char *, size_t); -int smb_rq_sign(struct smb_rq *); +void smb_rq_sign(struct smb_rq *); int smb_rq_verify(struct smb_rq *); int smb_calcv2mackey(struct smb_vc *, const uchar_t *, const uchar_t *, size_t); @@ -130,4 +130,16 @@ int smb_calcmackey(struct smb_vc *, const uchar_t *, const uchar_t *, size_t); void smb_crypto_mech_init(void); +void smb_time_init(void); +void smb_time_fini(void); + +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, struct timespec *tsp); +void smb_time_local2NT(struct timespec *tsp, 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); + #endif /* !_NETSMB_SMB_SUBR_H_ */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c index 98963b5583..bfacaaa55d 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c @@ -134,7 +134,7 @@ smb_errmsg(int cel, const char *func_name, const char *fmt, ...) * Don't bother to log these, but just * fire a dtrace probe with the message. */ - vsnprintf(buf, sizeof (buf), fmt, adx); + (void) vsnprintf(buf, sizeof (buf), fmt, adx); DTRACE_PROBE2(debugmsg2, (char *), func_name, (char *), buf); @@ -144,7 +144,7 @@ smb_errmsg(int cel, const char *func_name, const char *fmt, ...) * Add a prefix to the fmt string, * then let vcmn_err do the args. */ - snprintf(buf, sizeof (buf), "?%s: %s", func_name, fmt); + (void) snprintf(buf, sizeof (buf), "?%s: %s", func_name, fmt); DTRACE_PROBE3(debugmsg3, (char *), func_name, (char *), buf, @@ -186,19 +186,22 @@ m_dumpm(mblk_t *m) #define ECOMM EIO #endif #ifndef ENOMEDIUM -#define ENOMEDIUM EIO +#define ENOMEDIUM ENXIO #endif #ifndef ETIME #define ETIME ETIMEDOUT #endif -static struct { - unsigned nterr; - unsigned errno; +static const struct { + unsigned int nterr; + unsigned int errno; } nt2errno[] = { + /* Alphabetical order. */ {NT_STATUS_ACCESS_DENIED, EACCES}, {NT_STATUS_ACCESS_VIOLATION, EACCES}, {NT_STATUS_ACCOUNT_DISABLED, EACCES}, + {NT_STATUS_ACCOUNT_EXPIRED, EACCES}, + {NT_STATUS_ACCOUNT_LOCKED_OUT, EACCES}, {NT_STATUS_ACCOUNT_RESTRICTION, EACCES}, {NT_STATUS_ADDRESS_ALREADY_EXISTS, EADDRINUSE}, {NT_STATUS_BAD_NETWORK_NAME, ENOENT}, @@ -209,6 +212,7 @@ static struct { {NT_STATUS_CONNECTION_DISCONNECTED, ECONNABORTED}, {NT_STATUS_CONNECTION_REFUSED, ECONNREFUSED}, {NT_STATUS_CONNECTION_RESET, ENETRESET}, + {NT_STATUS_DELETE_PENDING, EACCES}, {NT_STATUS_DEVICE_DOES_NOT_EXIST, ENODEV}, {NT_STATUS_DEVICE_PROTOCOL_ERROR, EPROTO}, {NT_STATUS_DIRECTORY_NOT_EMPTY, ENOTEMPTY}, @@ -216,13 +220,16 @@ static struct { {NT_STATUS_DLL_NOT_FOUND, ELIBACC}, {NT_STATUS_END_OF_FILE, ENODATA}, {NT_STATUS_FILE_IS_A_DIRECTORY, EISDIR}, + {NT_STATUS_FILE_LOCK_CONFLICT, EAGAIN}, {NT_STATUS_FLOAT_INEXACT_RESULT, ERANGE}, {NT_STATUS_FLOAT_OVERFLOW, ERANGE}, {NT_STATUS_FLOAT_UNDERFLOW, ERANGE}, {NT_STATUS_HOST_UNREACHABLE, EHOSTUNREACH}, {NT_STATUS_ILL_FORMED_PASSWORD, EACCES}, {NT_STATUS_INTEGER_OVERFLOW, ERANGE}, + {NT_STATUS_INVALID_ACCOUNT_NAME, EACCES}, {NT_STATUS_INVALID_HANDLE, EBADF}, + {NT_STATUS_INVALID_LEVEL, ENOTSUP}, {NT_STATUS_INVALID_LOGON_HOURS, EACCES}, {NT_STATUS_INVALID_PARAMETER, EINVAL}, {NT_STATUS_INVALID_PIPE_STATE, EPIPE}, @@ -232,6 +239,8 @@ static struct { {NT_STATUS_IP_ADDRESS_CONFLICT1, ENOTUNIQ}, {NT_STATUS_IP_ADDRESS_CONFLICT2, ENOTUNIQ}, {NT_STATUS_LICENSE_QUOTA_EXCEEDED, EDQUOT}, + {NT_STATUS_LOCK_NOT_GRANTED, EAGAIN}, + {NT_STATUS_LOGIN_TIME_RESTRICTION, EACCES}, {NT_STATUS_LOGON_FAILURE, EACCES}, {NT_STATUS_MEDIA_WRITE_PROTECTED, EROFS}, {NT_STATUS_MEMORY_NOT_ALLOCATED, EFAULT}, @@ -241,6 +250,7 @@ static struct { {NT_STATUS_NETWORK_UNREACHABLE, ENETUNREACH}, {NT_STATUS_NET_WRITE_FAULT, ECOMM}, {NT_STATUS_NONEXISTENT_SECTOR, ESPIPE}, + {NT_STATUS_NONE_MAPPED, EINVAL}, {NT_STATUS_NOT_A_DIRECTORY, ENOTDIR}, {NT_STATUS_NOT_IMPLEMENTED, ENOSYS}, {NT_STATUS_NOT_MAPPED_VIEW, EINVAL}, @@ -251,11 +261,13 @@ static struct { {NT_STATUS_NO_SUCH_DEVICE, ENODEV}, {NT_STATUS_NO_SUCH_FILE, ENOENT}, {NT_STATUS_OBJECT_NAME_COLLISION, EEXIST}, - {NT_STATUS_OBJECT_NAME_NOT_FOUND, ENOENT}, {NT_STATUS_OBJECT_NAME_INVALID, EINVAL}, + {NT_STATUS_OBJECT_NAME_NOT_FOUND, ENOENT}, {NT_STATUS_OBJECT_PATH_INVALID, ENOTDIR}, + {NT_STATUS_OBJECT_PATH_NOT_FOUND, ENOENT}, {NT_STATUS_PAGEFILE_QUOTA, EDQUOT}, {NT_STATUS_PASSWORD_EXPIRED, EACCES}, + {NT_STATUS_PASSWORD_MUST_CHANGE, EACCES}, {NT_STATUS_PASSWORD_RESTRICTION, EACCES}, {NT_STATUS_PATH_NOT_COVERED, ENOENT}, {NT_STATUS_PIPE_BROKEN, EPIPE}, @@ -268,6 +280,7 @@ static struct { {NT_STATUS_PORT_UNREACHABLE, EHOSTUNREACH}, {NT_STATUS_PROTOCOL_UNREACHABLE, ENOPROTOOPT}, {NT_STATUS_QUOTA_EXCEEDED, EDQUOT}, + {NT_STATUS_RANGE_NOT_LOCKED, EIO}, {NT_STATUS_REGISTRY_QUOTA_LIMIT, EDQUOT}, {NT_STATUS_REMOTE_DISCONNECT, ESHUTDOWN}, {NT_STATUS_REMOTE_NOT_LISTENING, ECONNREFUSED}, @@ -283,10 +296,10 @@ static struct { {0, 0} }; -static struct { - unsigned dclass; - unsigned derr; - unsigned nterr; +static const struct { + unsigned short dclass; + unsigned short derr; + unsigned int nterr; } nt2doserr[] = { {ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, {ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, @@ -614,7 +627,7 @@ static struct { {ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, {ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, - {ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, + {ERRDOS, ERRunknownlevel, NT_STATUS_INVALID_LEVEL}, {ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, {ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, @@ -652,7 +665,8 @@ static struct { {ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, {ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, {ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, - {ERRHRD, ERRgeneral, NT_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT}, + {ERRHRD, ERRgeneral, + NT_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT}, {ERRHRD, ERRgeneral, NT_STATUS_16F}, {ERRHRD, ERRgeneral, NT_STATUS_170}, {ERRHRD, ERRgeneral, NT_STATUS_171}, @@ -695,7 +709,7 @@ static struct { {ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, {ERRDOS, ERRnoaccess, - NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, + NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, @@ -853,12 +867,13 @@ smb_maperror(int eclass, int eno) switch (eclass) { case ERRDOS: switch (eno) { + case ERRunknownlevel: + return (ENOTSUP); case ERRbadfunc: case ERRbadenv: case ERRbadformat: case ERRremcd: case ERRrmuns: - case ERRunknownlevel: return (EINVAL); case ERRbadfile: case ERRbadpath: @@ -910,7 +925,7 @@ smb_maperror(int eclass, int eno) case ERRnofiles: return (0); /* eeof ? */ case ERRlock: - return (EDEADLK); + return (EAGAIN); case ERRfilexists: return (EEXIST); case ERRinvalidname: /* samba maps as noent */ @@ -937,9 +952,7 @@ smb_maperror(int eclass, int eno) case ERRinvnid: return (ENETRESET); case ERRinvnetname: - SMBERROR("NetBIOS name is invalid: %d\n", - ERRinvnetname); - return (EAUTH); + return (ENXIO); case ERRbadtype: /* reserved and returned */ return (EIO); case ERRacctexpired: /* NT: account exists but disabled */ @@ -957,7 +970,7 @@ smb_maperror(int eclass, int eno) case ERRbadshare: return (ETXTBSY); case ERRlock: - return (EDEADLK); + return (EAGAIN); case ERRdiskfull: return (EFBIG); case ERRnotready: diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_time.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_time.c new file mode 100644 index 0000000000..e984ded911 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_time.c @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2000-2001, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: smbfs_subr.c,v 1.18 2005/02/02 00:22:23 lindak Exp $ + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Time conversion functions (to/from DOS, NT times) + * From BSD/Darwin smbfs_subr.c + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/vnode.h> +#include <sys/sunddi.h> + +#include <netsmb/smb_osdep.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> + +/* + * 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; + +/* Lock for the lastxxx variables */ +static kmutex_t lastdt_lock; + +/* + * Number of seconds between 1970 and 1601 year + * (134774 days) + */ +const uint64_t DIFF1970TO1601 = 11644473600ULL; +const uint32_t TEN_MIL = 10000000UL; + +/* + * Convert NT time (tenths of microseconds since 1601) + * to Unix seconds+nanoseconds since 1970. Any time + * earlier than 1970 is converted to Unix time zero. + * Both are GMT-based (no time zone adjustments). + */ +void +smb_time_NT2local(uint64_t nt_time, struct timespec *tsp) +{ + uint64_t nt_sec; /* seconds */ + uint64_t nt_tus; /* tenths of uSec. */ + + /* Optimize time zero. */ + if (nt_time == 0) { + tsp->tv_sec = 0; + tsp->tv_nsec = 0; + return; + } + + nt_sec = nt_time / TEN_MIL; + nt_tus = nt_time % TEN_MIL; + + if (nt_sec <= DIFF1970TO1601) { + tsp->tv_sec = 0; + tsp->tv_nsec = 0; + return; + } + tsp->tv_sec = nt_sec - DIFF1970TO1601; + tsp->tv_nsec = nt_tus * 100; +} + +/* + * Convert Unix time (seconds+nanoseconds since 1970) + * to NT time (tenths of microseconds since 1601). + * Exception: Convert time zero (really any time in + * the first second of 1970) to NT time zero. + * Both are GMT-based (no time zone adjustments). + */ +void +smb_time_local2NT(struct timespec *tsp, uint64_t *nt_time) +{ + uint64_t nt_sec; /* seconds */ + uint64_t nt_tus; /* tenths of uSec. */ + + if (tsp->tv_sec == 0) { + *nt_time = 0; + return; + } + + nt_sec = tsp->tv_sec + DIFF1970TO1601; + nt_tus = tsp->tv_nsec / 100; + + *nt_time = (uint64_t)nt_sec * TEN_MIL + nt_tus; +} + +/* + * Time zone conversion stuff, only used in old dialects. + * Don't adjust time zero for either conversion. + */ +void +smb_time_local2server(struct timespec *tsp, int tzoff, long *seconds) +{ + if (tsp->tv_sec <= (tzoff * 60)) + *seconds = 0; + else + *seconds = tsp->tv_sec - (tzoff * 60); +} + +void +smb_time_server2local(ulong_t seconds, int tzoff, struct timespec *tsp) +{ + if (seconds == 0) + tsp->tv_sec = 0; + else + tsp->tv_sec = seconds + tzoff * 60; + tsp->tv_nsec = 0; +} + +/* + * Time conversions to/from DOS format, for old dialects. + */ + +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; + + mutex_enter(&lastdt_lock); + + /* + * 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 (dhp) + *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000; + + *ddp = lastddate; + *dtp = lastdtime; + + mutex_exit(&lastdt_lock); +} + +/* + * 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. + */ + mutex_enter(&lastdt_lock); + 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; + mutex_exit(&lastdt_lock); +} + +void +smb_time_init(void) +{ + mutex_init(&lastdt_lock, NULL, MUTEX_DEFAULT, NULL); +} + +void +smb_time_fini(void) +{ + mutex_destroy(&lastdt_lock); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.c index 7a646b48c7..79163f4afe 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.c @@ -32,7 +32,6 @@ * Selected code from smb_conn.c */ -#pragma ident "%Z%%M% %I% %E% SMI" /* * Helper functions for smb_trantcp.c * (and maybe future transports) @@ -46,14 +45,8 @@ #include <netinet/in.h> #include <netinet/tcp.h> -/* Like smb_dev.h, this knows about all our sockaddr formats. */ -#include <netsmb/netbios.h> - -#ifdef APPLE -#include <sys/smb_apple.h> -#else #include <netsmb/smb_osdep.h> -#endif +#include <netsmb/netbios.h> #include <netsmb/smb.h> #include <netsmb/smb_conn.h> diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c index 1cd73649c4..566bc27e46 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c @@ -62,12 +62,7 @@ #include <netinet/in.h> #include <netinet/tcp.h> -#ifdef APPLE -#include <sys/smb_apple.h> -#else #include <netsmb/smb_osdep.h> -#endif - #include <netsmb/mchain.h> #include <netsmb/netbios.h> @@ -201,7 +196,7 @@ discon: * cleanup and state change on any call. */ freemsg(tm); - nb_disconnect(nbp); + (void) nb_disconnect(nbp); return (ENOTCONN); } @@ -261,7 +256,7 @@ nb_snddis(TIUSER *tiptr) /* * Stuff the NetBIOS header into space already prepended. */ -static int +static void nb_sethdr(mblk_t *m, uint8_t type, uint32_t len) { uint32_t *p; @@ -272,7 +267,6 @@ nb_sethdr(mblk_t *m, uint8_t type, uint32_t len) /*LINTED*/ p = (uint32_t *)m->b_rptr; *p = htonl(len); - return (0); } /* @@ -517,9 +511,9 @@ smb_nbst_done(struct smb_vc *vcp) * But it's harmless. */ if (nbp->nbp_flags & NBF_CONNECTED) - nb_disconnect(nbp); + (void) nb_disconnect(nbp); if (nbp->nbp_tiptr) - t_kclose(nbp->nbp_tiptr, 0); + (void) t_kclose(nbp->nbp_tiptr, 0); if (nbp->nbp_laddr) smb_free_sockaddr((struct sockaddr *)nbp->nbp_laddr); if (nbp->nbp_paddr) @@ -542,7 +536,7 @@ smb_nbst_loan_fp(struct smb_vc *vcp, struct file *fp, cred_t *cr) * Un-loan the existing one, if any. */ if (nbp->nbp_tiptr != NULL) { - t_kclose(nbp->nbp_tiptr, 0); + (void) t_kclose(nbp->nbp_tiptr, 0); nbp->nbp_tiptr = NULL; nbp->nbp_flags &= ~NBF_CONNECTED; nbp->nbp_state = NBST_CLOSED; @@ -595,6 +589,7 @@ nb_disconnect(struct nbpcb *nbp) { TIUSER *tiptr; int save_flags; + int err = 0; tiptr = nbp->nbp_tiptr; if (tiptr == NULL) @@ -610,12 +605,13 @@ nb_disconnect(struct nbpcb *nbp) mutex_exit(&nbp->nbp_lock); if (save_flags & NBF_CONNECTED) - nb_snddis(tiptr); + err = nb_snddis(tiptr); if (nbp->nbp_state != NBST_RETARGET) { nbp->nbp_state = NBST_CLOSED; } - return (0); + + return (err); } /* diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c index 19ef7bdc97..c4843e8578 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c @@ -194,7 +194,7 @@ smb_usr_simplerq(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) ioc->ioc_errclass = rqp->sr_errclass; ioc->ioc_serror = rqp->sr_serror; ioc->ioc_error = rqp->sr_error; - ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); + (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); out: if (rqp != NULL) @@ -310,7 +310,7 @@ smb_usr_t2request(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) ioc->ioc_error = t2p->t2_sr_error; ioc->ioc_rpflags2 = t2p->t2_sr_rpflags2; - ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); + (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); out: @@ -404,7 +404,7 @@ smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) */ ioc->ioc_cnt -= auio.uio_resid; - ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); + (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); out: if (ioc != NULL) @@ -612,7 +612,7 @@ smb_usr_get_tree(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) * the tree connect response, so they can * see if they got the requested type. */ - memcpy(tcon->tc_sh.sh_type_ret, + (void) memcpy(tcon->tc_sh.sh_type_ret, ssp->ss_type_ret, SMBIOC_STYPE_LEN); /* diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c b/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c index 525beb671d..ecd2322da7 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c @@ -48,12 +48,7 @@ #include <sys/sunddi.h> #include <sys/cmn_err.h> -#ifdef APPLE -#include <sys/smb_apple.h> -#else #include <netsmb/smb_osdep.h> -#endif - #include <netsmb/mchain.h> #include <netsmb/smb.h> @@ -623,7 +618,7 @@ md_done(struct mdchain *mdp) * Append a new message (separate mbuf chain). * It is caller responsibility to prevent * multiple calls to fetch/record routines. - * XXX: Note (mis)use of mblk->b_next here. + * Note unusual use of mblk->b_next here. */ void md_append_record(struct mdchain *mdp, mblk_t *top) @@ -644,23 +639,31 @@ md_append_record(struct mdchain *mdp, mblk_t *top) /* * Advance mdp->md_top to the next message. - * XXX: Note (mis)use of mblk->b_next here. + * Note unusual use of mblk->b_next here. */ -int +void md_next_record(struct mdchain *mdp) { - mblk_t *m; + mblk_t *m, *top; + + if ((top = mdp->md_top) == NULL) + return; - if (mdp->md_top == NULL) - return (ENOENT); - /* Get to next message (not b_cont chain) */ - m = mdp->md_top->b_next; - mdp->md_top->b_next = NULL; + /* + * Get the next message, if any, + * stored by md_append_record. + * Note: NOT b_cont chain + */ + m = top->b_next; + top->b_next = NULL; + + /* Done with old "top". */ md_done(mdp); if (m == NULL) - return (ENOENT); + return; + + /* Setup new "top". */ md_initm(mdp, m); - return (0); } /* @@ -961,7 +964,7 @@ m_copym(mblk_t *m, int off, int len, int wait) if (len < dsz) { adj = (ssize_t)len - (ssize_t)dsz; ASSERT(adj < 0); - adjmsg(n, adj); + (void) adjmsg(n, adj); } } 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); } diff --git a/usr/src/uts/common/netsmb/mchain.h b/usr/src/uts/common/netsmb/mchain.h index bccb9aab7f..c5c8512fd7 100644 --- a/usr/src/uts/common/netsmb/mchain.h +++ b/usr/src/uts/common/netsmb/mchain.h @@ -87,8 +87,24 @@ #endif /* (BYTE_ORDER == LITTLE_ENDIAN) */ +/* + * Additions for Solaris to replace things that came from + * <sys/mbuf.h> in the Darwin code. These are mostly just + * wrappers for streams functions. See: subr_mchain.c + */ + #ifdef _KERNEL +/* + * BSD-style mbuf "shim" for kernel code. Note, this + * does NOT implement BSD mbufs in the kernel. Rather, + * macros and wrapper functions are used so that code + * fomerly using mbuf_t now use STREAMS mblk_t instead. + */ + +#include <sys/stream.h> /* mblk_t */ +typedef mblk_t mbuf_t; + /* BEGIN CSTYLED */ /* * BSD-style mbufs, vs SysV-style mblks: @@ -115,7 +131,42 @@ */ /* END CSTYLED */ -#include <sys/stream.h> /* mblk_t */ +#define mtod(m, t) ((t)((m)->b_rptr)) + +/* length arg for m_copym to "copy all" */ +#define M_COPYALL -1 + +mblk_t *m_copym(mblk_t *, int, int, int); +mblk_t *m_pullup(mblk_t *, int); +mblk_t *m_split(mblk_t *, int, int); +void m_cat(mblk_t *, mblk_t *); +#define m_freem(x) freemsg(x) +mblk_t *m_getblk(int, int); +int m_fixhdr(mblk_t *m); + +#else /* _KERNEL */ + +/* + * BSD-style mbuf work-alike, for user-level. + * See libsmbfs mbuf.c + */ +typedef struct mbuf { + int m_len; + int m_maxlen; + char *m_data; + struct mbuf *m_next; +} mbuf_t; + +#define mtod(m, t) ((t)(m)->m_data) + +int m_get(int, mbuf_t **); +void m_freem(mbuf_t *); + +#endif /* _KERNEL */ + +/* + * BSD-style mbchain/mdchain work-alike + */ /* * Type of copy for mb_{put|get}_mem() @@ -126,6 +177,8 @@ #define MB_MZERO 3 /* bzero(), mb_put_mem only */ #define MB_MCUSTOM 4 /* use an user defined function */ +#ifdef _KERNEL + struct mbchain { mblk_t *mb_top; mblk_t *mb_cur; @@ -136,65 +189,64 @@ typedef struct mbchain mbchain_t; struct mdchain { mblk_t *md_top; /* head of mblk chain */ mblk_t *md_cur; /* current mblk */ - uchar_t *md_pos; /* offset in the current mblk */ + uchar_t *md_pos; /* position in md_cur */ + /* NB: md_pos is same type as mblk_t b_rptr, b_wptr members. */ }; typedef struct mdchain mdchain_t; -int m_fixhdr(mblk_t *m); +mblk_t *mb_detach(mbchain_t *mbp); +int mb_fixhdr(mbchain_t *mbp); +int mb_put_uio(mbchain_t *mbp, uio_t *uiop, size_t size); -int mb_init(struct mbchain *mbp); -void mb_initm(struct mbchain *mbp, mblk_t *m); -void mb_done(struct mbchain *mbp); -mblk_t *mb_detach(struct mbchain *mbp); -int mb_fixhdr(struct mbchain *mbp); -void *mb_reserve(struct mbchain *mbp, int size); - -int mb_put_padbyte(struct mbchain *mbp); -int mb_put_uint8(struct mbchain *mbp, uint8_t x); -int mb_put_uint16be(struct mbchain *mbp, uint16_t x); -int mb_put_uint16le(struct mbchain *mbp, uint16_t x); -int mb_put_uint32be(struct mbchain *mbp, uint32_t x); -int mb_put_uint32le(struct mbchain *mbp, uint32_t x); -int mb_put_uint64be(struct mbchain *mbp, uint64_t x); -int mb_put_uint64le(struct mbchain *mbp, uint64_t x); -int mb_put_mem(struct mbchain *mbp, const void *src, int size, int type); - -int mb_put_mbuf(struct mbchain *mbp, mblk_t *m); -int mb_put_uio(struct mbchain *mbp, uio_t *uiop, size_t size); - -int md_init(struct mdchain *mdp); -void md_initm(struct mdchain *mbp, mblk_t *m); -void md_done(struct mdchain *mdp); -void md_append_record(struct mdchain *mdp, mblk_t *top); -int md_next_record(struct mdchain *mdp); -int md_get_uint8(struct mdchain *mdp, uint8_t *x); -int md_get_uint16le(struct mdchain *mdp, uint16_t *x); -int md_get_uint16be(struct mdchain *mdp, uint16_t *x); -int md_get_uint32be(struct mdchain *mdp, uint32_t *x); -int md_get_uint32le(struct mdchain *mdp, uint32_t *x); -int md_get_uint64be(struct mdchain *mdp, uint64_t *x); -int md_get_uint64le(struct mdchain *mdp, uint64_t *x); -int md_get_mem(struct mdchain *mdp, void *vdst, int size, int type); -int md_get_mbuf(struct mdchain *mdp, int size, mblk_t **m); -int md_get_uio(struct mdchain *mdp, uio_t *uiop, size_t size); +void md_append_record(mdchain_t *mdp, mblk_t *top); +void md_next_record(mdchain_t *mdp); +int md_get_uio(mdchain_t *mdp, uio_t *uiop, size_t size); + +#else /* _KERNEL */ /* - * Additions for Solaris to replace things that came from - * <sys/mbuf.h> in the Darwin code. These are mostly just - * wrappers for streams functions. See: subr_mchain.c + * user-level code uses the same struct for both (MB, MD) */ +typedef struct mbdata { + mbuf_t *mb_top; /* head of mbuf chain */ + mbuf_t *mb_cur; /* current mbuf */ + char *mb_pos; /* position in mb_cur (get) */ + /* NB: mb_pos is same type as mbuf_t m_data member. */ + int mb_count; /* bytes marshalled (put) */ +} mbdata_t; +typedef struct mbdata mbchain_t; +typedef struct mbdata mdchain_t; + +#endif /* _KERNEL */ + +int mb_init(mbchain_t *); +void mb_initm(mbchain_t *, mbuf_t *); +void mb_done(mbchain_t *); +void *mb_reserve(mbchain_t *, int size); + +int mb_put_padbyte(mbchain_t *mbp); +int mb_put_uint8(mbchain_t *, uint8_t); +int mb_put_uint16be(mbchain_t *, uint16_t); +int mb_put_uint16le(mbchain_t *, uint16_t); +int mb_put_uint32be(mbchain_t *, uint32_t); +int mb_put_uint32le(mbchain_t *, uint32_t); +int mb_put_uint64be(mbchain_t *, uint64_t); +int mb_put_uint64le(mbchain_t *, uint64_t); +int mb_put_mem(mbchain_t *, const void *, int, int); +int mb_put_mbuf(mbchain_t *, mbuf_t *); + +int md_init(mdchain_t *mdp); +void md_initm(mdchain_t *mbp, mbuf_t *m); +void md_done(mdchain_t *mdp); + +int md_get_uint8(mdchain_t *, uint8_t *); +int md_get_uint16be(mdchain_t *, uint16_t *); +int md_get_uint16le(mdchain_t *, uint16_t *); +int md_get_uint32be(mdchain_t *, uint32_t *); +int md_get_uint32le(mdchain_t *, uint32_t *); +int md_get_uint64be(mdchain_t *, uint64_t *); +int md_get_uint64le(mdchain_t *, uint64_t *); +int md_get_mem(mdchain_t *, void *, int, int); +int md_get_mbuf(mdchain_t *, int, mbuf_t **); -#define mtod(m, t) ((t)((m)->b_rptr)) - -/* length to m_copym to copy all */ -#define M_COPYALL -1 - -mblk_t *m_copym(mblk_t *, int, int, int); -mblk_t *m_pullup(mblk_t *, int); -mblk_t *m_split(mblk_t *, int, int); -void m_cat(mblk_t *, mblk_t *); -#define m_freem(x) freemsg(x) -mblk_t *m_getblk(int, int); - -#endif /* ifdef _KERNEL */ #endif /* !_MCHAIN_H_ */ diff --git a/usr/src/uts/common/sys/fs/smbfs_mount.h b/usr/src/uts/common/sys/fs/smbfs_mount.h index 6eeb8f909f..2e4981e02b 100644 --- a/usr/src/uts/common/sys/fs/smbfs_mount.h +++ b/usr/src/uts/common/sys/fs/smbfs_mount.h @@ -33,15 +33,13 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SMBFS_MOUNT_H #define _SMBFS_MOUNT_H -#pragma ident "%Z%%M% %I% %E% SMI" - /* * This file defines the interface used by mount_smbfs. * Some of this came from the Darwin file: @@ -49,43 +47,34 @@ */ #define SMBFS_VERMAJ 1 -#define SMBFS_VERMIN 3200 +#define SMBFS_VERMIN 3300 #define SMBFS_VERSION (SMBFS_VERMAJ*100000 + SMBFS_VERMIN) -#define SMBFS_VER_STR "1.32" +#define SMBFS_VER_STR "1.33" #define SMBFS_VFSNAME "smbfs" -/* Values for flags */ -#define SMBFS_MOUNT_SOFT 0x0001 -#define SMBFS_MOUNT_INTR 0x0002 -#define SMBFS_MOUNT_STRONG 0x0004 -#define SMBFS_MOUNT_HAVE_NLS 0x0008 -#define SMBFS_MOUNT_NO_LONG 0x0010 -#define SMBFS_MOUNT_HOSTNAME 0x020 -#define SMBFS_MOUNT_SEMISOFT 0x200000 /* read soft, modify hard */ -#define SMBFS_MOUNT_NOPRINT 0x400000 /* don't print messages */ - -#define MNT_RDONLY 0x0001 -#define MNT_NODEV 0x0002 -#define MNT_NOEXEC 0x0004 -#define MNT_NOSUID 0x0008 -#define MNT_UNION 0x0010 -#define MNT_DONTBROWSE 0x0020 -#define MNT_AUTOMOUNTED 0x0040 +/* Values for smbfs_args.flags */ +#define SMBFS_MF_SOFT 0x0001 +#define SMBFS_MF_INTR 0x0002 +#define SMBFS_MF_NOAC 0x0004 +#define SMBFS_MF_ACREGMIN 0x0100 /* set min secs for file attr cache */ +#define SMBFS_MF_ACREGMAX 0x0200 /* set max secs for file attr cache */ +#define SMBFS_MF_ACDIRMIN 0x0400 /* set min secs for dir attr cache */ +#define SMBFS_MF_ACDIRMAX 0x0800 /* set max secs for dir attr cache */ /* Layout of the mount control block for an smb file system. */ struct smbfs_args { int version; /* smbfs mount version */ int devfd; /* file descriptor */ - uint_t flags; /* mount options, eg: soft */ - mode_t file_mode; /* octal srwx for files */ - mode_t dir_mode; /* octal srwx for dirs */ - int caseopt; /* convert upper|lower|none */ - caddr_t addr; /* file server address */ - caddr_t hostname; /* server's hostname */ - caddr_t sharename; /* server's sharename */ + uint_t flags; /* SMBFS_MF_ flags */ uid_t uid; /* octal user id */ gid_t gid; /* octal group id */ + mode_t file_mode; /* octal srwx for files */ + mode_t dir_mode; /* octal srwx for dirs */ + int acregmin; /* attr cache file min secs */ + int acregmax; /* attr cache file max secs */ + int acdirmin; /* attr cache dir min secs */ + int acdirmax; /* attr cache dir max secs */ }; #ifdef _SYSCALL32 @@ -94,15 +83,15 @@ struct smbfs_args { struct smbfs_args32 { int32_t version; /* smbfs mount version */ int32_t devfd; /* file descriptor */ - uint_t flags; /* mount options, eg: soft */ - mode_t file_mode; /* octal srwx for files */ - mode_t dir_mode; /* octal srwx for dirs */ - int32_t caseopt; /* convert upper|lower|none */ - caddr32_t addr; /* file server address */ - caddr32_t hostname; /* server's hostname */ - caddr32_t sharename; /* server's sharename */ + uint32_t flags; /* SMBFS_MF_ flags */ uid32_t uid; /* octal user id */ gid32_t gid; /* octal group id */ + mode32_t file_mode; /* octal srwx for files */ + mode32_t dir_mode; /* octal srwx for dirs */ + int32_t acregmin; /* attr cache file min secs */ + int32_t acregmax; /* attr cache file max secs */ + int32_t acdirmin; /* attr cache dir min secs */ + int32_t acdirmax; /* attr cache dir max secs */ }; #endif /* _SYSCALL32 */ |