at v2.6.13 662 lines 20 kB view raw
1/* 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" 31#include "cifs_unicode.h" 32 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){ 455 netfile = list_entry(tmp1,struct cifsFileInfo, 456 tlist); 457 if(pSMB->Fid == netfile->netfid) { 458 struct cifsInodeInfo *pCifsInode; 459 read_unlock(&GlobalSMBSeslock); 460 cFYI(1,("file id match, oplock break")); 461 pCifsInode = 462 CIFS_I(netfile->pInode); 463 pCifsInode->clientCanCacheAll = FALSE; 464 if(pSMB->OplockLevel == 0) 465 pCifsInode->clientCanCacheRead 466 = FALSE; 467 pCifsInode->oplockPending = TRUE; 468 AllocOplockQEntry(netfile->pInode, 469 netfile->netfid, 470 tcon); 471 cFYI(1,("about to wake up oplock thd")); 472 if(oplockThread) 473 wake_up_process(oplockThread); 474 return TRUE; 475 } 476 } 477 read_unlock(&GlobalSMBSeslock); 478 cFYI(1,("No matching file for oplock break")); 479 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++) { 499 if (i % 8 == 0) { /* have reached the beginning of line */ 500 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 510 if (i % 8 == 7) { /* reached end of line, time to print ascii */ 511 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} 523 524/* 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 */ 527#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) 534 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; 544 __u16 src_char; 545 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; 560 /* 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;*/ 566 case UNI_PIPE: 567 target[j] = '|'; 568 break; 569 case UNI_GRTRTHAN: 570 target[j] = '>'; 571 break; 572 case UNI_LESSTHAN: 573 target[j] = '<'; 574 break; 575 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++; 586 /* make sure we do not overrun callers allocated temp buffer */ 587 if(j >= (2 * NAME_MAX)) 588 break; 589 } 590cUCS_out: 591 target[j] = 0; 592 return j; 593} 594 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) 608 return cifs_strtoUCS((wchar_t *) target, source, PATH_MAX, cp); 609 610 for(i = 0, j = 0; i < maxlen; j++) { 611 src_char = source[i]; 612 switch (src_char) { 613 case 0: 614 target[j] = 0; 615 goto ctoUCS_out; 616 case ':': 617 target[j] = cpu_to_le16(UNI_COLON); 618 break; 619 case '*': 620 target[j] = cpu_to_le16(UNI_ASTERIK); 621 break; 622 case '?': 623 target[j] = cpu_to_le16(UNI_QUESTION); 624 break; 625 case '<': 626 target[j] = cpu_to_le16(UNI_LESSTHAN); 627 break; 628 case '>': 629 target[j] = cpu_to_le16(UNI_GRTRTHAN); 630 break; 631 case '|': 632 target[j] = cpu_to_le16(UNI_PIPE); 633 break; 634 /* BB We can not handle remapping slash until 635 all the calls to build_path_from_dentry 636 are modified, as they use slash as separator BB */ 637 /* case '\\': 638 target[j] = cpu_to_le16(UNI_SLASH); 639 break;*/ 640 default: 641 charlen = cp->char2uni(source+i, 642 len_remaining, target+j); 643 /* if no match, use question mark, which 644 at least in some cases servers as wild card */ 645 if(charlen < 1) { 646 target[j] = cpu_to_le16(0x003f); 647 charlen = 1; 648 } 649 len_remaining -= charlen; 650 /* character may take more than one byte in the 651 the source string, but will take exactly two 652 bytes in the target string */ 653 i+= charlen; 654 continue; 655 } 656 i++; /* move to next char in source string */ 657 len_remaining--; 658 } 659 660ctoUCS_out: 661 return i; 662}