Module Name: src
Committed By: tron
Date: Sun Oct 18 22:53:36 UTC 2009
Modified Files:
src/sys/netsmb: smb_smb.c
Log Message:
Fix detection of SMB capabilities according to the CIFS spec:
1.) SMB_CAP_LARGE_FILES advertises support for 64-bit file offsets.
2.) SMB_CAP_LARGE_READX and SMB_CAP_LARGE_WRITEX advertise support for
large reads and writes (larger than 64KB).
The code previously only used SMB_CAP_LARGE_READX and SMB_CAP_LARGE_WRITEX
which is not correct and doesn't work for the Apple Time Capsule which
only supports SMB_CAP_LARGE_FILES. With these changes SMBFS can copy a
5GB to a Time Capsule and read it back without problems.
Thanks a lot to Allen Briggs for pointing out the broke assumptions
and explaining the CIFS spec to me. This fixes PR kern/42175.
To generate a diff of this commit:
cvs rdiff -u -r1.30 -r1.31 src/sys/netsmb/smb_smb.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/netsmb/smb_smb.c
diff -u src/sys/netsmb/smb_smb.c:1.30 src/sys/netsmb/smb_smb.c:1.31
--- src/sys/netsmb/smb_smb.c:1.30 Wed Mar 18 16:00:24 2009
+++ src/sys/netsmb/smb_smb.c Sun Oct 18 22:53:36 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: smb_smb.c,v 1.30 2009/03/18 16:00:24 cegger Exp $ */
+/* $NetBSD: smb_smb.c,v 1.31 2009/10/18 22:53:36 tron Exp $ */
/*
* Copyright (c) 2000-2001 Boris Popov
@@ -38,7 +38,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: smb_smb.c,v 1.30 2009/03/18 16:00:24 cegger Exp $");
+__KERNEL_RCSID(0, "$NetBSD: smb_smb.c,v 1.31 2009/10/18 22:53:36 tron Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -594,6 +594,22 @@
u_int16_t residhi, residlo, off, doff;
u_int32_t resid;
+ if (!(SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_LARGE_FILES) &&
+ uio->uio_offset >= (1LL << 32)) {
+ /* Cannot read at/beyond 4G */
+ return (EFBIG);
+ }
+
+ if (!(SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_LARGE_WRITEX)) {
+ size_t blksz;
+
+ blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 64;
+ if (blksz > 0xffff)
+ blksz = 0xffff;
+
+ *len = min(blksz, *len);
+ }
+
error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp);
if (error)
return error;
@@ -674,8 +690,26 @@
u_int8_t wc;
u_int16_t resid;
+ if (!(SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_LARGE_FILES) &&
+ uio->uio_offset >= (1LL << 32)) {
+ /* Cannot write at/beyond 4G */
+ return (EFBIG);
+ }
+
+ if (SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_LARGE_WRITEX) {
+ *len = min(SSTOVC(ssp)->vc_wxmax, *len);
+ } else {
+ size_t blksz;
+
+ blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 64;
+ if (blksz > 0xffff)
+ blksz = 0xffff;
+
+ *len = min(blksz, *len);
+ }
+
error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp);
- if (error)
+ if (error != 0)
return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
@@ -687,7 +721,6 @@
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, *len >> 16);
mb_put_uint16le(mbp, *len);
mb_put_uint16le(mbp, 64); /* data offset from header start */
@@ -785,7 +818,8 @@
{
size_t tsize, len, resid;
int error = 0;
- int rx = (SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_LARGE_READX);
+ bool rx = (SMB_CAPS(SSTOVC(ssp)) &
+ (SMB_CAP_LARGE_FILES|SMB_CAP_LARGE_READX)) != 0;
resid = 0; /* XXX gcc */
@@ -866,7 +900,8 @@
{
int error = 0;
size_t len, tsize, resid;
- int wx = (SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_LARGE_WRITEX);
+ bool wx = (SMB_CAPS(SSTOVC(ssp)) &
+ (SMB_CAP_LARGE_FILES|SMB_CAP_LARGE_WRITEX)) != 0;
resid = 0; /* XXX gcc */
@@ -877,7 +912,7 @@
error = smb_smb_writex(ssp, fid, &len, &resid, uio, scred);
else
error = smb_smb_write(ssp, fid, &len, &resid, uio, scred);
- if (error)
+ if (error != 0)
break;
if (resid < len) {
error = EIO;