CD exfat support for Tomato. https://github.com/dorimanx/exfat-nofuse.
[tomato.git] / release / src-rt / linux / linux-2.6 / fs / exfat / exfat_nls.c
blobf5e002d31404dfc9d20f79bf21ac1e6da0b95005
1 /*
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 /************************************************************************/
20 /* */
21 /* PROJECT : exFAT & FAT12/16/32 File System */
22 /* FILE : exfat_nls.c */
23 /* PURPOSE : exFAT NLS Manager */
24 /* */
25 /*----------------------------------------------------------------------*/
26 /* NOTES */
27 /* */
28 /*----------------------------------------------------------------------*/
29 /* REVISION HISTORY (Ver 0.9) */
30 /* */
31 /* - 2010.11.15 [Joosun Hahn] : first writing */
32 /* */
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"
42 #include "exfat.h"
44 #include <linux/nls.h>
46 /*----------------------------------------------------------------------*/
47 /* Global Variable Definitions */
48 /*----------------------------------------------------------------------*/
50 /*----------------------------------------------------------------------*/
51 /* Local Variable Definitions */
52 /*----------------------------------------------------------------------*/
54 static UINT16 bad_dos_chars[] = {
55 /* + , ; = [ ] */
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)
84 return(a);
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)];
87 else
88 return 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)
98 INT32 i;
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);
104 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++) {
118 *(dosname+i) = ' ';
121 if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_CUR_DIR_NAME)) {
122 *(dosname) = '.';
123 p_dosname->name_case = 0x0;
124 if (p_lossy != NULL) *p_lossy = FALSE;
125 return;
128 if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_PAR_DIR_NAME)) {
129 *(dosname) = '.';
130 *(dosname+1) = '.';
131 p_dosname->name_case = 0x0;
132 if (p_lossy != NULL) *p_lossy = FALSE;
133 return;
136 /* search for the last embedded period */
137 last_period = NULL;
138 for (p = uniname; *p; p++) {
139 if (*p == (UINT16) '.') last_period = p;
142 i = 0;
143 while (i < DOS_NAME_LENGTH) {
144 if (i == 8) {
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') {
154 break;
155 } else if (*uniname == (UINT16) ' ') {
156 lossy = TRUE;
157 } else if (*uniname == (UINT16) '.') {
158 if (uniname < last_period) lossy = TRUE;
159 else i = 8;
160 } else if (WSTRCHR(bad_dos_chars, *uniname)) {
161 lossy = TRUE;
162 *(dosname+i) = '_';
163 i++;
164 } else {
165 len = convert_uni_to_ch(nls, buf, *uniname, &lossy);
167 if (len > 1) {
168 if ((i >= 8) && ((i+len) > DOS_NAME_LENGTH)) {
169 break;
171 if ((i < 8) && ((i+len) > 8)) {
172 i = 8;
173 continue;
176 lower = 0xFF;
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;
186 else lower |= 0x10;
187 } else if ((*buf >= 'A') && (*buf <= 'Z')) {
188 *(dosname+i) = *buf;
190 if (i < 8) upper |= 0x08;
191 else upper |= 0x10;
192 } else {
193 *(dosname+i) = *buf;
195 i++;
199 uniname++;
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) {
220 *buf = 0xE5;
221 i++;
222 n++;
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');
230 else
231 *(buf+n) = *(dosname+i);
233 if (*(dosname+8) != ' ') {
234 *(buf+n) = '.';
235 n++;
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');
243 else
244 *(buf+n) = *(dosname+i);
246 *(buf+n) = '\0';
248 i = j = 0;
249 while (j < (MAX_NAME_LENGTH-1)) {
250 if (*(buf+i) == '\0') break;
252 i += convert_ch_to_uni(nls, uniname, (buf+i), NULL);
254 uniname++;
255 j++;
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)
263 INT32 i, j, len;
264 UINT8 buf[MAX_CHARSET_SIZE];
265 UINT16 *uniname = p_uniname->name;
266 struct nls_table *nls = EXFAT_SB(sb)->nls_io;
268 if (nls == NULL) {
269 len = utf16s_to_utf8s(uniname, MAX_NAME_LENGTH, UTF16_HOST_ENDIAN, p_cstring, MAX_NAME_LENGTH);
270 p_cstring[len] = 0;
271 return;
274 i = 0;
275 while (i < (MAX_NAME_LENGTH-1)) {
276 if (*uniname == (UINT16) '\0') break;
278 len = convert_uni_to_ch(nls, buf, *uniname, NULL);
280 if (len > 1) {
281 for (j = 0; j < len; j++)
282 *p_cstring++ = (INT8) *(buf+j);
283 } else { /* len == 1 */
284 *p_cstring++ = (INT8) *buf;
287 uniname++;
288 i++;
291 *p_cstring = '\0';
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;
297 UINT8 *end_of_name;
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')
321 lossy = TRUE;
323 if (nls == NULL) {
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]));
327 uniname[i] = '\0';
329 else {
330 i = j = 0;
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))
337 lossy = TRUE;
339 SET16_A(upname + j * 2, nls_upper(sb, *uniname));
341 uniname++;
342 j++;
345 if (*(p_cstring+i) != '\0')
346 lossy = TRUE;
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);
353 if (p_lossy != NULL)
354 *p_lossy = lossy;
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)
363 int len;
365 *uni = 0x0;
367 if (ch[0] < 0x80) {
368 *uni = (UINT16) ch[0];
369 return(1);
372 if ((len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni)) < 0) {
373 /* conversion failed */
374 printk("%s: fail to use nls \n", __func__);
375 if (lossy != NULL)
376 *lossy = TRUE;
377 *uni = (UINT16) '_';
378 if (!strcmp(nls->charset, "utf8")) return(1);
379 else return(2);
382 return(len);
383 } /* end of convert_ch_to_uni */
385 static INT32 convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy)
387 int len;
389 ch[0] = 0x0;
391 if (uni < 0x0080) {
392 ch[0] = (UINT8) uni;
393 return(1);
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;
400 ch[0] = '_';
401 return(1);
404 return(len);
406 } /* end of convert_uni_to_ch */
408 /* end of exfat_nls.c */