/* * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "exfat_config.h" #include "exfat_global.h" #include "exfat_data.h" #include "exfat_nls.h" #include "exfat_api.h" #include "exfat_super.h" #include "exfat.h" #include static UINT16 bad_dos_chars[] = { 0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D, 0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D, 0 }; static UINT16 bad_uni_chars[] = { 0x0022, 0x002A, 0x002F, 0x003A, 0x003C, 0x003E, 0x003F, 0x005C, 0x007C, 0 }; static INT32 convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy); static INT32 convert_ch_to_uni(struct nls_table *nls, UINT16 *uni, UINT8 *ch, INT32 *lossy); UINT16 nls_upper(struct super_block *sb, UINT16 a) { FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); if (EXFAT_SB(sb)->options.casesensitive) return(a); if ((p_fs->vol_utbl)[get_col_index(a)] != NULL) return (p_fs->vol_utbl)[get_col_index(a)][get_row_index(a)]; else return a; } INT32 nls_dosname_cmp(struct super_block *sb, UINT8 *a, UINT8 *b) { return(STRNCMP((void *) a, (void *) b, DOS_NAME_LENGTH)); } INT32 nls_uniname_cmp(struct super_block *sb, UINT16 *a, UINT16 *b) { INT32 i; for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) { if (nls_upper(sb, *a) != nls_upper(sb, *b)) return(1); if (*a == 0x0) return(0); } return(0); } void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, INT32 *p_lossy) { INT32 i, j, len, lossy = FALSE; UINT8 buf[MAX_CHARSET_SIZE]; UINT8 lower = 0, upper = 0; UINT8 *dosname = p_dosname->name; UINT16 *uniname = p_uniname->name; UINT16 *p, *last_period; struct nls_table *nls = EXFAT_SB(sb)->nls_disk; for (i = 0; i < DOS_NAME_LENGTH; i++) { *(dosname+i) = ' '; } if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_CUR_DIR_NAME)) { *(dosname) = '.'; p_dosname->name_case = 0x0; if (p_lossy != NULL) *p_lossy = FALSE; return; } if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_PAR_DIR_NAME)) { *(dosname) = '.'; *(dosname+1) = '.'; p_dosname->name_case = 0x0; if (p_lossy != NULL) *p_lossy = FALSE; return; } last_period = NULL; for (p = uniname; *p; p++) { if (*p == (UINT16) '.') last_period = p; } i = 0; while (i < DOS_NAME_LENGTH) { if (i == 8) { if (last_period == NULL) break; if (uniname <= last_period) { if (uniname < last_period) lossy = TRUE; uniname = last_period + 1; } } if (*uniname == (UINT16) '\0') { break; } else if (*uniname == (UINT16) ' ') { lossy = TRUE; } else if (*uniname == (UINT16) '.') { if (uniname < last_period) lossy = TRUE; else i = 8; } else if (WSTRCHR(bad_dos_chars, *uniname)) { lossy = TRUE; *(dosname+i) = '_'; i++; } else { len = convert_uni_to_ch(nls, buf, *uniname, &lossy); if (len > 1) { if ((i >= 8) && ((i+len) > DOS_NAME_LENGTH)) { break; } if ((i < 8) && ((i+len) > 8)) { i = 8; continue; } lower = 0xFF; for (j = 0; j < len; j++, i++) { *(dosname+i) = *(buf+j); } } else { if ((*buf >= 'a') && (*buf <= 'z')) { *(dosname+i) = *buf - ('a' - 'A'); if (i < 8) lower |= 0x08; else lower |= 0x10; } else if ((*buf >= 'A') && (*buf <= 'Z')) { *(dosname+i) = *buf; if (i < 8) upper |= 0x08; else upper |= 0x10; } else { *(dosname+i) = *buf; } i++; } } uniname++; } if (*dosname == 0xE5) *dosname = 0x05; if (*uniname != 0x0) lossy = TRUE; if (upper & lower) p_dosname->name_case = 0xFF; else p_dosname->name_case = lower; if (p_lossy != NULL) *p_lossy = lossy; } void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) { INT32 i = 0, j, n = 0; UINT8 buf[DOS_NAME_LENGTH+2]; UINT8 *dosname = p_dosname->name; UINT16 *uniname = p_uniname->name; struct nls_table *nls = EXFAT_SB(sb)->nls_disk; if (*dosname == 0x05) { *buf = 0xE5; i++; n++; } for ( ; i < 8; i++, n++) { if (*(dosname+i) == ' ') break; if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x08)) *(buf+n) = *(dosname+i) + ('a' - 'A'); else *(buf+n) = *(dosname+i); } if (*(dosname+8) != ' ') { *(buf+n) = '.'; n++; } for (i = 8; i < DOS_NAME_LENGTH; i++, n++) { if (*(dosname+i) == ' ') break; if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x10)) *(buf+n) = *(dosname+i) + ('a' - 'A'); else *(buf+n) = *(dosname+i); } *(buf+n) = '\0'; i = j = 0; while (j < (MAX_NAME_LENGTH-1)) { if (*(buf+i) == '\0') break; i += convert_ch_to_uni(nls, uniname, (buf+i), NULL); uniname++; j++; } *uniname = (UINT16) '\0'; } void nls_uniname_to_cstring(struct super_block *sb, UINT8 *p_cstring, UNI_NAME_T *p_uniname) { INT32 i, j, len; UINT8 buf[MAX_CHARSET_SIZE]; UINT16 *uniname = p_uniname->name; struct nls_table *nls = EXFAT_SB(sb)->nls_io; i = 0; while (i < (MAX_NAME_LENGTH-1)) { if (*uniname == (UINT16) '\0') break; len = convert_uni_to_ch(nls, buf, *uniname, NULL); if (len > 1) { for (j = 0; j < len; j++) *p_cstring++ = (INT8) *(buf+j); } else { *p_cstring++ = (INT8) *buf; } uniname++; i++; } *p_cstring = '\0'; } void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, UINT8 *p_cstring, INT32 *p_lossy) { INT32 i, j, lossy = FALSE; UINT8 *end_of_name; UINT16 upname[MAX_NAME_LENGTH]; UINT16 *uniname = p_uniname->name; struct nls_table *nls = EXFAT_SB(sb)->nls_io; end_of_name = p_cstring + STRLEN((INT8 *) p_cstring); while (*(--end_of_name) == ' ') { if (end_of_name < p_cstring) break; } *(++end_of_name) = '\0'; if (STRCMP((INT8 *) p_cstring, ".") && STRCMP((INT8 *) p_cstring, "..")) { while (*(--end_of_name) == '.') { if (end_of_name < p_cstring) break; } *(++end_of_name) = '\0'; } if (*p_cstring == '\0') lossy = TRUE; i = j = 0; while (j < (MAX_NAME_LENGTH-1)) { if (*(p_cstring+i) == '\0') break; i += convert_ch_to_uni(nls, uniname, (UINT8 *)(p_cstring+i), &lossy); if ((*uniname < 0x0020) || WSTRCHR(bad_uni_chars, *uniname)) lossy = TRUE; *(upname+j) = nls_upper(sb, *uniname); uniname++; j++; } if (*(p_cstring+i) != '\0') lossy = TRUE; *uniname = (UINT16) '\0'; p_uniname->name_len = j; p_uniname->name_hash = calc_checksum_2byte((void *) upname, j<<1, 0, CS_DEFAULT); if (p_lossy != NULL) *p_lossy = lossy; } static INT32 convert_ch_to_uni(struct nls_table *nls, UINT16 *uni, UINT8 *ch, INT32 *lossy) { int len; *uni = 0x0; if (ch[0] < 0x80) { *uni = (UINT16) ch[0]; return(1); } if ((len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni)) < 0) { printk("%s: fail to use nls \n", __func__); if (lossy != NULL) *lossy = TRUE; *uni = (UINT16) '_'; if (!strcmp(nls->charset, "utf8")) return(1); else return(2); } return(len); } static INT32 convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy) { int len; ch[0] = 0x0; if (uni < 0x0080) { ch[0] = (UINT8) uni; return(1); } if ((len = nls->uni2char(uni, ch, NLS_MAX_CHARSET_SIZE)) < 0) { printk("%s: fail to use nls \n", __func__); if (lossy != NULL) *lossy = TRUE; ch[0] = '_'; return(1); } return(len); }