/*
* fs/cifs/misc.c
*
- * Copyright (C) International Business Machines Corp., 2002,2003
+ * Copyright (C) International Business Machines Corp., 2002,2005
* Author(s): Steve French (sfrench@us.ibm.com)
*
* This library is free software; you can redistribute it and/or modify
#include "cifs_debug.h"
#include "smberr.h"
#include "nterr.h"
+#include "cifs_unicode.h"
+extern mempool_t *cifs_sm_req_poolp;
extern mempool_t *cifs_req_poolp;
extern struct task_struct * oplockThread;
-__u16 GlobalMid; /* multiplex id - rotating counter */
-
/* The xid serves as a useful identifier for each incoming vfs request,
in a similar way to the mid which is useful to track each sent smb,
and CurrentXid can also provide a running counter (although it
GlobalTotalActiveXid++;
if (GlobalTotalActiveXid > GlobalMaxActiveXid)
GlobalMaxActiveXid = GlobalTotalActiveXid; /* keep high water mark for number of simultaneous vfs ops in our filesystem */
+ if(GlobalTotalActiveXid > 65000)
+ cFYI(1,("warning: more than 65000 requests active"));
xid = GlobalCurrentXid++;
spin_unlock(&GlobalMid_Lock);
return xid;
atomic_dec(&sesInfoAllocCount);
list_del(&buf_to_free->cifsSessionList);
write_unlock(&GlobalSMBSeslock);
- if (buf_to_free->serverOS)
- kfree(buf_to_free->serverOS);
- if (buf_to_free->serverDomain)
- kfree(buf_to_free->serverDomain);
- if (buf_to_free->serverNOS)
- kfree(buf_to_free->serverNOS);
- if (buf_to_free->password)
- kfree(buf_to_free->password);
+ kfree(buf_to_free->serverOS);
+ kfree(buf_to_free->serverDomain);
+ kfree(buf_to_free->serverNOS);
+ kfree(buf_to_free->password);
kfree(buf_to_free);
}
INIT_LIST_HEAD(&ret_buf->openFileList);
init_MUTEX(&ret_buf->tconSem);
#ifdef CONFIG_CIFS_STATS
- ret_buf->stat_lock = SPIN_LOCK_UNLOCKED;
+ spin_lock_init(&ret_buf->stat_lock);
#endif
write_unlock(&GlobalSMBSeslock);
}
atomic_dec(&tconInfoAllocCount);
list_del(&buf_to_free->cifsConnectionList);
write_unlock(&GlobalSMBSeslock);
- if (buf_to_free->nativeFileSystem)
- kfree(buf_to_free->nativeFileSystem);
+ kfree(buf_to_free->nativeFileSystem);
kfree(buf_to_free);
}
struct smb_hdr *
cifs_buf_get(void)
{
- struct smb_hdr *ret_buf = 0;
+ struct smb_hdr *ret_buf = NULL;
/* We could use negotiated size instead of max_msgsize -
but it may be more efficient to always alloc same size
(struct smb_hdr *) mempool_alloc(cifs_req_poolp, SLAB_KERNEL | SLAB_NOFS);
/* clear the first few header bytes */
+ /* for most paths, more is cleared in header_assemble */
if (ret_buf) {
- memset(ret_buf, 0, sizeof (struct smb_hdr));
+ memset(ret_buf, 0, sizeof(struct smb_hdr) + 3);
atomic_inc(&bufAllocCount);
+#ifdef CONFIG_CIFS_STATS2
+ atomic_inc(&totBufAllocCount);
+#endif /* CONFIG_CIFS_STATS2 */
}
return ret_buf;
{
if (buf_to_free == NULL) {
- cFYI(1, ("Null buffer passed to cifs_buf_release"));
+ /* cFYI(1, ("Null buffer passed to cifs_buf_release"));*/
return;
}
mempool_free(buf_to_free,cifs_req_poolp);
return;
}
+struct smb_hdr *
+cifs_small_buf_get(void)
+{
+ struct smb_hdr *ret_buf = NULL;
+
+/* We could use negotiated size instead of max_msgsize -
+ but it may be more efficient to always alloc same size
+ albeit slightly larger than necessary and maxbuffersize
+ defaults to this and can not be bigger */
+ ret_buf =
+ (struct smb_hdr *) mempool_alloc(cifs_sm_req_poolp, SLAB_KERNEL | SLAB_NOFS);
+ if (ret_buf) {
+ /* No need to clear memory here, cleared in header assemble */
+ /* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/
+ atomic_inc(&smBufAllocCount);
+#ifdef CONFIG_CIFS_STATS2
+ atomic_inc(&totSmBufAllocCount);
+#endif /* CONFIG_CIFS_STATS2 */
+
+ }
+ return ret_buf;
+}
+
+void
+cifs_small_buf_release(void *buf_to_free)
+{
+
+ if (buf_to_free == NULL) {
+ cFYI(1, ("Null buffer passed to cifs_small_buf_release"));
+ return;
+ }
+ mempool_free(buf_to_free,cifs_sm_req_poolp);
+
+ atomic_dec(&smBufAllocCount);
+ return;
+}
+
+/*
+ Find a free multiplex id (SMB mid). Otherwise there could be
+ mid collisions which might cause problems, demultiplexing the
+ wrong response to this request. Multiplex ids could collide if
+ one of a series requests takes much longer than the others, or
+ if a very large number of long lived requests (byte range
+ locks or FindNotify requests) are pending. No more than
+ 64K-1 requests can be outstanding at one time. If no
+ mids are available, return zero. A future optimization
+ could make the combination of mids and uid the key we use
+ to demultiplex on (rather than mid alone).
+ In addition to the above check, the cifs demultiplex
+ code already used the command code as a secondary
+ check of the frame and if signing is negotiated the
+ response would be discarded if the mid were the same
+ but the signature was wrong. Since the mid is not put in the
+ pending queue until later (when it is about to be dispatched)
+ we do have to limit the number of outstanding requests
+ to somewhat less than 64K-1 although it is hard to imagine
+ so many threads being in the vfs at one time.
+*/
+__u16 GetNextMid(struct TCP_Server_Info *server)
+{
+ __u16 mid = 0;
+ __u16 last_mid;
+ int collision;
+
+ if(server == NULL)
+ return mid;
+
+ spin_lock(&GlobalMid_Lock);
+ last_mid = server->CurrentMid; /* we do not want to loop forever */
+ server->CurrentMid++;
+ /* This nested loop looks more expensive than it is.
+ In practice the list of pending requests is short,
+ fewer than 50, and the mids are likely to be unique
+ on the first pass through the loop unless some request
+ takes longer than the 64 thousand requests before it
+ (and it would also have to have been a request that
+ did not time out) */
+ while(server->CurrentMid != last_mid) {
+ struct list_head *tmp;
+ struct mid_q_entry *mid_entry;
+
+ collision = 0;
+ if(server->CurrentMid == 0)
+ server->CurrentMid++;
+
+ list_for_each(tmp, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+
+ if ((mid_entry->mid == server->CurrentMid) &&
+ (mid_entry->midState == MID_REQUEST_SUBMITTED)) {
+ /* This mid is in use, try a different one */
+ collision = 1;
+ break;
+ }
+ }
+ if(collision == 0) {
+ mid = server->CurrentMid;
+ break;
+ }
+ server->CurrentMid++;
+ }
+ spin_unlock(&GlobalMid_Lock);
+ return mid;
+}
+
+/* NB: MID can not be set if treeCon not passed in, in that
+ case it is responsbility of caller to set the mid */
void
header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
const struct cifsTconInfo *treeCon, int word_count
- /* length of fixed section (word count) in two byte units */
- )
+ /* length of fixed section (word count) in two byte units */)
{
- int i;
- __u32 tmp;
struct list_head* temp_item;
struct cifsSesInfo * ses;
char *temp = (char *) buffer;
- for (i = 0; i < MAX_CIFS_HDR_SIZE; i++) {
- temp[i] = 0; /* BB is this needed ?? */
- }
+ memset(temp,0,256); /* bigger than MAX_CIFS_HDR_SIZE */
buffer->smb_buf_length =
(2 * word_count) + sizeof (struct smb_hdr) -
4 /* RFC 1001 length field does not count */ +
2 /* for bcc field itself */ ;
- /* Note that this is the only network field that has to be converted to big endian and it is done just before we send it */
+ /* Note that this is the only network field that has to be converted
+ to big endian and it is done just before we send it */
buffer->Protocol[0] = 0xFF;
buffer->Protocol[1] = 'S';
buffer->Command = smb_command;
buffer->Flags = 0x00; /* case sensitive */
buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES;
- tmp = cpu_to_le32(current->tgid);
- buffer->Pid = tmp & 0xFFFF;
- tmp >>= 16;
- buffer->PidHigh = tmp & 0xFFFF;
+ buffer->Pid = cpu_to_le16((__u16)current->tgid);
+ buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16));
spin_lock(&GlobalMid_Lock);
- GlobalMid++;
- buffer->Mid = GlobalMid;
spin_unlock(&GlobalMid_Lock);
if (treeCon) {
buffer->Tid = treeCon->tid;
if (treeCon->ses->capabilities & CAP_STATUS32) {
buffer->Flags2 |= SMBFLG2_ERR_STATUS;
}
-
- buffer->Uid = treeCon->ses->Suid; /* always in LE format */
+ /* Uid is not converted */
+ buffer->Uid = treeCon->ses->Suid;
+ buffer->Mid = GetNextMid(treeCon->ses->server);
if(multiuser_mount != 0) {
/* For the multiuser case, there are few obvious technically */
/* possible mechanisms to match the local linux user (uid) */
/* BB Add support for establishing new tCon and SMB Session */
/* with userid/password pairs found on the smb session */
/* for other target tcp/ip addresses BB */
- if(current->uid != treeCon->ses->linux_uid) {
- cFYI(1,("Multiuser mode and UID did not match tcon uid "));
+ if(current->fsuid != treeCon->ses->linux_uid) {
+ cFYI(1,("Multiuser mode and UID did not match tcon uid"));
read_lock(&GlobalSMBSeslock);
list_for_each(temp_item, &GlobalSMBSessionList) {
ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList);
- if(ses->linux_uid == current->uid) {
+ if(ses->linux_uid == current->fsuid) {
if(ses->server == treeCon->ses->server) {
cFYI(1,("found matching uid substitute right smb_uid"));
buffer->Uid = ses->Suid;
}
if (treeCon->Flags & SMB_SHARE_IS_IN_DFS)
buffer->Flags2 |= SMBFLG2_DFS;
- if(treeCon->ses->server)
+ if (treeCon->nocase)
+ buffer->Flags |= SMBFLG_CASELESS;
+ if((treeCon->ses) && (treeCon->ses->server))
if(treeCon->ses->server->secMode &
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
{
/* Make sure that this really is an SMB, that it is a response,
and that the message ids match */
- if ((*(unsigned int *) smb->Protocol == cpu_to_le32(0x424d53ff)) &&
+ if ((*(__le32 *) smb->Protocol == cpu_to_le32(0x424d53ff)) &&
(mid == smb->Mid)) {
if(smb->Flags & SMBFLG_RESPONSE)
return 0;
if(smb->Command == SMB_COM_LOCKING_ANDX)
return 0;
else
- cERROR(1, ("Rcvd Request not response "));
+ cERROR(1, ("Rcvd Request not response"));
}
} else { /* bad signature or mid */
- if (*(unsigned int *) smb->Protocol != cpu_to_le32(0x424d53ff))
+ if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff))
cERROR(1,
- ("Bad protocol string signature header %x ",
+ ("Bad protocol string signature header %x",
*(unsigned int *) smb->Protocol));
if (mid != smb->Mid)
cERROR(1, ("Mids do not match"));
int
checkSMB(struct smb_hdr *smb, __u16 mid, int length)
{
+ __u32 len = smb->smb_buf_length;
+ __u32 clc_len; /* calculated length */
cFYI(0,
- ("Entering checkSMB with Length: %x, smb_buf_length: %x ",
- length, ntohl(smb->smb_buf_length)));
- if (((unsigned int)length < 2 + sizeof (struct smb_hdr))
- || (ntohl(smb->smb_buf_length) >
- CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4)) {
+ ("Entering checkSMB with Length: %x, smb_buf_length: %x",
+ length, len));
+ if (((unsigned int)length < 2 + sizeof (struct smb_hdr)) ||
+ (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4)) {
if ((unsigned int)length < 2 + sizeof (struct smb_hdr)) {
- cERROR(1, ("Length less than 2 + sizeof smb_hdr "));
- if (((unsigned int)length >= sizeof (struct smb_hdr) - 1)
- && (smb->Status.CifsError != 0))
+ if (((unsigned int)length >=
+ sizeof (struct smb_hdr) - 1)
+ && (smb->Status.CifsError != 0)) {
+ smb->WordCount = 0;
return 0; /* some error cases do not return wct and bcc */
+ } else {
+ cERROR(1, ("Length less than smb header size"));
+ }
}
- if (ntohl(smb->smb_buf_length) >
- CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4)
+ if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4)
cERROR(1,
- ("smb_buf_length greater than CIFS_MAX_MSGSIZE ... "));
+ ("smb_buf_length greater than MaxBufSize"));
cERROR(1,
- ("bad smb detected. Illegal length. The mid=%d",
+ ("bad smb detected. Illegal length. mid=%d",
smb->Mid));
return 1;
}
if (checkSMBhdr(smb, mid))
return 1;
-
- if ((4 + ntohl(smb->smb_buf_length) != smbCalcSize(smb))
- || (4 + ntohl(smb->smb_buf_length) != (unsigned int)length)) {
- return 0;
- } else {
- cERROR(1, ("smbCalcSize %x ", smbCalcSize(smb)));
- cERROR(1,
- ("bad smb size detected. The Mid=%d", smb->Mid));
- return 1;
+ clc_len = smbCalcSize_LE(smb);
+ if ((4 + len != clc_len)
+ || (4 + len != (unsigned int)length)) {
+ cERROR(1, ("Calculated size 0x%x vs actual length 0x%x",
+ clc_len, 4 + len));
+ cERROR(1, ("bad smb size detected for Mid=%d", smb->Mid));
+ /* Windows XP can return a few bytes too much, presumably
+ an illegal pad, at the end of byte range lock responses
+ so we allow for that three byte pad, as long as actual
+ received length is as long or longer than calculated length */
+ /* We have now had to extend this more, since there is a
+ case in which it needs to be bigger still to handle a
+ malformed response to transact2 findfirst from WinXP when
+ access denied is returned and thus bcc and wct are zero
+ but server says length is 0x21 bytes too long as if the server
+ forget to reset the smb rfc1001 length when it reset the
+ wct and bcc to minimum size and drop the t2 parms and data */
+ if((4+len > clc_len) && (len <= clc_len + 512))
+ return 0;
+ else
+ return 1;
}
+ return 0;
}
int
-is_valid_oplock_break(struct smb_hdr *buf)
+is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
{
struct smb_com_lock_req * pSMB = (struct smb_com_lock_req *)buf;
struct list_head *tmp;
struct cifsTconInfo *tcon;
struct cifsFileInfo *netfile;
- /* could add check for smb response flag 0x80 */
- cFYI(1,("Checking for oplock break"));
+ cFYI(1,("Checking for oplock break or dnotify response"));
+ if((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
+ (pSMB->hdr.Flags & SMBFLG_RESPONSE)) {
+ struct smb_com_transaction_change_notify_rsp * pSMBr =
+ (struct smb_com_transaction_change_notify_rsp *)buf;
+ struct file_notify_information * pnotify;
+ __u32 data_offset = 0;
+ if(pSMBr->ByteCount > sizeof(struct file_notify_information)) {
+ data_offset = le32_to_cpu(pSMBr->DataOffset);
+
+ pnotify = (struct file_notify_information *)((char *)&pSMBr->hdr.Protocol
+ + data_offset);
+ cFYI(1,("dnotify on %s with action: 0x%x",pnotify->FileName,
+ pnotify->Action)); /* BB removeme BB */
+ /* cifs_dump_mem("Received notify Data is: ",buf,sizeof(struct smb_hdr)+60); */
+ return TRUE;
+ }
+ if(pSMBr->hdr.Status.CifsError) {
+ cFYI(1,("notify err 0x%d",pSMBr->hdr.Status.CifsError));
+ return TRUE;
+ }
+ return FALSE;
+ }
if(pSMB->hdr.Command != SMB_COM_LOCKING_ANDX)
return FALSE;
if(pSMB->hdr.Flags & SMBFLG_RESPONSE) {
read_lock(&GlobalSMBSeslock);
list_for_each(tmp, &GlobalTreeConnectionList) {
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
- if (tcon->tid == buf->Tid) {
-#ifdef CONFIG_CIFS_STATS
- atomic_inc(&tcon->num_oplock_brks);
-#endif
+ if ((tcon->tid == buf->Tid) && (srv == tcon->ses->server)) {
+ cifs_stats_inc(&tcon->num_oplock_brks);
list_for_each(tmp1,&tcon->openFileList){
- netfile = list_entry(tmp1,struct cifsFileInfo,tlist);
+ netfile = list_entry(tmp1,struct cifsFileInfo,
+ tlist);
if(pSMB->Fid == netfile->netfid) {
struct cifsInodeInfo *pCifsInode;
read_unlock(&GlobalSMBSeslock);
- cFYI(1,("Matching file id, processing oplock break"));
+ cFYI(1,("file id match, oplock break"));
pCifsInode =
CIFS_I(netfile->pInode);
pCifsInode->clientCanCacheAll = FALSE;
if(pSMB->OplockLevel == 0)
- pCifsInode->clientCanCacheRead = FALSE;
+ pCifsInode->clientCanCacheRead
+ = FALSE;
pCifsInode->oplockPending = TRUE;
- AllocOplockQEntry(netfile->pInode, netfile->netfid, tcon);
+ AllocOplockQEntry(netfile->pInode,
+ netfile->netfid,
+ tcon);
cFYI(1,("about to wake up oplock thd"));
- wake_up_process(oplockThread);
+ if(oplockThread)
+ wake_up_process(oplockThread);
return TRUE;
}
}
read_unlock(&GlobalSMBSeslock);
- cFYI(1,("No matching file for oplock break on connection"));
+ cFYI(1,("No matching file for oplock break"));
return TRUE;
}
}
buffer = (unsigned char *) smb_buf;
for (i = 0, j = 0; i < smb_buf_length; i++, j++) {
- if (i % 8 == 0) { /* we have reached the beginning of line */
+ if (i % 8 == 0) { /* have reached the beginning of line */
printk(KERN_DEBUG "| ");
j = 0;
}
else
debug_line[1 + (2 * j)] = '_';
- if (i % 8 == 7) { /* we have reached end of line, time to print ascii */
+ if (i % 8 == 7) { /* reached end of line, time to print ascii */
debug_line[16] = 0;
printk(" | %s\n", debug_line);
}
printk( " | %s\n", debug_line);
return;
}
+
+/* Windows maps these to the user defined 16 bit Unicode range since they are
+ reserved symbols (along with \ and /), otherwise illegal to store
+ in filenames in NTFS */
+#define UNI_ASTERIK (__u16) ('*' + 0xF000)
+#define UNI_QUESTION (__u16) ('?' + 0xF000)
+#define UNI_COLON (__u16) (':' + 0xF000)
+#define UNI_GRTRTHAN (__u16) ('>' + 0xF000)
+#define UNI_LESSTHAN (__u16) ('<' + 0xF000)
+#define UNI_PIPE (__u16) ('|' + 0xF000)
+#define UNI_SLASH (__u16) ('\\' + 0xF000)
+
+/* Convert 16 bit Unicode pathname from wire format to string in current code
+ page. Conversion may involve remapping up the seven characters that are
+ only legal in POSIX-like OS (if they are present in the string). Path
+ names are little endian 16 bit Unicode on the wire */
+int
+cifs_convertUCSpath(char *target, const __le16 * source, int maxlen,
+ const struct nls_table * cp)
+{
+ int i,j,len;
+ __u16 src_char;
+
+ for(i = 0, j = 0; i < maxlen; i++) {
+ src_char = le16_to_cpu(source[i]);
+ switch (src_char) {
+ case 0:
+ goto cUCS_out; /* BB check this BB */
+ case UNI_COLON:
+ target[j] = ':';
+ break;
+ case UNI_ASTERIK:
+ target[j] = '*';
+ break;
+ case UNI_QUESTION:
+ target[j] = '?';
+ break;
+ /* BB We can not handle remapping slash until
+ all the calls to build_path_from_dentry
+ are modified, as they use slash as separator BB */
+ /* case UNI_SLASH:
+ target[j] = '\\';
+ break;*/
+ case UNI_PIPE:
+ target[j] = '|';
+ break;
+ case UNI_GRTRTHAN:
+ target[j] = '>';
+ break;
+ case UNI_LESSTHAN:
+ target[j] = '<';
+ break;
+ default:
+ len = cp->uni2char(src_char, &target[j],
+ NLS_MAX_CHARSET_SIZE);
+ if(len > 0) {
+ j += len;
+ continue;
+ } else {
+ target[j] = '?';
+ }
+ }
+ j++;
+ /* make sure we do not overrun callers allocated temp buffer */
+ if(j >= (2 * NAME_MAX))
+ break;
+ }
+cUCS_out:
+ target[j] = 0;
+ return j;
+}
+
+/* Convert 16 bit Unicode pathname to wire format from string in current code
+ page. Conversion may involve remapping up the seven characters that are
+ only legal in POSIX-like OS (if they are present in the string). Path
+ names are little endian 16 bit Unicode on the wire */
+int
+cifsConvertToUCS(__le16 * target, const char *source, int maxlen,
+ const struct nls_table * cp, int mapChars)
+{
+ int i,j,charlen;
+ int len_remaining = maxlen;
+ char src_char;
+ __u16 temp;
+
+ if(!mapChars)
+ return cifs_strtoUCS(target, source, PATH_MAX, cp);
+
+ for(i = 0, j = 0; i < maxlen; j++) {
+ src_char = source[i];
+ switch (src_char) {
+ case 0:
+ target[j] = 0;
+ goto ctoUCS_out;
+ case ':':
+ target[j] = cpu_to_le16(UNI_COLON);
+ break;
+ case '*':
+ target[j] = cpu_to_le16(UNI_ASTERIK);
+ break;
+ case '?':
+ target[j] = cpu_to_le16(UNI_QUESTION);
+ break;
+ case '<':
+ target[j] = cpu_to_le16(UNI_LESSTHAN);
+ break;
+ case '>':
+ target[j] = cpu_to_le16(UNI_GRTRTHAN);
+ break;
+ case '|':
+ target[j] = cpu_to_le16(UNI_PIPE);
+ break;
+ /* BB We can not handle remapping slash until
+ all the calls to build_path_from_dentry
+ are modified, as they use slash as separator BB */
+ /* case '\\':
+ target[j] = cpu_to_le16(UNI_SLASH);
+ break;*/
+ default:
+ charlen = cp->char2uni(source+i,
+ len_remaining, &temp);
+ /* if no match, use question mark, which
+ at least in some cases servers as wild card */
+ if(charlen < 1) {
+ target[j] = cpu_to_le16(0x003f);
+ charlen = 1;
+ } else
+ target[j] = cpu_to_le16(temp);
+ len_remaining -= charlen;
+ /* character may take more than one byte in the
+ the source string, but will take exactly two
+ bytes in the target string */
+ i+= charlen;
+ continue;
+ }
+ i++; /* move to next char in source string */
+ len_remaining--;
+ }
+
+ctoUCS_out:
+ return i;
+}