1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
4  */
5 
6 #include <linux/string.h>
7 #include <linux/nls.h>
8 #include "exfat.h"
9 
10 static u16 bad_dos_chars[] = {
11 	/* + , ; = [ ] */
12 	0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D,
13 	0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D,
14 	0
15 };
16 
17 static u16 bad_uni_chars[] = {
18 	/* " * / : < > ? \ | */
19 	0x0022,         0x002A, 0x002F, 0x003A,
20 	0x003C, 0x003E, 0x003F, 0x005C, 0x007C,
21 	0
22 };
23 
convert_ch_to_uni(struct nls_table * nls,u16 * uni,u8 * ch,bool * lossy)24 static int convert_ch_to_uni(struct nls_table *nls, u16 *uni, u8 *ch,
25 			     bool *lossy)
26 {
27 	int len;
28 
29 	*uni = 0x0;
30 
31 	if (ch[0] < 0x80) {
32 		*uni = (u16)ch[0];
33 		return 1;
34 	}
35 
36 	len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni);
37 	if (len < 0) {
38 		/* conversion failed */
39 		pr_info("%s: fail to use nls\n", __func__);
40 		if (lossy)
41 			*lossy = true;
42 		*uni = (u16)'_';
43 		if (!strcmp(nls->charset, "utf8"))
44 			return 1;
45 		else
46 			return 2;
47 	}
48 
49 	return len;
50 }
51 
convert_uni_to_ch(struct nls_table * nls,u8 * ch,u16 uni,bool * lossy)52 static int convert_uni_to_ch(struct nls_table *nls, u8 *ch, u16 uni,
53 			     bool *lossy)
54 {
55 	int len;
56 
57 	ch[0] = 0x0;
58 
59 	if (uni < 0x0080) {
60 		ch[0] = (u8)uni;
61 		return 1;
62 	}
63 
64 	len = nls->uni2char(uni, ch, NLS_MAX_CHARSET_SIZE);
65 	if (len < 0) {
66 		/* conversion failed */
67 		pr_info("%s: fail to use nls\n", __func__);
68 		if (lossy)
69 			*lossy = true;
70 		ch[0] = '_';
71 		return 1;
72 	}
73 
74 	return len;
75 }
76 
nls_upper(struct super_block * sb,u16 a)77 u16 nls_upper(struct super_block *sb, u16 a)
78 {
79 	struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
80 
81 	if (EXFAT_SB(sb)->options.casesensitive)
82 		return a;
83 	if (p_fs->vol_utbl && p_fs->vol_utbl[get_col_index(a)])
84 		return p_fs->vol_utbl[get_col_index(a)][get_row_index(a)];
85 	else
86 		return a;
87 }
88 
nls_wstrchr(u16 * str,u16 wchar)89 static u16 *nls_wstrchr(u16 *str, u16 wchar)
90 {
91 	while (*str) {
92 		if (*(str++) == wchar)
93 			return str;
94 	}
95 
96 	return NULL;
97 }
98 
nls_dosname_cmp(struct super_block * sb,u8 * a,u8 * b)99 int nls_dosname_cmp(struct super_block *sb, u8 *a, u8 *b)
100 {
101 	return strncmp(a, b, DOS_NAME_LENGTH);
102 }
103 
nls_uniname_cmp(struct super_block * sb,u16 * a,u16 * b)104 int nls_uniname_cmp(struct super_block *sb, u16 *a, u16 *b)
105 {
106 	int i;
107 
108 	for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) {
109 		if (nls_upper(sb, *a) != nls_upper(sb, *b))
110 			return 1;
111 		if (*a == 0x0)
112 			return 0;
113 	}
114 	return 0;
115 }
116 
nls_uniname_to_dosname(struct super_block * sb,struct dos_name_t * p_dosname,struct uni_name_t * p_uniname,bool * p_lossy)117 void nls_uniname_to_dosname(struct super_block *sb,
118 			    struct dos_name_t *p_dosname,
119 			    struct uni_name_t *p_uniname, bool *p_lossy)
120 {
121 	int i, j, len;
122 	bool lossy = false;
123 	u8 buf[MAX_CHARSET_SIZE];
124 	u8 lower = 0, upper = 0;
125 	u8 *dosname = p_dosname->name;
126 	u16 *uniname = p_uniname->name;
127 	u16 *p, *last_period;
128 	struct nls_table *nls = EXFAT_SB(sb)->nls_disk;
129 
130 	for (i = 0; i < DOS_NAME_LENGTH; i++)
131 		*(dosname + i) = ' ';
132 
133 	if (!nls_uniname_cmp(sb, uniname, (u16 *)UNI_CUR_DIR_NAME)) {
134 		*(dosname) = '.';
135 		p_dosname->name_case = 0x0;
136 		if (p_lossy)
137 			*p_lossy = false;
138 		return;
139 	}
140 
141 	if (!nls_uniname_cmp(sb, uniname, (u16 *)UNI_PAR_DIR_NAME)) {
142 		*(dosname) = '.';
143 		*(dosname + 1) = '.';
144 		p_dosname->name_case = 0x0;
145 		if (p_lossy)
146 			*p_lossy = false;
147 		return;
148 	}
149 
150 	/* search for the last embedded period */
151 	last_period = NULL;
152 	for (p = uniname; *p; p++) {
153 		if (*p == (u16)'.')
154 			last_period = p;
155 	}
156 
157 	i = 0;
158 	while (i < DOS_NAME_LENGTH) {
159 		if (i == 8) {
160 			if (!last_period)
161 				break;
162 
163 			if (uniname <= last_period) {
164 				if (uniname < last_period)
165 					lossy = true;
166 				uniname = last_period + 1;
167 			}
168 		}
169 
170 		if (*uniname == (u16)'\0') {
171 			break;
172 		} else if (*uniname == (u16)' ') {
173 			lossy = true;
174 		} else if (*uniname == (u16)'.') {
175 			if (uniname < last_period)
176 				lossy = true;
177 			else
178 				i = 8;
179 		} else if (nls_wstrchr(bad_dos_chars, *uniname)) {
180 			lossy = true;
181 			*(dosname + i) = '_';
182 			i++;
183 		} else {
184 			len = convert_uni_to_ch(nls, buf, *uniname, &lossy);
185 
186 			if (len > 1) {
187 				if ((i >= 8) && ((i + len) > DOS_NAME_LENGTH))
188 					break;
189 
190 				if ((i < 8) && ((i + len) > 8)) {
191 					i = 8;
192 					continue;
193 				}
194 
195 				lower = 0xFF;
196 
197 				for (j = 0; j < len; j++, i++)
198 					*(dosname + i) = *(buf + j);
199 			} else { /* len == 1 */
200 				if ((*buf >= 'a') && (*buf <= 'z')) {
201 					*(dosname + i) = *buf - ('a' - 'A');
202 
203 					if (i < 8)
204 						lower |= 0x08;
205 					else
206 						lower |= 0x10;
207 				} else if ((*buf >= 'A') && (*buf <= 'Z')) {
208 					*(dosname + i) = *buf;
209 
210 					if (i < 8)
211 						upper |= 0x08;
212 					else
213 						upper |= 0x10;
214 				} else {
215 					*(dosname + i) = *buf;
216 				}
217 				i++;
218 			}
219 		}
220 
221 		uniname++;
222 	}
223 
224 	if (*dosname == 0xE5)
225 		*dosname = 0x05;
226 
227 	if (*uniname != 0x0)
228 		lossy = true;
229 
230 	if (upper & lower)
231 		p_dosname->name_case = 0xFF;
232 	else
233 		p_dosname->name_case = lower;
234 
235 	if (p_lossy)
236 		*p_lossy = lossy;
237 }
238 
nls_dosname_to_uniname(struct super_block * sb,struct uni_name_t * p_uniname,struct dos_name_t * p_dosname)239 void nls_dosname_to_uniname(struct super_block *sb,
240 			    struct uni_name_t *p_uniname,
241 			    struct dos_name_t *p_dosname)
242 {
243 	int i = 0, j, n = 0;
244 	u8 buf[DOS_NAME_LENGTH + 2];
245 	u8 *dosname = p_dosname->name;
246 	u16 *uniname = p_uniname->name;
247 	struct nls_table *nls = EXFAT_SB(sb)->nls_disk;
248 
249 	if (*dosname == 0x05) {
250 		*buf = 0xE5;
251 		i++;
252 		n++;
253 	}
254 
255 	for (; i < 8; i++, n++) {
256 		if (*(dosname + i) == ' ')
257 			break;
258 
259 		if ((*(dosname + i) >= 'A') && (*(dosname + i) <= 'Z') &&
260 		    (p_dosname->name_case & 0x08))
261 			*(buf + n) = *(dosname + i) + ('a' - 'A');
262 		else
263 			*(buf + n) = *(dosname + i);
264 	}
265 	if (*(dosname + 8) != ' ') {
266 		*(buf + n) = '.';
267 		n++;
268 	}
269 
270 	for (i = 8; i < DOS_NAME_LENGTH; i++, n++) {
271 		if (*(dosname + i) == ' ')
272 			break;
273 
274 		if ((*(dosname + i) >= 'A') && (*(dosname + i) <= 'Z') &&
275 		    (p_dosname->name_case & 0x10))
276 			*(buf + n) = *(dosname + i) + ('a' - 'A');
277 		else
278 			*(buf + n) = *(dosname + i);
279 	}
280 	*(buf + n) = '\0';
281 
282 	i = 0;
283 	j = 0;
284 	while (j < (MAX_NAME_LENGTH - 1)) {
285 		if (*(buf + i) == '\0')
286 			break;
287 
288 		i += convert_ch_to_uni(nls, uniname, (buf + i), NULL);
289 
290 		uniname++;
291 		j++;
292 	}
293 
294 	*uniname = (u16)'\0';
295 }
296 
nls_uniname_to_cstring(struct super_block * sb,u8 * p_cstring,struct uni_name_t * p_uniname)297 void nls_uniname_to_cstring(struct super_block *sb, u8 *p_cstring,
298 			    struct uni_name_t *p_uniname)
299 {
300 	int i, j, len;
301 	u8 buf[MAX_CHARSET_SIZE];
302 	u16 *uniname = p_uniname->name;
303 	struct nls_table *nls = EXFAT_SB(sb)->nls_io;
304 
305 	if (!nls) {
306 		len = utf16s_to_utf8s(uniname, MAX_NAME_LENGTH,
307 				      UTF16_HOST_ENDIAN, p_cstring,
308 				      MAX_NAME_LENGTH);
309 		p_cstring[len] = 0;
310 		return;
311 	}
312 
313 	i = 0;
314 	while (i < (MAX_NAME_LENGTH - 1)) {
315 		if (*uniname == (u16)'\0')
316 			break;
317 
318 		len = convert_uni_to_ch(nls, buf, *uniname, NULL);
319 
320 		if (len > 1) {
321 			for (j = 0; j < len; j++)
322 				*p_cstring++ = (char)*(buf + j);
323 		} else { /* len == 1 */
324 			*p_cstring++ = (char)*buf;
325 		}
326 
327 		uniname++;
328 		i++;
329 	}
330 
331 	*p_cstring = '\0';
332 }
333 
nls_cstring_to_uniname(struct super_block * sb,struct uni_name_t * p_uniname,u8 * p_cstring,bool * p_lossy)334 void nls_cstring_to_uniname(struct super_block *sb,
335 			    struct uni_name_t *p_uniname, u8 *p_cstring,
336 			    bool *p_lossy)
337 {
338 	int i, j;
339 	bool lossy = false;
340 	u8 *end_of_name;
341 	u8 upname[MAX_NAME_LENGTH * 2];
342 	u16 *uniname = p_uniname->name;
343 	struct nls_table *nls = EXFAT_SB(sb)->nls_io;
344 
345 	/* strip all trailing spaces */
346 	end_of_name = p_cstring + strlen(p_cstring);
347 
348 	while (*(--end_of_name) == ' ') {
349 		if (end_of_name < p_cstring)
350 			break;
351 	}
352 	*(++end_of_name) = '\0';
353 
354 	if (strcmp(p_cstring, ".") && strcmp(p_cstring, "..")) {
355 		/* strip all trailing periods */
356 		while (*(--end_of_name) == '.') {
357 			if (end_of_name < p_cstring)
358 				break;
359 		}
360 		*(++end_of_name) = '\0';
361 	}
362 
363 	if (*p_cstring == '\0')
364 		lossy = true;
365 
366 	if (!nls) {
367 		i = utf8s_to_utf16s(p_cstring, MAX_NAME_LENGTH,
368 				    UTF16_HOST_ENDIAN, uniname,
369 				    MAX_NAME_LENGTH);
370 		for (j = 0; j < i; j++)
371 			SET16_A(upname + j * 2, nls_upper(sb, uniname[j]));
372 		uniname[i] = '\0';
373 	} else {
374 		i = 0;
375 		j = 0;
376 		while (j < (MAX_NAME_LENGTH - 1)) {
377 			if (*(p_cstring + i) == '\0')
378 				break;
379 
380 			i += convert_ch_to_uni(nls, uniname,
381 					       (u8 *)(p_cstring + i), &lossy);
382 
383 			if ((*uniname < 0x0020) ||
384 			    nls_wstrchr(bad_uni_chars, *uniname))
385 				lossy = true;
386 
387 			SET16_A(upname + j * 2, nls_upper(sb, *uniname));
388 
389 			uniname++;
390 			j++;
391 		}
392 
393 		if (*(p_cstring + i) != '\0')
394 			lossy = true;
395 		*uniname = (u16)'\0';
396 	}
397 
398 	p_uniname->name_len = j;
399 	p_uniname->name_hash = calc_checksum_2byte(upname, j << 1, 0,
400 						   CS_DEFAULT);
401 
402 	if (p_lossy)
403 		*p_lossy = lossy;
404 }
405