master
cpp 340 lines 7.48 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 escape.c
8
9 Abstract:
10
11 This file contains support for escaping Linux paths for us on NTFS using
12 the DrvFs escape conventions.
13
14 --*/
15
16 #include "common.h"
17 #include "escape.h"
18 #include "util.h"
19
20 //
21 // List indicating which characters are legal in NTFS.
22 // This differs from the Windows logic in two ways:
23 // 1. Slashes are allowed (because escaping is done on full Linux paths).
24 // 2. Colons are disallowed (because they indicate alternate data streams).
25 //
26
27 static constexpr bool EscapeNtfsLegalAnsiCharacterArray[128] = {
28 false, // 0x00 ^@
29 false, // 0x01 ^A
30 false, // 0x02 ^B
31 false, // 0x03 ^C
32 false, // 0x04 ^D
33 false, // 0x05 ^E
34 false, // 0x06 ^F
35 false, // 0x07 ^G
36 false, // 0x08 ^H
37 false, // 0x09 ^I
38 false, // 0x0A ^J
39 false, // 0x0B ^K
40 false, // 0x0C ^L
41 false, // 0x0D ^M
42 false, // 0x0E ^N
43 false, // 0x0F ^O
44 false, // 0x10 ^P
45 false, // 0x11 ^Q
46 false, // 0x12 ^R
47 false, // 0x13 ^S
48 false, // 0x14 ^T
49 false, // 0x15 ^U
50 false, // 0x16 ^V
51 false, // 0x17 ^W
52 false, // 0x18 ^X
53 false, // 0x19 ^Y
54 false, // 0x1A ^Z
55 false, // 0x1B ESC
56 false, // 0x1C FS
57 false, // 0x1D GS
58 false, // 0x1E RS
59 false, // 0x1F US
60 true, // 0x20 space
61 true, // 0x21 !
62 false, // 0x22 "
63 true, // 0x23 #
64 true, // 0x24 $
65 true, // 0x25 %
66 true, // 0x26 &
67 true, // 0x27 '
68 true, // 0x28 (
69 true, // 0x29 )
70 false, // 0x2A *
71 true, // 0x2B +
72 true, // 0x2C,
73 true, // 0x2D -
74 true, // 0x2E .
75 true, // 0x2F / *** Normally "false"
76 true, // 0x30 0
77 true, // 0x31 1
78 true, // 0x32 2
79 true, // 0x33 3
80 true, // 0x34 4
81 true, // 0x35 5
82 true, // 0x36 6
83 true, // 0x37 7
84 true, // 0x38 8
85 true, // 0x39 9
86 false, // 0x3A : *** Normally "true"
87 true, // 0x3B ;
88 false, // 0x3C <
89 true, // 0x3D =
90 false, // 0x3E >
91 false, // 0x3F ?
92 true, // 0x40 @
93 true, // 0x41 A
94 true, // 0x42 B
95 true, // 0x43 C
96 true, // 0x44 D
97 true, // 0x45 E
98 true, // 0x46 F
99 true, // 0x47 G
100 true, // 0x48 H
101 true, // 0x49 I
102 true, // 0x4A J
103 true, // 0x4B K
104 true, // 0x4C L
105 true, // 0x4D M
106 true, // 0x4E N
107 true, // 0x4F O
108 true, // 0x50 P
109 true, // 0x51 Q
110 true, // 0x52 R
111 true, // 0x53 S
112 true, // 0x54 T
113 true, // 0x55 U
114 true, // 0x56 V
115 true, // 0x57 W
116 true, // 0x58 X
117 true, // 0x59 Y
118 true, // 0x5A Z
119 true, // 0x5B [
120 false, // 0x5C backslash
121 true, // 0x5D ]
122 true, // 0x5E ^
123 true, // 0x5F _
124 true, // 0x60 `
125 true, // 0x61 a
126 true, // 0x62 b
127 true, // 0x63 c
128 true, // 0x64 d
129 true, // 0x65 e
130 true, // 0x66 f
131 true, // 0x67 g
132 true, // 0x68 h
133 true, // 0x69 i
134 true, // 0x6A j
135 true, // 0x6B k
136 true, // 0x6C l
137 true, // 0x6D m
138 true, // 0x6E n
139 true, // 0x6F o
140 true, // 0x70 p
141 true, // 0x71 q
142 true, // 0x72 r
143 true, // 0x73 s
144 true, // 0x74 t
145 true, // 0x75 u
146 true, // 0x76 v
147 true, // 0x77 w
148 true, // 0x78 x
149 true, // 0x79 y
150 true, // 0x7A z
151 true, // 0x7B {
152 false, // 0x7C |
153 true, // 0x7D }
154 true, // 0x7E ~
155 true // 0x7F 
156 };
157
158 //
159 // This is the utf-8 sequence for character 0xf000, the first character in the
160 // range used to escape unsupported characters.
161 //
162
163 static const char UtilEscapeCharBase[] = {0xef, 0x80, 0x80};
164
165 bool EscapeCharNeedsEscape(char Character)
166
167 /*++
168
169 Description:
170
171 This routine checks whether a character needs to be escaped to be used in
172 a path.
173
174 N.B. Slashes are allowed because this function is used on complete Linux
175 paths. The caller should translate those to backslashes after
176 calling this function.
177
178 Parameters:
179
180 Character - Supplies the character.
181
182 Return:
183
184 True if the character needs to be escaped; otherwise, false.
185
186 --*/
187
188 {
189 //
190 // Check if the character needs to be escaped.
191 //
192
193 return (static_cast<unsigned char>(Character) <= SCHAR_MAX) && (EscapeNtfsLegalAnsiCharacterArray[static_cast<int>(Character)] == false);
194 }
195
196 void EscapePathForNt(const char* Path, char* EscapedPath)
197
198 /*++
199
200 Description:
201
202 This routine escapes a Linux path for use with NT.
203
204 N.B. The path is assumed to use Linux separators (forward slash), so those
205 are not escaped, but rather replaced with backslashes.
206
207 Parameters:
208
209 Path - Supplies the path to escape.
210
211 EscapedPath - Supplies a buffer that receives the escaped path. This
212 buffer is assumed to be the right length.
213
214 Return:
215
216 None.
217
218 --*/
219
220 {
221 const char* Current;
222 size_t InsertionIndex;
223
224 InsertionIndex = 0;
225 for (Current = Path; *Current != '\0'; Current += 1)
226 {
227 if (*Current == PATH_SEP)
228 {
229 EscapedPath[InsertionIndex] = PATH_SEP_NT;
230 InsertionIndex += 1;
231 }
232 else if (EscapeCharNeedsEscape(*Current) == false)
233 {
234 EscapedPath[InsertionIndex] = *Current;
235 InsertionIndex += 1;
236 }
237 else
238 {
239 //
240 // Insert the utf-8 sequence for the escaped character.
241 //
242 // N.B. The last byte of the sequence can hold only 6 bits of data
243 // due to utf-8 encoding, so the 2 most significant bits of
244 // the character are encoded in the second byte.
245 //
246
247 EscapedPath[InsertionIndex] = UtilEscapeCharBase[0];
248 EscapedPath[InsertionIndex + 1] = UtilEscapeCharBase[1] | (*Current >> 6);
249 EscapedPath[InsertionIndex + 2] = UtilEscapeCharBase[2] | (*Current & 0x3f);
250 InsertionIndex += sizeof(UtilEscapeCharBase);
251 }
252 }
253 }
254
255 size_t EscapePathForNtLength(const char* Path)
256
257 /*++
258
259 Description:
260
261 This routine determines the length needed to escape a Linux path for use
262 in NT.
263
264 N.B. The path is assumed to use Linux separators (forward slash), so those
265 are not escaped.
266
267 Parameters:
268
269 Path - Supplies the path to escape.
270
271 Return:
272
273 The length in bytes. If this equals the length of the original string,
274 there are no characters that need to e escaped.
275
276 --*/
277
278 {
279 const char* Current;
280 size_t Length;
281
282 Length = 0;
283 for (Current = Path; *Current != '\0'; Current += 1)
284 {
285 if (EscapeCharNeedsEscape(*Current) == false)
286 {
287 Length += 1;
288 }
289 else
290 {
291 Length += sizeof(UtilEscapeCharBase);
292 }
293 }
294
295 return Length;
296 }
297
298 void UnescapePathInplace(char* Path)
299
300 /*++
301
302 Description:
303
304 This routine unescapes the supplied string inplace.
305
306 Parameters:
307
308 Path - Supplies the path to be unescaped.
309
310 Return:
311
312 None.
313
314 --*/
315
316 {
317 char* Current;
318 char* Remaining;
319 char Unescaped;
320
321 for (Current = Path; *Current != '\0'; Current += 1)
322 {
323 //
324 // If the current character is a utf-8 sequence that can be unescaped,
325 // replace the character and shift down the remainder of the string.
326 //
327
328 if ((Current[0] == UtilEscapeCharBase[0]) && ((Current[1] & UtilEscapeCharBase[1]) != 0) &&
329 ((Current[2] & UtilEscapeCharBase[2]) != 0))
330 {
331 Unescaped = (Current[1] << 6) | (Current[2] & 0x3f);
332 if (EscapeCharNeedsEscape(Unescaped) != false)
333 {
334 *Current = Unescaped;
335 Remaining = &Current[3];
336 memmove(Current + 1, Remaining, strlen(Remaining) + 1);
337 }
338 }
339 }
340 }