diff options
| author | Steve French <sfrench@us.ibm.com> | 2006-10-12 13:49:24 -0400 |
|---|---|---|
| committer | Steve French <sfrench@us.ibm.com> | 2006-10-12 13:49:24 -0400 |
| commit | d103e164bee2f21d0efe7d713cbbb0a443ba480d (patch) | |
| tree | 88e1d33b0a247535b181ac5bbb65514a6cc63bd0 | |
| parent | 230a03950ecd63bc613c6adeffbe9049189d9f05 (diff) | |
[CIFS] Workaround incomplete byte length returned by some
servers on small SMB responses
Signed-off-by: Steve French <sfrench@us.ibm.com>
| -rw-r--r-- | fs/cifs/cifsproto.h | 2 | ||||
| -rw-r--r-- | fs/cifs/misc.c | 42 |
2 files changed, 30 insertions, 14 deletions
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 4a4fd2dbca63..f1f8225102f0 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
| @@ -55,7 +55,7 @@ extern int SendReceiveBlockingLock(const unsigned int /* xid */ , | |||
| 55 | struct smb_hdr * /* input */ , | 55 | struct smb_hdr * /* input */ , |
| 56 | struct smb_hdr * /* out */ , | 56 | struct smb_hdr * /* out */ , |
| 57 | int * /* bytes returned */); | 57 | int * /* bytes returned */); |
| 58 | extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length); | 58 | extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); |
| 59 | extern int is_valid_oplock_break(struct smb_hdr *smb, struct TCP_Server_Info *); | 59 | extern int is_valid_oplock_break(struct smb_hdr *smb, struct TCP_Server_Info *); |
| 60 | extern int is_size_safe_to_change(struct cifsInodeInfo *); | 60 | extern int is_size_safe_to_change(struct cifsInodeInfo *); |
| 61 | extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *); | 61 | extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *); |
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index ca6e9b1413fa..bbc9cd34b6ea 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c | |||
| @@ -418,26 +418,42 @@ checkSMBhdr(struct smb_hdr *smb, __u16 mid) | |||
| 418 | } | 418 | } |
| 419 | 419 | ||
| 420 | int | 420 | int |
| 421 | checkSMB(struct smb_hdr *smb, __u16 mid, int length) | 421 | checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length) |
| 422 | { | 422 | { |
| 423 | __u32 len = smb->smb_buf_length; | 423 | __u32 len = smb->smb_buf_length; |
| 424 | __u32 clc_len; /* calculated length */ | 424 | __u32 clc_len; /* calculated length */ |
| 425 | cFYI(0, ("checkSMB Length: 0x%x, smb_buf_length: 0x%x", length, len)); | 425 | cFYI(0, ("checkSMB Length: 0x%x, smb_buf_length: 0x%x", length, len)); |
| 426 | if (((unsigned int)length < 2 + sizeof (struct smb_hdr)) || | 426 | |
| 427 | (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4)) { | 427 | if (length < 2 + sizeof (struct smb_hdr)) { |
| 428 | if ((unsigned int)length < 2 + sizeof (struct smb_hdr)) { | 428 | if ((length >= sizeof (struct smb_hdr) - 1) |
| 429 | if (((unsigned int)length >= | ||
| 430 | sizeof (struct smb_hdr) - 1) | ||
| 431 | && (smb->Status.CifsError != 0)) { | 429 | && (smb->Status.CifsError != 0)) { |
| 432 | smb->WordCount = 0; | 430 | smb->WordCount = 0; |
| 433 | /* some error cases do not return wct and bcc */ | 431 | /* some error cases do not return wct and bcc */ |
| 432 | return 0; | ||
| 433 | } else if ((length == sizeof(struct smb_hdr) + 1) && | ||
| 434 | (smb->WordCount == 0)) { | ||
| 435 | char * tmp = (char *)smb; | ||
| 436 | /* Need to work around a bug in two servers here */ | ||
| 437 | /* First, check if the part of bcc they sent was zero */ | ||
| 438 | if (tmp[sizeof(struct smb_hdr)] == 0) { | ||
| 439 | /* some servers return only half of bcc | ||
| 440 | * on simple responses (wct, bcc both zero) | ||
| 441 | * in particular have seen this on | ||
| 442 | * ulogoffX and FindClose. This leaves | ||
| 443 | * one byte of bcc potentially unitialized | ||
| 444 | */ | ||
| 445 | /* zero rest of bcc */ | ||
| 446 | tmp[sizeof(struct smb_hdr)+1] = 0; | ||
| 434 | return 0; | 447 | return 0; |
| 435 | } else { | ||
| 436 | cERROR(1, ("Length less than smb header size")); | ||
| 437 | } | 448 | } |
| 449 | cERROR(1,("rcvd invalid byte count (bcc)")); | ||
| 450 | } else { | ||
| 451 | cERROR(1, ("Length less than smb header size")); | ||
| 438 | } | 452 | } |
| 439 | if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) | 453 | return 1; |
| 440 | cERROR(1, ("smb length greater than MaxBufSize, mid=%d", | 454 | } |
| 455 | if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { | ||
| 456 | cERROR(1, ("smb length greater than MaxBufSize, mid=%d", | ||
| 441 | smb->Mid)); | 457 | smb->Mid)); |
| 442 | return 1; | 458 | return 1; |
| 443 | } | 459 | } |
| @@ -446,7 +462,7 @@ checkSMB(struct smb_hdr *smb, __u16 mid, int length) | |||
| 446 | return 1; | 462 | return 1; |
| 447 | clc_len = smbCalcSize_LE(smb); | 463 | clc_len = smbCalcSize_LE(smb); |
| 448 | 464 | ||
| 449 | if(4 + len != (unsigned int)length) { | 465 | if(4 + len != length) { |
| 450 | cERROR(1, ("Length read does not match RFC1001 length %d",len)); | 466 | cERROR(1, ("Length read does not match RFC1001 length %d",len)); |
| 451 | return 1; | 467 | return 1; |
| 452 | } | 468 | } |
