tizen 2.4 release
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / fs / exfat / exfat_nls.c
1 /*
2  *  Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 #include "exfat_config.h"
20 #include "exfat_global.h"
21 #include "exfat_data.h"
22
23 #include "exfat_nls.h"
24 #include "exfat_api.h"
25 #include "exfat_super.h"
26 #include "exfat.h"
27
28 #include <linux/nls.h>
29
30 static UINT16 bad_dos_chars[] = {
31         0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D,
32         0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D,
33         0
34 };
35
36 static UINT16 bad_uni_chars[] = {
37         0x0022,         0x002A, 0x002F, 0x003A,
38         0x003C, 0x003E, 0x003F, 0x005C, 0x007C,
39         0
40 };
41
42 static INT32  convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy);
43 static INT32  convert_ch_to_uni(struct nls_table *nls, UINT16 *uni, UINT8 *ch, INT32 *lossy);
44
45 UINT16 nls_upper(struct super_block *sb, UINT16 a)
46 {
47         FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
48
49         if (EXFAT_SB(sb)->options.casesensitive)
50                 return(a);
51         if ((p_fs->vol_utbl)[get_col_index(a)] != NULL)
52                 return (p_fs->vol_utbl)[get_col_index(a)][get_row_index(a)];
53         else
54                 return a;
55 }
56
57 INT32 nls_dosname_cmp(struct super_block *sb, UINT8 *a, UINT8 *b)
58 {
59         return(STRNCMP((void *) a, (void *) b, DOS_NAME_LENGTH));
60 }
61
62 INT32 nls_uniname_cmp(struct super_block *sb, UINT16 *a, UINT16 *b)
63 {
64         INT32 i;
65
66         for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) {
67                 if (nls_upper(sb, *a) != nls_upper(sb, *b)) return(1);
68                 if (*a == 0x0) return(0);
69         }
70         return(0);
71 }
72
73 void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, INT32 *p_lossy)
74 {
75         INT32 i, j, len, lossy = FALSE;
76         UINT8 buf[MAX_CHARSET_SIZE];
77         UINT8 lower = 0, upper = 0;
78         UINT8 *dosname = p_dosname->name;
79         UINT16 *uniname = p_uniname->name;
80         UINT16 *p, *last_period;
81         struct nls_table *nls = EXFAT_SB(sb)->nls_disk;
82
83         for (i = 0; i < DOS_NAME_LENGTH; i++) {
84                 *(dosname+i) = ' ';
85         }
86
87         if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_CUR_DIR_NAME)) {
88                 *(dosname) = '.';
89                 p_dosname->name_case = 0x0;
90                 if (p_lossy != NULL) *p_lossy = FALSE;
91                 return;
92         }
93
94         if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_PAR_DIR_NAME)) {
95                 *(dosname) = '.';
96                 *(dosname+1) = '.';
97                 p_dosname->name_case = 0x0;
98                 if (p_lossy != NULL) *p_lossy = FALSE;
99                 return;
100         }
101
102         last_period = NULL;
103         for (p = uniname; *p; p++) {
104                 if (*p == (UINT16) '.') last_period = p;
105         }
106
107         i = 0;
108         while (i < DOS_NAME_LENGTH) {
109                 if (i == 8) {
110                         if (last_period == NULL) break;
111
112                         if (uniname <= last_period) {
113                                 if (uniname < last_period) lossy = TRUE;
114                                 uniname = last_period + 1;
115                         }
116                 }
117
118                 if (*uniname == (UINT16) '\0') {
119                         break;
120                 } else if (*uniname == (UINT16) ' ') {
121                         lossy = TRUE;
122                 } else if (*uniname == (UINT16) '.') {
123                         if (uniname < last_period) lossy = TRUE;
124                         else i = 8;
125                 } else if (WSTRCHR(bad_dos_chars, *uniname)) {
126                         lossy = TRUE;
127                         *(dosname+i) = '_';
128                         i++;
129                 } else {
130                         len = convert_uni_to_ch(nls, buf, *uniname, &lossy);
131
132                         if (len > 1) {
133                                 if ((i >= 8) && ((i+len) > DOS_NAME_LENGTH)) {
134                                         break;
135                                 }
136                                 if ((i <  8) && ((i+len) > 8)) {
137                                         i = 8;
138                                         continue;
139                                 }
140
141                                 lower = 0xFF;
142
143                                 for (j = 0; j < len; j++, i++) {
144                                         *(dosname+i) = *(buf+j);
145                                 }
146                         } else {
147                                 if ((*buf >= 'a') && (*buf <= 'z')) {
148                                         *(dosname+i) = *buf - ('a' - 'A');
149
150                                         if (i < 8) lower |= 0x08;
151                                         else lower |= 0x10;
152                                 } else if ((*buf >= 'A') && (*buf <= 'Z')) {
153                                         *(dosname+i) = *buf;
154
155                                         if (i < 8) upper |= 0x08;
156                                         else upper |= 0x10;
157                                 } else {
158                                         *(dosname+i) = *buf;
159                                 }
160                                 i++;
161                         }
162                 }
163
164                 uniname++;
165         }
166
167         if (*dosname == 0xE5) *dosname = 0x05;
168         if (*uniname != 0x0) lossy = TRUE;
169
170         if (upper & lower) p_dosname->name_case = 0xFF;
171         else p_dosname->name_case = lower;
172
173         if (p_lossy != NULL) *p_lossy = lossy;
174 }
175
176 void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname)
177 {
178         INT32 i = 0, j, n = 0;
179         UINT8 buf[DOS_NAME_LENGTH+2];
180         UINT8 *dosname = p_dosname->name;
181         UINT16 *uniname = p_uniname->name;
182         struct nls_table *nls = EXFAT_SB(sb)->nls_disk;
183
184         if (*dosname == 0x05) {
185                 *buf = 0xE5;
186                 i++;
187                 n++;
188         }
189
190         for ( ; i < 8; i++, n++) {
191                 if (*(dosname+i) == ' ') break;
192
193                 if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x08))
194                         *(buf+n) = *(dosname+i) + ('a' - 'A');
195                 else
196                         *(buf+n) = *(dosname+i);
197         }
198         if (*(dosname+8) != ' ') {
199                 *(buf+n) = '.';
200                 n++;
201         }
202
203         for (i = 8; i < DOS_NAME_LENGTH; i++, n++) {
204                 if (*(dosname+i) == ' ') break;
205
206                 if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x10))
207                         *(buf+n) = *(dosname+i) + ('a' - 'A');
208                 else
209                         *(buf+n) = *(dosname+i);
210         }
211         *(buf+n) = '\0';
212
213         i = j = 0;
214         while (j < (MAX_NAME_LENGTH-1)) {
215                 if (*(buf+i) == '\0') break;
216
217                 i += convert_ch_to_uni(nls, uniname, (buf+i), NULL);
218
219                 uniname++;
220                 j++;
221         }
222
223         *uniname = (UINT16) '\0';
224 }
225
226 void nls_uniname_to_cstring(struct super_block *sb, UINT8 *p_cstring, UNI_NAME_T *p_uniname)
227 {
228         INT32 i, j, len;
229         UINT8 buf[MAX_CHARSET_SIZE];
230         UINT16 *uniname = p_uniname->name;
231         struct nls_table *nls = EXFAT_SB(sb)->nls_io;
232
233         i = 0;
234         while (i < (MAX_NAME_LENGTH-1)) {
235                 if (*uniname == (UINT16) '\0') break;
236
237                 len = convert_uni_to_ch(nls, buf, *uniname, NULL);
238
239                 if (len > 1) {
240                         for (j = 0; j < len; j++)
241                                 *p_cstring++ = (INT8) *(buf+j);
242                 } else {
243                         *p_cstring++ = (INT8) *buf;
244                 }
245
246                 uniname++;
247                 i++;
248         }
249
250         *p_cstring = '\0';
251 }
252
253 void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, UINT8 *p_cstring, INT32 *p_lossy)
254 {
255         INT32 i, j, lossy = FALSE;
256         UINT8 *end_of_name;
257         UINT16 upname[MAX_NAME_LENGTH];
258         UINT16 *uniname = p_uniname->name;
259         struct nls_table *nls = EXFAT_SB(sb)->nls_io;
260
261         end_of_name = p_cstring + STRLEN((INT8 *) p_cstring);
262
263         while (*(--end_of_name) == ' ') {
264                 if (end_of_name < p_cstring) break;
265         }
266         *(++end_of_name) = '\0';
267
268         if (STRCMP((INT8 *) p_cstring, ".") && STRCMP((INT8 *) p_cstring, "..")) {
269                 while (*(--end_of_name) == '.') {
270                         if (end_of_name < p_cstring) break;
271                 }
272                 *(++end_of_name) = '\0';
273         }
274
275         if (*p_cstring == '\0')
276                 lossy = TRUE;
277
278         i = j = 0;
279         while (j < (MAX_NAME_LENGTH-1)) {
280                 if (*(p_cstring+i) == '\0') break;
281
282                 i += convert_ch_to_uni(nls, uniname, (UINT8 *)(p_cstring+i), &lossy);
283
284                 if ((*uniname < 0x0020) || WSTRCHR(bad_uni_chars, *uniname))
285                         lossy = TRUE;
286
287                 *(upname+j) = nls_upper(sb, *uniname);
288
289                 uniname++;
290                 j++;
291         }
292
293         if (*(p_cstring+i) != '\0')
294                 lossy = TRUE;
295         *uniname = (UINT16) '\0';
296
297         p_uniname->name_len = j;
298         p_uniname->name_hash = calc_checksum_2byte((void *) upname, j<<1, 0, CS_DEFAULT);
299
300         if (p_lossy != NULL)
301                 *p_lossy = lossy;
302 }
303
304 static INT32 convert_ch_to_uni(struct nls_table *nls, UINT16 *uni, UINT8 *ch, INT32 *lossy)
305 {
306         int len;
307
308         *uni = 0x0;
309
310         if (ch[0] < 0x80) {
311                 *uni = (UINT16) ch[0];
312                 return(1);
313         }
314
315         if ((len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni)) < 0) {
316                 printk("%s: fail to use nls \n", __func__);
317                 if (lossy != NULL)
318                         *lossy = TRUE;
319                 *uni = (UINT16) '_';
320                 if (!strcmp(nls->charset, "utf8")) return(1);
321                 else return(2);
322         }
323
324         return(len);
325 }
326
327 static INT32 convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy)
328 {
329         int len;
330
331         ch[0] = 0x0;
332
333         if (uni < 0x0080) {
334                 ch[0] = (UINT8) uni;
335                 return(1);
336         }
337
338         if ((len = nls->uni2char(uni, ch, NLS_MAX_CHARSET_SIZE)) < 0) {
339                 printk("%s: fail to use nls \n", __func__);
340                 if (lossy != NULL) *lossy = TRUE;
341                 ch[0] = '_';
342                 return(1);
343         }
344
345         return(len);
346
347 }