]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ShellPkg/Library/UefiShellLevel2CommandsLib/Cd.c
ShellPkg/cd: Fix "cd" to support "fs0:dir" (no slash after ':')
[mirror_edk2.git] / ShellPkg / Library / UefiShellLevel2CommandsLib / Cd.c
index 0967bc7c521026317253734bc6f96ac18244b8fe..d459a7a67e84787b5f11ae76ae51806e684ebfe0 100644 (file)
 \r
 #include "UefiShellLevel2CommandsLib.h"\r
 \r
+/**\r
+  Function will replace drive identifier with CWD.\r
+\r
+  If FullPath begining with ':' is invalid path, then ASSERT.\r
+  If FullPath not include dirve identifier , then do nothing.\r
+  If FullPath likes "fs0:\xx" or "fs0:/xx" , then do nothing.\r
+  If FullPath likes "fs0:xxx" or "fs0:", the drive replaced by CWD.\r
+\r
+  @param[in, out]   FullPath    The pointer to the string containing the path.\r
+  @param[in]        Cwd         Current directory.\r
+\r
+  @retval   EFI_SUCCESS         Success.\r
+  @retval   EFI_OUT_OF_SOURCES  A memory allocation failed.\r
+**/\r
+EFI_STATUS\r
+ReplaceDriveWithCwd (\r
+  IN OUT    CHAR16  **FullPath,\r
+  IN CONST  CHAR16  *Cwd\r
+  )\r
+{\r
+  CHAR16        *Splitter;\r
+  CHAR16        *TempBuffer;\r
+  UINTN         TotalSize;\r
+\r
+  Splitter   = NULL;\r
+  TempBuffer = NULL;\r
+  TotalSize  = 0;\r
+\r
+  if (FullPath == NULL || *FullPath == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Splitter = StrStr (*FullPath, L":");\r
+  ASSERT(Splitter != *FullPath);\r
+\r
+  if (Splitter != NULL && *(Splitter + 1) != L'\\' && *(Splitter + 1) != L'/') {\r
+    TotalSize = StrSize (Cwd) + StrSize (Splitter + 1);\r
+    TempBuffer = AllocateZeroPool (TotalSize);\r
+    if (TempBuffer == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    StrCpyS (TempBuffer, TotalSize / sizeof(CHAR16), Cwd);\r
+    StrCatS (TempBuffer, TotalSize / sizeof(CHAR16), L"\\");\r
+    StrCatS (TempBuffer, TotalSize / sizeof(CHAR16), Splitter + 1);\r
+\r
+    FreePool(*FullPath);\r
+    *FullPath = TempBuffer;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  function to determine if FullPath is under current filesystem.\r
+\r
+  @param[in]    FullPath    The target location to determine.\r
+  @param[in]    Cwd         Current directory.\r
+\r
+  @retval       TRUE        The FullPath is in the current filesystem.\r
+  @retval       FALSE       The FullPaht isn't in the current filesystem.\r
+**/\r
+BOOLEAN\r
+IsCurrentFileSystem (\r
+  IN CONST CHAR16   *FullPath,\r
+  IN CONST CHAR16   *Cwd\r
+  )\r
+{\r
+  CHAR16 *Splitter1;\r
+  CHAR16 *Splitter2;\r
+\r
+  Splitter1 = NULL;\r
+  Splitter2 = NULL;\r
+\r
+  ASSERT(FullPath != NULL);\r
+\r
+  Splitter1 = StrStr (FullPath, L":");\r
+  if (Splitter1 == NULL) {\r
+    return TRUE;\r
+  }\r
+\r
+  Splitter2 = StrStr (Cwd, L":");\r
+\r
+  if ((UINTN) (Splitter1 - FullPath) != (UINTN) (Splitter2 - Cwd)) {\r
+    return FALSE;\r
+  } else {\r
+    if (StrniCmp (FullPath, Cwd, (UINTN) (Splitter1 - FullPath)) == NULL) {\r
+      return TRUE;\r
+    } else {\r
+      return FALSE;\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Extract drive string and path string from FullPath.\r
+\r
+  The caller must be free Drive and Path.\r
+\r
+  @param[in]    FullPath    A path to be extracted.\r
+  @param[out]   Drive       Buffer to save drive identifier.\r
+  @param[out]   Path        Buffer to save path.\r
+\r
+  @retval       EFI_SUCCESS           Success.\r
+  @retval       EFI_OUT_OF_RESOUCES   A memory allocation failed.\r
+**/\r
+EFI_STATUS\r
+ExtractDriveAndPath (\r
+  IN CONST CHAR16   *FullPath,\r
+  OUT CHAR16        **Drive,\r
+  OUT CHAR16        **Path\r
+  )\r
+{\r
+  CHAR16 *Splitter;\r
+\r
+  ASSERT (FullPath != NULL);\r
+\r
+  Splitter = StrStr (FullPath, L":");\r
+\r
+  if (Splitter == NULL) {\r
+    *Drive = NULL;\r
+    *Path = AllocateCopyPool (StrSize (FullPath), FullPath);\r
+    if (*Path == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  } else {\r
+    if (*(Splitter + 1) == CHAR_NULL) {\r
+      *Drive = AllocateCopyPool (StrSize (FullPath), FullPath);\r
+      *Path = NULL;\r
+      if (*Drive == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+    } else {\r
+      *Drive = AllocateCopyPool ((Splitter - FullPath + 2) * sizeof(CHAR16), FullPath);\r
+      if (*Drive == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+      (*Drive)[Splitter - FullPath + 1] = CHAR_NULL;\r
+\r
+      *Path = AllocateCopyPool (StrSize (Splitter + 1), Splitter + 1);\r
+      if (*Path == NULL) {\r
+        FreePool (*Drive);\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
 /**\r
   Function for 'cd' command.\r
 \r
@@ -31,23 +181,26 @@ ShellCommandRunCd (
 {\r
   EFI_STATUS        Status;\r
   LIST_ENTRY        *Package;\r
-  CONST CHAR16      *Directory;\r
-  CHAR16            *Cwd;\r
+  CONST CHAR16      *Cwd;\r
   CHAR16            *Path;\r
   CHAR16            *Drive;\r
-  UINTN             CwdSize;\r
-  UINTN             DriveSize;\r
   CHAR16            *ProblemParam;\r
   SHELL_STATUS      ShellStatus;\r
-  SHELL_FILE_HANDLE Handle;\r
   CONST CHAR16      *Param1;\r
   CHAR16            *Param1Copy;\r
-  CHAR16*           Walker;\r
+  CHAR16            *Walker;\r
+  CHAR16            *Splitter;\r
+  CHAR16            *TempBuffer;\r
+  UINTN             TotalSize;\r
 \r
-  ProblemParam = NULL;\r
-  ShellStatus = SHELL_SUCCESS;\r
-  Drive = NULL;\r
-  DriveSize = 0;\r
+  ProblemParam  = NULL;\r
+  ShellStatus   = SHELL_SUCCESS;\r
+  Cwd           = NULL;\r
+  Path          = NULL;\r
+  Drive         = NULL;\r
+  Splitter      = NULL;\r
+  TempBuffer    = NULL;\r
+  TotalSize     = 0;\r
 \r
   Status = CommandInit();\r
   ASSERT_EFI_ERROR(Status);\r
@@ -87,194 +240,106 @@ ShellCommandRunCd (
     // else If there are 2 value parameters, then print the error message\r
     // else If there is  1 value paramerer , then change the directory\r
     //\r
-    Param1 = ShellCommandLineGetRawValue(Package, 1);\r
-    if (Param1 == NULL) {\r
-      //\r
-      // display the current directory\r
-      //\r
-      Directory = ShellGetCurrentDir(NULL);\r
-      if (Directory != NULL) {\r
-        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CD_PRINT), gShellLevel2HiiHandle, Directory);\r
-      } else {\r
-        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"cd");  \r
-        ShellStatus = SHELL_NOT_FOUND;\r
-      }\r
+    Cwd = ShellGetCurrentDir (NULL);\r
+    if (Cwd == NULL) {\r
+      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN(STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"cd");\r
+      ShellStatus = SHELL_NOT_FOUND;\r
     } else {\r
-      Param1Copy = CatSPrint(NULL, L"%s", Param1, NULL);\r
-      for (Walker = Param1Copy; Walker != NULL && *Walker != CHAR_NULL ; Walker++) {\r
-        if (*Walker == L'\"') {\r
-          CopyMem(Walker, Walker+1, StrSize(Walker) - sizeof(Walker[0]));\r
-        }\r
-      }\r
-      \r
-      if (Param1Copy != NULL) {\r
-        Param1Copy = PathCleanUpDirectories(Param1Copy);\r
-      }\r
-      if (Param1Copy != NULL) {\r
-        if (StrCmp(Param1Copy, L".") == 0) {\r
-          //\r
-          // nothing to do... change to current directory\r
-          //\r
-        } else if (StrCmp(Param1Copy, L"..") == 0) {\r
-          //\r
-          // Change up one directory...\r
-          //\r
-          Directory = ShellGetCurrentDir(NULL);\r
-          if (Directory == NULL) {\r
-            ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"cd");  \r
-            ShellStatus = SHELL_NOT_FOUND;\r
-          } else {\r
-            CwdSize = StrSize(Directory) + sizeof(CHAR16);\r
-            Cwd = AllocateZeroPool(CwdSize);\r
-            if (Cwd == NULL) {\r
-              ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_OUT_MEM), gShellLevel2HiiHandle, L"cd");\r
-              ShellStatus = SHELL_OUT_OF_RESOURCES;\r
-            } else {\r
-              StrCpyS (Cwd, StrSize (Directory) / sizeof (CHAR16) + 1, Directory);\r
-              StrCatS (Cwd, StrSize (Directory) / sizeof (CHAR16) + 1, L"\\");\r
-              Drive = GetFullyQualifiedPath (Cwd);\r
-              PathRemoveLastItem (Drive);\r
-              FreePool (Cwd);\r
-            }\r
+      Param1 = ShellCommandLineGetRawValue (Package, 1);\r
+      if (Param1 == NULL) {\r
+        //\r
+        // display the current directory\r
+        //\r
+        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN(STR_CD_PRINT), gShellLevel2HiiHandle, Cwd);\r
+      } else {\r
+        Param1Copy = CatSPrint (NULL, L"%s", Param1, NULL);\r
+        for (Walker = Param1Copy; Walker != NULL && *Walker != CHAR_NULL; Walker++) {\r
+          if (*Walker == L'\"') {\r
+            CopyMem (Walker, Walker + 1, StrSize(Walker) - sizeof(Walker[0]));\r
           }\r
-          if (ShellStatus == SHELL_SUCCESS && Drive != NULL) {\r
-            //\r
-            // change directory on current drive letter\r
-            //\r
-            Status = gEfiShellProtocol->SetCurDir(NULL, Drive);\r
-            if (Status == EFI_NOT_FOUND) {\r
-              ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CD_NF), gShellLevel2HiiHandle, L"cd");  \r
-              ShellStatus = SHELL_NOT_FOUND;\r
-            }\r
+        }\r
+\r
+        if (Param1Copy != NULL && IsCurrentFileSystem (Param1Copy, Cwd)) {\r
+          Status = ReplaceDriveWithCwd (&Param1Copy,Cwd);\r
+          if (!EFI_ERROR (Status)) {\r
+            Param1Copy = PathCleanUpDirectories (Param1Copy);\r
           }\r
-        } else if (StrCmp(Param1Copy, L"\\") == 0) {\r
+        } else {\r
           //\r
-          // Move to root of current drive\r
+          // Can't use cd command to change filesystem.\r
           //\r
-          Directory = ShellGetCurrentDir(NULL);\r
-          if (Directory == NULL) {\r
-            ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"cd");  \r
-            ShellStatus = SHELL_NOT_FOUND;\r
-          } else {\r
-            CwdSize = StrSize(Directory) + sizeof(CHAR16);\r
-            Cwd = AllocateZeroPool(CwdSize);\r
-            if (Cwd == NULL) {\r
-              ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_OUT_MEM), gShellLevel2HiiHandle, L"cd");\r
-              ShellStatus = SHELL_OUT_OF_RESOURCES;\r
-            } else {\r
-              StrCpyS (Cwd, StrSize (Directory) / sizeof (CHAR16) + 1, Directory);\r
-              StrCatS (Cwd, StrSize (Directory) / sizeof (CHAR16) + 1, L"\\");\r
-              Drive = GetFullyQualifiedPath (Cwd);\r
-              while (PathRemoveLastItem (Drive)) {\r
-                //\r
-                // Check if Drive contains 'fsx:\' only or still points to a sub-directory.\r
-                // Don't remove trailing '\' from Drive if it points to the root directory.\r
-                //\r
-                Path = StrStr (Drive, L":\\");\r
-                if ((Path != NULL) && (*(Path + 2) == CHAR_NULL)) {\r
-                  break;\r
-                }\r
-              }\r
-              FreePool (Cwd);\r
-            }\r
-          }\r
-          if (ShellStatus == SHELL_SUCCESS && Drive != NULL) {\r
+          ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_CD_NF), gShellLevel2HiiHandle, L"cd");\r
+          Status = EFI_NOT_FOUND;\r
+        }\r
+\r
+        if (!EFI_ERROR(Status) && Param1Copy != NULL) {\r
+          Splitter = StrStr (Cwd, L":");\r
+          if (Param1Copy[0] == L'\\') {\r
             //\r
-            // change directory on current drive letter\r
+            // Absolute Path on current drive letter.\r
             //\r
-            Status = gEfiShellProtocol->SetCurDir(NULL, Drive);\r
-            if (Status == EFI_NOT_FOUND) {\r
-              ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CD_NF), gShellLevel2HiiHandle, L"cd");  \r
-              ShellStatus = SHELL_NOT_FOUND;\r
-            }\r
-          }\r
-        } else if (StrStr(Param1Copy, L":") == NULL) {\r
-          //\r
-          // change directory without a drive identifier\r
-          //\r
-          if (ShellGetCurrentDir(NULL) == NULL) {\r
-            ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"cd");  \r
-            ShellStatus = SHELL_NOT_FOUND;\r
-          } else {\r
-            ASSERT((Drive == NULL && DriveSize == 0) || (Drive != NULL));\r
-            Drive = StrnCatGrow(&Drive, &DriveSize, ShellGetCurrentDir(NULL), 0);\r
-            Drive = StrnCatGrow(&Drive, &DriveSize, L"\\", 0);\r
-            if (*Param1Copy == L'\\') {\r
-              while (PathRemoveLastItem(Drive)) ;\r
-              Drive = StrnCatGrow(&Drive, &DriveSize, Param1Copy+1, 0);\r
+            TotalSize = ((Splitter - Cwd + 1) * sizeof(CHAR16)) + StrSize(Param1Copy);\r
+            TempBuffer = AllocateZeroPool (TotalSize);\r
+            if (TempBuffer == NULL) {\r
+              Status = EFI_OUT_OF_RESOURCES;\r
             } else {\r
-              Drive = StrnCatGrow(&Drive, &DriveSize, Param1Copy, 0);\r
-            }\r
-            //\r
-            // Verify that this is a valid directory\r
-            //\r
-            Status = gEfiShellProtocol->OpenFileByName(Drive, &Handle, EFI_FILE_MODE_READ);\r
-            if (EFI_ERROR(Status)) {\r
-              ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_DIR_NF), gShellLevel2HiiHandle, L"cd", Drive);  \r
-              ShellStatus = SHELL_NOT_FOUND;\r
-            } else if (EFI_ERROR(FileHandleIsDirectory(Handle))) {\r
-              ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NOT_DIR), gShellLevel2HiiHandle, L"cd", Drive);  \r
-              ShellStatus = SHELL_NOT_FOUND;\r
+              StrnCpyS (TempBuffer, TotalSize / sizeof(CHAR16), Cwd, (Splitter - Cwd + 1));\r
+              StrCatS (TempBuffer, TotalSize / sizeof(CHAR16), Param1Copy);\r
+\r
+              FreePool (Param1Copy);\r
+              Param1Copy = TempBuffer;\r
+              TempBuffer = NULL;\r
             }\r
-            if (ShellStatus == SHELL_SUCCESS && Drive != NULL) {\r
-              //\r
-              // change directory on current drive letter\r
-              //\r
-              Status = gEfiShellProtocol->SetCurDir(NULL, Drive);\r
-              if (Status == EFI_NOT_FOUND) {\r
-                ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CD_NF), gShellLevel2HiiHandle, L"cd");  \r
-                ShellStatus = SHELL_NOT_FOUND;\r
+          } else {\r
+            if (StrStr (Param1Copy,L":") == NULL) {\r
+              TotalSize = StrSize (Cwd) + StrSize (Param1Copy);\r
+              TempBuffer = AllocateZeroPool (TotalSize);\r
+              if (TempBuffer == NULL) {\r
+                Status = EFI_OUT_OF_RESOURCES;\r
+              } else {\r
+                StrCpyS (TempBuffer, TotalSize / sizeof (CHAR16), Cwd);\r
+                StrCatS (TempBuffer, TotalSize / sizeof (CHAR16), L"\\");\r
+                StrCatS (TempBuffer, TotalSize / sizeof (CHAR16), Param1Copy);\r
+\r
+                FreePool (Param1Copy);\r
+                Param1Copy = PathCleanUpDirectories (TempBuffer);\r
               }\r
             }\r
-            if (Handle != NULL) {\r
-              gEfiShellProtocol->CloseFile(Handle);\r
-              DEBUG_CODE(Handle = NULL;);\r
-            }\r
           }\r
-        } else {\r
-          //\r
-          // change directory with a drive letter\r
-          //\r
-          Drive = AllocateCopyPool(StrSize(Param1Copy), Param1Copy);\r
-          if (Drive == NULL) {\r
-            ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_MEM), gShellLevel2HiiHandle, L"cd");  \r
-            ShellStatus = SHELL_OUT_OF_RESOURCES;\r
+        }\r
+\r
+        if (!EFI_ERROR(Status)) {\r
+          Status = ExtractDriveAndPath (Param1Copy, &Drive, &Path);\r
+        }\r
+\r
+        if (!EFI_ERROR (Status) && Drive != NULL && Path != NULL) {\r
+          if (EFI_ERROR(ShellIsDirectory (Param1Copy))) {\r
+            ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN(STR_GEN_NOT_DIR), gShellLevel2HiiHandle, L"cd", Param1Copy);\r
+            ShellStatus = SHELL_NOT_FOUND;\r
           } else {\r
-            Path = StrStr(Drive, L":");\r
-            ASSERT(Path != NULL);\r
-            if (EFI_ERROR(ShellIsDirectory(Param1Copy))) {\r
-              ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NOT_DIR), gShellLevel2HiiHandle, L"cd", Param1Copy);  \r
-              ShellStatus = SHELL_NOT_FOUND;\r
-            } else if (*(Path+1) == CHAR_NULL) {\r
-              ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CD_NF), gShellLevel2HiiHandle, L"cd");  \r
+            Status = gEfiShellProtocol->SetCurDir (Drive, Path + 1);\r
+            if (EFI_ERROR (Status)) {\r
+              ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN(STR_GEN_DIR_NF), gShellLevel2HiiHandle, L"cd", Param1Copy);\r
               ShellStatus = SHELL_NOT_FOUND;\r
             } else {\r
-              *(Path+1) = CHAR_NULL;\r
-              if (Path == Drive + StrLen(Drive)) {\r
-                ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CD_NF), gShellLevel2HiiHandle, L"cd");  \r
-                ShellStatus = SHELL_NOT_FOUND;\r
-              } else {\r
-                Status = gEfiShellProtocol->SetCurDir(Drive, Path+2);\r
-                ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CD_PRINT), gShellLevel2HiiHandle, ShellGetCurrentDir(Drive));\r
-              }\r
-            }\r
-            if (Status == EFI_NOT_FOUND) {\r
-              ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CD_NF), gShellLevel2HiiHandle, L"cd");  \r
-              Status = SHELL_NOT_FOUND;\r
-            } else if (EFI_ERROR(Status)) {\r
-              ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_DIR_NF), gShellLevel2HiiHandle, L"cd", Param1Copy);  \r
-              Status = SHELL_NOT_FOUND;\r
+              ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN(STR_CD_PRINT), gShellLevel2HiiHandle, ShellGetCurrentDir(Drive));\r
             }\r
           }\r
         }\r
+\r
+        if (Drive != NULL) {\r
+          FreePool (Drive);\r
+        }\r
+\r
+        if (Path != NULL) {\r
+          FreePool (Path);\r
+        }\r
+\r
+        FreePool (Param1Copy);\r
       }\r
-      FreePool(Param1Copy);\r
     }\r
   }\r
 \r
-  if (Drive != NULL) {\r
-    FreePool(Drive);\r
-  }\r
   //\r
   // free the command line package\r
   //\r