root/drivers/staging/exfat/exfat_nls.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. convert_ch_to_uni
  2. convert_uni_to_ch
  3. nls_upper
  4. nls_wstrchr
  5. nls_dosname_cmp
  6. nls_uniname_cmp
  7. nls_uniname_to_dosname
  8. nls_dosname_to_uniname
  9. nls_uniname_to_cstring
  10. nls_cstring_to_uniname

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  *  Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
   4  */
   5 
   6 #include <linux/string.h>
   7 #include <linux/nls.h>
   8 #include "exfat.h"
   9 
  10 static u16 bad_dos_chars[] = {
  11         /* + , ; = [ ] */
  12         0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D,
  13         0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D,
  14         0
  15 };
  16 
  17 static u16 bad_uni_chars[] = {
  18         /* " * / : < > ? \ | */
  19         0x0022,         0x002A, 0x002F, 0x003A,
  20         0x003C, 0x003E, 0x003F, 0x005C, 0x007C,
  21         0
  22 };
  23 
  24 static int convert_ch_to_uni(struct nls_table *nls, u16 *uni, u8 *ch,
  25                              bool *lossy)
  26 {
  27         int len;
  28 
  29         *uni = 0x0;
  30 
  31         if (ch[0] < 0x80) {
  32                 *uni = (u16)ch[0];
  33                 return 1;
  34         }
  35 
  36         len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni);
  37         if (len < 0) {
  38                 /* conversion failed */
  39                 pr_info("%s: fail to use nls\n", __func__);
  40                 if (lossy)
  41                         *lossy = true;
  42                 *uni = (u16)'_';
  43                 if (!strcmp(nls->charset, "utf8"))
  44                         return 1;
  45                 else
  46                         return 2;
  47         }
  48 
  49         return len;
  50 }
  51 
  52 static int convert_uni_to_ch(struct nls_table *nls, u8 *ch, u16 uni,
  53                              bool *lossy)
  54 {
  55         int len;
  56 
  57         ch[0] = 0x0;
  58 
  59         if (uni < 0x0080) {
  60                 ch[0] = (u8)uni;
  61                 return 1;
  62         }
  63 
  64         len = nls->uni2char(uni, ch, NLS_MAX_CHARSET_SIZE);
  65         if (len < 0) {
  66                 /* conversion failed */
  67                 pr_info("%s: fail to use nls\n", __func__);
  68                 if (lossy)
  69                         *lossy = true;
  70                 ch[0] = '_';
  71                 return 1;
  72         }
  73 
  74         return len;
  75 }
  76 
  77 u16 nls_upper(struct super_block *sb, u16 a)
  78 {
  79         struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
  80 
  81         if (EXFAT_SB(sb)->options.casesensitive)
  82                 return a;
  83         if (p_fs->vol_utbl && p_fs->vol_utbl[get_col_index(a)])
  84                 return p_fs->vol_utbl[get_col_index(a)][get_row_index(a)];
  85         else
  86                 return a;
  87 }
  88 
  89 static u16 *nls_wstrchr(u16 *str, u16 wchar)
  90 {
  91         while (*str) {
  92                 if (*(str++) == wchar)
  93                         return str;
  94         }
  95 
  96         return NULL;
  97 }
  98 
  99 int nls_dosname_cmp(struct super_block *sb, u8 *a, u8 *b)
 100 {
 101         return strncmp(a, b, DOS_NAME_LENGTH);
 102 }
 103 
 104 int nls_uniname_cmp(struct super_block *sb, u16 *a, u16 *b)
 105 {
 106         int i;
 107 
 108         for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) {
 109                 if (nls_upper(sb, *a) != nls_upper(sb, *b))
 110                         return 1;
 111                 if (*a == 0x0)
 112                         return 0;
 113         }
 114         return 0;
 115 }
 116 
 117 void nls_uniname_to_dosname(struct super_block *sb,
 118                             struct dos_name_t *p_dosname,
 119                             struct uni_name_t *p_uniname, bool *p_lossy)
 120 {
 121         int i, j, len;
 122         bool lossy = false;
 123         u8 buf[MAX_CHARSET_SIZE];
 124         u8 lower = 0, upper = 0;
 125         u8 *dosname = p_dosname->name;
 126         u16 *uniname = p_uniname->name;
 127         u16 *p, *last_period;
 128         struct nls_table *nls = EXFAT_SB(sb)->nls_disk;
 129 
 130         for (i = 0; i < DOS_NAME_LENGTH; i++)
 131                 *(dosname + i) = ' ';
 132 
 133         if (!nls_uniname_cmp(sb, uniname, (u16 *)UNI_CUR_DIR_NAME)) {
 134                 *(dosname) = '.';
 135                 p_dosname->name_case = 0x0;
 136                 if (p_lossy)
 137                         *p_lossy = false;
 138                 return;
 139         }
 140 
 141         if (!nls_uniname_cmp(sb, uniname, (u16 *)UNI_PAR_DIR_NAME)) {
 142                 *(dosname) = '.';
 143                 *(dosname + 1) = '.';
 144                 p_dosname->name_case = 0x0;
 145                 if (p_lossy)
 146                         *p_lossy = false;
 147                 return;
 148         }
 149 
 150         /* search for the last embedded period */
 151         last_period = NULL;
 152         for (p = uniname; *p; p++) {
 153                 if (*p == (u16)'.')
 154                         last_period = p;
 155         }
 156 
 157         i = 0;
 158         while (i < DOS_NAME_LENGTH) {
 159                 if (i == 8) {
 160                         if (!last_period)
 161                                 break;
 162 
 163                         if (uniname <= last_period) {
 164                                 if (uniname < last_period)
 165                                         lossy = true;
 166                                 uniname = last_period + 1;
 167                         }
 168                 }
 169 
 170                 if (*uniname == (u16)'\0') {
 171                         break;
 172                 } else if (*uniname == (u16)' ') {
 173                         lossy = true;
 174                 } else if (*uniname == (u16)'.') {
 175                         if (uniname < last_period)
 176                                 lossy = true;
 177                         else
 178                                 i = 8;
 179                 } else if (nls_wstrchr(bad_dos_chars, *uniname)) {
 180                         lossy = true;
 181                         *(dosname + i) = '_';
 182                         i++;
 183                 } else {
 184                         len = convert_uni_to_ch(nls, buf, *uniname, &lossy);
 185 
 186                         if (len > 1) {
 187                                 if ((i >= 8) && ((i + len) > DOS_NAME_LENGTH))
 188                                         break;
 189 
 190                                 if ((i < 8) && ((i + len) > 8)) {
 191                                         i = 8;
 192                                         continue;
 193                                 }
 194 
 195                                 lower = 0xFF;
 196 
 197                                 for (j = 0; j < len; j++, i++)
 198                                         *(dosname + i) = *(buf + j);
 199                         } else { /* len == 1 */
 200                                 if ((*buf >= 'a') && (*buf <= 'z')) {
 201                                         *(dosname + i) = *buf - ('a' - 'A');
 202 
 203                                         if (i < 8)
 204                                                 lower |= 0x08;
 205                                         else
 206                                                 lower |= 0x10;
 207                                 } else if ((*buf >= 'A') && (*buf <= 'Z')) {
 208                                         *(dosname + i) = *buf;
 209 
 210                                         if (i < 8)
 211                                                 upper |= 0x08;
 212                                         else
 213                                                 upper |= 0x10;
 214                                 } else {
 215                                         *(dosname + i) = *buf;
 216                                 }
 217                                 i++;
 218                         }
 219                 }
 220 
 221                 uniname++;
 222         }
 223 
 224         if (*dosname == 0xE5)
 225                 *dosname = 0x05;
 226 
 227         if (*uniname != 0x0)
 228                 lossy = true;
 229 
 230         if (upper & lower)
 231                 p_dosname->name_case = 0xFF;
 232         else
 233                 p_dosname->name_case = lower;
 234 
 235         if (p_lossy)
 236                 *p_lossy = lossy;
 237 }
 238 
 239 void nls_dosname_to_uniname(struct super_block *sb,
 240                             struct uni_name_t *p_uniname,
 241                             struct dos_name_t *p_dosname)
 242 {
 243         int i = 0, j, n = 0;
 244         u8 buf[DOS_NAME_LENGTH + 2];
 245         u8 *dosname = p_dosname->name;
 246         u16 *uniname = p_uniname->name;
 247         struct nls_table *nls = EXFAT_SB(sb)->nls_disk;
 248 
 249         if (*dosname == 0x05) {
 250                 *buf = 0xE5;
 251                 i++;
 252                 n++;
 253         }
 254 
 255         for (; i < 8; i++, n++) {
 256                 if (*(dosname + i) == ' ')
 257                         break;
 258 
 259                 if ((*(dosname + i) >= 'A') && (*(dosname + i) <= 'Z') &&
 260                     (p_dosname->name_case & 0x08))
 261                         *(buf + n) = *(dosname + i) + ('a' - 'A');
 262                 else
 263                         *(buf + n) = *(dosname + i);
 264         }
 265         if (*(dosname + 8) != ' ') {
 266                 *(buf + n) = '.';
 267                 n++;
 268         }
 269 
 270         for (i = 8; i < DOS_NAME_LENGTH; i++, n++) {
 271                 if (*(dosname + i) == ' ')
 272                         break;
 273 
 274                 if ((*(dosname + i) >= 'A') && (*(dosname + i) <= 'Z') &&
 275                     (p_dosname->name_case & 0x10))
 276                         *(buf + n) = *(dosname + i) + ('a' - 'A');
 277                 else
 278                         *(buf + n) = *(dosname + i);
 279         }
 280         *(buf + n) = '\0';
 281 
 282         i = 0;
 283         j = 0;
 284         while (j < (MAX_NAME_LENGTH - 1)) {
 285                 if (*(buf + i) == '\0')
 286                         break;
 287 
 288                 i += convert_ch_to_uni(nls, uniname, (buf + i), NULL);
 289 
 290                 uniname++;
 291                 j++;
 292         }
 293 
 294         *uniname = (u16)'\0';
 295 }
 296 
 297 void nls_uniname_to_cstring(struct super_block *sb, u8 *p_cstring,
 298                             struct uni_name_t *p_uniname)
 299 {
 300         int i, j, len;
 301         u8 buf[MAX_CHARSET_SIZE];
 302         u16 *uniname = p_uniname->name;
 303         struct nls_table *nls = EXFAT_SB(sb)->nls_io;
 304 
 305         if (!nls) {
 306                 len = utf16s_to_utf8s(uniname, MAX_NAME_LENGTH,
 307                                       UTF16_HOST_ENDIAN, p_cstring,
 308                                       MAX_NAME_LENGTH);
 309                 p_cstring[len] = 0;
 310                 return;
 311         }
 312 
 313         i = 0;
 314         while (i < (MAX_NAME_LENGTH - 1)) {
 315                 if (*uniname == (u16)'\0')
 316                         break;
 317 
 318                 len = convert_uni_to_ch(nls, buf, *uniname, NULL);
 319 
 320                 if (len > 1) {
 321                         for (j = 0; j < len; j++)
 322                                 *p_cstring++ = (char)*(buf + j);
 323                 } else { /* len == 1 */
 324                         *p_cstring++ = (char)*buf;
 325                 }
 326 
 327                 uniname++;
 328                 i++;
 329         }
 330 
 331         *p_cstring = '\0';
 332 }
 333 
 334 void nls_cstring_to_uniname(struct super_block *sb,
 335                             struct uni_name_t *p_uniname, u8 *p_cstring,
 336                             bool *p_lossy)
 337 {
 338         int i, j;
 339         bool lossy = false;
 340         u8 *end_of_name;
 341         u8 upname[MAX_NAME_LENGTH * 2];
 342         u16 *uniname = p_uniname->name;
 343         struct nls_table *nls = EXFAT_SB(sb)->nls_io;
 344 
 345         /* strip all trailing spaces */
 346         end_of_name = p_cstring + strlen(p_cstring);
 347 
 348         while (*(--end_of_name) == ' ') {
 349                 if (end_of_name < p_cstring)
 350                         break;
 351         }
 352         *(++end_of_name) = '\0';
 353 
 354         if (strcmp(p_cstring, ".") && strcmp(p_cstring, "..")) {
 355                 /* strip all trailing periods */
 356                 while (*(--end_of_name) == '.') {
 357                         if (end_of_name < p_cstring)
 358                                 break;
 359                 }
 360                 *(++end_of_name) = '\0';
 361         }
 362 
 363         if (*p_cstring == '\0')
 364                 lossy = true;
 365 
 366         if (!nls) {
 367                 i = utf8s_to_utf16s(p_cstring, MAX_NAME_LENGTH,
 368                                     UTF16_HOST_ENDIAN, uniname,
 369                                     MAX_NAME_LENGTH);
 370                 for (j = 0; j < i; j++)
 371                         SET16_A(upname + j * 2, nls_upper(sb, uniname[j]));
 372                 uniname[i] = '\0';
 373         } else {
 374                 i = 0;
 375                 j = 0;
 376                 while (j < (MAX_NAME_LENGTH - 1)) {
 377                         if (*(p_cstring + i) == '\0')
 378                                 break;
 379 
 380                         i += convert_ch_to_uni(nls, uniname,
 381                                                (u8 *)(p_cstring + i), &lossy);
 382 
 383                         if ((*uniname < 0x0020) ||
 384                             nls_wstrchr(bad_uni_chars, *uniname))
 385                                 lossy = true;
 386 
 387                         SET16_A(upname + j * 2, nls_upper(sb, *uniname));
 388 
 389                         uniname++;
 390                         j++;
 391                 }
 392 
 393                 if (*(p_cstring + i) != '\0')
 394                         lossy = true;
 395                 *uniname = (u16)'\0';
 396         }
 397 
 398         p_uniname->name_len = j;
 399         p_uniname->name_hash = calc_checksum_2byte(upname, j << 1, 0,
 400                                                    CS_DEFAULT);
 401 
 402         if (p_lossy)
 403                 *p_lossy = lossy;
 404 }

/* [<][>][^][v][top][bottom][index][help] */