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