blob: 072b4ee8c53e1a28c51002d8a331c497a0b8c05c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * fs/cifs/misc.c
3 *
4 * Copyright (C) International Business Machines Corp., 2002,2004
5 * Author(s): Steve French (sfrench@us.ibm.com)
6 *
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
15 * the GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <linux/slab.h>
23#include <linux/ctype.h>
24#include <linux/mempool.h>
25#include "cifspdu.h"
26#include "cifsglob.h"
27#include "cifsproto.h"
28#include "cifs_debug.h"
29#include "smberr.h"
30#include "nterr.h"
Steve French6c91d362005-04-28 22:41:06 -070031#include "cifs_unicode.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
33extern mempool_t *cifs_sm_req_poolp;
34extern mempool_t *cifs_req_poolp;
35extern struct task_struct * oplockThread;
36
37static __u16 GlobalMid; /* multiplex id - rotating counter */
38
39/* The xid serves as a useful identifier for each incoming vfs request,
40 in a similar way to the mid which is useful to track each sent smb,
41 and CurrentXid can also provide a running counter (although it
42 will eventually wrap past zero) of the total vfs operations handled
43 since the cifs fs was mounted */
44
45unsigned int
46_GetXid(void)
47{
48 unsigned int xid;
49
50 spin_lock(&GlobalMid_Lock);
51 GlobalTotalActiveXid++;
52 if (GlobalTotalActiveXid > GlobalMaxActiveXid)
53 GlobalMaxActiveXid = GlobalTotalActiveXid; /* keep high water mark for number of simultaneous vfs ops in our filesystem */
54 xid = GlobalCurrentXid++;
55 spin_unlock(&GlobalMid_Lock);
56 return xid;
57}
58
59void
60_FreeXid(unsigned int xid)
61{
62 spin_lock(&GlobalMid_Lock);
63 /* if(GlobalTotalActiveXid == 0)
64 BUG(); */
65 GlobalTotalActiveXid--;
66 spin_unlock(&GlobalMid_Lock);
67}
68
69struct cifsSesInfo *
70sesInfoAlloc(void)
71{
72 struct cifsSesInfo *ret_buf;
73
74 ret_buf =
75 (struct cifsSesInfo *) kmalloc(sizeof (struct cifsSesInfo),
76 GFP_KERNEL);
77 if (ret_buf) {
78 memset(ret_buf, 0, sizeof (struct cifsSesInfo));
79 write_lock(&GlobalSMBSeslock);
80 atomic_inc(&sesInfoAllocCount);
81 ret_buf->status = CifsNew;
82 list_add(&ret_buf->cifsSessionList, &GlobalSMBSessionList);
83 init_MUTEX(&ret_buf->sesSem);
84 write_unlock(&GlobalSMBSeslock);
85 }
86 return ret_buf;
87}
88
89void
90sesInfoFree(struct cifsSesInfo *buf_to_free)
91{
92 if (buf_to_free == NULL) {
93 cFYI(1, ("Null buffer passed to sesInfoFree"));
94 return;
95 }
96
97 write_lock(&GlobalSMBSeslock);
98 atomic_dec(&sesInfoAllocCount);
99 list_del(&buf_to_free->cifsSessionList);
100 write_unlock(&GlobalSMBSeslock);
101 if (buf_to_free->serverOS)
102 kfree(buf_to_free->serverOS);
103 if (buf_to_free->serverDomain)
104 kfree(buf_to_free->serverDomain);
105 if (buf_to_free->serverNOS)
106 kfree(buf_to_free->serverNOS);
107 if (buf_to_free->password)
108 kfree(buf_to_free->password);
109 kfree(buf_to_free);
110}
111
112struct cifsTconInfo *
113tconInfoAlloc(void)
114{
115 struct cifsTconInfo *ret_buf;
116 ret_buf =
117 (struct cifsTconInfo *) kmalloc(sizeof (struct cifsTconInfo),
118 GFP_KERNEL);
119 if (ret_buf) {
120 memset(ret_buf, 0, sizeof (struct cifsTconInfo));
121 write_lock(&GlobalSMBSeslock);
122 atomic_inc(&tconInfoAllocCount);
123 list_add(&ret_buf->cifsConnectionList,
124 &GlobalTreeConnectionList);
125 ret_buf->tidStatus = CifsNew;
126 INIT_LIST_HEAD(&ret_buf->openFileList);
127 init_MUTEX(&ret_buf->tconSem);
128#ifdef CONFIG_CIFS_STATS
129 spin_lock_init(&ret_buf->stat_lock);
130#endif
131 write_unlock(&GlobalSMBSeslock);
132 }
133 return ret_buf;
134}
135
136void
137tconInfoFree(struct cifsTconInfo *buf_to_free)
138{
139 if (buf_to_free == NULL) {
140 cFYI(1, ("Null buffer passed to tconInfoFree"));
141 return;
142 }
143 write_lock(&GlobalSMBSeslock);
144 atomic_dec(&tconInfoAllocCount);
145 list_del(&buf_to_free->cifsConnectionList);
146 write_unlock(&GlobalSMBSeslock);
147 if (buf_to_free->nativeFileSystem)
148 kfree(buf_to_free->nativeFileSystem);
149 kfree(buf_to_free);
150}
151
152struct smb_hdr *
153cifs_buf_get(void)
154{
155 struct smb_hdr *ret_buf = NULL;
156
157/* We could use negotiated size instead of max_msgsize -
158 but it may be more efficient to always alloc same size
159 albeit slightly larger than necessary and maxbuffersize
160 defaults to this and can not be bigger */
161 ret_buf =
162 (struct smb_hdr *) mempool_alloc(cifs_req_poolp, SLAB_KERNEL | SLAB_NOFS);
163
164 /* clear the first few header bytes */
165 /* for most paths, more is cleared in header_assemble */
166 if (ret_buf) {
167 memset(ret_buf, 0, sizeof(struct smb_hdr) + 3);
168 atomic_inc(&bufAllocCount);
169 }
170
171 return ret_buf;
172}
173
174void
175cifs_buf_release(void *buf_to_free)
176{
177
178 if (buf_to_free == NULL) {
179 /* cFYI(1, ("Null buffer passed to cifs_buf_release"));*/
180 return;
181 }
182 mempool_free(buf_to_free,cifs_req_poolp);
183
184 atomic_dec(&bufAllocCount);
185 return;
186}
187
188struct smb_hdr *
189cifs_small_buf_get(void)
190{
191 struct smb_hdr *ret_buf = NULL;
192
193/* We could use negotiated size instead of max_msgsize -
194 but it may be more efficient to always alloc same size
195 albeit slightly larger than necessary and maxbuffersize
196 defaults to this and can not be bigger */
197 ret_buf =
198 (struct smb_hdr *) mempool_alloc(cifs_sm_req_poolp, SLAB_KERNEL | SLAB_NOFS);
199 if (ret_buf) {
200 /* No need to clear memory here, cleared in header assemble */
201 /* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/
202 atomic_inc(&smBufAllocCount);
203 }
204 return ret_buf;
205}
206
207void
208cifs_small_buf_release(void *buf_to_free)
209{
210
211 if (buf_to_free == NULL) {
212 cFYI(1, ("Null buffer passed to cifs_small_buf_release"));
213 return;
214 }
215 mempool_free(buf_to_free,cifs_sm_req_poolp);
216
217 atomic_dec(&smBufAllocCount);
218 return;
219}
220
221void
222header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
223 const struct cifsTconInfo *treeCon, int word_count
224 /* length of fixed section (word count) in two byte units */)
225{
226 struct list_head* temp_item;
227 struct cifsSesInfo * ses;
228 char *temp = (char *) buffer;
229
230 memset(temp,0,MAX_CIFS_HDR_SIZE);
231
232 buffer->smb_buf_length =
233 (2 * word_count) + sizeof (struct smb_hdr) -
234 4 /* RFC 1001 length field does not count */ +
235 2 /* for bcc field itself */ ;
236 /* 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 */
237
238 buffer->Protocol[0] = 0xFF;
239 buffer->Protocol[1] = 'S';
240 buffer->Protocol[2] = 'M';
241 buffer->Protocol[3] = 'B';
242 buffer->Command = smb_command;
243 buffer->Flags = 0x00; /* case sensitive */
244 buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES;
245 buffer->Pid = cpu_to_le16((__u16)current->tgid);
246 buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16));
247 spin_lock(&GlobalMid_Lock);
248 GlobalMid++;
249 buffer->Mid = GlobalMid;
250 spin_unlock(&GlobalMid_Lock);
251 if (treeCon) {
252 buffer->Tid = treeCon->tid;
253 if (treeCon->ses) {
254 if (treeCon->ses->capabilities & CAP_UNICODE)
255 buffer->Flags2 |= SMBFLG2_UNICODE;
256 if (treeCon->ses->capabilities & CAP_STATUS32) {
257 buffer->Flags2 |= SMBFLG2_ERR_STATUS;
258 }
259
260 buffer->Uid = treeCon->ses->Suid; /* always in LE format */
261 if(multiuser_mount != 0) {
262 /* For the multiuser case, there are few obvious technically */
263 /* possible mechanisms to match the local linux user (uid) */
264 /* to a valid remote smb user (smb_uid): */
265 /* 1) Query Winbind (or other local pam/nss daemon */
266 /* for userid/password/logon_domain or credential */
267 /* 2) Query Winbind for uid to sid to username mapping */
268 /* and see if we have a matching password for existing*/
269 /* session for that user perhas getting password by */
270 /* adding a new pam_cifs module that stores passwords */
271 /* so that the cifs vfs can get at that for all logged*/
272 /* on users */
273 /* 3) (Which is the mechanism we have chosen) */
274 /* Search through sessions to the same server for a */
275 /* a match on the uid that was passed in on mount */
276 /* with the current processes uid (or euid?) and use */
277 /* that smb uid. If no existing smb session for */
278 /* that uid found, use the default smb session ie */
279 /* the smb session for the volume mounted which is */
280 /* the same as would be used if the multiuser mount */
281 /* flag were disabled. */
282
283 /* BB Add support for establishing new tCon and SMB Session */
284 /* with userid/password pairs found on the smb session */
285 /* for other target tcp/ip addresses BB */
286 if(current->uid != treeCon->ses->linux_uid) {
287 cFYI(1,("Multiuser mode and UID did not match tcon uid "));
288 read_lock(&GlobalSMBSeslock);
289 list_for_each(temp_item, &GlobalSMBSessionList) {
290 ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList);
291 if(ses->linux_uid == current->uid) {
292 if(ses->server == treeCon->ses->server) {
293 cFYI(1,("found matching uid substitute right smb_uid"));
294 buffer->Uid = ses->Suid;
295 break;
296 } else {
297 /* BB eventually call cifs_setup_session here */
298 cFYI(1,("local UID found but smb sess with this server does not exist"));
299 }
300 }
301 }
302 read_unlock(&GlobalSMBSeslock);
303 }
304 }
305 }
306 if (treeCon->Flags & SMB_SHARE_IS_IN_DFS)
307 buffer->Flags2 |= SMBFLG2_DFS;
308 if((treeCon->ses) && (treeCon->ses->server))
309 if(treeCon->ses->server->secMode &
310 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
311 buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
312 }
313
314/* endian conversion of flags is now done just before sending */
315 buffer->WordCount = (char) word_count;
316 return;
317}
318
319int
320checkSMBhdr(struct smb_hdr *smb, __u16 mid)
321{
322 /* Make sure that this really is an SMB, that it is a response,
323 and that the message ids match */
324 if ((*(__le32 *) smb->Protocol == cpu_to_le32(0x424d53ff)) &&
325 (mid == smb->Mid)) {
326 if(smb->Flags & SMBFLG_RESPONSE)
327 return 0;
328 else {
329 /* only one valid case where server sends us request */
330 if(smb->Command == SMB_COM_LOCKING_ANDX)
331 return 0;
332 else
333 cERROR(1, ("Rcvd Request not response "));
334 }
335 } else { /* bad signature or mid */
336 if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff))
337 cERROR(1,
338 ("Bad protocol string signature header %x ",
339 *(unsigned int *) smb->Protocol));
340 if (mid != smb->Mid)
341 cERROR(1, ("Mids do not match"));
342 }
343 cERROR(1, ("bad smb detected. The Mid=%d", smb->Mid));
344 return 1;
345}
346
347int
348checkSMB(struct smb_hdr *smb, __u16 mid, int length)
349{
350 __u32 len = be32_to_cpu(smb->smb_buf_length);
351 cFYI(0,
352 ("Entering checkSMB with Length: %x, smb_buf_length: %x ",
353 length, len));
354 if (((unsigned int)length < 2 + sizeof (struct smb_hdr)) ||
355 (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4)) {
356 if ((unsigned int)length < 2 + sizeof (struct smb_hdr)) {
357 if (((unsigned int)length >=
358 sizeof (struct smb_hdr) - 1)
359 && (smb->Status.CifsError != 0)) {
360 smb->WordCount = 0;
361 return 0; /* some error cases do not return wct and bcc */
362 } else {
363 cERROR(1, ("Length less than smb header size"));
364 }
365
366 }
367 if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4)
368 cERROR(1,
369 ("smb_buf_length greater than MaxBufSize"));
370 cERROR(1,
371 ("bad smb detected. Illegal length. The mid=%d",
372 smb->Mid));
373 return 1;
374 }
375
376 if (checkSMBhdr(smb, mid))
377 return 1;
378
379 if ((4 + len != smbCalcSize(smb))
380 || (4 + len != (unsigned int)length)) {
381 return 0;
382 } else {
383 cERROR(1, ("smbCalcSize %x ", smbCalcSize(smb)));
384 cERROR(1,
385 ("bad smb size detected. The Mid=%d", smb->Mid));
386 return 1;
387 }
388}
389int
390is_valid_oplock_break(struct smb_hdr *buf)
391{
392 struct smb_com_lock_req * pSMB = (struct smb_com_lock_req *)buf;
393 struct list_head *tmp;
394 struct list_head *tmp1;
395 struct cifsTconInfo *tcon;
396 struct cifsFileInfo *netfile;
397
398 cFYI(1,("Checking for oplock break or dnotify response"));
399 if((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
400 (pSMB->hdr.Flags & SMBFLG_RESPONSE)) {
401 struct smb_com_transaction_change_notify_rsp * pSMBr =
402 (struct smb_com_transaction_change_notify_rsp *)buf;
403 struct file_notify_information * pnotify;
404 __u32 data_offset = 0;
405 if(pSMBr->ByteCount > sizeof(struct file_notify_information)) {
406 data_offset = le32_to_cpu(pSMBr->DataOffset);
407
408 pnotify = (struct file_notify_information *)((char *)&pSMBr->hdr.Protocol
409 + data_offset);
410 cFYI(1,("dnotify on %s with action: 0x%x",pnotify->FileName,
411 pnotify->Action)); /* BB removeme BB */
412 /* cifs_dump_mem("Received notify Data is: ",buf,sizeof(struct smb_hdr)+60); */
413 return TRUE;
414 }
415 if(pSMBr->hdr.Status.CifsError) {
416 cFYI(1,("notify err 0x%d",pSMBr->hdr.Status.CifsError));
417 return TRUE;
418 }
419 return FALSE;
420 }
421 if(pSMB->hdr.Command != SMB_COM_LOCKING_ANDX)
422 return FALSE;
423 if(pSMB->hdr.Flags & SMBFLG_RESPONSE) {
424 /* no sense logging error on invalid handle on oplock
425 break - harmless race between close request and oplock
426 break response is expected from time to time writing out
427 large dirty files cached on the client */
428 if ((NT_STATUS_INVALID_HANDLE) ==
429 le32_to_cpu(pSMB->hdr.Status.CifsError)) {
430 cFYI(1,("invalid handle on oplock break"));
431 return TRUE;
432 } else if (ERRbadfid ==
433 le16_to_cpu(pSMB->hdr.Status.DosError.Error)) {
434 return TRUE;
435 } else {
436 return FALSE; /* on valid oplock brk we get "request" */
437 }
438 }
439 if(pSMB->hdr.WordCount != 8)
440 return FALSE;
441
442 cFYI(1,(" oplock type 0x%d level 0x%d",pSMB->LockType,pSMB->OplockLevel));
443 if(!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE))
444 return FALSE;
445
446 /* look up tcon based on tid & uid */
447 read_lock(&GlobalSMBSeslock);
448 list_for_each(tmp, &GlobalTreeConnectionList) {
449 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
450 if (tcon->tid == buf->Tid) {
451#ifdef CONFIG_CIFS_STATS
452 atomic_inc(&tcon->num_oplock_brks);
453#endif
454 list_for_each(tmp1,&tcon->openFileList){
Steve French57337e42005-04-28 22:41:10 -0700455 netfile = list_entry(tmp1,struct cifsFileInfo,
456 tlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 if(pSMB->Fid == netfile->netfid) {
458 struct cifsInodeInfo *pCifsInode;
459 read_unlock(&GlobalSMBSeslock);
Steve French57337e42005-04-28 22:41:10 -0700460 cFYI(1,("file id match, oplock break"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 pCifsInode =
462 CIFS_I(netfile->pInode);
463 pCifsInode->clientCanCacheAll = FALSE;
464 if(pSMB->OplockLevel == 0)
Steve French57337e42005-04-28 22:41:10 -0700465 pCifsInode->clientCanCacheRead
466 = FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 pCifsInode->oplockPending = TRUE;
Steve French57337e42005-04-28 22:41:10 -0700468 AllocOplockQEntry(netfile->pInode,
469 netfile->netfid,
470 tcon);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 cFYI(1,("about to wake up oplock thd"));
Steve French57337e42005-04-28 22:41:10 -0700472 if(oplockThread)
473 wake_up_process(oplockThread);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 return TRUE;
475 }
476 }
477 read_unlock(&GlobalSMBSeslock);
Steve French57337e42005-04-28 22:41:10 -0700478 cFYI(1,("No matching file for oplock break"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 return TRUE;
480 }
481 }
482 read_unlock(&GlobalSMBSeslock);
483 cFYI(1,("Can not process oplock break for non-existent connection"));
484 return TRUE;
485}
486
487void
488dump_smb(struct smb_hdr *smb_buf, int smb_buf_length)
489{
490 int i, j;
491 char debug_line[17];
492 unsigned char *buffer;
493
494 if (traceSMB == 0)
495 return;
496
497 buffer = (unsigned char *) smb_buf;
498 for (i = 0, j = 0; i < smb_buf_length; i++, j++) {
Steve French57337e42005-04-28 22:41:10 -0700499 if (i % 8 == 0) { /* have reached the beginning of line */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 printk(KERN_DEBUG "| ");
501 j = 0;
502 }
503 printk("%0#4x ", buffer[i]);
504 debug_line[2 * j] = ' ';
505 if (isprint(buffer[i]))
506 debug_line[1 + (2 * j)] = buffer[i];
507 else
508 debug_line[1 + (2 * j)] = '_';
509
Steve French57337e42005-04-28 22:41:10 -0700510 if (i % 8 == 7) { /* reached end of line, time to print ascii */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 debug_line[16] = 0;
512 printk(" | %s\n", debug_line);
513 }
514 }
515 for (; j < 8; j++) {
516 printk(" ");
517 debug_line[2 * j] = ' ';
518 debug_line[1 + (2 * j)] = ' ';
519 }
520 printk( " | %s\n", debug_line);
521 return;
522}
Steve French6a0b4822005-04-28 22:41:05 -0700523
Steve French6a0b4822005-04-28 22:41:05 -0700524/* Windows maps these to the user defined 16 bit Unicode range since they are
525 reserved symbols (along with \ and /), otherwise illegal to store
526 in filenames in NTFS */
Steve Frenchd0724712005-04-28 22:41:06 -0700527#define UNI_ASTERIK (__u16) ('*' + 0xF000)
528#define UNI_QUESTION (__u16) ('?' + 0xF000)
529#define UNI_COLON (__u16) (':' + 0xF000)
530#define UNI_GRTRTHAN (__u16) ('>' + 0xF000)
531#define UNI_LESSTHAN (__u16) ('<' + 0xF000)
532#define UNI_PIPE (__u16) ('|' + 0xF000)
533#define UNI_SLASH (__u16) ('\\' + 0xF000)
Steve French6a0b4822005-04-28 22:41:05 -0700534
535/* Convert 16 bit Unicode pathname from wire format to string in current code
536 page. Conversion may involve remapping up the seven characters that are
537 only legal in POSIX-like OS (if they are present in the string). Path
538 names are little endian 16 bit Unicode on the wire */
539int
540cifs_convertUCSpath(char *target, const __le16 * source, int maxlen,
541 const struct nls_table * cp)
542{
543 int i,j,len;
Steve Frenchd0724712005-04-28 22:41:06 -0700544 __u16 src_char;
Steve French6a0b4822005-04-28 22:41:05 -0700545
546 for(i = 0, j = 0; i < maxlen; i++) {
547 src_char = le16_to_cpu(source[i]);
548 switch (src_char) {
549 case 0:
550 goto cUCS_out; /* BB check this BB */
551 case UNI_COLON:
552 target[j] = ':';
553 break;
554 case UNI_ASTERIK:
555 target[j] = '*';
556 break;
557 case UNI_QUESTION:
558 target[j] = '?';
559 break;
Steve French6c91d362005-04-28 22:41:06 -0700560 /* BB We can not handle remapping slash until
561 all the calls to build_path_from_dentry
562 are modified, as they use slash as separator BB */
563 /* case UNI_SLASH:
564 target[j] = '\\';
565 break;*/
Steve French6a0b4822005-04-28 22:41:05 -0700566 case UNI_PIPE:
567 target[j] = '|';
568 break;
569 case UNI_GRTRTHAN:
570 target[j] = '>';
571 break;
572 case UNI_LESSTHAN:
573 target[j] = '<';
Steve French67594fe2005-05-17 13:04:49 -0500574 break;
Steve French6a0b4822005-04-28 22:41:05 -0700575 default:
576 len = cp->uni2char(src_char, &target[j],
577 NLS_MAX_CHARSET_SIZE);
578 if(len > 0) {
579 j += len;
580 continue;
581 } else {
582 target[j] = '?';
583 }
584 }
585 j++;
Steve French57337e42005-04-28 22:41:10 -0700586 /* make sure we do not overrun callers allocated temp buffer */
Steve French6a0b4822005-04-28 22:41:05 -0700587 if(j >= (2 * NAME_MAX))
588 break;
589 }
590cUCS_out:
591 target[j] = 0;
592 return j;
593}
Steve French6c91d362005-04-28 22:41:06 -0700594
595/* Convert 16 bit Unicode pathname to wire format from string in current code
596 page. Conversion may involve remapping up the seven characters that are
597 only legal in POSIX-like OS (if they are present in the string). Path
598 names are little endian 16 bit Unicode on the wire */
599int
600cifsConvertToUCS(__le16 * target, const char *source, int maxlen,
601 const struct nls_table * cp, int mapChars)
602{
603 int i,j,charlen;
604 int len_remaining = maxlen;
605 char src_char;
606
607 if(!mapChars)
Steve French57337e42005-04-28 22:41:10 -0700608 return cifs_strtoUCS((wchar_t *) target, source, PATH_MAX, cp);
Steve French6c91d362005-04-28 22:41:06 -0700609
610 for(i = 0, j = 0; i < maxlen; j++) {
611 src_char = source[i];
612 switch (src_char) {
613 case 0:
614 goto ctoUCS_out;
615 case ':':
616 target[j] = cpu_to_le16(UNI_COLON);
617 break;
618 case '*':
619 target[j] = cpu_to_le16(UNI_ASTERIK);
620 break;
621 case '?':
622 target[j] = cpu_to_le16(UNI_QUESTION);
623 break;
624 case '<':
625 target[j] = cpu_to_le16(UNI_LESSTHAN);
626 break;
627 case '>':
628 target[j] = cpu_to_le16(UNI_GRTRTHAN);
629 break;
630 case '|':
631 target[j] = cpu_to_le16(UNI_PIPE);
632 break;
633 /* BB We can not handle remapping slash until
634 all the calls to build_path_from_dentry
635 are modified, as they use slash as separator BB */
636 /* case '\\':
637 target[j] = cpu_to_le16(UNI_SLASH);
638 break;*/
639 default:
640 charlen = cp->char2uni(source+i,
641 len_remaining, target+j);
642 /* if no match, use question mark, which
643 at least in some cases servers as wild card */
644 if(charlen < 1) {
645 target[j] = cpu_to_le16(0x003f);
646 charlen = 1;
647 }
648 len_remaining -= charlen;
649 /* character may take more than one byte in the
650 the source string, but will take exactly two
651 bytes in the target string */
652 i+= charlen;
653 continue;
654 }
655 i++; /* move to next char in source string */
656 len_remaining--;
657 }
658
659ctoUCS_out:
660 return i;
661}