1*da6c28aaSamw /* 2*da6c28aaSamw * CDDL HEADER START 3*da6c28aaSamw * 4*da6c28aaSamw * The contents of this file are subject to the terms of the 5*da6c28aaSamw * Common Development and Distribution License (the "License"). 6*da6c28aaSamw * You may not use this file except in compliance with the License. 7*da6c28aaSamw * 8*da6c28aaSamw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*da6c28aaSamw * or http://www.opensolaris.org/os/licensing. 10*da6c28aaSamw * See the License for the specific language governing permissions 11*da6c28aaSamw * and limitations under the License. 12*da6c28aaSamw * 13*da6c28aaSamw * When distributing Covered Code, include this CDDL HEADER in each 14*da6c28aaSamw * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*da6c28aaSamw * If applicable, add the following below this CDDL HEADER, with the 16*da6c28aaSamw * fields enclosed by brackets "[]" replaced with your own identifying 17*da6c28aaSamw * information: Portions Copyright [yyyy] [name of copyright owner] 18*da6c28aaSamw * 19*da6c28aaSamw * CDDL HEADER END 20*da6c28aaSamw */ 21*da6c28aaSamw /* 22*da6c28aaSamw * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23*da6c28aaSamw * Use is subject to license terms. 24*da6c28aaSamw */ 25*da6c28aaSamw 26*da6c28aaSamw #pragma ident "%Z%%M% %I% %E% SMI" 27*da6c28aaSamw 28*da6c28aaSamw #include <sys/types.h> 29*da6c28aaSamw #include <sys/sunddi.h> 30*da6c28aaSamw #include <sys/errno.h> 31*da6c28aaSamw #include <smbsrv/string.h> 32*da6c28aaSamw #include <smbsrv/ctype.h> 33*da6c28aaSamw #include <smbsrv/smb_i18n.h> 34*da6c28aaSamw #include <smbsrv/smb_vops.h> 35*da6c28aaSamw #include <smbsrv/smb_incl.h> 36*da6c28aaSamw #include <smbsrv/smb_fsops.h> 37*da6c28aaSamw 38*da6c28aaSamw static int smb_match_unknown(char *name, char *pattern); 39*da6c28aaSamw static int smb_is_reserved_dos_name(char *name); 40*da6c28aaSamw static int smb_match_reserved(char *name, char *rsrv); 41*da6c28aaSamw 42*da6c28aaSamw /* 43*da6c28aaSamw * smb_match_name 44*da6c28aaSamw * 45*da6c28aaSamw * This function will mangle the "name" field and save the resulted 46*da6c28aaSamw * shortname to the "shortname" field and 8.3 name to "name83" field. 47*da6c28aaSamw * The three fields, "name", "shortname" and "name83" will then be 48*da6c28aaSamw * sent for pattern match with "pattern" field. 49*da6c28aaSamw * 50*da6c28aaSamw * The 0 is returned when the name is a reserved dos name, no match 51*da6c28aaSamw * for the pattern or any type of failure. The 1 is returned when 52*da6c28aaSamw * there is a match. 53*da6c28aaSamw */ 54*da6c28aaSamw int 55*da6c28aaSamw smb_match_name(ino64_t fileid, char *name, char *shortname, 56*da6c28aaSamw char *name83, char *pattern, int ignore_case) 57*da6c28aaSamw { 58*da6c28aaSamw int rc = 0; 59*da6c28aaSamw int force; 60*da6c28aaSamw 61*da6c28aaSamw /* Leading or trailing dots are disallowed */ 62*da6c28aaSamw if (smb_is_reserved_dos_name(name)) 63*da6c28aaSamw return (0); 64*da6c28aaSamw 65*da6c28aaSamw for (force = 0; (force < 2 && rc == 0); force++) { 66*da6c28aaSamw (void) smb_mangle_name(fileid, name, shortname, name83, force); 67*da6c28aaSamw 68*da6c28aaSamw rc = smb_match_ci(pattern, name); 69*da6c28aaSamw 70*da6c28aaSamw /* If no match, check for shortname (if any) */ 71*da6c28aaSamw 72*da6c28aaSamw if (rc == 0 && strchr(pattern, '~')) 73*da6c28aaSamw if (*shortname != 0) 74*da6c28aaSamw rc = smb_match_ci(pattern, shortname); 75*da6c28aaSamw 76*da6c28aaSamw /* 77*da6c28aaSamw * Sigh... DOS Shells use short name 78*da6c28aaSamw * interchangeably with long case sensitive 79*da6c28aaSamw * names. So check that too... 80*da6c28aaSamw */ 81*da6c28aaSamw if ((rc == 0) && !ignore_case) 82*da6c28aaSamw rc = smb_match83(pattern, name83); 83*da6c28aaSamw 84*da6c28aaSamw /* 85*da6c28aaSamw * Still not found and potentially a premangled name... 86*da6c28aaSamw * Check to see if the butt-head programmer is 87*da6c28aaSamw * assuming that we mangle names in the same manner 88*da6c28aaSamw * as NT... 89*da6c28aaSamw */ 90*da6c28aaSamw if (rc == 0) 91*da6c28aaSamw rc = smb_match_unknown(name, pattern); 92*da6c28aaSamw } 93*da6c28aaSamw 94*da6c28aaSamw return (rc); 95*da6c28aaSamw } 96*da6c28aaSamw 97*da6c28aaSamw /* 98*da6c28aaSamw * smb_match_unknown 99*da6c28aaSamw * 100*da6c28aaSamw * I couldn't figure out what the assumptions of this peice of 101*da6c28aaSamw * code about the format of pattern and name are and so how 102*da6c28aaSamw * it's trying to match them. I just cleaned it up a little bit! 103*da6c28aaSamw * 104*da6c28aaSamw * If anybody could figure out what this is doing, please put 105*da6c28aaSamw * comment here and change the function's name! 106*da6c28aaSamw */ 107*da6c28aaSamw static int 108*da6c28aaSamw smb_match_unknown(char *name, char *pattern) 109*da6c28aaSamw { 110*da6c28aaSamw int rc; 111*da6c28aaSamw char nc, pc; 112*da6c28aaSamw char *np, *pp; 113*da6c28aaSamw 114*da6c28aaSamw rc = 0; 115*da6c28aaSamw if (utf8_isstrupr(pattern) <= 0) 116*da6c28aaSamw return (rc); 117*da6c28aaSamw 118*da6c28aaSamw np = name; 119*da6c28aaSamw pp = pattern; 120*da6c28aaSamw 121*da6c28aaSamw pc = *pattern; 122*da6c28aaSamw while ((nc = *np++) != 0) { 123*da6c28aaSamw if (nc == ' ') 124*da6c28aaSamw continue; 125*da6c28aaSamw 126*da6c28aaSamw nc = mts_toupper(nc); 127*da6c28aaSamw if ((pc = *pp++) != nc) 128*da6c28aaSamw break; 129*da6c28aaSamw } 130*da6c28aaSamw 131*da6c28aaSamw if ((pc == '~') && 132*da6c28aaSamw (pp != (pattern + 1)) && 133*da6c28aaSamw ((pc = *pp++) != 0)) { 134*da6c28aaSamw while (mts_isdigit(pc)) 135*da6c28aaSamw pc = *pp++; 136*da6c28aaSamw 137*da6c28aaSamw if (pc == '.') { 138*da6c28aaSamw while ((nc = *np++) != 0) { 139*da6c28aaSamw if (nc == '.') 140*da6c28aaSamw break; 141*da6c28aaSamw } 142*da6c28aaSamw 143*da6c28aaSamw while ((nc = *np++) != 0) { 144*da6c28aaSamw nc = mts_toupper(nc); 145*da6c28aaSamw if ((pc = *pp++) != nc) 146*da6c28aaSamw break; 147*da6c28aaSamw } 148*da6c28aaSamw } 149*da6c28aaSamw 150*da6c28aaSamw if (pc == 0) 151*da6c28aaSamw rc = 1; 152*da6c28aaSamw } 153*da6c28aaSamw 154*da6c28aaSamw return (rc); 155*da6c28aaSamw } 156*da6c28aaSamw 157*da6c28aaSamw /* 158*da6c28aaSamw * smb_match_reserved 159*da6c28aaSamw * 160*da6c28aaSamw * Checks if the given name matches given 161*da6c28aaSamw * DOS reserved name prefix. 162*da6c28aaSamw * 163*da6c28aaSamw * Returns 1 if match, 0 otherwise 164*da6c28aaSamw */ 165*da6c28aaSamw static int 166*da6c28aaSamw smb_match_reserved(char *name, char *rsrv) 167*da6c28aaSamw { 168*da6c28aaSamw char ch; 169*da6c28aaSamw 170*da6c28aaSamw int len = strlen(rsrv); 171*da6c28aaSamw return (!utf8_strncasecmp(rsrv, name, len) && 172*da6c28aaSamw ((ch = *(name + len)) == 0 || ch == '.')); 173*da6c28aaSamw } 174*da6c28aaSamw 175*da6c28aaSamw /* 176*da6c28aaSamw * smb_is_reserved_dos_name 177*da6c28aaSamw * 178*da6c28aaSamw * This function checks if the name is a reserved dos name. 179*da6c28aaSamw * 180*da6c28aaSamw * The function returns 1 when the name is a reserved dos name; 181*da6c28aaSamw * otherwise, it returns 0. 182*da6c28aaSamw */ 183*da6c28aaSamw static int 184*da6c28aaSamw smb_is_reserved_dos_name(char *name) 185*da6c28aaSamw { 186*da6c28aaSamw char ch; 187*da6c28aaSamw 188*da6c28aaSamw /* 189*da6c28aaSamw * Eliminate all names reserved by DOS and Windows. 190*da6c28aaSamw */ 191*da6c28aaSamw ch = mts_toupper(*name); 192*da6c28aaSamw 193*da6c28aaSamw switch (ch) { 194*da6c28aaSamw case 'A': 195*da6c28aaSamw if (smb_match_reserved(name, "AUX")) 196*da6c28aaSamw return (1); 197*da6c28aaSamw break; 198*da6c28aaSamw 199*da6c28aaSamw case 'C': 200*da6c28aaSamw if (smb_match_reserved(name, "CLOCK$") || 201*da6c28aaSamw smb_match_reserved(name, "COM1") || 202*da6c28aaSamw smb_match_reserved(name, "COM2") || 203*da6c28aaSamw smb_match_reserved(name, "COM3") || 204*da6c28aaSamw smb_match_reserved(name, "COM4") || 205*da6c28aaSamw smb_match_reserved(name, "CON")) { 206*da6c28aaSamw return (1); 207*da6c28aaSamw } 208*da6c28aaSamw 209*da6c28aaSamw break; 210*da6c28aaSamw 211*da6c28aaSamw case 'L': 212*da6c28aaSamw if ((utf8_strncasecmp("LPT1", name, 4) == 0) || 213*da6c28aaSamw (utf8_strncasecmp("LPT2", name, 4) == 0) || 214*da6c28aaSamw (utf8_strncasecmp("LPT3", name, 4) == 0)) 215*da6c28aaSamw return (1); 216*da6c28aaSamw break; 217*da6c28aaSamw 218*da6c28aaSamw case 'N': 219*da6c28aaSamw if (smb_match_reserved(name, "NUL")) 220*da6c28aaSamw return (1); 221*da6c28aaSamw break; 222*da6c28aaSamw 223*da6c28aaSamw case 'P': 224*da6c28aaSamw if (smb_match_reserved(name, "PRN")) 225*da6c28aaSamw return (1); 226*da6c28aaSamw } 227*da6c28aaSamw 228*da6c28aaSamw /* 229*da6c28aaSamw * If the server is configured to support Catia Version 5 230*da6c28aaSamw * deployments, any filename that contains backslash will 231*da6c28aaSamw * have already been translated to the UTF-8 encoding of 232*da6c28aaSamw * Latin Small Letter Y with Diaeresis. Thus, the check 233*da6c28aaSamw * for backslash in the filename is not necessary. 234*da6c28aaSamw */ 235*da6c28aaSamw #ifdef CATIA_SUPPORT 236*da6c28aaSamw /* XXX Catia support */ 237*da6c28aaSamw if ((get_caps() & NFCAPS_CATIA) == 0) { 238*da6c28aaSamw while (*name != 0) { 239*da6c28aaSamw if (*name == '\\') 240*da6c28aaSamw return (1); 241*da6c28aaSamw name++; 242*da6c28aaSamw } 243*da6c28aaSamw } 244*da6c28aaSamw #endif /* CATIA_SUPPORT */ 245*da6c28aaSamw 246*da6c28aaSamw return (0); 247*da6c28aaSamw } 248*da6c28aaSamw 249*da6c28aaSamw /* 250*da6c28aaSamw * Characters we don't allow in DOS file names. 251*da6c28aaSamw * If a filename contains any of these chars, it should 252*da6c28aaSamw * get mangled. 253*da6c28aaSamw * 254*da6c28aaSamw * '.' is also an invalid DOS char but since it's a special 255*da6c28aaSamw * case it doesn't appear in the list. 256*da6c28aaSamw */ 257*da6c28aaSamw static char *invalid_dos_chars = 258*da6c28aaSamw "\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017" 259*da6c28aaSamw "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" 260*da6c28aaSamw " \"/\\:|<>*?"; 261*da6c28aaSamw 262*da6c28aaSamw /* 263*da6c28aaSamw * According to MSKB article #142982, Windows deletes invalid chars and 264*da6c28aaSamw * spaces from file name in mangling process; and invalid chars include: 265*da6c28aaSamw * ."/\[]:;=, 266*da6c28aaSamw * 267*da6c28aaSamw * But some of these chars and some other chars (e.g. +) are replaced 268*da6c28aaSamw * with underscore (_). They are introduced here as special chars. 269*da6c28aaSamw */ 270*da6c28aaSamw static char *special_chars = "[];=,+"; 271*da6c28aaSamw 272*da6c28aaSamw #define isinvalid(c) (strchr(invalid_dos_chars, c) || (c & 0x80)) 273*da6c28aaSamw 274*da6c28aaSamw /* 275*da6c28aaSamw * smb_needs_mangle 276*da6c28aaSamw * 277*da6c28aaSamw * Determines whether the given name needs to get mangled. 278*da6c28aaSamw * 279*da6c28aaSamw * Here are the (known) rules: 280*da6c28aaSamw * 281*da6c28aaSamw * 1st char is dot (.) 282*da6c28aaSamw * name length > 12 chars 283*da6c28aaSamw * # dots > 1 284*da6c28aaSamw * # dots == 0 and length > 8 285*da6c28aaSamw * # dots == 1 and name isn't 8.3 286*da6c28aaSamw * contains illegal chars 287*da6c28aaSamw */ 288*da6c28aaSamw int 289*da6c28aaSamw smb_needs_mangle(char *name, char **dot_pos) 290*da6c28aaSamw { 291*da6c28aaSamw int len, ndots; 292*da6c28aaSamw char *namep; 293*da6c28aaSamw char *last_dot; 294*da6c28aaSamw 295*da6c28aaSamw /* 296*da6c28aaSamw * Returning (1) for these cases forces consistency with how 297*da6c28aaSamw * these names are treated (smb_mangle_name() will produce an 8.3 name 298*da6c28aaSamw * for these) 299*da6c28aaSamw */ 300*da6c28aaSamw if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) 301*da6c28aaSamw return (1); 302*da6c28aaSamw 303*da6c28aaSamw /* skip the leading dots (if any) */ 304*da6c28aaSamw for (namep = name; *namep == '.'; namep++) 305*da6c28aaSamw ; 306*da6c28aaSamw 307*da6c28aaSamw len = ndots = 0; 308*da6c28aaSamw last_dot = 0; 309*da6c28aaSamw for (; *namep; namep++) { 310*da6c28aaSamw len++; 311*da6c28aaSamw if (*namep == '.') { 312*da6c28aaSamw /* keep the position of last dot */ 313*da6c28aaSamw last_dot = namep; 314*da6c28aaSamw ndots++; 315*da6c28aaSamw } 316*da6c28aaSamw } 317*da6c28aaSamw *dot_pos = last_dot; 318*da6c28aaSamw 319*da6c28aaSamw /* Windows mangles names like .a, .abc, or .abcd */ 320*da6c28aaSamw if (*name == '.') 321*da6c28aaSamw return (1); 322*da6c28aaSamw 323*da6c28aaSamw if (len > 12) 324*da6c28aaSamw return (1); 325*da6c28aaSamw 326*da6c28aaSamw switch (ndots) { 327*da6c28aaSamw case 0: 328*da6c28aaSamw /* no dot */ 329*da6c28aaSamw if (len > 8) 330*da6c28aaSamw return (1); 331*da6c28aaSamw break; 332*da6c28aaSamw 333*da6c28aaSamw case 1: 334*da6c28aaSamw /* just one dot */ 335*da6c28aaSamw /*LINTED E_PTR_DIFF_OVERFLOW*/ 336*da6c28aaSamw if (((last_dot - name) > 8) || /* name length > 8 */ 337*da6c28aaSamw (strlen(last_dot + 1) > 3)) /* extention > 3 */ 338*da6c28aaSamw return (1); 339*da6c28aaSamw break; 340*da6c28aaSamw 341*da6c28aaSamw default: 342*da6c28aaSamw /* more than one dot */ 343*da6c28aaSamw return (1); 344*da6c28aaSamw } 345*da6c28aaSamw 346*da6c28aaSamw for (namep = name; *namep; namep++) { 347*da6c28aaSamw if (!mts_isascii(*namep) || 348*da6c28aaSamw strchr(special_chars, *namep) || 349*da6c28aaSamw strchr(invalid_dos_chars, *namep)) 350*da6c28aaSamw return (1); 351*da6c28aaSamw } 352*da6c28aaSamw 353*da6c28aaSamw return (0); 354*da6c28aaSamw } 355*da6c28aaSamw 356*da6c28aaSamw /* 357*da6c28aaSamw * smb_needs_shortname 358*da6c28aaSamw * 359*da6c28aaSamw * Determine whether a shortname should be generated for a file name that is 360*da6c28aaSamw * already in 8.3 format. 361*da6c28aaSamw * 362*da6c28aaSamw * Paramters: 363*da6c28aaSamw * name - original file name 364*da6c28aaSamw * 365*da6c28aaSamw * Return: 366*da6c28aaSamw * 1 - Shortname is required to be generated. 367*da6c28aaSamw * 0 - No shortname needs to be generated. 368*da6c28aaSamw * 369*da6c28aaSamw * Note 370*da6c28aaSamw * ======= 371*da6c28aaSamw * Windows NT server: shortname is created only if either 372*da6c28aaSamw * the filename or extension portion of 373*da6c28aaSamw * a file is made up of mixed case. 374*da6c28aaSamw * Windows 2000 server: shortname is not created regardless 375*da6c28aaSamw * of the case. 376*da6c28aaSamw * Windows 2003 server: [Same as Windows NT server.] 377*da6c28aaSamw * 378*da6c28aaSamw * StorEdge will conform to the rule used by Windows NT/2003 server. 379*da6c28aaSamw * 380*da6c28aaSamw * For instance: 381*da6c28aaSamw * File | Create shortname? 382*da6c28aaSamw * ================================ 383*da6c28aaSamw * nf.txt | N 384*da6c28aaSamw * NF.TXT | N 385*da6c28aaSamw * NF.txt | N 386*da6c28aaSamw * nf | N 387*da6c28aaSamw * NF | N 388*da6c28aaSamw * nF.txt | Y 389*da6c28aaSamw * nf.TxT | Y 390*da6c28aaSamw * Nf | Y 391*da6c28aaSamw * nF | Y 392*da6c28aaSamw * 393*da6c28aaSamw */ 394*da6c28aaSamw static int 395*da6c28aaSamw smb_needs_shortname(char *name) 396*da6c28aaSamw { 397*da6c28aaSamw char buf[9]; 398*da6c28aaSamw int len; 399*da6c28aaSamw int create = 0; 400*da6c28aaSamw const char *dot_pos = 0; 401*da6c28aaSamw 402*da6c28aaSamw dot_pos = strrchr(name, '.'); 403*da6c28aaSamw /*LINTED E_PTRDIFF_OVERFLOW*/ 404*da6c28aaSamw len = (!dot_pos) ? strlen(name) : (dot_pos - name); 405*da6c28aaSamw /* First, examine the name portion of the file */ 406*da6c28aaSamw if (len) { 407*da6c28aaSamw (void) snprintf(buf, len + 1, "%s", name); 408*da6c28aaSamw /* if the name contains both lower and upper cases */ 409*da6c28aaSamw if (utf8_isstrupr(buf) == 0 && utf8_isstrlwr(buf) == 0) { 410*da6c28aaSamw /* create shortname */ 411*da6c28aaSamw create = 1; 412*da6c28aaSamw } else if (dot_pos) { 413*da6c28aaSamw /* Next, examine the extension portion of the file */ 414*da6c28aaSamw (void) snprintf(buf, sizeof (buf), "%s", dot_pos + 1); 415*da6c28aaSamw /* 416*da6c28aaSamw * if the extension contains both lower and upper 417*da6c28aaSamw * cases 418*da6c28aaSamw */ 419*da6c28aaSamw if (utf8_isstrupr(buf) == 0 && utf8_isstrlwr(buf) == 0) 420*da6c28aaSamw /* create shortname */ 421*da6c28aaSamw create = 1; 422*da6c28aaSamw } 423*da6c28aaSamw } 424*da6c28aaSamw 425*da6c28aaSamw return (create); 426*da6c28aaSamw } 427*da6c28aaSamw 428*da6c28aaSamw /* 429*da6c28aaSamw * smb_mangle_char 430*da6c28aaSamw * 431*da6c28aaSamw * If given char is an invalid DOS character or it's not an 432*da6c28aaSamw * ascii char, it should be deleted from mangled and 8.3 name. 433*da6c28aaSamw * 434*da6c28aaSamw * If given char is one of special chars, it should be replaced 435*da6c28aaSamw * with '_'. 436*da6c28aaSamw * 437*da6c28aaSamw * Otherwise just make it upper case. 438*da6c28aaSamw */ 439*da6c28aaSamw static unsigned char 440*da6c28aaSamw smb_mangle_char(unsigned char ch) 441*da6c28aaSamw { 442*da6c28aaSamw if (isinvalid(ch)) 443*da6c28aaSamw return (0); 444*da6c28aaSamw 445*da6c28aaSamw if (strchr(special_chars, ch)) 446*da6c28aaSamw return ('_'); 447*da6c28aaSamw 448*da6c28aaSamw return (mts_toupper(ch)); 449*da6c28aaSamw } 450*da6c28aaSamw 451*da6c28aaSamw /* 452*da6c28aaSamw * smb_generate_mangle 453*da6c28aaSamw * 454*da6c28aaSamw * Generates a mangle string which contains 455*da6c28aaSamw * at least 2 (considering fileid cannot be 0) 456*da6c28aaSamw * and at most 7 chars. 457*da6c28aaSamw * 458*da6c28aaSamw * Returns the number of chars in the generated mangle. 459*da6c28aaSamw */ 460*da6c28aaSamw static int 461*da6c28aaSamw smb_generate_mangle(ino64_t fileid, unsigned char *mangle_buf) 462*da6c28aaSamw { 463*da6c28aaSamw /* 464*da6c28aaSamw * 36**6 = 2176782336: more than enough to express inodes in 6 465*da6c28aaSamw * chars 466*da6c28aaSamw */ 467*da6c28aaSamw static char *base36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 468*da6c28aaSamw unsigned char *manglep = mangle_buf; 469*da6c28aaSamw 470*da6c28aaSamw for (*manglep++ = '~'; fileid > 0; fileid /= 36) 471*da6c28aaSamw *manglep++ = base36[fileid % 36]; 472*da6c28aaSamw *manglep = 0; 473*da6c28aaSamw 474*da6c28aaSamw /*LINTED E_PTRDIFF_OVERFLOW*/ 475*da6c28aaSamw return (manglep - mangle_buf); 476*da6c28aaSamw } 477*da6c28aaSamw 478*da6c28aaSamw /* 479*da6c28aaSamw * smb_maybe_mangled_name 480*da6c28aaSamw * 481*da6c28aaSamw * returns true if the passed name can possibly be a mangled name. 482*da6c28aaSamw * mangled names should be valid dos file names hence less than 12 characters 483*da6c28aaSamw * long and should contain at least one tilde character. 484*da6c28aaSamw * 485*da6c28aaSamw * note that this function can be further enhanced to check for invalid 486*da6c28aaSamw * dos characters/character patterns (such as "file..1.c") but this version 487*da6c28aaSamw * should be sufficient in most cases. 488*da6c28aaSamw */ 489*da6c28aaSamw int 490*da6c28aaSamw smb_maybe_mangled_name(char *name) 491*da6c28aaSamw { 492*da6c28aaSamw int i, has_tilde = 0; 493*da6c28aaSamw 494*da6c28aaSamw for (i = 0; *name && (i < 12); i++, name++) { 495*da6c28aaSamw if ((*name == '~') && (i < 8)) 496*da6c28aaSamw has_tilde = 1; 497*da6c28aaSamw 498*da6c28aaSamw if (*name == '.' && has_tilde == 0) 499*da6c28aaSamw return (0); 500*da6c28aaSamw } 501*da6c28aaSamw 502*da6c28aaSamw return ((*name == 0) && has_tilde); 503*da6c28aaSamw } 504*da6c28aaSamw 505*da6c28aaSamw /* 506*da6c28aaSamw * smb_mangle_name 507*da6c28aaSamw * 508*da6c28aaSamw * Microsoft knowledge base article #142982 describes how Windows 509*da6c28aaSamw * generates 8.3 filenames from long file names. Some other details 510*da6c28aaSamw * can be found in article #114816. 511*da6c28aaSamw * 512*da6c28aaSamw * The function first checks to see whether the given name needs mangling. 513*da6c28aaSamw * If not, and the force parameter is not set, then no mangling is done, 514*da6c28aaSamw * but both the shortname (if needed) and the 8.3 name are produced and 515*da6c28aaSamw * returned. 516*da6c28aaSamw * 517*da6c28aaSamw * If the "force" parameter is set (as will be the case for case-insensitive 518*da6c28aaSamw * collisions), then the name will be mangled. 519*da6c28aaSamw * 520*da6c28aaSamw * Whenever mangling is needed, both the shortname and the 8.3 names are 521*da6c28aaSamw * produced and returned. 522*da6c28aaSamw * 523*da6c28aaSamw * For example, the xxx.xy in 8.3 format will be "xxx .xy ". 524*da6c28aaSamw */ 525*da6c28aaSamw 526*da6c28aaSamw int smb_mangle_name( 527*da6c28aaSamw ino64_t fileid, /* inode number to generate unique mangle */ 528*da6c28aaSamw char *name, /* original file name */ 529*da6c28aaSamw char *shortname, /* mangled name (if applicable) */ 530*da6c28aaSamw char *name83, /* (mangled) name in 8.3 format */ 531*da6c28aaSamw int force) /* force mangling even if mangling is not */ 532*da6c28aaSamw /* needed according to standard algorithm */ 533*da6c28aaSamw { 534*da6c28aaSamw int avail; 535*da6c28aaSamw unsigned char ch; 536*da6c28aaSamw unsigned char mangle_buf[8]; 537*da6c28aaSamw unsigned char *namep; 538*da6c28aaSamw unsigned char *manglep; 539*da6c28aaSamw unsigned char *out_short; 540*da6c28aaSamw unsigned char *out_83; 541*da6c28aaSamw char *dot_pos = NULL; 542*da6c28aaSamw 543*da6c28aaSamw /* 544*da6c28aaSamw * NOTE: 545*da6c28aaSamw * This function used to consider filename case 546*da6c28aaSamw * in order to mangle. I removed those checks. 547*da6c28aaSamw */ 548*da6c28aaSamw 549*da6c28aaSamw *shortname = *name83 = 0; 550*da6c28aaSamw 551*da6c28aaSamw /* Allow dot and dot dot up front */ 552*da6c28aaSamw if (strcmp(name, ".") == 0) { 553*da6c28aaSamw /* no shortname */ 554*da6c28aaSamw (void) strcpy(name83, ". . "); 555*da6c28aaSamw return (1); 556*da6c28aaSamw } 557*da6c28aaSamw 558*da6c28aaSamw if (strcmp(name, "..") == 0) { 559*da6c28aaSamw /* no shortname */ 560*da6c28aaSamw (void) strcpy(name83, ".. . "); 561*da6c28aaSamw return (1); 562*da6c28aaSamw } 563*da6c28aaSamw 564*da6c28aaSamw out_short = (unsigned char *)shortname; 565*da6c28aaSamw out_83 = (unsigned char *)name83; 566*da6c28aaSamw 567*da6c28aaSamw if ((smb_needs_mangle(name, &dot_pos) == 0) && (force == 0)) { 568*da6c28aaSamw /* no mangle */ 569*da6c28aaSamw 570*da6c28aaSamw /* check if shortname is required or not */ 571*da6c28aaSamw if (smb_needs_shortname(name)) { 572*da6c28aaSamw namep = (unsigned char *)name; 573*da6c28aaSamw while (*namep) 574*da6c28aaSamw *out_short++ = mts_toupper(*namep++); 575*da6c28aaSamw *out_short = '\0'; 576*da6c28aaSamw } 577*da6c28aaSamw 578*da6c28aaSamw out_83 = (unsigned char *)name83; 579*da6c28aaSamw (void) strcpy((char *)out_83, " . "); 580*da6c28aaSamw while (*name && *name != '.') 581*da6c28aaSamw *out_83++ = mts_toupper(*name++); 582*da6c28aaSamw 583*da6c28aaSamw if (*name == '.') { 584*da6c28aaSamw /* copy extension */ 585*da6c28aaSamw name++; 586*da6c28aaSamw out_83 = (unsigned char *)name83 + 9; 587*da6c28aaSamw while (*name) 588*da6c28aaSamw *out_83++ = mts_toupper(*name++); 589*da6c28aaSamw } 590*da6c28aaSamw return (1); 591*da6c28aaSamw } 592*da6c28aaSamw 593*da6c28aaSamw avail = 8 - smb_generate_mangle(fileid, mangle_buf); 594*da6c28aaSamw 595*da6c28aaSamw /* 596*da6c28aaSamw * generated mangle part has always less than 8 chars, so 597*da6c28aaSamw * use the chars before the first dot in filename 598*da6c28aaSamw * and try to generate a full 8 char name. 599*da6c28aaSamw */ 600*da6c28aaSamw 601*da6c28aaSamw /* skip the leading dots (if any) */ 602*da6c28aaSamw for (namep = (unsigned char *)name; *namep == '.'; namep++) 603*da6c28aaSamw ; 604*da6c28aaSamw 605*da6c28aaSamw for (; avail && *namep && (*namep != '.'); namep++) { 606*da6c28aaSamw ch = smb_mangle_char(*namep); 607*da6c28aaSamw if (ch == 0) 608*da6c28aaSamw continue; 609*da6c28aaSamw *out_short++ = *out_83++ = ch; 610*da6c28aaSamw avail--; 611*da6c28aaSamw } 612*da6c28aaSamw 613*da6c28aaSamw /* Copy in mangled part */ 614*da6c28aaSamw manglep = mangle_buf; 615*da6c28aaSamw 616*da6c28aaSamw while (*manglep) 617*da6c28aaSamw *out_short++ = *out_83++ = *(manglep++); 618*da6c28aaSamw 619*da6c28aaSamw /* Pad any leftover in 8.3 name with spaces */ 620*da6c28aaSamw while (avail--) 621*da6c28aaSamw *out_83++ = ' '; 622*da6c28aaSamw 623*da6c28aaSamw /* Work on extension now */ 624*da6c28aaSamw avail = 3; 625*da6c28aaSamw *out_83++ = '.'; 626*da6c28aaSamw if (dot_pos) { 627*da6c28aaSamw namep = (unsigned char *)dot_pos + 1; 628*da6c28aaSamw if (*namep != 0) { 629*da6c28aaSamw *out_short++ = '.'; 630*da6c28aaSamw for (; avail && *namep; namep++) { 631*da6c28aaSamw ch = smb_mangle_char(*namep); 632*da6c28aaSamw if (ch == 0) 633*da6c28aaSamw continue; 634*da6c28aaSamw 635*da6c28aaSamw *out_short++ = *out_83++ = ch; 636*da6c28aaSamw avail--; 637*da6c28aaSamw } 638*da6c28aaSamw } 639*da6c28aaSamw } 640*da6c28aaSamw 641*da6c28aaSamw while (avail--) 642*da6c28aaSamw *out_83++ = ' '; 643*da6c28aaSamw 644*da6c28aaSamw *out_short = *out_83 = '\0'; 645*da6c28aaSamw 646*da6c28aaSamw return (1); 647*da6c28aaSamw } 648*da6c28aaSamw 649*da6c28aaSamw /* 650*da6c28aaSamw * smb_unmangle_name 651*da6c28aaSamw * 652*da6c28aaSamw * Given a mangled name, try to find the real file name as it appears 653*da6c28aaSamw * in the directory entry. If the name does not contain a ~, it is most 654*da6c28aaSamw * likely not a mangled name but the caller can still try to get the 655*da6c28aaSamw * actual on-disk name by setting the "od" parameter. 656*da6c28aaSamw * 657*da6c28aaSamw * Returns 0 if a name has been returned in real_name. There are three 658*da6c28aaSamw * possible scenarios: 659*da6c28aaSamw * 1. Name did not contain a ~ and "od" was not set, in which 660*da6c28aaSamw * case, real_name contains name. 661*da6c28aaSamw * 2. Name did not contain a ~ and "od" was set, in which 662*da6c28aaSamw * case, real_name contains the actual directory entry name. 663*da6c28aaSamw * 3. Name did contain a ~, in which case, name was mangled and 664*da6c28aaSamw * real_name contains the actual directory entry name. 665*da6c28aaSamw * 666*da6c28aaSamw * EINVAL: a parameter was invalid. 667*da6c28aaSamw * ENOENT: an unmangled name could not be found. 668*da6c28aaSamw */ 669*da6c28aaSamw 670*da6c28aaSamw int 671*da6c28aaSamw smb_unmangle_name(struct smb_request *sr, cred_t *cred, smb_node_t *dir_node, 672*da6c28aaSamw char *name, char *real_name, int realname_size, char *shortname, 673*da6c28aaSamw char *name83, int od) 674*da6c28aaSamw { 675*da6c28aaSamw int err; 676*da6c28aaSamw int len; 677*da6c28aaSamw int force = 0; 678*da6c28aaSamw ino64_t inode; 679*da6c28aaSamw uint32_t cookie; 680*da6c28aaSamw struct smb_node *snode = NULL; 681*da6c28aaSamw smb_attr_t ret_attr; 682*da6c28aaSamw char *dot_pos = NULL; 683*da6c28aaSamw char *readdir_name; 684*da6c28aaSamw char *shortp; 685*da6c28aaSamw char xxx[MANGLE_NAMELEN]; 686*da6c28aaSamw 687*da6c28aaSamw if (dir_node == NULL || name == NULL || real_name == NULL || 688*da6c28aaSamw realname_size == 0) 689*da6c28aaSamw return (EINVAL); 690*da6c28aaSamw 691*da6c28aaSamw *real_name = '\0'; 692*da6c28aaSamw snode = NULL; 693*da6c28aaSamw 694*da6c28aaSamw if (smb_maybe_mangled_name(name) == 0) { 695*da6c28aaSamw if (od == 0) { 696*da6c28aaSamw (void) strlcpy(real_name, name, realname_size); 697*da6c28aaSamw return (0); 698*da6c28aaSamw } 699*da6c28aaSamw 700*da6c28aaSamw err = smb_fsop_lookup(sr, cred, 0, sr->tid_tree->t_snode, 701*da6c28aaSamw dir_node, name, &snode, &ret_attr, NULL, NULL); 702*da6c28aaSamw 703*da6c28aaSamw if (err != 0) 704*da6c28aaSamw return (err); 705*da6c28aaSamw 706*da6c28aaSamw (void) strlcpy(real_name, snode->od_name, realname_size); 707*da6c28aaSamw smb_node_release(snode); 708*da6c28aaSamw return (0); 709*da6c28aaSamw } 710*da6c28aaSamw 711*da6c28aaSamw if (shortname == 0) 712*da6c28aaSamw shortname = xxx; 713*da6c28aaSamw if (name83 == 0) 714*da6c28aaSamw name83 = xxx; 715*da6c28aaSamw 716*da6c28aaSamw cookie = 0; 717*da6c28aaSamw 718*da6c28aaSamw readdir_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 719*da6c28aaSamw 720*da6c28aaSamw snode = NULL; 721*da6c28aaSamw while (cookie != 0x7FFFFFFF) { 722*da6c28aaSamw 723*da6c28aaSamw len = realname_size - 1; 724*da6c28aaSamw 725*da6c28aaSamw err = smb_fsop_readdir(sr, cred, dir_node, &cookie, 726*da6c28aaSamw readdir_name, &len, &inode, NULL, &snode, &ret_attr); 727*da6c28aaSamw 728*da6c28aaSamw if (err || (cookie == 0x7FFFFFFF)) 729*da6c28aaSamw break; 730*da6c28aaSamw 731*da6c28aaSamw readdir_name[len] = 0; 732*da6c28aaSamw 733*da6c28aaSamw /* 734*da6c28aaSamw * smb_fsop_readdir() may return a mangled name if the 735*da6c28aaSamw * name has a case collision. 736*da6c28aaSamw * 737*da6c28aaSamw * If readdir_name is not a mangled name, we mangle 738*da6c28aaSamw * readdir_name to see if it will match the name the 739*da6c28aaSamw * client passed in. 740*da6c28aaSamw * 741*da6c28aaSamw * If smb_needs_mangle() does not succeed, we try again 742*da6c28aaSamw * using the force flag. It is possible that the client 743*da6c28aaSamw * is using a mangled name that resulted from a prior 744*da6c28aaSamw * case collision which no longer exists in the directory. 745*da6c28aaSamw * smb_needs_mangle(), with the force flag, will produce 746*da6c28aaSamw * a mangled name regardless of whether the name passed in 747*da6c28aaSamw * meets standard DOS criteria for name mangling. 748*da6c28aaSamw */ 749*da6c28aaSamw 750*da6c28aaSamw if (smb_maybe_mangled_name(readdir_name)) { 751*da6c28aaSamw shortp = readdir_name; 752*da6c28aaSamw } else { 753*da6c28aaSamw if (smb_needs_mangle(readdir_name, &dot_pos) == 0) 754*da6c28aaSamw force = 1; 755*da6c28aaSamw (void) smb_mangle_name(inode, readdir_name, shortname, 756*da6c28aaSamw name83, force); 757*da6c28aaSamw shortp = shortname; 758*da6c28aaSamw } 759*da6c28aaSamw 760*da6c28aaSamw if (utf8_strcasecmp(name, shortp) == 0) { 761*da6c28aaSamw kmem_free(readdir_name, MAXNAMELEN); 762*da6c28aaSamw (void) strlcpy(real_name, snode->od_name, 763*da6c28aaSamw realname_size); 764*da6c28aaSamw 765*da6c28aaSamw smb_node_release(snode); 766*da6c28aaSamw 767*da6c28aaSamw return (0); 768*da6c28aaSamw } else { 769*da6c28aaSamw smb_node_release(snode); 770*da6c28aaSamw snode = NULL; 771*da6c28aaSamw } 772*da6c28aaSamw } 773*da6c28aaSamw 774*da6c28aaSamw kmem_free(readdir_name, MAXNAMELEN); 775*da6c28aaSamw 776*da6c28aaSamw return (ENOENT); 777*da6c28aaSamw } 778