2 * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 /************************************************************************/
20 /* PROJECT : exFAT & FAT12/16/32 File System */
22 /* PURPOSE : sdFAT NLS Manager */
24 /*----------------------------------------------------------------------*/
28 /************************************************************************/
29 #include <linux/string.h>
30 #include <linux/nls.h>
35 /*----------------------------------------------------------------------*/
36 /* Global Variable Definitions */
37 /*----------------------------------------------------------------------*/
39 /*----------------------------------------------------------------------*/
40 /* Local Variable Definitions */
41 /*----------------------------------------------------------------------*/
43 static u16 bad_dos_chars
[] = {
45 0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D,
46 0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D,
51 * Allow full-width illegal characters :
52 * "MS windows 7" supports full-width-invalid-name-characters.
53 * So we should check half-width-invalid-name-characters(ASCII) only
60 static u16 bad_uni_chars
[] = {
61 0x0022, 0x002A, 0x002F, 0x003A,
62 0x003C, 0x003E, 0x003F, 0x005C, 0x007C,
63 #if 0 /* allow full-width characters */
64 0x201C, 0x201D, 0xFF0A, 0xFF0F, 0xFF1A,
65 0xFF1C, 0xFF1E, 0xFF1F, 0xFF3C, 0xFF5C,
70 /*----------------------------------------------------------------------*/
71 /* Local Function Declarations */
72 /*----------------------------------------------------------------------*/
73 static s32
convert_uni_to_ch(struct nls_table
*nls
, u16 uni
, u8
*ch
, s32
*lossy
);
74 static s32
convert_ch_to_uni(struct nls_table
*nls
, u8
*ch
, u16
*uni
, s32
*lossy
);
76 static u16
nls_upper(struct super_block
*sb
, u16 a
)
78 FS_INFO_T
*fsi
= &(SDFAT_SB(sb
)->fsi
);
80 if (SDFAT_SB(sb
)->options
.casesensitive
)
82 if ((fsi
->vol_utbl
)[get_col_index(a
)] != NULL
)
83 return (fsi
->vol_utbl
)[get_col_index(a
)][get_row_index(a
)];
87 /*======================================================================*/
88 /* Global Function Definitions */
89 /*======================================================================*/
90 u16
*nls_wstrchr(u16
*str
, u16 wchar
)
93 if (*(str
++) == wchar
)
100 s32
nls_cmp_sfn(struct super_block
*sb
, u8
*a
, u8
*b
)
102 return strncmp((void *)a
, (void *)b
, DOS_NAME_LENGTH
);
105 s32
nls_cmp_uniname(struct super_block
*sb
, u16
*a
, u16
*b
)
109 for (i
= 0; i
< MAX_NAME_LENGTH
; i
++, a
++, b
++) {
110 if (nls_upper(sb
, *a
) != nls_upper(sb
, *b
))
118 #define CASE_LOWER_BASE (0x08) /* base is lower case */
119 #define CASE_LOWER_EXT (0x10) /* extension is lower case */
121 s32
nls_uni16s_to_sfn(struct super_block
*sb
, UNI_NAME_T
*p_uniname
, DOS_NAME_T
*p_dosname
, s32
*p_lossy
)
123 s32 i
, j
, len
, lossy
= NLS_NAME_NO_LOSSY
;
124 u8 buf
[MAX_CHARSET_SIZE
];
125 u8 lower
= 0, upper
= 0;
126 u8
*dosname
= p_dosname
->name
;
127 u16
*uniname
= p_uniname
->name
;
128 u16
*p
, *last_period
;
129 struct nls_table
*nls
= SDFAT_SB(sb
)->nls_disk
;
131 /* DOSNAME is filled with space */
132 for (i
= 0; i
< DOS_NAME_LENGTH
; i
++)
135 /* DOT and DOTDOT are handled by VFS layer */
137 /* search for the last embedded period */
139 for (p
= uniname
; *p
; p
++) {
145 while (i
< DOS_NAME_LENGTH
) {
147 if (last_period
== NULL
)
150 if (uniname
<= last_period
) {
151 if (uniname
< last_period
)
152 lossy
|= NLS_NAME_OVERLEN
;
153 uniname
= last_period
+ 1;
157 if (*uniname
== (u16
) '\0') {
159 } else if (*uniname
== (u16
) ' ') {
160 lossy
|= NLS_NAME_LOSSY
;
161 } else if (*uniname
== (u16
) '.') {
162 if (uniname
< last_period
)
163 lossy
|= NLS_NAME_LOSSY
;
166 } else if (nls_wstrchr(bad_dos_chars
, *uniname
)) {
167 lossy
|= NLS_NAME_LOSSY
;
171 len
= convert_uni_to_ch(nls
, *uniname
, buf
, &lossy
);
174 if ((i
>= 8) && ((i
+len
) > DOS_NAME_LENGTH
))
177 if ((i
< 8) && ((i
+len
) > 8)) {
184 for (j
= 0; j
< len
; j
++, i
++)
185 *(dosname
+i
) = *(buf
+j
);
186 } else { /* len == 1 */
187 if ((*buf
>= 'a') && (*buf
<= 'z')) {
188 *(dosname
+i
) = *buf
- ('a' - 'A');
193 } else if ((*buf
>= 'A') && (*buf
<= 'Z')) {
209 if (*dosname
== 0xE5)
212 lossy
|= NLS_NAME_OVERLEN
;
215 p_dosname
->name_case
= 0xFF;
217 p_dosname
->name_case
= lower
;
224 s32
nls_sfn_to_uni16s(struct super_block
*sb
, DOS_NAME_T
*p_dosname
, UNI_NAME_T
*p_uniname
)
227 u8 buf
[MAX_DOSNAME_BUF_SIZE
];
228 u8
*dosname
= p_dosname
->name
;
229 u16
*uniname
= p_uniname
->name
;
230 struct nls_table
*nls
= SDFAT_SB(sb
)->nls_disk
;
232 if (*dosname
== 0x05) {
238 for ( ; i
< 8; i
++, n
++) {
239 if (*(dosname
+i
) == ' ')
242 if ((*(dosname
+i
) >= 'A') && (*(dosname
+i
) <= 'Z') &&
243 (p_dosname
->name_case
& CASE_LOWER_BASE
))
244 *(buf
+n
) = *(dosname
+i
) + ('a' - 'A');
246 *(buf
+n
) = *(dosname
+i
);
248 if (*(dosname
+8) != ' ') {
253 for (i
= 8; i
< DOS_NAME_LENGTH
; i
++, n
++) {
254 if (*(dosname
+i
) == ' ')
257 if ((*(dosname
+i
) >= 'A') && (*(dosname
+i
) <= 'Z') &&
258 (p_dosname
->name_case
& CASE_LOWER_EXT
))
259 *(buf
+n
) = *(dosname
+i
) + ('a' - 'A');
261 *(buf
+n
) = *(dosname
+i
);
266 while (j
< MAX_NAME_LENGTH
) {
267 if (*(buf
+i
) == '\0')
270 i
+= convert_ch_to_uni(nls
, (buf
+i
), uniname
, NULL
);
276 *uniname
= (u16
) '\0';
280 static s32
__nls_utf16s_to_vfsname(struct super_block
*sb
, UNI_NAME_T
*p_uniname
, u8
*p_cstring
, s32 buflen
)
283 const u16
*uniname
= p_uniname
->name
;
285 /* always len >= 0 */
286 len
= utf16s_to_utf8s(uniname
, MAX_NAME_LENGTH
, UTF16_HOST_ENDIAN
,
288 p_cstring
[len
] = '\0';
292 static s32
__nls_vfsname_to_utf16s(struct super_block
*sb
, const u8
*p_cstring
,
293 const s32 len
, UNI_NAME_T
*p_uniname
, s32
*p_lossy
)
295 s32 i
, unilen
, lossy
= NLS_NAME_NO_LOSSY
;
296 u16 upname
[MAX_NAME_LENGTH
+1];
297 u16
*uniname
= p_uniname
->name
;
301 unilen
= utf8s_to_utf16s(p_cstring
, len
, UTF16_HOST_ENDIAN
,
302 (wchar_t *)uniname
, MAX_NAME_LENGTH
+2);
304 MMSG("%s: failed to vfsname_to_utf16(err:%d) "
305 "vfsnamelen:%d", __func__
, unilen
, len
);
309 if (unilen
> MAX_NAME_LENGTH
) {
310 MMSG("%s: failed to vfsname_to_utf16(estr:ENAMETOOLONG) "
311 "vfsnamelen:%d, unilen:%d>%d",
312 __func__
, len
, unilen
, MAX_NAME_LENGTH
);
313 return -ENAMETOOLONG
;
316 p_uniname
->name_len
= (u8
)(unilen
& 0xFF);
318 for (i
= 0; i
< unilen
; i
++) {
319 if ((*uniname
< 0x0020) || nls_wstrchr(bad_uni_chars
, *uniname
))
320 lossy
|= NLS_NAME_LOSSY
;
322 *(upname
+i
) = nls_upper(sb
, *uniname
);
326 *uniname
= (u16
)'\0';
327 p_uniname
->name_len
= unilen
;
328 p_uniname
->name_hash
= calc_chksum_2byte((void *) upname
,
329 unilen
<< 1, 0, CS_DEFAULT
);
337 static s32
__nls_uni16s_to_vfsname(struct super_block
*sb
, UNI_NAME_T
*p_uniname
, u8
*p_cstring
, s32 buflen
)
339 s32 i
, j
, len
, out_len
= 0;
340 u8 buf
[MAX_CHARSET_SIZE
];
341 const u16
*uniname
= p_uniname
->name
;
342 struct nls_table
*nls
= SDFAT_SB(sb
)->nls_io
;
345 while ((i
< MAX_NAME_LENGTH
) && (out_len
< (buflen
-1))) {
346 if (*uniname
== (u16
)'\0')
349 len
= convert_uni_to_ch(nls
, *uniname
, buf
, NULL
);
351 if (out_len
+ len
>= buflen
)
352 len
= (buflen
- 1) - out_len
;
357 for (j
= 0; j
< len
; j
++)
358 *p_cstring
++ = (s8
) *(buf
+j
);
359 } else { /* len == 1 */
360 *p_cstring
++ = (s8
) *buf
;
371 static s32
__nls_vfsname_to_uni16s(struct super_block
*sb
, const u8
*p_cstring
,
372 const s32 len
, UNI_NAME_T
*p_uniname
, s32
*p_lossy
)
374 s32 i
, unilen
, lossy
= NLS_NAME_NO_LOSSY
;
375 u16 upname
[MAX_NAME_LENGTH
+1];
376 u16
*uniname
= p_uniname
->name
;
377 struct nls_table
*nls
= SDFAT_SB(sb
)->nls_io
;
382 while ((unilen
< MAX_NAME_LENGTH
) && (i
< len
)) {
383 i
+= convert_ch_to_uni(nls
, (u8
*)(p_cstring
+i
), uniname
, &lossy
);
385 if ((*uniname
< 0x0020) || nls_wstrchr(bad_uni_chars
, *uniname
))
386 lossy
|= NLS_NAME_LOSSY
;
388 *(upname
+unilen
) = nls_upper(sb
, *uniname
);
394 if (*(p_cstring
+i
) != '\0')
395 lossy
|= NLS_NAME_OVERLEN
;
397 *uniname
= (u16
)'\0';
398 p_uniname
->name_len
= unilen
;
399 p_uniname
->name_hash
=
400 calc_chksum_2byte((void *) upname
, unilen
<<1, 0, CS_DEFAULT
);
408 s32
nls_uni16s_to_vfsname(struct super_block
*sb
, UNI_NAME_T
*uniname
, u8
*p_cstring
, s32 buflen
)
410 if (SDFAT_SB(sb
)->options
.utf8
)
411 return __nls_utf16s_to_vfsname(sb
, uniname
, p_cstring
, buflen
);
413 return __nls_uni16s_to_vfsname(sb
, uniname
, p_cstring
, buflen
);
416 s32
nls_vfsname_to_uni16s(struct super_block
*sb
, const u8
*p_cstring
, const s32 len
, UNI_NAME_T
*uniname
, s32
*p_lossy
)
418 if (SDFAT_SB(sb
)->options
.utf8
)
419 return __nls_vfsname_to_utf16s(sb
, p_cstring
, len
, uniname
, p_lossy
);
420 return __nls_vfsname_to_uni16s(sb
, p_cstring
, len
, uniname
, p_lossy
);
423 /*======================================================================*/
424 /* Local Function Definitions */
425 /*======================================================================*/
427 static s32
convert_ch_to_uni(struct nls_table
*nls
, u8
*ch
, u16
*uni
, s32
*lossy
)
438 len
= nls
->char2uni(ch
, MAX_CHARSET_SIZE
, uni
);
440 /* conversion failed */
441 DMSG("%s: fail to use nls\n", __func__
);
443 *lossy
|= NLS_NAME_LOSSY
;
445 if (!strcmp(nls
->charset
, "utf8"))
451 } /* end of convert_ch_to_uni */
453 static s32
convert_uni_to_ch(struct nls_table
*nls
, u16 uni
, u8
*ch
, s32
*lossy
)
464 len
= nls
->uni2char(uni
, ch
, MAX_CHARSET_SIZE
);
466 /* conversion failed */
467 DMSG("%s: fail to use nls\n", __func__
);
469 *lossy
|= NLS_NAME_LOSSY
;
476 } /* end of convert_uni_to_ch */