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, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 /************************************************************************/
21 /* PROJECT : exFAT & FAT12/16/32 File System */
22 /* FILE : exfat_nls.c */
23 /* PURPOSE : exFAT NLS Manager */
25 /*----------------------------------------------------------------------*/
28 /*----------------------------------------------------------------------*/
29 /* REVISION HISTORY (Ver 0.9) */
31 /* - 2010.11.15 [Joosun Hahn] : first writing */
33 /************************************************************************/
35 #include "exfat_config.h"
36 #include "exfat_global.h"
37 #include "exfat_data.h"
39 #include "exfat_nls.h"
40 #include "exfat_api.h"
41 #include "exfat_super.h"
44 #include <linux/nls.h>
46 /*----------------------------------------------------------------------*/
47 /* Global Variable Definitions */
48 /*----------------------------------------------------------------------*/
50 /*----------------------------------------------------------------------*/
51 /* Local Variable Definitions */
52 /*----------------------------------------------------------------------*/
54 static UINT16 bad_dos_chars
[] = {
56 0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D,
57 0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D,
61 static UINT16 bad_uni_chars
[] = {
62 /* " * / : < > ? \ | */
63 0x0022, 0x002A, 0x002F, 0x003A,
64 0x003C, 0x003E, 0x003F, 0x005C, 0x007C,
68 /*----------------------------------------------------------------------*/
69 /* Local Function Declarations */
70 /*----------------------------------------------------------------------*/
72 static INT32
convert_uni_to_ch(struct nls_table
*nls
, UINT8
*ch
, UINT16 uni
, INT32
*lossy
);
73 static INT32
convert_ch_to_uni(struct nls_table
*nls
, UINT16
*uni
, UINT8
*ch
, INT32
*lossy
);
75 /*======================================================================*/
76 /* Global Function Definitions */
77 /*======================================================================*/
79 UINT16
nls_upper(struct super_block
*sb
, UINT16 a
)
81 FS_INFO_T
*p_fs
= &(EXFAT_SB(sb
)->fs_info
);
83 if (EXFAT_SB(sb
)->options
.casesensitive
)
85 if (p_fs
->vol_utbl
!= NULL
&& (p_fs
->vol_utbl
)[get_col_index(a
)] != NULL
)
86 return (p_fs
->vol_utbl
)[get_col_index(a
)][get_row_index(a
)];
91 INT32
nls_dosname_cmp(struct super_block
*sb
, UINT8
*a
, UINT8
*b
)
93 return(STRNCMP((void *) a
, (void *) b
, DOS_NAME_LENGTH
));
94 } /* end of nls_dosname_cmp */
96 INT32
nls_uniname_cmp(struct super_block
*sb
, UINT16
*a
, UINT16
*b
)
100 for (i
= 0; i
< MAX_NAME_LENGTH
; i
++, a
++, b
++) {
101 if (nls_upper(sb
, *a
) != nls_upper(sb
, *b
)) return(1);
102 if (*a
== 0x0) return(0);
105 } /* end of nls_uniname_cmp */
107 void nls_uniname_to_dosname(struct super_block
*sb
, DOS_NAME_T
*p_dosname
, UNI_NAME_T
*p_uniname
, INT32
*p_lossy
)
109 INT32 i
, j
, len
, lossy
= FALSE
;
110 UINT8 buf
[MAX_CHARSET_SIZE
];
111 UINT8 lower
= 0, upper
= 0;
112 UINT8
*dosname
= p_dosname
->name
;
113 UINT16
*uniname
= p_uniname
->name
;
114 UINT16
*p
, *last_period
;
115 struct nls_table
*nls
= EXFAT_SB(sb
)->nls_disk
;
117 for (i
= 0; i
< DOS_NAME_LENGTH
; i
++) {
121 if (!nls_uniname_cmp(sb
, uniname
, (UINT16
*) UNI_CUR_DIR_NAME
)) {
123 p_dosname
->name_case
= 0x0;
124 if (p_lossy
!= NULL
) *p_lossy
= FALSE
;
128 if (!nls_uniname_cmp(sb
, uniname
, (UINT16
*) UNI_PAR_DIR_NAME
)) {
131 p_dosname
->name_case
= 0x0;
132 if (p_lossy
!= NULL
) *p_lossy
= FALSE
;
136 /* search for the last embedded period */
138 for (p
= uniname
; *p
; p
++) {
139 if (*p
== (UINT16
) '.') last_period
= p
;
143 while (i
< DOS_NAME_LENGTH
) {
145 if (last_period
== NULL
) break;
147 if (uniname
<= last_period
) {
148 if (uniname
< last_period
) lossy
= TRUE
;
149 uniname
= last_period
+ 1;
153 if (*uniname
== (UINT16
) '\0') {
155 } else if (*uniname
== (UINT16
) ' ') {
157 } else if (*uniname
== (UINT16
) '.') {
158 if (uniname
< last_period
) lossy
= TRUE
;
160 } else if (WSTRCHR(bad_dos_chars
, *uniname
)) {
165 len
= convert_uni_to_ch(nls
, buf
, *uniname
, &lossy
);
168 if ((i
>= 8) && ((i
+len
) > DOS_NAME_LENGTH
)) {
171 if ((i
< 8) && ((i
+len
) > 8)) {
178 for (j
= 0; j
< len
; j
++, i
++) {
179 *(dosname
+i
) = *(buf
+j
);
181 } else { /* len == 1 */
182 if ((*buf
>= 'a') && (*buf
<= 'z')) {
183 *(dosname
+i
) = *buf
- ('a' - 'A');
185 if (i
< 8) lower
|= 0x08;
187 } else if ((*buf
>= 'A') && (*buf
<= 'Z')) {
190 if (i
< 8) upper
|= 0x08;
202 if (*dosname
== 0xE5) *dosname
= 0x05;
203 if (*uniname
!= 0x0) lossy
= TRUE
;
205 if (upper
& lower
) p_dosname
->name_case
= 0xFF;
206 else p_dosname
->name_case
= lower
;
208 if (p_lossy
!= NULL
) *p_lossy
= lossy
;
209 } /* end of nls_uniname_to_dosname */
211 void nls_dosname_to_uniname(struct super_block
*sb
, UNI_NAME_T
*p_uniname
, DOS_NAME_T
*p_dosname
)
213 INT32 i
= 0, j
, n
= 0;
214 UINT8 buf
[DOS_NAME_LENGTH
+2];
215 UINT8
*dosname
= p_dosname
->name
;
216 UINT16
*uniname
= p_uniname
->name
;
217 struct nls_table
*nls
= EXFAT_SB(sb
)->nls_disk
;
219 if (*dosname
== 0x05) {
225 for ( ; i
< 8; i
++, n
++) {
226 if (*(dosname
+i
) == ' ') break;
228 if ((*(dosname
+i
) >= 'A') && (*(dosname
+i
) <= 'Z') && (p_dosname
->name_case
& 0x08))
229 *(buf
+n
) = *(dosname
+i
) + ('a' - 'A');
231 *(buf
+n
) = *(dosname
+i
);
233 if (*(dosname
+8) != ' ') {
238 for (i
= 8; i
< DOS_NAME_LENGTH
; i
++, n
++) {
239 if (*(dosname
+i
) == ' ') break;
241 if ((*(dosname
+i
) >= 'A') && (*(dosname
+i
) <= 'Z') && (p_dosname
->name_case
& 0x10))
242 *(buf
+n
) = *(dosname
+i
) + ('a' - 'A');
244 *(buf
+n
) = *(dosname
+i
);
249 while (j
< (MAX_NAME_LENGTH
-1)) {
250 if (*(buf
+i
) == '\0') break;
252 i
+= convert_ch_to_uni(nls
, uniname
, (buf
+i
), NULL
);
258 *uniname
= (UINT16
) '\0';
259 } /* end of nls_dosname_to_uniname */
261 void nls_uniname_to_cstring(struct super_block
*sb
, UINT8
*p_cstring
, UNI_NAME_T
*p_uniname
)
264 UINT8 buf
[MAX_CHARSET_SIZE
];
265 UINT16
*uniname
= p_uniname
->name
;
266 struct nls_table
*nls
= EXFAT_SB(sb
)->nls_io
;
269 len
= utf16s_to_utf8s(uniname
, MAX_NAME_LENGTH
, UTF16_HOST_ENDIAN
, p_cstring
, MAX_NAME_LENGTH
);
275 while (i
< (MAX_NAME_LENGTH
-1)) {
276 if (*uniname
== (UINT16
) '\0') break;
278 len
= convert_uni_to_ch(nls
, buf
, *uniname
, NULL
);
281 for (j
= 0; j
< len
; j
++)
282 *p_cstring
++ = (INT8
) *(buf
+j
);
283 } else { /* len == 1 */
284 *p_cstring
++ = (INT8
) *buf
;
292 } /* end of nls_uniname_to_cstring */
294 void nls_cstring_to_uniname(struct super_block
*sb
, UNI_NAME_T
*p_uniname
, UINT8
*p_cstring
, INT32
*p_lossy
)
296 INT32 i
, j
, lossy
= FALSE
;
298 UINT8 upname
[MAX_NAME_LENGTH
* 2];
299 UINT16
*uniname
= p_uniname
->name
;
300 struct nls_table
*nls
= EXFAT_SB(sb
)->nls_io
;
303 /* strip all trailing spaces */
304 end_of_name
= p_cstring
+ STRLEN((INT8
*) p_cstring
);
306 while (*(--end_of_name
) == ' ') {
307 if (end_of_name
< p_cstring
) break;
309 *(++end_of_name
) = '\0';
311 if (STRCMP((INT8
*) p_cstring
, ".") && STRCMP((INT8
*) p_cstring
, "..")) {
313 /* strip all trailing periods */
314 while (*(--end_of_name
) == '.') {
315 if (end_of_name
< p_cstring
) break;
317 *(++end_of_name
) = '\0';
320 if (*p_cstring
== '\0')
324 i
= utf8s_to_utf16s(p_cstring
, MAX_NAME_LENGTH
, UTF16_HOST_ENDIAN
, uniname
, MAX_NAME_LENGTH
);
325 for (j
= 0; j
< i
; j
++)
326 SET16_A(upname
+ j
* 2, nls_upper(sb
, uniname
[j
]));
331 while (j
< (MAX_NAME_LENGTH
-1)) {
332 if (*(p_cstring
+i
) == '\0') break;
334 i
+= convert_ch_to_uni(nls
, uniname
, (UINT8
*)(p_cstring
+i
), &lossy
);
336 if ((*uniname
< 0x0020) || WSTRCHR(bad_uni_chars
, *uniname
))
339 SET16_A(upname
+ j
* 2, nls_upper(sb
, *uniname
));
345 if (*(p_cstring
+i
) != '\0')
347 *uniname
= (UINT16
) '\0';
350 p_uniname
->name_len
= j
;
351 p_uniname
->name_hash
= calc_checksum_2byte((void *) upname
, j
<<1, 0, CS_DEFAULT
);
355 } /* end of nls_cstring_to_uniname */
357 /*======================================================================*/
358 /* Local Function Definitions */
359 /*======================================================================*/
361 static INT32
convert_ch_to_uni(struct nls_table
*nls
, UINT16
*uni
, UINT8
*ch
, INT32
*lossy
)
368 *uni
= (UINT16
) ch
[0];
372 if ((len
= nls
->char2uni(ch
, NLS_MAX_CHARSET_SIZE
, uni
)) < 0) {
373 /* conversion failed */
374 printk("%s: fail to use nls \n", __func__
);
378 if (!strcmp(nls
->charset
, "utf8")) return(1);
383 } /* end of convert_ch_to_uni */
385 static INT32
convert_uni_to_ch(struct nls_table
*nls
, UINT8
*ch
, UINT16 uni
, INT32
*lossy
)
396 if ((len
= nls
->uni2char(uni
, ch
, NLS_MAX_CHARSET_SIZE
)) < 0) {
397 /* conversion failed */
398 printk("%s: fail to use nls \n", __func__
);
399 if (lossy
!= NULL
) *lossy
= TRUE
;
406 } /* end of convert_uni_to_ch */
408 /* end of exfat_nls.c */