diff options
author | bp <bp@FreeBSD.org> | 2002-09-16 10:18:34 +0000 |
---|---|---|
committer | bp <bp@FreeBSD.org> | 2002-09-16 10:18:34 +0000 |
commit | e97cf22b3b28791666dab966ef7d0fc2c0848b8b (patch) | |
tree | d89349121f5089a8e1b7b59bff332137a618108e /sys | |
parent | b6330b0e91e947151d65b6d959d3186fd0cce742 (diff) | |
download | FreeBSD-src-e97cf22b3b28791666dab966ef7d0fc2c0848b8b.zip FreeBSD-src-e97cf22b3b28791666dab966ef7d0fc2c0848b8b.tar.gz |
Add support for large readx and writex functions if server supports them.
Obtained from: Darwin
MFC after: 2 weeks
Diffstat (limited to 'sys')
-rw-r--r-- | sys/netsmb/smb_conn.h | 2 | ||||
-rw-r--r-- | sys/netsmb/smb_smb.c | 176 |
2 files changed, 178 insertions, 0 deletions
diff --git a/sys/netsmb/smb_conn.h b/sys/netsmb/smb_conn.h index 29b1277..a2a2df7 100644 --- a/sys/netsmb/smb_conn.h +++ b/sys/netsmb/smb_conn.h @@ -264,6 +264,8 @@ struct smb_vc { u_short vc_mid; /* multiplex id */ struct smb_sopt vc_sopt; /* server options */ int vc_txmax; /* max tx/rx packet size */ + int vc_rxmax; /* max readx data size */ + int vc_wxmax; /* max writex data size */ struct smbiod * vc_iod; struct smb_slock vc_stlock; }; diff --git a/sys/netsmb/smb_smb.c b/sys/netsmb/smb_smb.c index 6b9b2cc..37fb091 100644 --- a/sys/netsmb/smb_smb.c +++ b/sys/netsmb/smb_smb.c @@ -71,6 +71,32 @@ static struct smb_dialect smb_dialects[] = { #define SMB_DIALECT_MAX (sizeof(smb_dialects) / sizeof(struct smb_dialect) - 2) +static u_int32_t +smb_vc_maxread(struct smb_vc *vcp) +{ + /* + * Specs say up to 64k data bytes, but Windows traffic + * uses 60k... no doubt for some good reason. + */ + if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_READX) + return (60*1024); + else + return (vcp->vc_sopt.sv_maxtx); +} + +static u_int32_t +smb_vc_maxwrite(struct smb_vc *vcp) +{ + /* + * Specs say up to 64k data bytes, but Windows traffic + * uses 60k... probably for some good reason. + */ + if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX) + return (60*1024); + else + return (vcp->vc_sopt.sv_maxtx); +} + static int smb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name) { @@ -211,7 +237,13 @@ smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred) } if (sp->sv_maxtx <= 0 || sp->sv_maxtx > 0xffff) sp->sv_maxtx = 1024; + else + sp->sv_maxtx = min(sp->sv_maxtx, + 63*1024 + SMB_HDRLEN + 16); + SMB_TRAN_GETPARAM(vcp, SMBTP_RCVSZ, &maxqsz); + vcp->vc_rxmax = min(smb_vc_maxread(vcp), maxqsz - 1024); SMB_TRAN_GETPARAM(vcp, SMBTP_SNDSZ, &maxqsz); + vcp->vc_wxmax = min(smb_vc_maxwrite(vcp), maxqsz - 1024); vcp->vc_txmax = min(sp->sv_maxtx, maxqsz); SMBSDEBUG("TZ = %d\n", sp->sv_tz); SMBSDEBUG("CAPS = %x\n", sp->sv_caps); @@ -486,6 +518,144 @@ smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred) } static __inline int +smb_smb_readx(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid, + struct uio *uio, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + u_int8_t wc; + int error; + u_int16_t residhi, residlo, off, doff; + u_int32_t resid; + + error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp); + if (error) + return error; + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint8(mbp, 0xff); /* no secondary command */ + mb_put_uint8(mbp, 0); /* MBZ */ + mb_put_uint16le(mbp, 0); /* offset to secondary */ + mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM); + mb_put_uint32le(mbp, uio->uio_offset); + *len = min(SSTOVC(ssp)->vc_rxmax, *len); + mb_put_uint16le(mbp, *len); /* MaxCount */ + mb_put_uint16le(mbp, *len); /* MinCount (only indicates blocking) */ + mb_put_uint32le(mbp, (unsigned)*len >> 16); /* MaxCountHigh */ + mb_put_uint16le(mbp, *len); /* Remaining ("obsolete") */ + mb_put_uint32le(mbp, uio->uio_offset >> 32); /* OffsetHigh */ + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + do { + error = smb_rq_simple(rqp); + if (error) + break; + smb_rq_getreply(rqp, &mdp); + off = SMB_HDRLEN; + md_get_uint8(mdp, &wc); + off++; + if (wc != 12) { + error = EBADRPC; + break; + } + md_get_uint8(mdp, NULL); + off++; + md_get_uint8(mdp, NULL); + off++; + md_get_uint16le(mdp, NULL); + off += 2; + md_get_uint16le(mdp, NULL); + off += 2; + md_get_uint16le(mdp, NULL); /* data compaction mode */ + off += 2; + md_get_uint16le(mdp, NULL); + off += 2; + md_get_uint16le(mdp, &residlo); + off += 2; + md_get_uint16le(mdp, &doff); /* data offset */ + off += 2; + md_get_uint16le(mdp, &residhi); + off += 2; + resid = (residhi << 16) | residlo; + md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM); + off += 4*2; + md_get_uint16le(mdp, NULL); /* ByteCount */ + off += 2; + if (doff > off) /* pad byte(s)? */ + md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM); + if (resid == 0) { + *rresid = resid; + break; + } + error = md_get_uio(mdp, uio, resid); + if (error) + break; + *rresid = resid; + } while(0); + smb_rq_done(rqp); + return (error); +} + +static __inline int +smb_smb_writex(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid, + struct uio *uio, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + int error; + u_int8_t wc; + u_int16_t resid; + + error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp); + if (error) + return (error); + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint8(mbp, 0xff); /* no secondary command */ + mb_put_uint8(mbp, 0); /* MBZ */ + mb_put_uint16le(mbp, 0); /* offset to secondary */ + mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM); + mb_put_uint32le(mbp, uio->uio_offset); + mb_put_uint32le(mbp, 0); /* MBZ (timeout) */ + mb_put_uint16le(mbp, 0); /* !write-thru */ + mb_put_uint16le(mbp, 0); + *len = min(SSTOVC(ssp)->vc_wxmax, *len); + mb_put_uint16le(mbp, (unsigned)*len >> 16); + mb_put_uint16le(mbp, *len); + mb_put_uint16le(mbp, 64); /* data offset from header start */ + mb_put_uint32le(mbp, uio->uio_offset >> 32); /* OffsetHigh */ + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + do { + mb_put_uint8(mbp, 0xee); /* mimic xp pad byte! */ + error = mb_put_uio(mbp, uio, *len); + if (error) + break; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (error) + break; + smb_rq_getreply(rqp, &mdp); + md_get_uint8(mdp, &wc); + if (wc != 6) { + error = EBADRPC; + break; + } + md_get_uint8(mdp, NULL); + md_get_uint8(mdp, NULL); + md_get_uint16le(mdp, NULL); + md_get_uint16le(mdp, &resid); + *rresid = resid; + } while(0); + + smb_rq_done(rqp); + return (error); +} + +static __inline int smb_smb_read(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid, struct uio *uio, struct smb_cred *scred) { @@ -496,6 +666,9 @@ smb_smb_read(struct smb_share *ssp, u_int16_t fid, u_int8_t wc; int error, rlen, blksz; + if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_READX) + return (smb_smb_readx(ssp, fid, len, rresid, uio, scred)); + error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp); if (error) return error; @@ -571,6 +744,9 @@ smb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid, u_int8_t wc; int error, blksz; + if (*len && SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX) + return (smb_smb_writex(ssp, fid, len, rresid, uio, scred)); + blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16; if (blksz > 0xffff) blksz = 0xffff; |