123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479 |
- /*
- * 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, see <http://www.gnu.org/licenses/>.
- */
- /************************************************************************/
- /* */
- /* PROJECT : exFAT & FAT12/16/32 File System */
- /* FILE : nls.c */
- /* PURPOSE : sdFAT NLS Manager */
- /* */
- /*----------------------------------------------------------------------*/
- /* NOTES */
- /* */
- /* */
- /************************************************************************/
- #include <linux/string.h>
- #include <linux/nls.h>
- #include "sdfat.h"
- #include "core.h"
- /*----------------------------------------------------------------------*/
- /* Global Variable Definitions */
- /*----------------------------------------------------------------------*/
- /*----------------------------------------------------------------------*/
- /* Local Variable Definitions */
- /*----------------------------------------------------------------------*/
- static u16 bad_dos_chars[] = {
- /* + , ; = [ ] */
- 0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D,
- 0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D,
- 0
- };
- /*
- * Allow full-width illegal characters :
- * "MS windows 7" supports full-width-invalid-name-characters.
- * So we should check half-width-invalid-name-characters(ASCII) only
- * for compatibility.
- *
- * " * / : < > ? \ |
- *
- * patch 1.2.0
- */
- static u16 bad_uni_chars[] = {
- 0x0022, 0x002A, 0x002F, 0x003A,
- 0x003C, 0x003E, 0x003F, 0x005C, 0x007C,
- #if 0 /* allow full-width characters */
- 0x201C, 0x201D, 0xFF0A, 0xFF0F, 0xFF1A,
- 0xFF1C, 0xFF1E, 0xFF1F, 0xFF3C, 0xFF5C,
- #endif
- 0
- };
- /*----------------------------------------------------------------------*/
- /* Local Function Declarations */
- /*----------------------------------------------------------------------*/
- static s32 convert_uni_to_ch(struct nls_table *nls, u16 uni, u8 *ch, s32 *lossy);
- static s32 convert_ch_to_uni(struct nls_table *nls, u8 *ch, u16 *uni, s32 *lossy);
- static u16 nls_upper(struct super_block *sb, u16 a)
- {
- FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
- if (SDFAT_SB(sb)->options.casesensitive)
- return a;
- if ((fsi->vol_utbl)[get_col_index(a)] != NULL)
- return (fsi->vol_utbl)[get_col_index(a)][get_row_index(a)];
- else
- return a;
- }
- /*======================================================================*/
- /* Global Function Definitions */
- /*======================================================================*/
- u16 *nls_wstrchr(u16 *str, u16 wchar)
- {
- while (*str) {
- if (*(str++) == wchar)
- return str;
- }
- return 0;
- }
- s32 nls_cmp_sfn(struct super_block *sb, u8 *a, u8 *b)
- {
- return strncmp((void *)a, (void *)b, DOS_NAME_LENGTH);
- }
- s32 nls_cmp_uniname(struct super_block *sb, u16 *a, u16 *b)
- {
- s32 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;
- }
- #define CASE_LOWER_BASE (0x08) /* base is lower case */
- #define CASE_LOWER_EXT (0x10) /* extension is lower case */
- s32 nls_uni16s_to_sfn(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname, s32 *p_lossy)
- {
- s32 i, j, len, lossy = NLS_NAME_NO_LOSSY;
- u8 buf[MAX_CHARSET_SIZE];
- u8 lower = 0, upper = 0;
- u8 *dosname = p_dosname->name;
- u16 *uniname = p_uniname->name;
- u16 *p, *last_period;
- struct nls_table *nls = SDFAT_SB(sb)->nls_disk;
- /* DOSNAME is filled with space */
- for (i = 0; i < DOS_NAME_LENGTH; i++)
- *(dosname+i) = ' ';
- /* DOT and DOTDOT are handled by VFS layer */
- /* search for the last embedded period */
- last_period = NULL;
- for (p = uniname; *p; p++) {
- if (*p == (u16) '.')
- 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 |= NLS_NAME_OVERLEN;
- uniname = last_period + 1;
- }
- }
- if (*uniname == (u16) '\0') {
- break;
- } else if (*uniname == (u16) ' ') {
- lossy |= NLS_NAME_LOSSY;
- } else if (*uniname == (u16) '.') {
- if (uniname < last_period)
- lossy |= NLS_NAME_LOSSY;
- else
- i = 8;
- } else if (nls_wstrchr(bad_dos_chars, *uniname)) {
- lossy |= NLS_NAME_LOSSY;
- *(dosname+i) = '_';
- i++;
- } else {
- len = convert_uni_to_ch(nls, *uniname, buf, &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 { /* len == 1 */
- if ((*buf >= 'a') && (*buf <= 'z')) {
- *(dosname+i) = *buf - ('a' - 'A');
- lower |= (i < 8) ?
- CASE_LOWER_BASE :
- CASE_LOWER_EXT;
- } else if ((*buf >= 'A') && (*buf <= 'Z')) {
- *(dosname+i) = *buf;
- upper |= (i < 8) ?
- CASE_LOWER_BASE :
- CASE_LOWER_EXT;
- } else {
- *(dosname+i) = *buf;
- }
- i++;
- }
- }
- uniname++;
- }
- if (*dosname == 0xE5)
- *dosname = 0x05;
- if (*uniname != 0x0)
- lossy |= NLS_NAME_OVERLEN;
- if (upper & lower)
- p_dosname->name_case = 0xFF;
- else
- p_dosname->name_case = lower;
- if (p_lossy)
- *p_lossy = lossy;
- return i;
- }
- s32 nls_sfn_to_uni16s(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname)
- {
- s32 i = 0, j, n = 0;
- u8 buf[MAX_DOSNAME_BUF_SIZE];
- u8 *dosname = p_dosname->name;
- u16 *uniname = p_uniname->name;
- struct nls_table *nls = SDFAT_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 & CASE_LOWER_BASE))
- *(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 & CASE_LOWER_EXT))
- *(buf+n) = *(dosname+i) + ('a' - 'A');
- else
- *(buf+n) = *(dosname+i);
- }
- *(buf+n) = '\0';
- i = j = 0;
- while (j < MAX_NAME_LENGTH) {
- if (*(buf+i) == '\0')
- break;
- i += convert_ch_to_uni(nls, (buf+i), uniname, NULL);
- uniname++;
- j++;
- }
- *uniname = (u16) '\0';
- return j;
- }
- static s32 __nls_utf16s_to_vfsname(struct super_block *sb, UNI_NAME_T *p_uniname, u8 *p_cstring, s32 buflen)
- {
- s32 len;
- const u16 *uniname = p_uniname->name;
- /* always len >= 0 */
- len = utf16s_to_utf8s(uniname, MAX_NAME_LENGTH, UTF16_HOST_ENDIAN,
- p_cstring, buflen);
- p_cstring[len] = '\0';
- return len;
- }
- static s32 __nls_vfsname_to_utf16s(struct super_block *sb, const u8 *p_cstring,
- const s32 len, UNI_NAME_T *p_uniname, s32 *p_lossy)
- {
- s32 i, unilen, lossy = NLS_NAME_NO_LOSSY;
- u16 upname[MAX_NAME_LENGTH+1];
- u16 *uniname = p_uniname->name;
- BUG_ON(!len);
- unilen = utf8s_to_utf16s(p_cstring, len, UTF16_HOST_ENDIAN,
- (wchar_t *)uniname, MAX_NAME_LENGTH+2);
- if (unilen < 0) {
- MMSG("%s: failed to vfsname_to_utf16(err:%d) "
- "vfsnamelen:%d", __func__, unilen, len);
- return unilen;
- }
- if (unilen > MAX_NAME_LENGTH) {
- MMSG("%s: failed to vfsname_to_utf16(estr:ENAMETOOLONG) "
- "vfsnamelen:%d, unilen:%d>%d",
- __func__, len, unilen, MAX_NAME_LENGTH);
- return -ENAMETOOLONG;
- }
- p_uniname->name_len = (u8)(unilen & 0xFF);
- for (i = 0; i < unilen; i++) {
- if ((*uniname < 0x0020) || nls_wstrchr(bad_uni_chars, *uniname))
- lossy |= NLS_NAME_LOSSY;
- *(upname+i) = nls_upper(sb, *uniname);
- uniname++;
- }
- *uniname = (u16)'\0';
- p_uniname->name_len = unilen;
- p_uniname->name_hash = calc_chksum_2byte((void *) upname,
- unilen << 1, 0, CS_DEFAULT);
- if (p_lossy)
- *p_lossy = lossy;
- return unilen;
- }
- static s32 __nls_uni16s_to_vfsname(struct super_block *sb, UNI_NAME_T *p_uniname, u8 *p_cstring, s32 buflen)
- {
- s32 i, j, len, out_len = 0;
- u8 buf[MAX_CHARSET_SIZE];
- const u16 *uniname = p_uniname->name;
- struct nls_table *nls = SDFAT_SB(sb)->nls_io;
- i = 0;
- while ((i < MAX_NAME_LENGTH) && (out_len < (buflen-1))) {
- if (*uniname == (u16)'\0')
- break;
- len = convert_uni_to_ch(nls, *uniname, buf, NULL);
- if (out_len + len >= buflen)
- len = (buflen - 1) - out_len;
- out_len += len;
- if (len > 1) {
- for (j = 0; j < len; j++)
- *p_cstring++ = (s8) *(buf+j);
- } else { /* len == 1 */
- *p_cstring++ = (s8) *buf;
- }
- uniname++;
- i++;
- }
- *p_cstring = '\0';
- return out_len;
- }
- static s32 __nls_vfsname_to_uni16s(struct super_block *sb, const u8 *p_cstring,
- const s32 len, UNI_NAME_T *p_uniname, s32 *p_lossy)
- {
- s32 i, unilen, lossy = NLS_NAME_NO_LOSSY;
- u16 upname[MAX_NAME_LENGTH+1];
- u16 *uniname = p_uniname->name;
- struct nls_table *nls = SDFAT_SB(sb)->nls_io;
- BUG_ON(!len);
- i = unilen = 0;
- while ((unilen < MAX_NAME_LENGTH) && (i < len)) {
- i += convert_ch_to_uni(nls, (u8 *)(p_cstring+i), uniname, &lossy);
- if ((*uniname < 0x0020) || nls_wstrchr(bad_uni_chars, *uniname))
- lossy |= NLS_NAME_LOSSY;
- *(upname+unilen) = nls_upper(sb, *uniname);
- uniname++;
- unilen++;
- }
- if (*(p_cstring+i) != '\0')
- lossy |= NLS_NAME_OVERLEN;
- *uniname = (u16)'\0';
- p_uniname->name_len = unilen;
- p_uniname->name_hash =
- calc_chksum_2byte((void *) upname, unilen<<1, 0, CS_DEFAULT);
- if (p_lossy)
- *p_lossy = lossy;
- return unilen;
- }
- s32 nls_uni16s_to_vfsname(struct super_block *sb, UNI_NAME_T *uniname, u8 *p_cstring, s32 buflen)
- {
- if (SDFAT_SB(sb)->options.utf8)
- return __nls_utf16s_to_vfsname(sb, uniname, p_cstring, buflen);
- return __nls_uni16s_to_vfsname(sb, uniname, p_cstring, buflen);
- }
- s32 nls_vfsname_to_uni16s(struct super_block *sb, const u8 *p_cstring, const s32 len, UNI_NAME_T *uniname, s32 *p_lossy)
- {
- if (SDFAT_SB(sb)->options.utf8)
- return __nls_vfsname_to_utf16s(sb, p_cstring, len, uniname, p_lossy);
- return __nls_vfsname_to_uni16s(sb, p_cstring, len, uniname, p_lossy);
- }
- /*======================================================================*/
- /* Local Function Definitions */
- /*======================================================================*/
- static s32 convert_ch_to_uni(struct nls_table *nls, u8 *ch, u16 *uni, s32 *lossy)
- {
- int len;
- *uni = 0x0;
- if (ch[0] < 0x80) {
- *uni = (u16) ch[0];
- return 1;
- }
- len = nls->char2uni(ch, MAX_CHARSET_SIZE, uni);
- if (len < 0) {
- /* conversion failed */
- DMSG("%s: fail to use nls\n", __func__);
- if (lossy != NULL)
- *lossy |= NLS_NAME_LOSSY;
- *uni = (u16) '_';
- if (!strcmp(nls->charset, "utf8"))
- return 1;
- return 2;
- }
- return len;
- } /* end of convert_ch_to_uni */
- static s32 convert_uni_to_ch(struct nls_table *nls, u16 uni, u8 *ch, s32 *lossy)
- {
- int len;
- ch[0] = 0x0;
- if (uni < 0x0080) {
- ch[0] = (u8) uni;
- return 1;
- }
- len = nls->uni2char(uni, ch, MAX_CHARSET_SIZE);
- if (len < 0) {
- /* conversion failed */
- DMSG("%s: fail to use nls\n", __func__);
- if (lossy != NULL)
- *lossy |= NLS_NAME_LOSSY;
- ch[0] = '_';
- return 1;
- }
- return len;
- } /* end of convert_uni_to_ch */
- /* end of nls.c */
|