diff options
Diffstat (limited to 'fs/cifs/cifssmb.c')
| -rw-r--r-- | fs/cifs/cifssmb.c | 285 | 
1 files changed, 193 insertions, 92 deletions
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index a3d74fea162..6ce4e0954b9 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -463,7 +463,6 @@ decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)  			       cifs_max_pending);  	set_credits(server, server->maxReq);  	server->maxBuf = le16_to_cpu(rsp->MaxBufSize); -	server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);  	/* even though we do not use raw we might as well set this  	accurately, in case we ever find a need for it */  	if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { @@ -1274,104 +1273,124 @@ OldOpenRetry:  }  int -CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon, -	    const char *fileName, const int openDisposition, -	    const int access_flags, const int create_options, __u16 *netfid, -	    int *pOplock, FILE_ALL_INFO *pfile_info, -	    const struct nls_table *nls_codepage, int remap) +CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock, +	  FILE_ALL_INFO *buf)  {  	int rc = -EACCES; -	OPEN_REQ *pSMB = NULL; -	OPEN_RSP *pSMBr = NULL; +	OPEN_REQ *req = NULL; +	OPEN_RSP *rsp = NULL;  	int bytes_returned;  	int name_len;  	__u16 count; +	struct cifs_sb_info *cifs_sb = oparms->cifs_sb; +	struct cifs_tcon *tcon = oparms->tcon; +	int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR; +	const struct nls_table *nls = cifs_sb->local_nls; +	int create_options = oparms->create_options; +	int desired_access = oparms->desired_access; +	int disposition = oparms->disposition; +	const char *path = oparms->path;  openRetry: -	rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB, -		      (void **) &pSMBr); +	rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **)&req, +		      (void **)&rsp);  	if (rc)  		return rc; -	pSMB->AndXCommand = 0xFF;	/* none */ +	/* no commands go after this */ +	req->AndXCommand = 0xFF; -	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { -		count = 1;	/* account for one byte pad to word boundary */ -		name_len = -		    cifsConvertToUTF16((__le16 *) (pSMB->fileName + 1), -				       fileName, PATH_MAX, nls_codepage, remap); -		name_len++;	/* trailing null */ +	if (req->hdr.Flags2 & SMBFLG2_UNICODE) { +		/* account for one byte pad to word boundary */ +		count = 1; +		name_len = cifsConvertToUTF16((__le16 *)(req->fileName + 1), +					      path, PATH_MAX, nls, remap); +		/* trailing null */ +		name_len++;  		name_len *= 2; -		pSMB->NameLength = cpu_to_le16(name_len); -	} else {		/* BB improve check for buffer overruns BB */ -		count = 0;	/* no pad */ -		name_len = strnlen(fileName, PATH_MAX); -		name_len++;	/* trailing null */ -		pSMB->NameLength = cpu_to_le16(name_len); -		strncpy(pSMB->fileName, fileName, name_len); +		req->NameLength = cpu_to_le16(name_len); +	} else { +		/* BB improve check for buffer overruns BB */ +		/* no pad */ +		count = 0; +		name_len = strnlen(path, PATH_MAX); +		/* trailing null */ +		name_len++; +		req->NameLength = cpu_to_le16(name_len); +		strncpy(req->fileName, path, name_len);  	} -	if (*pOplock & REQ_OPLOCK) -		pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK); -	else if (*pOplock & REQ_BATCHOPLOCK) -		pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK); -	pSMB->DesiredAccess = cpu_to_le32(access_flags); -	pSMB->AllocationSize = 0; -	/* set file as system file if special file such -	   as fifo and server expecting SFU style and -	   no Unix extensions */ + +	if (*oplock & REQ_OPLOCK) +		req->OpenFlags = cpu_to_le32(REQ_OPLOCK); +	else if (*oplock & REQ_BATCHOPLOCK) +		req->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK); + +	req->DesiredAccess = cpu_to_le32(desired_access); +	req->AllocationSize = 0; + +	/* +	 * Set file as system file if special file such as fifo and server +	 * expecting SFU style and no Unix extensions. +	 */  	if (create_options & CREATE_OPTION_SPECIAL) -		pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM); +		req->FileAttributes = cpu_to_le32(ATTR_SYSTEM);  	else -		pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL); +		req->FileAttributes = cpu_to_le32(ATTR_NORMAL); -	/* XP does not handle ATTR_POSIX_SEMANTICS */ -	/* but it helps speed up case sensitive checks for other -	servers such as Samba */ +	/* +	 * XP does not handle ATTR_POSIX_SEMANTICS but it helps speed up case +	 * sensitive checks for other servers such as Samba. +	 */  	if (tcon->ses->capabilities & CAP_UNIX) -		pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS); +		req->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);  	if (create_options & CREATE_OPTION_READONLY) -		pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY); +		req->FileAttributes |= cpu_to_le32(ATTR_READONLY); + +	req->ShareAccess = cpu_to_le32(FILE_SHARE_ALL); +	req->CreateDisposition = cpu_to_le32(disposition); +	req->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); -	pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL); -	pSMB->CreateDisposition = cpu_to_le32(openDisposition); -	pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);  	/* BB Expirement with various impersonation levels and verify */ -	pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); -	pSMB->SecurityFlags = -	    SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY; +	req->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); +	req->SecurityFlags = SECURITY_CONTEXT_TRACKING|SECURITY_EFFECTIVE_ONLY;  	count += name_len; -	inc_rfc1001_len(pSMB, count); +	inc_rfc1001_len(req, count); -	pSMB->ByteCount = cpu_to_le16(count); -	/* long_op set to 1 to allow for oplock break timeouts */ -	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, -			(struct smb_hdr *)pSMBr, &bytes_returned, 0); +	req->ByteCount = cpu_to_le16(count); +	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)req, +			 (struct smb_hdr *)rsp, &bytes_returned, 0);  	cifs_stats_inc(&tcon->stats.cifs_stats.num_opens);  	if (rc) {  		cifs_dbg(FYI, "Error in Open = %d\n", rc); -	} else { -		*pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */ -		*netfid = pSMBr->Fid;	/* cifs fid stays in le */ -		/* Let caller know file was created so we can set the mode. */ -		/* Do we care about the CreateAction in any other cases? */ -		if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) -			*pOplock |= CIFS_CREATE_ACTION; -		if (pfile_info) { -			memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime, -				36 /* CreationTime to Attributes */); -			/* the file_info buf is endian converted by caller */ -			pfile_info->AllocationSize = pSMBr->AllocationSize; -			pfile_info->EndOfFile = pSMBr->EndOfFile; -			pfile_info->NumberOfLinks = cpu_to_le32(1); -			pfile_info->DeletePending = 0; -		} +		cifs_buf_release(req); +		if (rc == -EAGAIN) +			goto openRetry; +		return rc;  	} -	cifs_buf_release(pSMB); -	if (rc == -EAGAIN) -		goto openRetry; +	/* 1 byte no need to le_to_cpu */ +	*oplock = rsp->OplockLevel; +	/* cifs fid stays in le */ +	oparms->fid->netfid = rsp->Fid; + +	/* Let caller know file was created so we can set the mode. */ +	/* Do we care about the CreateAction in any other cases? */ +	if (cpu_to_le32(FILE_CREATE) == rsp->CreateAction) +		*oplock |= CIFS_CREATE_ACTION; + +	if (buf) { +		/* copy from CreationTime to Attributes */ +		memcpy((char *)buf, (char *)&rsp->CreationTime, 36); +		/* the file_info buf is endian converted by caller */ +		buf->AllocationSize = rsp->AllocationSize; +		buf->EndOfFile = rsp->EndOfFile; +		buf->NumberOfLinks = cpu_to_le32(1); +		buf->DeletePending = 0; +	} + +	cifs_buf_release(req);  	return rc;  } @@ -1891,7 +1910,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)  	do {  		server = tlink_tcon(wdata->cfile->tlink)->ses->server; -		rc = server->ops->async_writev(wdata); +		rc = server->ops->async_writev(wdata, cifs_writedata_release);  	} while (rc == -EAGAIN);  	for (i = 0; i < wdata->nr_pages; i++) { @@ -1943,15 +1962,9 @@ cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete)  {  	struct cifs_writedata *wdata; -	/* this would overflow */ -	if (nr_pages == 0) { -		cifs_dbg(VFS, "%s: called with nr_pages == 0!\n", __func__); -		return NULL; -	} -  	/* writedata + number of page pointers */  	wdata = kzalloc(sizeof(*wdata) + -			sizeof(struct page *) * (nr_pages - 1), GFP_NOFS); +			sizeof(struct page *) * nr_pages, GFP_NOFS);  	if (wdata != NULL) {  		kref_init(&wdata->refcount);  		INIT_LIST_HEAD(&wdata->list); @@ -2012,7 +2025,8 @@ cifs_writev_callback(struct mid_q_entry *mid)  /* cifs_async_writev - send an async write, and set up mid to handle result */  int -cifs_async_writev(struct cifs_writedata *wdata) +cifs_async_writev(struct cifs_writedata *wdata, +		  void (*release)(struct kref *kref))  {  	int rc = -EACCES;  	WRITE_REQ *smb = NULL; @@ -2086,7 +2100,7 @@ cifs_async_writev(struct cifs_writedata *wdata)  	if (rc == 0)  		cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);  	else -		kref_put(&wdata->refcount, cifs_writedata_release); +		kref_put(&wdata->refcount, release);  async_writev_out:  	cifs_small_buf_release(smb); @@ -3089,7 +3103,8 @@ CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,  	bool is_unicode;  	unsigned int sub_len;  	char *sub_start; -	struct reparse_data *reparse_buf; +	struct reparse_symlink_data *reparse_buf; +	struct reparse_posix_data *posix_buf;  	__u32 data_offset, data_count;  	char *end_of_smb; @@ -3138,20 +3153,47 @@ CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,  		goto qreparse_out;  	}  	end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount; -	reparse_buf = (struct reparse_data *) +	reparse_buf = (struct reparse_symlink_data *)  				((char *)&pSMBr->hdr.Protocol + data_offset);  	if ((char *)reparse_buf >= end_of_smb) {  		rc = -EIO;  		goto qreparse_out;  	} -	if ((reparse_buf->PathBuffer + reparse_buf->PrintNameOffset + -				reparse_buf->PrintNameLength) > end_of_smb) { +	if (reparse_buf->ReparseTag == cpu_to_le32(IO_REPARSE_TAG_NFS)) { +		cifs_dbg(FYI, "NFS style reparse tag\n"); +		posix_buf =  (struct reparse_posix_data *)reparse_buf; + +		if (posix_buf->InodeType != cpu_to_le64(NFS_SPECFILE_LNK)) { +			cifs_dbg(FYI, "unsupported file type 0x%llx\n", +				 le64_to_cpu(posix_buf->InodeType)); +			rc = -EOPNOTSUPP; +			goto qreparse_out; +		} +		is_unicode = true; +		sub_len = le16_to_cpu(reparse_buf->ReparseDataLength); +		if (posix_buf->PathBuffer + sub_len > end_of_smb) { +			cifs_dbg(FYI, "reparse buf beyond SMB\n"); +			rc = -EIO; +			goto qreparse_out; +		} +		*symlinkinfo = cifs_strndup_from_utf16(posix_buf->PathBuffer, +				sub_len, is_unicode, nls_codepage); +		goto qreparse_out; +	} else if (reparse_buf->ReparseTag != +			cpu_to_le32(IO_REPARSE_TAG_SYMLINK)) { +		rc = -EOPNOTSUPP; +		goto qreparse_out; +	} + +	/* Reparse tag is NTFS symlink */ +	sub_start = le16_to_cpu(reparse_buf->SubstituteNameOffset) + +				reparse_buf->PathBuffer; +	sub_len = le16_to_cpu(reparse_buf->SubstituteNameLength); +	if (sub_start + sub_len > end_of_smb) {  		cifs_dbg(FYI, "reparse buf beyond SMB\n");  		rc = -EIO;  		goto qreparse_out;  	} -	sub_start = reparse_buf->SubstituteNameOffset + reparse_buf->PathBuffer; -	sub_len = reparse_buf->SubstituteNameLength;  	if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)  		is_unicode = true;  	else @@ -3172,6 +3214,60 @@ qreparse_out:  	return rc;  } +int +CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon, +		    __u16 fid) +{ +	int rc = 0; +	int bytes_returned; +	struct smb_com_transaction_compr_ioctl_req *pSMB; +	struct smb_com_transaction_ioctl_rsp *pSMBr; + +	cifs_dbg(FYI, "Set compression for %u\n", fid); +	rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, +		      (void **) &pSMBr); +	if (rc) +		return rc; + +	pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); + +	pSMB->TotalParameterCount = 0; +	pSMB->TotalDataCount = __constant_cpu_to_le32(2); +	pSMB->MaxParameterCount = 0; +	pSMB->MaxDataCount = 0; +	pSMB->MaxSetupCount = 4; +	pSMB->Reserved = 0; +	pSMB->ParameterOffset = 0; +	pSMB->DataCount = __constant_cpu_to_le32(2); +	pSMB->DataOffset = +		cpu_to_le32(offsetof(struct smb_com_transaction_compr_ioctl_req, +				compression_state) - 4);  /* 84 */ +	pSMB->SetupCount = 4; +	pSMB->SubCommand = __constant_cpu_to_le16(NT_TRANSACT_IOCTL); +	pSMB->ParameterCount = 0; +	pSMB->FunctionCode = __constant_cpu_to_le32(FSCTL_SET_COMPRESSION); +	pSMB->IsFsctl = 1; /* FSCTL */ +	pSMB->IsRootFlag = 0; +	pSMB->Fid = fid; /* file handle always le */ +	/* 3 byte pad, followed by 2 byte compress state */ +	pSMB->ByteCount = __constant_cpu_to_le16(5); +	inc_rfc1001_len(pSMB, 5); + +	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, +			 (struct smb_hdr *) pSMBr, &bytes_returned, 0); +	if (rc) +		cifs_dbg(FYI, "Send error in SetCompression = %d\n", rc); + +	cifs_buf_release(pSMB); + +	/* +	 * Note: On -EAGAIN error only caller can retry on handle based calls +	 * since file handle passed in no longer valid. +	 */ +	return rc; +} + +  #ifdef CONFIG_CIFS_POSIX  /*Convert an Access Control Entry from wire format to local POSIX xattr format*/ @@ -3288,11 +3384,13 @@ static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,  		return 0;  	}  	cifs_acl->version = cpu_to_le16(1); -	if (acl_type == ACL_TYPE_ACCESS) +	if (acl_type == ACL_TYPE_ACCESS) {  		cifs_acl->access_entry_count = cpu_to_le16(count); -	else if (acl_type == ACL_TYPE_DEFAULT) +		cifs_acl->default_entry_count = __constant_cpu_to_le16(0xFFFF); +	} else if (acl_type == ACL_TYPE_DEFAULT) {  		cifs_acl->default_entry_count = cpu_to_le16(count); -	else { +		cifs_acl->access_entry_count = __constant_cpu_to_le16(0xFFFF); +	} else {  		cifs_dbg(FYI, "unknown ACL type %d\n", acl_type);  		return 0;  	} @@ -3927,7 +4025,7 @@ QFileInfoRetry:  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc); +		cifs_dbg(FYI, "Send error in QFileInfo = %d", rc);  	} else {		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr); @@ -4096,7 +4194,7 @@ UnixQFileInfoRetry:  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc); +		cifs_dbg(FYI, "Send error in UnixQFileInfo = %d", rc);  	} else {		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr); @@ -4180,7 +4278,7 @@ UnixQPathInfoRetry:  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc); +		cifs_dbg(FYI, "Send error in UnixQPathInfo = %d", rc);  	} else {		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr); @@ -6099,6 +6197,9 @@ QAllEAsRetry:  	cifs_dbg(FYI, "ea length %d\n", list_len);  	if (list_len <= 8) {  		cifs_dbg(FYI, "empty EA list returned from server\n"); +		/* didn't find the named attribute */ +		if (ea_name) +			rc = -ENODATA;  		goto QAllEAsOut;  	}  | 
