]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/staging/exfat/exfat_nls.c
Merge tag 'armsoc-dt' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
[linux.git] / drivers / staging / exfat / exfat_nls.c
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_uni_chars[] = {
11         /* " * / : < > ? \ | */
12         0x0022,         0x002A, 0x002F, 0x003A,
13         0x003C, 0x003E, 0x003F, 0x005C, 0x007C,
14         0
15 };
16
17 static int convert_ch_to_uni(struct nls_table *nls, u16 *uni, u8 *ch,
18                              bool *lossy)
19 {
20         int len;
21
22         *uni = 0x0;
23
24         if (ch[0] < 0x80) {
25                 *uni = (u16)ch[0];
26                 return 1;
27         }
28
29         len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni);
30         if (len < 0) {
31                 /* conversion failed */
32                 pr_info("%s: fail to use nls\n", __func__);
33                 if (lossy)
34                         *lossy = true;
35                 *uni = (u16)'_';
36                 if (!strcmp(nls->charset, "utf8"))
37                         return 1;
38                 else
39                         return 2;
40         }
41
42         return len;
43 }
44
45 static int convert_uni_to_ch(struct nls_table *nls, u8 *ch, u16 uni,
46                              bool *lossy)
47 {
48         int len;
49
50         ch[0] = 0x0;
51
52         if (uni < 0x0080) {
53                 ch[0] = (u8)uni;
54                 return 1;
55         }
56
57         len = nls->uni2char(uni, ch, NLS_MAX_CHARSET_SIZE);
58         if (len < 0) {
59                 /* conversion failed */
60                 pr_info("%s: fail to use nls\n", __func__);
61                 if (lossy)
62                         *lossy = true;
63                 ch[0] = '_';
64                 return 1;
65         }
66
67         return len;
68 }
69
70 u16 nls_upper(struct super_block *sb, u16 a)
71 {
72         struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
73
74         if (EXFAT_SB(sb)->options.casesensitive)
75                 return a;
76         if (p_fs->vol_utbl && p_fs->vol_utbl[get_col_index(a)])
77                 return p_fs->vol_utbl[get_col_index(a)][get_row_index(a)];
78         else
79                 return a;
80 }
81
82 static u16 *nls_wstrchr(u16 *str, u16 wchar)
83 {
84         while (*str) {
85                 if (*(str++) == wchar)
86                         return str;
87         }
88
89         return NULL;
90 }
91
92 int nls_uniname_cmp(struct super_block *sb, u16 *a, u16 *b)
93 {
94         int i;
95
96         for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) {
97                 if (nls_upper(sb, *a) != nls_upper(sb, *b))
98                         return 1;
99                 if (*a == 0x0)
100                         return 0;
101         }
102         return 0;
103 }
104
105 void nls_uniname_to_cstring(struct super_block *sb, u8 *p_cstring,
106                             struct uni_name_t *p_uniname)
107 {
108         int i, j, len;
109         u8 buf[MAX_CHARSET_SIZE];
110         u16 *uniname = p_uniname->name;
111         struct nls_table *nls = EXFAT_SB(sb)->nls_io;
112
113         if (!nls) {
114                 len = utf16s_to_utf8s(uniname, MAX_NAME_LENGTH,
115                                       UTF16_HOST_ENDIAN, p_cstring,
116                                       MAX_NAME_LENGTH);
117                 p_cstring[len] = 0;
118                 return;
119         }
120
121         i = 0;
122         while (i < (MAX_NAME_LENGTH - 1)) {
123                 if (*uniname == (u16)'\0')
124                         break;
125
126                 len = convert_uni_to_ch(nls, buf, *uniname, NULL);
127
128                 if (len > 1) {
129                         for (j = 0; j < len; j++)
130                                 *p_cstring++ = (char)*(buf + j);
131                 } else { /* len == 1 */
132                         *p_cstring++ = (char)*buf;
133                 }
134
135                 uniname++;
136                 i++;
137         }
138
139         *p_cstring = '\0';
140 }
141
142 void nls_cstring_to_uniname(struct super_block *sb,
143                             struct uni_name_t *p_uniname, u8 *p_cstring,
144                             bool *p_lossy)
145 {
146         int i, j;
147         bool lossy = false;
148         u8 *end_of_name;
149         u8 upname[MAX_NAME_LENGTH * 2];
150         u16 *uniname = p_uniname->name;
151         struct nls_table *nls = EXFAT_SB(sb)->nls_io;
152
153         /* strip all trailing spaces */
154         end_of_name = p_cstring + strlen(p_cstring);
155
156         while (*(--end_of_name) == ' ') {
157                 if (end_of_name < p_cstring)
158                         break;
159         }
160         *(++end_of_name) = '\0';
161
162         if (strcmp(p_cstring, ".") && strcmp(p_cstring, "..")) {
163                 /* strip all trailing periods */
164                 while (*(--end_of_name) == '.') {
165                         if (end_of_name < p_cstring)
166                                 break;
167                 }
168                 *(++end_of_name) = '\0';
169         }
170
171         if (*p_cstring == '\0')
172                 lossy = true;
173
174         if (!nls) {
175                 i = utf8s_to_utf16s(p_cstring, MAX_NAME_LENGTH,
176                                     UTF16_HOST_ENDIAN, uniname,
177                                     MAX_NAME_LENGTH);
178                 for (j = 0; j < i; j++)
179                         SET16_A(upname + j * 2, nls_upper(sb, uniname[j]));
180                 uniname[i] = '\0';
181         } else {
182                 i = 0;
183                 j = 0;
184                 while (j < (MAX_NAME_LENGTH - 1)) {
185                         if (*(p_cstring + i) == '\0')
186                                 break;
187
188                         i += convert_ch_to_uni(nls, uniname,
189                                                (u8 *)(p_cstring + i), &lossy);
190
191                         if ((*uniname < 0x0020) ||
192                             nls_wstrchr(bad_uni_chars, *uniname))
193                                 lossy = true;
194
195                         SET16_A(upname + j * 2, nls_upper(sb, *uniname));
196
197                         uniname++;
198                         j++;
199                 }
200
201                 if (*(p_cstring + i) != '\0')
202                         lossy = true;
203                 *uniname = (u16)'\0';
204         }
205
206         p_uniname->name_len = j;
207         p_uniname->name_hash = calc_checksum_2byte(upname, j << 1, 0,
208                                                    CS_DEFAULT);
209
210         if (p_lossy)
211                 *p_lossy = lossy;
212 }