]>
Commit | Line | Data |
---|---|---|
53e1e5c6 | 1 | /** @file\r |
2 | Device Abstraction: Path manipulation utilities.\r | |
3 | \r | |
4 | Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r | |
5 | This program and the accompanying materials are licensed and made available under\r | |
6 | the terms and conditions of the BSD License that accompanies this distribution.\r | |
7 | The full text of the license may be found at\r | |
8 | http://opensource.org/licenses/bsd-license.php.\r | |
9 | \r | |
10 | THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r | |
11 | WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r | |
12 | **/\r | |
13 | #include <Library/BaseLib.h>\r | |
14 | \r | |
15 | #include <LibConfig.h>\r | |
16 | \r | |
17 | #include <errno.h>\r | |
18 | #include <stdlib.h>\r | |
19 | #include <wchar.h>\r | |
20 | #include <wctype.h>\r | |
21 | #include <kfile.h>\r | |
22 | #include <Device/Device.h>\r | |
23 | #include <MainData.h>\r | |
24 | \r | |
25 | /** Identify the type of path pointed to by Path.\r | |
26 | \r | |
27 | Paths are classified based upon their initial character sequences.\r | |
28 | ^\\ Absolute Path\r | |
29 | ^\. Relative Path\r | |
30 | ^[^:\\]: Mapping Path\r | |
31 | .* Relative Path\r | |
32 | \r | |
33 | Mapping paths are broken into two parts at the ':'. The part to the left of the ':'\r | |
34 | is the Map Name, pointed to by Path, and the part to the right of the ':' is pointed\r | |
35 | to by NewPath.\r | |
36 | \r | |
37 | If Path was not a Mapping Path, then NewPath is set to Path.\r | |
38 | \r | |
39 | @param[in] Path Pointer to the path to be classified.\r | |
40 | @param[out] NewPath Pointer to the path portion of a mapping path.\r | |
41 | @param[out] Length Length of the Map Name portion of the path.\r | |
42 | \r | |
43 | @retval PathAbsolute Path is an absolute path. NewPath points to the first '\'.\r | |
44 | @retval PathRelative Path is a relative path. NewPath = Path.\r | |
45 | @retval PathMapping Path is a mapping path. NewPath points to the character following ':'.\r | |
46 | @retval PathError Path is NULL.\r | |
47 | **/\r | |
48 | PATH_CLASS\r | |
49 | EFIAPI\r | |
50 | ClassifyPath(\r | |
51 | IN wchar_t * Path,\r | |
52 | OUT wchar_t ** NewPath,\r | |
53 | OUT int * const Length\r | |
54 | )\r | |
55 | {\r | |
56 | size_t MapLen;\r | |
57 | \r | |
58 | if(Path == NULL) {\r | |
59 | return PathError; // Bad parameter\r | |
60 | }\r | |
61 | if(NewPath != NULL) {\r | |
62 | *NewPath = Path; // Setup default condition\r | |
63 | }\r | |
64 | if((*Path == L'\\') || (*Path == L'\0')) {\r | |
65 | return PathAbsolute;\r | |
66 | }\r | |
67 | if(*Path == L'.') {\r | |
68 | return PathRelative;\r | |
69 | }\r | |
70 | /* The easy stuff has been done, now see if this is a mapping path.\r | |
71 | See if there is a ':' in Path that isn't the first character and is before\r | |
72 | any '\\' characters.\r | |
73 | */\r | |
74 | MapLen = wcscspn(Path, L"\\:");\r | |
75 | if(Length != NULL) {\r | |
76 | *Length = (int)MapLen;\r | |
77 | }\r | |
78 | /* MapLen == 0 means that the first character is a ':'\r | |
79 | == PathLen means that there are no '\\' or ':'\r | |
80 | Otherwise, Path[MapLen] == ':' for a mapping path\r | |
81 | or '\\' for a relative path.\r | |
82 | */\r | |
83 | if(MapLen == 0) {\r | |
84 | return PathError;\r | |
85 | }\r | |
86 | if(Path[MapLen] == L':') {\r | |
87 | if(NewPath != NULL) {\r | |
88 | *NewPath = &Path[MapLen + 1]; // Point to character after then ':'. Might be '\0'.\r | |
89 | }\r | |
90 | return PathMapping;\r | |
91 | }\r | |
92 | return PathRelative;\r | |
93 | }\r | |
94 | \r | |
95 | /* Normalize a narrow-character path and produce a wide-character path\r | |
96 | that has forward slashes replaced with backslashes.\r | |
97 | Backslashes are directory separators in UEFI File Paths.\r | |
98 | \r | |
99 | It is the caller's responsibility to eventually free() the returned buffer.\r | |
100 | \r | |
101 | @param[in] path A pointer to the narrow-character path to be normalized.\r | |
102 | \r | |
103 | @return A pointer to a buffer containing the normalized, wide-character, path.\r | |
104 | */\r | |
105 | wchar_t *\r | |
106 | NormalizePath( const char *path)\r | |
107 | {\r | |
108 | wchar_t *temp;\r | |
109 | wchar_t *OldPath;\r | |
110 | wchar_t *NewPath;\r | |
111 | size_t Length;\r | |
112 | \r | |
113 | OldPath = AsciiStrToUnicodeStr(path, gMD->UString);\r | |
114 | Length = wcslen(OldPath) + 1;\r | |
115 | \r | |
116 | NewPath = calloc(Length, sizeof(wchar_t));\r | |
117 | if(NewPath != NULL) {\r | |
118 | temp = NewPath;\r | |
119 | for( ; *OldPath; ++OldPath) {\r | |
120 | if(*OldPath == L'/') {\r | |
121 | *temp = L'\\';\r | |
122 | }\r | |
123 | else {\r | |
124 | *temp = *OldPath;\r | |
125 | }\r | |
126 | ++temp;\r | |
127 | }\r | |
128 | }\r | |
129 | else {\r | |
130 | errno = ENOMEM;\r | |
131 | EFIerrno = RETURN_OUT_OF_RESOURCES;\r | |
132 | }\r | |
133 | return NewPath;\r | |
134 | }\r | |
135 | \r | |
136 | /** Process a wide character string representing a Mapping Path and extract the instance number.\r | |
137 | \r | |
138 | The instance number is the sequence of decimal digits immediately to the left\r | |
139 | of the ":" in the Map Name portion of a Mapping Path.\r | |
140 | \r | |
141 | This function is called with a pointer to beginning of the Map Name.\r | |
142 | Thus Path[Length] must be a ':' and Path[Length - 1] must be a decimal digit.\r | |
143 | If either of these are not true, an instance value of 0 is returned.\r | |
144 | \r | |
145 | If Path is NULL, an instance value of 0 is returned.\r | |
146 | \r | |
147 | @param[in] Path Points to the beginning of a Mapping Path\r | |
148 | @param[in] Length Number of valid characters to the left of the ':'\r | |
149 | \r | |
150 | @return Returns either 0 or the value of the contiguous digits to the left of the ':'.\r | |
151 | **/\r | |
152 | int\r | |
153 | EFIAPI\r | |
154 | PathInstance(\r | |
155 | const wchar_t *Path,\r | |
156 | int Length\r | |
157 | )\r | |
158 | {\r | |
159 | wchar_t *temp;\r | |
160 | int instance = 0;\r | |
161 | \r | |
162 | if((Path != NULL) && (Path[Length] == L':') && (Length > 0)) {\r | |
163 | for(temp = __UNCONST(&Path[Length - 1]); Length > 0; --Length) {\r | |
164 | if(!iswdigit(*temp)) {\r | |
165 | break;\r | |
166 | }\r | |
167 | --temp;\r | |
168 | }\r | |
169 | instance = (int)wcstol(temp+1, NULL, 10);\r | |
170 | }\r | |
171 | return instance;\r | |
172 | }\r | |
173 | \r | |
174 | /** Transform a relative path into an absolute path.\r | |
175 | \r | |
176 | If Path is NULL, return NULL.\r | |
177 | Otherwise, pre-pend the CWD to Path then process the resulting path to:\r | |
178 | - Replace "/./" with "/"\r | |
179 | - Replace "/<dirname>/../" with "/"\r | |
180 | - Do not allow one to back up past the root, "/"\r | |
181 | \r | |
182 | Also sets the Current Working Device to the Root Device.\r | |
183 | \r | |
184 | Path must be a previously allocated buffer. PathAdjust will\r | |
185 | allocate a new buffer to hold the results of the transformation\r | |
186 | and free Path. A pointer to the newly allocated buffer is returned.\r | |
187 | \r | |
188 | @param[in] Path A pointer to the path to be transformed. This buffer\r | |
189 | will always be freed.\r | |
190 | \r | |
191 | @return A pointer to a buffer containing the transformed path.\r | |
192 | **/\r | |
193 | wchar_t *\r | |
194 | EFIAPI\r | |
195 | PathAdjust(\r | |
196 | wchar_t *Path\r | |
197 | )\r | |
198 | {\r | |
199 | wchar_t *NewPath;\r | |
200 | \r | |
201 | NewPath = calloc(PATH_MAX, sizeof(wchar_t));\r | |
202 | if(NewPath != NULL) {\r | |
203 | wmemcpy(NewPath, Path, PATH_MAX);\r | |
204 | }\r | |
205 | else {\r | |
206 | errno = ENOMEM;\r | |
207 | }\r | |
208 | free(Path);\r | |
209 | return NewPath;\r | |
210 | }\r | |
211 | \r | |
212 | /** Replace the leading portion of Path with any aliases.\r | |
213 | \r | |
214 | Aliases are read from /etc/fstab. If there is an associated device, the\r | |
215 | Current Working Device is set to that device.\r | |
216 | \r | |
217 | Path must be a previously allocated buffer. PathAlias will\r | |
218 | allocate a new buffer to hold the results of the transformation\r | |
219 | then free Path. A pointer to the newly allocated buffer is returned.\r | |
220 | \r | |
221 | @param[in] Path A pointer to the original, unaliased, path. This\r | |
222 | buffer is always freed.\r | |
223 | @param[out] Node Filled in with a pointer to the Device Node describing\r | |
224 | the device abstraction associated with this path.\r | |
225 | \r | |
226 | @return A pointer to a buffer containing the aliased path.\r | |
227 | **/\r | |
228 | wchar_t *\r | |
229 | EFIAPI\r | |
230 | PathAlias(\r | |
231 | wchar_t *Path,\r | |
232 | DeviceNode **Node\r | |
233 | )\r | |
234 | {\r | |
235 | wchar_t *NewPath;\r | |
236 | \r | |
237 | NewPath = calloc(PATH_MAX, sizeof(wchar_t));\r | |
238 | if(NewPath != NULL) {\r | |
239 | wmemcpy(NewPath, Path, PATH_MAX);\r | |
240 | }\r | |
241 | else {\r | |
242 | errno = ENOMEM;\r | |
243 | }\r | |
244 | free(Path);\r | |
245 | *Node = NULL;\r | |
246 | return NewPath;\r | |
247 | }\r | |
248 | \r | |
249 | /** Parse a path producing the target device, device instance, and file path.\r | |
250 | \r | |
d7ce7006 | 251 | It is the caller's responsibility to free() FullPath and MapPath when they\r |
252 | are no longer needed.\r | |
253 | \r | |
53e1e5c6 | 254 | @param[in] path\r |
255 | @param[out] FullPath\r | |
256 | @param[out] DevNode\r | |
257 | @param[out] Which\r | |
d7ce7006 | 258 | @param[out] MapPath OPTIONAL. If not NULL, it points to the place to save a pointer\r |
259 | to the extracted map name. If the path didn't have a map name,\r | |
260 | then *MapPath is set to NULL.\r | |
53e1e5c6 | 261 | \r |
262 | @retval RETURN_SUCCESS The path was parsed successfully.\r | |
263 | @retval RETURN_NOT_FOUND The path does not map to a valid device.\r | |
264 | @retval RETURN_OUT_OF_RESOURCES Insufficient memory to calloc a MapName buffer.\r | |
265 | The errno variable is set to ENOMEM.\r | |
266 | @retval RETURN_INVALID_PARAMETER The path parameter is not valid.\r | |
267 | The errno variable is set to EINVAL.\r | |
268 | **/\r | |
269 | RETURN_STATUS\r | |
270 | EFIAPI\r | |
271 | ParsePath(\r | |
272 | IN const char *path,\r | |
273 | OUT wchar_t **FullPath,\r | |
274 | OUT DeviceNode **DevNode,\r | |
d7ce7006 | 275 | OUT int *Which,\r |
276 | OUT wchar_t **MapPath\r | |
53e1e5c6 | 277 | )\r |
278 | {\r | |
279 | int MapLen;\r | |
280 | PATH_CLASS PathClass;\r | |
281 | wchar_t *NewPath;\r | |
282 | wchar_t *WPath = NULL;\r | |
283 | wchar_t *MPath = NULL;\r | |
284 | DeviceNode *Node = NULL;\r | |
285 | RETURN_STATUS Status = RETURN_NOT_FOUND;\r | |
286 | int Instance = 0;\r | |
287 | BOOLEAN ReMapped;\r | |
288 | \r | |
289 | ReMapped = FALSE;\r | |
290 | \r | |
291 | // Convert name from MBCS to WCS and change '/' to '\\'\r | |
292 | WPath = NormalizePath( path);\r | |
293 | PathClass = ClassifyPath(WPath, &NewPath, &MapLen);\r | |
294 | \r | |
295 | reclassify:\r | |
296 | switch(PathClass) {\r | |
297 | case PathMapping:\r | |
298 | if(!ReMapped) {\r | |
299 | if((NewPath == NULL) || (*NewPath == L'\0')) { /* Nothing after the ':' */\r | |
300 | PathClass = PathAbsolute;\r | |
301 | }\r | |
302 | else {\r | |
303 | Instance = PathInstance(WPath, MapLen);\r | |
304 | PathClass = ClassifyPath(NewPath, NULL, NULL);\r | |
305 | }\r | |
306 | ReMapped = TRUE;\r | |
307 | if(WPath[MapLen] == L':') {\r | |
308 | // Get the Map Name, including the trailing ':'. */\r | |
309 | MPath = calloc(MapLen+2, sizeof(wchar_t));\r | |
310 | if(MPath != NULL) {\r | |
d7ce7006 | 311 | wmemcpy(MPath, WPath, MapLen+1);\r |
53e1e5c6 | 312 | }\r |
313 | else {\r | |
314 | errno = ENOMEM;\r | |
315 | Status = RETURN_OUT_OF_RESOURCES;\r | |
316 | break; // Exit the switch(PathClass) statement.\r | |
317 | }\r | |
318 | }\r | |
319 | if(WPath != NewPath) {\r | |
320 | /* Shift the RHS of the path down to the start of the buffer. */\r | |
321 | wmemmove(WPath, NewPath, wcslen(NewPath)+1);\r | |
322 | NewPath = WPath;\r | |
323 | }\r | |
324 | goto reclassify;\r | |
325 | }\r | |
326 | /* Fall through to PathError if Remapped.\r | |
327 | This means that the path looked like "foo:bar:something".\r | |
328 | */\r | |
329 | \r | |
330 | case PathError:\r | |
331 | errno = EINVAL;\r | |
332 | Status = RETURN_INVALID_PARAMETER;\r | |
333 | break;\r | |
334 | \r | |
335 | case PathRelative:\r | |
336 | /* Transform a relative path into an Absolute path.\r | |
337 | Prepends CWD and handles ./ and ../ entries.\r | |
338 | It is the caller's responsibility to free the space\r | |
339 | allocated to WPath.\r | |
340 | */\r | |
341 | WPath = PathAdjust(NewPath); // WPath was malloc()ed by PathAdjust\r | |
342 | \r | |
343 | case PathAbsolute:\r | |
344 | /* Perform any path aliasing. For example: /dev/foo -> { node.foo, "" }\r | |
345 | The current volume and directory are updated in the path as needed.\r | |
346 | It is the caller's responsibility to free the space\r | |
347 | allocated to WPath.\r | |
348 | */\r | |
349 | Status = RETURN_SUCCESS;\r | |
350 | WPath = PathAlias(WPath, &Node); // PathAlias frees its argument and malloc()s a new one.\r | |
351 | break;\r | |
352 | }\r | |
353 | if(!RETURN_ERROR(Status)) {\r | |
354 | *FullPath = WPath;\r | |
355 | *Which = Instance;\r | |
d7ce7006 | 356 | if(MapPath != NULL) {\r |
357 | *MapPath = MPath;\r | |
358 | }\r | |
359 | else if(MPath != NULL) {\r | |
360 | free(MPath); /* Caller doesn't want it so let MPath go free */\r | |
361 | }\r | |
53e1e5c6 | 362 | \r |
363 | /* At this point, WPath is an absolute path,\r | |
364 | MPath is either NULL or points to the Map Name,\r | |
365 | and Instance is the instance number.\r | |
366 | */\r | |
367 | if(MPath == NULL) {\r | |
368 | /* This is NOT a mapped path. */\r | |
369 | if(Node == NULL) {\r | |
370 | Node = daDefaultDevice;\r | |
371 | }\r | |
372 | if(Node != NULL) {\r | |
373 | Status = RETURN_SUCCESS;\r | |
374 | }\r | |
d7ce7006 | 375 | else {\r |
376 | Status = RETURN_NOT_FOUND;\r | |
377 | }\r | |
53e1e5c6 | 378 | }\r |
379 | else {\r | |
380 | /* This is a mapped path. */\r | |
381 | Status = __DevSearch( MPath, NULL, &Node);\r | |
382 | if(Status == RETURN_NOT_FOUND) {\r | |
383 | Node = daDefaultDevice;\r | |
384 | \r | |
385 | if(Node != NULL) {\r | |
386 | Status = RETURN_SUCCESS;\r | |
387 | }\r | |
388 | }\r | |
389 | }\r | |
390 | if(DevNode != NULL) {\r | |
391 | *DevNode = Node;\r | |
392 | }\r | |
393 | }\r | |
53e1e5c6 | 394 | return Status;\r |
395 | }\r | |
d7ce7006 | 396 | \r |
397 | /**\r | |
398 | Parses a normalized wide character path and returns a pointer to the entry\r | |
399 | following the last \. If a \ is not found in the path the return value will\r | |
400 | be the same as the input value. All error conditions return NULL.\r | |
401 | \r | |
402 | The behavior when passing in a path that has not been normalized is undefined.\r | |
403 | \r | |
404 | @param Path - A pointer to a wide character string containing a path to a\r | |
405 | directory or a file.\r | |
406 | \r | |
407 | @return Pointer to the file name or terminal directory. NULL if an error is\r | |
408 | detected.\r | |
409 | **/\r | |
410 | wchar_t *\r | |
411 | EFIAPI\r | |
412 | GetFileNameFromPath (\r | |
413 | const wchar_t *Path\r | |
414 | )\r | |
415 | {\r | |
416 | wchar_t *Tail;\r | |
417 | \r | |
418 | if (Path == NULL) {\r | |
419 | return NULL;\r | |
420 | }\r | |
421 | \r | |
422 | Tail = wcsrchr(Path, L'\\');\r | |
423 | if(Tail == NULL) {\r | |
424 | Tail = (wchar_t *) Path;\r | |
425 | } else {\r | |
426 | // Move to the next character after the '\\' to get the file name.\r | |
427 | Tail++;\r | |
428 | }\r | |
429 | \r | |
430 | return Tail;\r | |
431 | }\r |