+++ /dev/null
-/** @file\r
- Device Abstraction: Path manipulation utilities.\r
-\r
- Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
- This program and the accompanying materials are licensed and made available under\r
- the terms and conditions of the BSD License that accompanies this distribution.\r
- The full text of the license may be found at\r
- http://opensource.org/licenses/bsd-license.php.\r
-\r
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
- WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
-**/\r
-#include <Library/BaseLib.h>\r
-\r
-#include <LibConfig.h>\r
-\r
-#include <errno.h>\r
-#include <stdlib.h>\r
-#include <wchar.h>\r
-#include <wctype.h>\r
-#include <kfile.h>\r
-#include <Device/Device.h>\r
-#include <MainData.h>\r
-\r
-/** Identify the type of path pointed to by Path.\r
-\r
- Paths are classified based upon their initial character sequences.\r
- ^\\ Absolute Path\r
- ^\. Relative Path\r
- ^[^:\\]: Mapping Path\r
- .* Relative Path\r
-\r
- Mapping paths are broken into two parts at the ':'. The part to the left of the ':'\r
- is the Map Name, pointed to by Path, and the part to the right of the ':' is pointed\r
- to by NewPath.\r
-\r
- If Path was not a Mapping Path, then NewPath is set to Path.\r
-\r
- @param[in] Path Pointer to the path to be classified.\r
- @param[out] NewPath Pointer to the path portion of a mapping path.\r
- @param[out] Length Length of the Map Name portion of the path.\r
-\r
- @retval PathAbsolute Path is an absolute path. NewPath points to the first '\'.\r
- @retval PathRelative Path is a relative path. NewPath = Path.\r
- @retval PathMapping Path is a mapping path. NewPath points to the character following ':'.\r
- @retval PathError Path is NULL.\r
-**/\r
-PATH_CLASS\r
-EFIAPI\r
-ClassifyPath(\r
- IN wchar_t * Path,\r
- OUT wchar_t ** NewPath,\r
- OUT int * const Length\r
- )\r
-{\r
- size_t MapLen;\r
-\r
- if(Path == NULL) {\r
- return PathError; // Bad parameter\r
- }\r
- if(NewPath != NULL) {\r
- *NewPath = Path; // Setup default condition\r
- }\r
- if((*Path == L'\\') || (*Path == L'\0')) {\r
- return PathAbsolute;\r
- }\r
- if(*Path == L'.') {\r
- return PathRelative;\r
- }\r
- /* The easy stuff has been done, now see if this is a mapping path.\r
- See if there is a ':' in Path that isn't the first character and is before\r
- any '\\' characters.\r
- */\r
- MapLen = wcscspn(Path, L"\\:");\r
- if(Length != NULL) {\r
- *Length = (int)MapLen;\r
- }\r
- /* MapLen == 0 means that the first character is a ':'\r
- == PathLen means that there are no '\\' or ':'\r
- Otherwise, Path[MapLen] == ':' for a mapping path\r
- or '\\' for a relative path.\r
- */\r
- if(MapLen == 0) {\r
- return PathError;\r
- }\r
- if(Path[MapLen] == L':') {\r
- if(NewPath != NULL) {\r
- *NewPath = &Path[MapLen + 1]; // Point to character after then ':'. Might be '\0'.\r
- }\r
- return PathMapping;\r
- }\r
- return PathRelative;\r
-}\r
-\r
-/* Normalize a narrow-character path and produce a wide-character path\r
- that has forward slashes replaced with backslashes.\r
- Backslashes are directory separators in UEFI File Paths.\r
-\r
- It is the caller's responsibility to eventually free() the returned buffer.\r
-\r
- @param[in] path A pointer to the narrow-character path to be normalized.\r
-\r
- @return A pointer to a buffer containing the normalized, wide-character, path.\r
-*/\r
-wchar_t *\r
-NormalizePath( const char *path)\r
-{\r
- wchar_t *temp;\r
- wchar_t *OldPath;\r
- wchar_t *NewPath;\r
- size_t Length;\r
-\r
- OldPath = AsciiStrToUnicodeStr(path, gMD->UString);\r
- Length = wcslen(OldPath) + 1;\r
-\r
- NewPath = calloc(Length, sizeof(wchar_t));\r
- if(NewPath != NULL) {\r
- temp = NewPath;\r
- for( ; *OldPath; ++OldPath) {\r
- if(*OldPath == L'/') {\r
- *temp = L'\\';\r
- }\r
- else {\r
- *temp = *OldPath;\r
- }\r
- ++temp;\r
- }\r
- }\r
- else {\r
- errno = ENOMEM;\r
- EFIerrno = RETURN_OUT_OF_RESOURCES;\r
- }\r
- return NewPath;\r
-}\r
-\r
-/** Process a wide character string representing a Mapping Path and extract the instance number.\r
-\r
- The instance number is the sequence of decimal digits immediately to the left\r
- of the ":" in the Map Name portion of a Mapping Path.\r
-\r
- This function is called with a pointer to beginning of the Map Name.\r
- Thus Path[Length] must be a ':' and Path[Length - 1] must be a decimal digit.\r
- If either of these are not true, an instance value of 0 is returned.\r
-\r
- If Path is NULL, an instance value of 0 is returned.\r
-\r
- @param[in] Path Points to the beginning of a Mapping Path\r
- @param[in] Length Number of valid characters to the left of the ':'\r
-\r
- @return Returns either 0 or the value of the contiguous digits to the left of the ':'.\r
-**/\r
-int\r
-EFIAPI\r
-PathInstance(\r
- const wchar_t *Path,\r
- int Length\r
- )\r
-{\r
- wchar_t *temp;\r
- int instance = 0;\r
-\r
- if((Path != NULL) && (Path[Length] == L':') && (Length > 0)) {\r
- for(temp = __UNCONST(&Path[Length - 1]); Length > 0; --Length) {\r
- if(!iswdigit(*temp)) {\r
- break;\r
- }\r
- --temp;\r
- }\r
- instance = (int)wcstol(temp+1, NULL, 10);\r
- }\r
- return instance;\r
-}\r
-\r
-/** Transform a relative path into an absolute path.\r
-\r
- If Path is NULL, return NULL.\r
- Otherwise, pre-pend the CWD to Path then process the resulting path to:\r
- - Replace "/./" with "/"\r
- - Replace "/<dirname>/../" with "/"\r
- - Do not allow one to back up past the root, "/"\r
-\r
- Also sets the Current Working Device to the Root Device.\r
-\r
- Path must be a previously allocated buffer. PathAdjust will\r
- allocate a new buffer to hold the results of the transformation\r
- and free Path. A pointer to the newly allocated buffer is returned.\r
-\r
- @param[in] Path A pointer to the path to be transformed. This buffer\r
- will always be freed.\r
-\r
- @return A pointer to a buffer containing the transformed path.\r
-**/\r
-wchar_t *\r
-EFIAPI\r
-PathAdjust(\r
- wchar_t *Path\r
- )\r
-{\r
- wchar_t *NewPath;\r
-\r
- NewPath = calloc(PATH_MAX, sizeof(wchar_t));\r
- if(NewPath != NULL) {\r
- wmemcpy(NewPath, Path, PATH_MAX);\r
- }\r
- else {\r
- errno = ENOMEM;\r
- }\r
- free(Path);\r
- return NewPath;\r
-}\r
-\r
-/** Replace the leading portion of Path with any aliases.\r
-\r
- Aliases are read from /etc/fstab. If there is an associated device, the\r
- Current Working Device is set to that device.\r
-\r
- Path must be a previously allocated buffer. PathAlias will\r
- allocate a new buffer to hold the results of the transformation\r
- then free Path. A pointer to the newly allocated buffer is returned.\r
-\r
- @param[in] Path A pointer to the original, unaliased, path. This\r
- buffer is always freed.\r
- @param[out] Node Filled in with a pointer to the Device Node describing\r
- the device abstraction associated with this path.\r
-\r
- @return A pointer to a buffer containing the aliased path.\r
-**/\r
-wchar_t *\r
-EFIAPI\r
-PathAlias(\r
- wchar_t *Path,\r
- DeviceNode **Node\r
- )\r
-{\r
- wchar_t *NewPath;\r
-\r
- NewPath = calloc(PATH_MAX, sizeof(wchar_t));\r
- if(NewPath != NULL) {\r
- wmemcpy(NewPath, Path, PATH_MAX);\r
- }\r
- else {\r
- errno = ENOMEM;\r
- }\r
- free(Path);\r
- *Node = NULL;\r
- return NewPath;\r
-}\r
-\r
-/** Parse a path producing the target device, device instance, and file path.\r
-\r
- It is the caller's responsibility to free() FullPath and MapPath when they\r
- are no longer needed.\r
-\r
- @param[in] path\r
- @param[out] FullPath\r
- @param[out] DevNode\r
- @param[out] Which\r
- @param[out] MapPath OPTIONAL. If not NULL, it points to the place to save a pointer\r
- to the extracted map name. If the path didn't have a map name,\r
- then *MapPath is set to NULL.\r
-\r
- @retval RETURN_SUCCESS The path was parsed successfully.\r
- @retval RETURN_NOT_FOUND The path does not map to a valid device.\r
- @retval RETURN_OUT_OF_RESOURCES Insufficient memory to calloc a MapName buffer.\r
- The errno variable is set to ENOMEM.\r
- @retval RETURN_INVALID_PARAMETER The path parameter is not valid.\r
- The errno variable is set to EINVAL.\r
-**/\r
-RETURN_STATUS\r
-EFIAPI\r
-ParsePath(\r
- IN const char *path,\r
- OUT wchar_t **FullPath,\r
- OUT DeviceNode **DevNode,\r
- OUT int *Which,\r
- OUT wchar_t **MapPath\r
- )\r
-{\r
- int MapLen;\r
- PATH_CLASS PathClass;\r
- wchar_t *NewPath;\r
- wchar_t *WPath = NULL;\r
- wchar_t *MPath = NULL;\r
- DeviceNode *Node = NULL;\r
- RETURN_STATUS Status = RETURN_NOT_FOUND;\r
- int Instance = 0;\r
- BOOLEAN ReMapped;\r
-\r
- ReMapped = FALSE;\r
-\r
- // Convert name from MBCS to WCS and change '/' to '\\'\r
- WPath = NormalizePath( path);\r
- PathClass = ClassifyPath(WPath, &NewPath, &MapLen);\r
-\r
-reclassify:\r
- switch(PathClass) {\r
- case PathMapping:\r
- if(!ReMapped) {\r
- if((NewPath == NULL) || (*NewPath == L'\0')) { /* Nothing after the ':' */\r
- PathClass = PathAbsolute;\r
- }\r
- else {\r
- Instance = PathInstance(WPath, MapLen);\r
- PathClass = ClassifyPath(NewPath, NULL, NULL);\r
- }\r
- ReMapped = TRUE;\r
- if(WPath[MapLen] == L':') {\r
- // Get the Map Name, including the trailing ':'. */\r
- MPath = calloc(MapLen+2, sizeof(wchar_t));\r
- if(MPath != NULL) {\r
- wmemcpy(MPath, WPath, MapLen+1);\r
- }\r
- else {\r
- errno = ENOMEM;\r
- Status = RETURN_OUT_OF_RESOURCES;\r
- break; // Exit the switch(PathClass) statement.\r
- }\r
- }\r
- if(WPath != NewPath) {\r
- /* Shift the RHS of the path down to the start of the buffer. */\r
- wmemmove(WPath, NewPath, wcslen(NewPath)+1);\r
- NewPath = WPath;\r
- }\r
- goto reclassify;\r
- }\r
- /* Fall through to PathError if Remapped.\r
- This means that the path looked like "foo:bar:something".\r
- */\r
-\r
- case PathError:\r
- errno = EINVAL;\r
- Status = RETURN_INVALID_PARAMETER;\r
- break;\r
-\r
- case PathRelative:\r
- /* Transform a relative path into an Absolute path.\r
- Prepends CWD and handles ./ and ../ entries.\r
- It is the caller's responsibility to free the space\r
- allocated to WPath.\r
- */\r
- WPath = PathAdjust(NewPath); // WPath was malloc()ed by PathAdjust\r
-\r
- case PathAbsolute:\r
- /* Perform any path aliasing. For example: /dev/foo -> { node.foo, "" }\r
- The current volume and directory are updated in the path as needed.\r
- It is the caller's responsibility to free the space\r
- allocated to WPath.\r
- */\r
- Status = RETURN_SUCCESS;\r
- WPath = PathAlias(WPath, &Node); // PathAlias frees its argument and malloc()s a new one.\r
- break;\r
- }\r
- if(!RETURN_ERROR(Status)) {\r
- *FullPath = WPath;\r
- *Which = Instance;\r
- if(MapPath != NULL) {\r
- *MapPath = MPath;\r
- }\r
- else if(MPath != NULL) {\r
- free(MPath); /* Caller doesn't want it so let MPath go free */\r
- }\r
-\r
- /* At this point, WPath is an absolute path,\r
- MPath is either NULL or points to the Map Name,\r
- and Instance is the instance number.\r
- */\r
- if(MPath == NULL) {\r
- /* This is NOT a mapped path. */\r
- if(Node == NULL) {\r
- Node = daDefaultDevice;\r
- }\r
- if(Node != NULL) {\r
- Status = RETURN_SUCCESS;\r
- }\r
- else {\r
- Status = RETURN_NOT_FOUND;\r
- }\r
- }\r
- else {\r
- /* This is a mapped path. */\r
- Status = __DevSearch( MPath, NULL, &Node);\r
- if(Status == RETURN_NOT_FOUND) {\r
- Node = daDefaultDevice;\r
-\r
- if(Node != NULL) {\r
- Status = RETURN_SUCCESS;\r
- }\r
- }\r
- }\r
- if(DevNode != NULL) {\r
- *DevNode = Node;\r
- }\r
- }\r
- return Status;\r
-}\r
-\r
-/**\r
- Parses a normalized wide character path and returns a pointer to the entry\r
- following the last \. If a \ is not found in the path the return value will\r
- be the same as the input value. All error conditions return NULL.\r
-\r
- The behavior when passing in a path that has not been normalized is undefined.\r
-\r
- @param Path - A pointer to a wide character string containing a path to a\r
- directory or a file.\r
-\r
- @return Pointer to the file name or terminal directory. NULL if an error is\r
- detected.\r
-**/\r
-wchar_t *\r
-EFIAPI\r
-GetFileNameFromPath (\r
- const wchar_t *Path\r
- )\r
-{\r
- wchar_t *Tail;\r
-\r
- if (Path == NULL) {\r
- return NULL;\r
- }\r
-\r
- Tail = wcsrchr(Path, L'\\');\r
- if(Tail == NULL) {\r
- Tail = (wchar_t *) Path;\r
- } else {\r
- // Move to the next character after the '\\' to get the file name.\r
- Tail++;\r
- }\r
-\r
- return Tail;\r
-}\r