]> git.proxmox.com Git - mirror_edk2.git/blobdiff - EmbeddedPkg/Library/EfiFileLib/EfiFileLib.c
Needed to fix defaulting / to be \ as it is required by EFI Simple File System.
[mirror_edk2.git] / EmbeddedPkg / Library / EfiFileLib / EfiFileLib.c
index 5effe7fcb99d8d5b495f033e200ba96e52724cae..a956e610fc044f50e7260895b42b0d4cec39efbf 100644 (file)
@@ -55,6 +55,9 @@
 #include <Library/EblNetworkLib.h>
 
 
+CHAR8 *gCwd = NULL;
+
+
 #define EFI_OPEN_FILE_GUARD_HEADER  0x4B4D4641
 #define EFI_OPEN_FILE_GUARD_FOOTER  0x444D5A56
 
@@ -635,6 +638,7 @@ EfiOpen (
   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
   UINTN                     Size;
   EFI_IP_ADDRESS            Ip;
+  CHAR8                     *CwdPlusPathName;
 
   EblUpdateDeviceLists ();
  
@@ -643,7 +647,7 @@ EfiOpen (
   File->FvSectionType = SectionType;
 
   StrLen = AsciiStrSize (PathName);
-  if (StrLen <= 2) {
+  if (StrLen <= 1) {
     // Smallest valid path is 1 char and a null
     return NULL;
   }
@@ -655,9 +659,46 @@ EfiOpen (
     }
   }
 
-  if (FileStart == 0) {
+  if (FileStart == StrLen) {
+    if (gCwd == NULL) {
+      // No CWD
+      return NULL;
+    }
+    
     // We could add a current working diretory concept 
-    return NULL;
+    CwdPlusPathName = AllocatePool (AsciiStrSize (gCwd) + AsciiStrSize (PathName));
+    if (CwdPlusPathName == NULL) {
+      return NULL;
+    }
+    
+    if ((PathName[0] == '/') || (PathName[0] == '\\')) {
+      // PathName starts in / so this means we go to the root of the device in the CWD. 
+      CwdPlusPathName[0] = '\0';
+      for (FileStart = 0; gCwd[FileStart] != '\0'; FileStart++) {
+        CwdPlusPathName[FileStart] = gCwd[FileStart];
+        if (gCwd[FileStart] == ':') {
+          FileStart++;
+          CwdPlusPathName[FileStart] = '\0';
+          break;
+        }
+      }
+    } else {
+      AsciiStrCpy (CwdPlusPathName, gCwd);
+      StrLen = AsciiStrLen (gCwd);
+      if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) {
+        AsciiStrCat (CwdPlusPathName, "\\");
+      }
+    }
+    
+    AsciiStrCat (CwdPlusPathName, PathName);
+    if (AsciiStrStr (CwdPlusPathName, ":") == NULL) {
+      // Extra error check to make sure we don't recusre and blow stack
+      return NULL;
+    }
+        
+    File = EfiOpen (CwdPlusPathName, OpenMode, SectionType);
+    FreePool (CwdPlusPathName);
+    return File;
   }
 
   //
@@ -672,6 +713,10 @@ EfiOpen (
   AsciiStrCpy (File->DeviceName, PathName);
   File->DeviceName[FileStart - 1] = '\0';
   File->FileName = &File->DeviceName[FileStart];
+  if (File->FileName[0] == '\0') {
+    // if it is just a file name use / as root
+    File->FileName = "\\";
+  } 
 
   //
   // Use best match algorithm on the dev names so we only need to look at the
@@ -1481,3 +1526,187 @@ EfiWrite (
   return Status;
 }
 
+
+/**
+  Given Cwd expand Path to remove .. and replace them with real 
+  directory names.
+  
+  @param  Cwd     Current Working Directory
+  @param  Path    Path to expand
+
+  @return NULL     Cwd or Path are not valid
+  @return 'other'  Path with .. expanded
+
+**/
+CHAR8 *
+ExpandPath (
+  IN CHAR8    *Cwd,
+  IN CHAR8    *Path
+  )
+{
+  CHAR8   *NewPath;
+  CHAR8   *Work, *Start, *End;
+  UINTN   StrLen;
+  UINTN   i;
+  
+  if (Cwd == NULL || Path == NULL) {
+    return NULL;
+  }
+  
+  StrLen = AsciiStrSize (Cwd);
+  if (StrLen <= 2) {
+    // Smallest valid path is 1 char and a null
+    return NULL;
+  }
+
+  StrLen = AsciiStrSize (Path);
+  NewPath = AllocatePool (AsciiStrSize (Cwd) + StrLen + 1);
+  if (NewPath == NULL) {
+    return NULL;
+  }
+  AsciiStrCpy (NewPath, Cwd);
+  
+  End = Path + StrLen;
+  for (Start = Path ;;) {
+    Work = AsciiStrStr (Start, "..") ;
+    if (Work == NULL) {
+      // Remaining part of Path contains no more ..
+      break;
+    } 
+    // append path prior to .. 
+    AsciiStrnCat (NewPath, Start, Work - Start);
+    StrLen = AsciiStrLen (NewPath);
+    for (i = StrLen; i >= 0; i--) {
+      if (NewPath[i] == ':') {
+        // too many ..
+        return NULL;
+      }
+      if (NewPath[i] == '/' || NewPath[i] == '\\') {
+        if ((i > 0) && (NewPath[i-1] == ':')) {
+          // leave the / before a :
+          NewPath[i+1] = '\0';
+        } else {
+          // replace / will Null to remove trailing file/dir reference
+          NewPath[i] = '\0';
+        }
+        break;
+      }
+    }
+    
+    Start = Work + 3;
+  } 
+  
+  // Handle the path that remains after the ..
+  AsciiStrnCat (NewPath, Start, End - Start);
+  
+  return NewPath;
+}
+
+
+/**
+  Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and 
+  the path does not contain a device name, The CWD is prepended to the path.
+  
+  @param  Cwd     Current Working Directory to set
+
+
+  @return EFI_SUCCESS           CWD is set
+  @return EFI_INVALID_PARAMETER Cwd is not a valid device:path
+
+**/
+EFI_STATUS
+EfiSetCwd (
+  IN  CHAR8   *Cwd
+  ) 
+{
+  EFI_OPEN_FILE *File;
+  UINTN         Len;
+  CHAR8         *Path;
+  
+  if (Cwd == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+  
+  if (AsciiStrCmp (Cwd, ".") == 0) {
+    // cd . is a no-op
+    return EFI_SUCCESS;
+  }
+  
+  Path = Cwd;
+  if (AsciiStrStr (Cwd, "..") != NULL) {
+    if (gCwd == NULL) {
+      // no parent 
+      return EFI_SUCCESS;
+    }
+    
+    Len = AsciiStrLen (gCwd);
+    if ((gCwd[Len-2] == ':') && ((gCwd[Len-1] == '/') || (gCwd[Len-1] == '\\'))) {
+      // parent is device so nothing to do
+      return EFI_SUCCESS;
+    }
+    
+    // Expand .. in Cwd, given we know current working directory
+    Path = ExpandPath (gCwd, Cwd);
+    if (Path == NULL) {
+      return EFI_NOT_FOUND;
+    }
+  }
+  
+  File = EfiOpen (Path, EFI_FILE_MODE_READ, 0);
+  if (File == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+    
+  if (gCwd != NULL) {
+    FreePool (gCwd);
+  }
+  
+  // Use the info returned from EfiOpen as it can add in CWD if needed. So Cwd could be
+  // relative to the current gCwd or not.
+  gCwd = AllocatePool (AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 1);
+  if (gCwd == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+  AsciiStrCpy (gCwd, File->DeviceName);
+  if (File->FileName == NULL) {
+    AsciiStrCat (gCwd, ":\\");
+  } else {
+    AsciiStrCat (gCwd, ":");
+    AsciiStrCat (gCwd, File->FileName);
+  }
+  
+  EfiClose (File);
+  if (Path != Cwd) {
+    FreePool (Path);
+  }
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and 
+  the path does not contain a device name, The CWD is prepended to the path.
+  The CWD buffer is only valid until a new call is made to EfiSetCwd(). After
+  a call to EfiSetCwd() it is not legal to use the pointer returned by 
+  this funciton.
+  
+  @param  Cwd     Current Working Directory 
+
+
+  @return ""      No CWD set
+  @return 'other' Returns buffer that contains CWD.
+  
+**/
+CHAR8 *
+EfiGetCwd (
+  VOID
+  )
+{
+  if (gCwd == NULL) {
+    return "";
+  }
+  return gCwd;
+}
+
+