diff options
Diffstat (limited to 'fs/cifs/misc.c')
| -rw-r--r-- | fs/cifs/misc.c | 96 | 
1 files changed, 80 insertions, 16 deletions
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 138a011633f..3b0c62e622d 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -278,7 +278,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,  }  static int -check_smb_hdr(struct smb_hdr *smb, __u16 mid) +check_smb_hdr(struct smb_hdr *smb)  {  	/* does it have the right SMB "signature" ? */  	if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) { @@ -287,13 +287,6 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid)  		return 1;  	} -	/* Make sure that message ids match */ -	if (mid != smb->Mid) { -		cifs_dbg(VFS, "Mids do not match. received=%u expected=%u\n", -			 smb->Mid, mid); -		return 1; -	} -  	/* if it's a response then accept */  	if (smb->Flags & SMBFLG_RESPONSE)  		return 0; @@ -302,7 +295,8 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid)  	if (smb->Command == SMB_COM_LOCKING_ANDX)  		return 0; -	cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", smb->Mid); +	cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", +		 get_mid(smb));  	return 1;  } @@ -310,7 +304,6 @@ int  checkSMB(char *buf, unsigned int total_read)  {  	struct smb_hdr *smb = (struct smb_hdr *)buf; -	__u16 mid = smb->Mid;  	__u32 rfclen = be32_to_cpu(smb->smb_buf_length);  	__u32 clc_len;  /* calculated length */  	cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n", @@ -348,7 +341,7 @@ checkSMB(char *buf, unsigned int total_read)  	}  	/* otherwise, there is enough to get to the BCC */ -	if (check_smb_hdr(smb, mid)) +	if (check_smb_hdr(smb))  		return -EIO;  	clc_len = smbCalcSize(smb); @@ -359,6 +352,7 @@ checkSMB(char *buf, unsigned int total_read)  	}  	if (4 + rfclen != clc_len) { +		__u16 mid = get_mid(smb);  		/* check if bcc wrapped around for large read responses */  		if ((rfclen > 64 * 1024) && (rfclen > clc_len)) {  			/* check if lengths match mod 64K */ @@ -366,11 +360,11 @@ checkSMB(char *buf, unsigned int total_read)  				return 0; /* bcc wrapped */  		}  		cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n", -			 clc_len, 4 + rfclen, smb->Mid); +			 clc_len, 4 + rfclen, mid);  		if (4 + rfclen < clc_len) {  			cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n", -				 rfclen, smb->Mid); +				 rfclen, mid);  			return -EIO;  		} else if (rfclen > clc_len + 512) {  			/* @@ -383,7 +377,7 @@ checkSMB(char *buf, unsigned int total_read)  			 * data to 512 bytes.  			 */  			cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n", -				 rfclen, smb->Mid); +				 rfclen, mid);  			return -EIO;  		}  	} @@ -472,8 +466,22 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)  				cifs_dbg(FYI, "file id match, oplock break\n");  				pCifsInode = CIFS_I(netfile->dentry->d_inode); -				cifs_set_oplock_level(pCifsInode, -					pSMB->OplockLevel ? OPLOCK_READ : 0); +				set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, +					&pCifsInode->flags); + +				/* +				 * Set flag if the server downgrades the oplock +				 * to L2 else clear. +				 */ +				if (pSMB->OplockLevel) +					set_bit( +					   CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, +					   &pCifsInode->flags); +				else +					clear_bit( +					   CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, +					   &pCifsInode->flags); +  				queue_work(cifsiod_wq,  					   &netfile->oplock_break);  				netfile->oplock_break_cancelled = false; @@ -557,6 +565,62 @@ void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)  		cinode->oplock = 0;  } +static int +cifs_oplock_break_wait(void *unused) +{ +	schedule(); +	return signal_pending(current) ? -ERESTARTSYS : 0; +} + +/* + * We wait for oplock breaks to be processed before we attempt to perform + * writes. + */ +int cifs_get_writer(struct cifsInodeInfo *cinode) +{ +	int rc; + +start: +	rc = wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK, +				   cifs_oplock_break_wait, TASK_KILLABLE); +	if (rc) +		return rc; + +	spin_lock(&cinode->writers_lock); +	if (!cinode->writers) +		set_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); +	cinode->writers++; +	/* Check to see if we have started servicing an oplock break */ +	if (test_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags)) { +		cinode->writers--; +		if (cinode->writers == 0) { +			clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); +			wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); +		} +		spin_unlock(&cinode->writers_lock); +		goto start; +	} +	spin_unlock(&cinode->writers_lock); +	return 0; +} + +void cifs_put_writer(struct cifsInodeInfo *cinode) +{ +	spin_lock(&cinode->writers_lock); +	cinode->writers--; +	if (cinode->writers == 0) { +		clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); +		wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); +	} +	spin_unlock(&cinode->writers_lock); +} + +void cifs_done_oplock_break(struct cifsInodeInfo *cinode) +{ +	clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); +	wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK); +} +  bool  backup_cred(struct cifs_sb_info *cifs_sb)  {  | 
