summaryrefslogtreecommitdiffstats
path: root/fs/cifs/cifssmb.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs/cifssmb.c')
-rw-r--r--fs/cifs/cifssmb.c222
1 files changed, 157 insertions, 65 deletions
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 994de7c9047..2af8626ced4 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -190,10 +190,10 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
/* need to prevent multiple threads trying to
simultaneously reconnect the same SMB session */
down(&tcon->ses->sesSem);
- if (tcon->ses->status == CifsNeedReconnect)
+ if (tcon->ses->need_reconnect)
rc = cifs_setup_session(0, tcon->ses,
nls_codepage);
- if (!rc && (tcon->tidStatus == CifsNeedReconnect)) {
+ if (!rc && (tcon->need_reconnect)) {
mark_open_files_invalid(tcon);
rc = CIFSTCon(0, tcon->ses, tcon->treeName,
tcon, nls_codepage);
@@ -337,10 +337,10 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
/* need to prevent multiple threads trying to
simultaneously reconnect the same SMB session */
down(&tcon->ses->sesSem);
- if (tcon->ses->status == CifsNeedReconnect)
+ if (tcon->ses->need_reconnect)
rc = cifs_setup_session(0, tcon->ses,
nls_codepage);
- if (!rc && (tcon->tidStatus == CifsNeedReconnect)) {
+ if (!rc && (tcon->need_reconnect)) {
mark_open_files_invalid(tcon);
rc = CIFSTCon(0, tcon->ses, tcon->treeName,
tcon, nls_codepage);
@@ -664,8 +664,9 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
rc = -EIO;
goto neg_err_exit;
}
-
- if (server->socketUseCount.counter > 1) {
+ read_lock(&cifs_tcp_ses_lock);
+ if (server->srv_count > 1) {
+ read_unlock(&cifs_tcp_ses_lock);
if (memcmp(server->server_GUID,
pSMBr->u.extended_response.
GUID, 16) != 0) {
@@ -674,9 +675,11 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
pSMBr->u.extended_response.GUID,
16);
}
- } else
+ } else {
+ read_unlock(&cifs_tcp_ses_lock);
memcpy(server->server_GUID,
pSMBr->u.extended_response.GUID, 16);
+ }
if (count == 16) {
server->secType = RawNTLMSSP;
@@ -739,50 +742,31 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
int rc = 0;
cFYI(1, ("In tree disconnect"));
- /*
- * If last user of the connection and
- * connection alive - disconnect it
- * If this is the last connection on the server session disconnect it
- * (and inside session disconnect we should check if tcp socket needs
- * to be freed and kernel thread woken up).
- */
- if (tcon)
- down(&tcon->tconSem);
- else
- return -EIO;
- atomic_dec(&tcon->useCount);
- if (atomic_read(&tcon->useCount) > 0) {
- up(&tcon->tconSem);
- return -EBUSY;
- }
+ /* BB: do we need to check this? These should never be NULL. */
+ if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
+ return -EIO;
- /* No need to return error on this operation if tid invalidated and
- closed on server already e.g. due to tcp session crashing */
- if (tcon->tidStatus == CifsNeedReconnect) {
- up(&tcon->tconSem);
+ /*
+ * No need to return error on this operation if tid invalidated and
+ * closed on server already e.g. due to tcp session crashing. Also,
+ * the tcon is no longer on the list, so no need to take lock before
+ * checking this.
+ */
+ if (tcon->need_reconnect)
return 0;
- }
- if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) {
- up(&tcon->tconSem);
- return -EIO;
- }
rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
(void **)&smb_buffer);
- if (rc) {
- up(&tcon->tconSem);
+ if (rc)
return rc;
- }
rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
if (rc)
cFYI(1, ("Tree disconnect failed %d", rc));
- up(&tcon->tconSem);
-
/* No need to return error on this operation if tid invalidated and
- closed on server already e.g. due to tcp session crashing */
+ closed on server already e.g. due to tcp session crashing */
if (rc == -EAGAIN)
rc = 0;
@@ -796,43 +780,36 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
int rc = 0;
cFYI(1, ("In SMBLogoff for session disconnect"));
- if (ses)
- down(&ses->sesSem);
- else
+
+ /*
+ * BB: do we need to check validity of ses and server? They should
+ * always be valid since we have an active reference. If not, that
+ * should probably be a BUG()
+ */
+ if (!ses || !ses->server)
return -EIO;
- atomic_dec(&ses->inUse);
- if (atomic_read(&ses->inUse) > 0) {
- up(&ses->sesSem);
- return -EBUSY;
- }
+ down(&ses->sesSem);
+ if (ses->need_reconnect)
+ goto session_already_dead; /* no need to send SMBlogoff if uid
+ already closed due to reconnect */
rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
if (rc) {
up(&ses->sesSem);
return rc;
}
- if (ses->server) {
- pSMB->hdr.Mid = GetNextMid(ses->server);
+ pSMB->hdr.Mid = GetNextMid(ses->server);
- if (ses->server->secMode &
+ if (ses->server->secMode &
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
- }
pSMB->hdr.Uid = ses->Suid;
pSMB->AndXCommand = 0xFF;
rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
- if (ses->server) {
- atomic_dec(&ses->server->socketUseCount);
- if (atomic_read(&ses->server->socketUseCount) == 0) {
- spin_lock(&GlobalMid_Lock);
- ses->server->tcpStatus = CifsExiting;
- spin_unlock(&GlobalMid_Lock);
- rc = -ESHUTDOWN;
- }
- }
+session_already_dead:
up(&ses->sesSem);
/* if session dead then we do not need to do ulogoff,
@@ -1309,6 +1286,7 @@ OldOpenRetry:
cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
pfile_info->EndOfFile = pfile_info->AllocationSize;
pfile_info->NumberOfLinks = cpu_to_le32(1);
+ pfile_info->DeletePending = 0;
}
}
@@ -1410,6 +1388,7 @@ openRetry:
pfile_info->AllocationSize = pSMBr->AllocationSize;
pfile_info->EndOfFile = pSMBr->EndOfFile;
pfile_info->NumberOfLinks = cpu_to_le32(1);
+ pfile_info->DeletePending = 0;
}
}
@@ -1534,7 +1513,7 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
__u32 bytes_sent;
__u16 byte_count;
- /* cFYI(1,("write at %lld %d bytes",offset,count));*/
+ /* cFYI(1, ("write at %lld %d bytes", offset, count));*/
if (tcon->ses == NULL)
return -ECONNABORTED;
@@ -2017,7 +1996,7 @@ renameRetry:
}
int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
- int netfid, char *target_name,
+ int netfid, const char *target_name,
const struct nls_table *nls_codepage, int remap)
{
struct smb_com_transaction2_sfi_req *pSMB = NULL;
@@ -2071,7 +2050,7 @@ int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
remap);
}
rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
- count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str) + 2;
+ count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
byte_count += count;
pSMB->DataCount = cpu_to_le16(count);
pSMB->TotalDataCount = pSMB->DataCount;
@@ -3614,6 +3593,8 @@ findFirstRetry:
/* BB remember to free buffer if error BB */
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
if (rc == 0) {
+ unsigned int lnoff;
+
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
psrch_inf->unicode = true;
else
@@ -3636,6 +3617,17 @@ findFirstRetry:
le16_to_cpu(parms->SearchCount);
psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
psrch_inf->entries_in_buffer;
+ lnoff = le16_to_cpu(parms->LastNameOffset);
+ if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
+ lnoff) {
+ cERROR(1, ("ignoring corrupt resume name"));
+ psrch_inf->last_entry = NULL;
+ return rc;
+ }
+
+ psrch_inf->last_entry = psrch_inf->srch_entries_start +
+ lnoff;
+
*pnetfid = parms->SearchHandle;
} else {
cifs_buf_release(pSMB);
@@ -3725,6 +3717,8 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
if (rc == 0) {
+ unsigned int lnoff;
+
/* BB fixme add lock for file (srch_info) struct here */
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
psrch_inf->unicode = true;
@@ -3751,6 +3745,16 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
le16_to_cpu(parms->SearchCount);
psrch_inf->index_of_last_entry +=
psrch_inf->entries_in_buffer;
+ lnoff = le16_to_cpu(parms->LastNameOffset);
+ if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
+ lnoff) {
+ cERROR(1, ("ignoring corrupt resume name"));
+ psrch_inf->last_entry = NULL;
+ return rc;
+ } else
+ psrch_inf->last_entry =
+ psrch_inf->srch_entries_start + lnoff;
+
/* cFYI(1,("fnxt2 entries in buf %d index_of_last %d",
psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */
@@ -3895,6 +3899,27 @@ GetInodeNumOut:
return rc;
}
+/* computes length of UCS string converted to host codepage
+ * @src: UCS string
+ * @maxlen: length of the input string in UCS characters
+ * (not in bytes)
+ *
+ * return: size of input string in host codepage
+ */
+static int hostlen_fromUCS(const __le16 *src, const int maxlen,
+ const struct nls_table *nls_codepage) {
+ int i;
+ int hostlen = 0;
+ char to[4];
+ int charlen;
+ for (i = 0; (i < maxlen) && src[i]; ++i) {
+ charlen = nls_codepage->uni2char(le16_to_cpu(src[i]),
+ to, NLS_MAX_CHARSET_SIZE);
+ hostlen += charlen > 0 ? charlen : 1;
+ }
+ return hostlen;
+}
+
/* parses DFS refferal V3 structure
* caller is responsible for freeing target_nodes
* returns:
@@ -3905,7 +3930,8 @@ static int
parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
unsigned int *num_of_nodes,
struct dfs_info3_param **target_nodes,
- const struct nls_table *nls_codepage)
+ const struct nls_table *nls_codepage, int remap,
+ const char *searchName)
{
int i, rc = 0;
char *data_end;
@@ -3956,7 +3982,17 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
struct dfs_info3_param *node = (*target_nodes)+i;
node->flags = le16_to_cpu(pSMBr->DFSFlags);
- node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
+ if (is_unicode) {
+ __le16 *tmp = kmalloc(strlen(searchName)*2, GFP_KERNEL);
+ cifsConvertToUCS((__le16 *) tmp, searchName,
+ PATH_MAX, nls_codepage, remap);
+ node->path_consumed = hostlen_fromUCS(tmp,
+ le16_to_cpu(pSMBr->PathConsumed)/2,
+ nls_codepage);
+ kfree(tmp);
+ } else
+ node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
+
node->server_type = le16_to_cpu(ref->ServerType);
node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
@@ -4089,7 +4125,8 @@ getDFSRetry:
/* parse returned result into more usable form */
rc = parse_DFS_referrals(pSMBr, num_of_nodes,
- target_nodes, nls_codepage);
+ target_nodes, nls_codepage, remap,
+ searchName);
GetDFSRefExit:
cifs_buf_release(pSMB);
@@ -4876,6 +4913,61 @@ CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
return rc;
}
+int
+CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
+ bool delete_file, __u16 fid, __u32 pid_of_opener)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ char *data_offset;
+ int rc = 0;
+ __u16 params, param_offset, offset, byte_count, count;
+
+ cFYI(1, ("Set File Disposition (via SetFileInfo)"));
+ rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
+
+ if (rc)
+ return rc;
+
+ pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
+ pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
+
+ params = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ offset = param_offset + params;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
+
+ count = 1;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB PDU from sess */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->Fid = fid;
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ *data_offset = delete_file ? 1 : 0;
+ rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+ if (rc)
+ cFYI(1, ("Send error in SetFileDisposition = %d", rc));
+
+ return rc;
+}
int
CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,