X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fcifs%2Fmisc.c;h=19cc294c7c70b5de4b96f193642df5487648a5e4;hb=refs%2Fheads%2Fvserver;hp=0d690174eac0f05feaed30020520a876b147df8c;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 0d690174e..19cc294c7 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -1,7 +1,7 @@ /* * 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 @@ -28,12 +28,12 @@ #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 @@ -49,6 +49,8 @@ _GetXid(void) 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; @@ -69,11 +71,8 @@ sesInfoAlloc(void) { struct cifsSesInfo *ret_buf; - ret_buf = - (struct cifsSesInfo *) kmalloc(sizeof (struct cifsSesInfo), - GFP_KERNEL); + ret_buf = kzalloc(sizeof (struct cifsSesInfo), GFP_KERNEL); if (ret_buf) { - memset(ret_buf, 0, sizeof (struct cifsSesInfo)); write_lock(&GlobalSMBSeslock); atomic_inc(&sesInfoAllocCount); ret_buf->status = CifsNew; @@ -96,14 +95,11 @@ sesInfoFree(struct cifsSesInfo *buf_to_free) 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->domainName); kfree(buf_to_free); } @@ -111,11 +107,8 @@ struct cifsTconInfo * tconInfoAlloc(void) { struct cifsTconInfo *ret_buf; - ret_buf = - (struct cifsTconInfo *) kmalloc(sizeof (struct cifsTconInfo), - GFP_KERNEL); + ret_buf = kzalloc(sizeof (struct cifsTconInfo), GFP_KERNEL); if (ret_buf) { - memset(ret_buf, 0, sizeof (struct cifsTconInfo)); write_lock(&GlobalSMBSeslock); atomic_inc(&tconInfoAllocCount); list_add(&ret_buf->cifsConnectionList, @@ -124,7 +117,7 @@ tconInfoAlloc(void) 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); } @@ -142,27 +135,30 @@ tconInfoFree(struct cifsTconInfo *buf_to_free) 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 albeit slightly larger than necessary and maxbuffersize defaults to this and can not be bigger */ ret_buf = - (struct smb_hdr *) mempool_alloc(cifs_req_poolp, SLAB_KERNEL | SLAB_NOFS); + (struct smb_hdr *) mempool_alloc(cifs_req_poolp, GFP_KERNEL | GFP_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; @@ -173,7 +169,7 @@ cifs_buf_release(void *buf_to_free) { 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); @@ -182,27 +178,130 @@ cifs_buf_release(void *buf_to_free) 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, GFP_KERNEL | GFP_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'; @@ -211,13 +310,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , 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; @@ -227,8 +322,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , 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) */ @@ -254,12 +350,12 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , /* 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; @@ -276,7 +372,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , } 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; @@ -287,12 +385,12 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , return; } -int +static int checkSMBhdr(struct smb_hdr *smb, __u16 mid) { /* 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; @@ -301,12 +399,12 @@ checkSMBhdr(struct smb_hdr *smb, __u16 mid) 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")); @@ -316,46 +414,87 @@ checkSMBhdr(struct smb_hdr *smb, __u16 mid) } int -checkSMB(struct smb_hdr *smb, __u16 mid, int length) +checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int 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)) { - 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)) - return 0; /* some error cases do not return wct and bcc */ - + __u32 len = smb->smb_buf_length; + __u32 clc_len; /* calculated length */ + cFYI(0, ("checkSMB Length: 0x%x, smb_buf_length: 0x%x", length, len)); + + if (length < 2 + sizeof (struct smb_hdr)) { + if ((length >= sizeof (struct smb_hdr) - 1) + && (smb->Status.CifsError != 0)) { + smb->WordCount = 0; + /* some error cases do not return wct and bcc */ + return 0; + } else if ((length == sizeof(struct smb_hdr) + 1) && + (smb->WordCount == 0)) { + char * tmp = (char *)smb; + /* Need to work around a bug in two servers here */ + /* First, check if the part of bcc they sent was zero */ + if (tmp[sizeof(struct smb_hdr)] == 0) { + /* some servers return only half of bcc + * on simple responses (wct, bcc both zero) + * in particular have seen this on + * ulogoffX and FindClose. This leaves + * one byte of bcc potentially unitialized + */ + /* zero rest of bcc */ + tmp[sizeof(struct smb_hdr)+1] = 0; + return 0; + } + cERROR(1,("rcvd invalid byte count (bcc)")); + } else { + cERROR(1, ("Length less than smb header size")); } - if (ntohl(smb->smb_buf_length) > - CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4) - cERROR(1, - ("smb_buf_length greater than CIFS_MAX_MSGSIZE ... ")); - cERROR(1, - ("bad smb detected. Illegal length. The mid=%d", - smb->Mid)); + return 1; + } + if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { + cERROR(1, ("smb length greater than MaxBufSize, mid=%d", + smb->Mid)); return 1; } if (checkSMBhdr(smb, mid)) return 1; + clc_len = smbCalcSize_LE(smb); - 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)); + if(4 + len != length) { + cERROR(1, ("Length read does not match RFC1001 length %d",len)); return 1; } + + if (4 + len != clc_len) { + /* check if bcc wrapped around for large read responses */ + if((len > 64 * 1024) && (len > clc_len)) { + /* check if lengths match mod 64K */ + if(((4 + len) & 0xFFFF) == (clc_len & 0xFFFF)) + return 0; /* bcc wrapped */ + } + cFYI(1, ("Calculated size %d vs length %d mismatch for mid %d", + clc_len, 4 + len, 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 { + cERROR(1, ("RFC1001 size %d bigger than SMB for Mid=%d", + len, smb->Mid)); + 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; @@ -363,8 +502,30 @@ is_valid_oplock_break(struct smb_hdr *buf) 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 Action: 0x%x",pnotify->FileName, + pnotify->Action)); /* BB removeme BB */ + /* cifs_dump_mem("Rcvd notify Data: ",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) { @@ -394,30 +555,33 @@ is_valid_oplock_break(struct smb_hdr *buf) 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; } } @@ -438,7 +602,7 @@ dump_smb(struct smb_hdr *smb_buf, int smb_buf_length) 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; } @@ -449,7 +613,7 @@ dump_smb(struct smb_hdr *smb_buf, int smb_buf_length) 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); } @@ -462,3 +626,145 @@ dump_smb(struct smb_hdr *smb_buf, int smb_buf_length) 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; +}