]> git.proxmox.com Git - mirror_edk2.git/commitdiff
ArmPkg/SemihostFs: Implement SetInfo() and handle seeking past the end of a file
authorRonald Cron <ronald.cron@arm.com>
Mon, 27 Oct 2014 10:42:51 +0000 (10:42 +0000)
committeroliviermartin <oliviermartin@Edk2>
Mon, 27 Oct 2014 10:42:51 +0000 (10:42 +0000)
Implement the resizing of the file with SetInfo().
Implement the renaming of a file with SetInfo().
Allow to seek past the end of a file.

The support of file resizing implies a rework of the read, write
and close functions. So does the support of seeking past the end
of a file. That why those two changes are done in the same patch.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ronald Cron <ronald.cron@arm.com>
Reviewed-By: Olivier Martin <olivier.martin@arm.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16244 6f19259b-4bc3-4df7-8a09-765794883524

ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.c
ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.h

index 34e2f62402b755a80da7fc786dcd565752a3eb33..6efdad9ebcce4f5d2e09853170e30d6c8ba993a7 100644 (file)
@@ -297,42 +297,166 @@ Error:
   return Status;\r
 }\r
 \r
+/**\r
+  Worker function that truncate a file specified by its name to a given size.\r
+\r
+  @param[in]  FileName  The Null-terminated string of the name of the file to be opened.\r
+  @param[in]  Size      The target size for the file.\r
+\r
+  @retval  EFI_SUCCESS       The file was truncated.\r
+  @retval  EFI_DEVICE_ERROR  The last issued semi-hosting operation failed.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+TruncateFile (\r
+  IN CHAR8  *FileName,\r
+  IN UINTN   Size\r
+  )\r
+{\r
+  EFI_STATUS     Status;\r
+  RETURN_STATUS  Return;\r
+  UINTN          FileHandle;\r
+  UINT8          *Buffer;\r
+  UINTN          Remaining;\r
+  UINTN          Read;\r
+  UINTN          ToRead;\r
+\r
+  Status     = EFI_DEVICE_ERROR;\r
+  FileHandle = 0;\r
+  Buffer     = NULL;\r
+\r
+  Return = SemihostFileOpen (\r
+             FileName,\r
+             SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY,\r
+             &FileHandle\r
+             );\r
+  if (RETURN_ERROR (Return)) {\r
+    goto Error;\r
+  }\r
+\r
+  Buffer = AllocatePool (Size);\r
+  if (Buffer == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+\r
+  Read = 0;\r
+  Remaining = Size;\r
+  while (Remaining > 0) {\r
+    ToRead = Remaining;\r
+    Return = SemihostFileRead (FileHandle, &ToRead, Buffer + Read);\r
+    if (RETURN_ERROR (Return)) {\r
+      goto Error;\r
+    }\r
+    Remaining -= ToRead;\r
+    Read      += ToRead;\r
+  }\r
+\r
+  Return = SemihostFileClose (FileHandle);\r
+  FileHandle = 0;\r
+  if (RETURN_ERROR (Return)) {\r
+    goto Error;\r
+  }\r
+\r
+  Return = SemihostFileOpen (\r
+             FileName,\r
+             SEMIHOST_FILE_MODE_WRITE | SEMIHOST_FILE_MODE_BINARY,\r
+             &FileHandle\r
+             );\r
+  if (RETURN_ERROR (Return)) {\r
+    goto Error;\r
+  }\r
+\r
+  if (Size > 0) {\r
+    Return = SemihostFileWrite (FileHandle, &Size, Buffer);\r
+    if (RETURN_ERROR (Return)) {\r
+      goto Error;\r
+    }\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
 \r
+Error:\r
+\r
+  if (FileHandle != 0) {\r
+    SemihostFileClose (FileHandle);\r
+  }\r
+  if (Buffer != NULL) {\r
+    FreePool (Buffer);\r
+  }\r
+\r
+  return (Status);\r
+\r
+}\r
+\r
+/**\r
+  Close a specified file handle.\r
+\r
+  @param[in]  This  A pointer to the EFI_FILE_PROTOCOL instance that is the file\r
+                    handle to close.\r
+\r
+  @retval  EFI_SUCCESS            The file was closed.\r
+  @retval  EFI_INVALID_PARAMETER  The parameter "This" is NULL.\r
+\r
+**/\r
 EFI_STATUS\r
 FileClose (\r
-  IN EFI_FILE *File\r
+  IN EFI_FILE  *This\r
   )\r
 {\r
-  SEMIHOST_FCB *Fcb    = NULL;\r
-  EFI_STATUS   Status  = EFI_SUCCESS;\r
+  SEMIHOST_FCB   *Fcb;\r
 \r
-  Fcb = SEMIHOST_FCB_FROM_THIS(File);\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
 \r
-  if (Fcb->IsRoot == TRUE) {\r
-    FreeFCB (Fcb);\r
-    Status = EFI_SUCCESS;\r
-  } else {\r
-    Status = SemihostFileClose (Fcb->SemihostHandle);\r
-    if (!EFI_ERROR(Status)) {\r
-      FreePool (Fcb->FileName);\r
-      FreeFCB (Fcb);\r
+  Fcb = SEMIHOST_FCB_FROM_THIS(This);\r
+\r
+  if (!Fcb->IsRoot) {\r
+    SemihostFileClose (Fcb->SemihostHandle);\r
+    //\r
+    // The file size might have been reduced from its actual\r
+    // size on the host file system with FileSetInfo(). In\r
+    // that case, the file has to be truncated.\r
+    //\r
+    if (Fcb->Info.FileSize < Fcb->Info.PhysicalSize) {\r
+      TruncateFile (Fcb->FileName, Fcb->Info.FileSize);\r
     }\r
+    FreePool (Fcb->FileName);\r
   }\r
 \r
-  return Status;\r
+  FreeFCB (Fcb);\r
+\r
+  return EFI_SUCCESS;\r
 }\r
 \r
+/**\r
+  Close and delete a file.\r
+\r
+  @param[in]  This  A pointer to the EFI_FILE_PROTOCOL instance that is the file\r
+                    handle to delete.\r
+\r
+  @retval  EFI_SUCCESS              The file was closed and deleted.\r
+  @retval  EFI_WARN_DELETE_FAILURE  The handle was closed, but the file was not deleted.\r
+  @retval  EFI_INVALID_PARAMETER    The parameter "This" is NULL.\r
+\r
+**/\r
 EFI_STATUS\r
 FileDelete (\r
-  IN EFI_FILE *File\r
+  IN EFI_FILE *This\r
   )\r
 {\r
-  SEMIHOST_FCB *Fcb = NULL;\r
-  EFI_STATUS   Status;\r
-  CHAR8        *FileName;\r
-  UINTN        NameSize;\r
+  SEMIHOST_FCB   *Fcb;\r
+  RETURN_STATUS  Return;\r
+  CHAR8          *FileName;\r
+  UINTN          NameSize;\r
 \r
-  Fcb = SEMIHOST_FCB_FROM_THIS(File);\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Fcb = SEMIHOST_FCB_FROM_THIS (This);\r
 \r
   if (!Fcb->IsRoot) {\r
     // Get the filename from the Fcb\r
@@ -343,57 +467,157 @@ FileDelete (
 \r
     // Close the file if it's open.  Disregard return status,\r
     // since it might give an error if the file isn't open.\r
-    File->Close (File);\r
+    This->Close (This);\r
 \r
     // Call the semihost interface to delete the file.\r
-    Status = SemihostFileRemove (FileName);\r
-    if (EFI_ERROR(Status)) {\r
-      Status = EFI_WARN_DELETE_FAILURE;\r
+    Return = SemihostFileRemove (FileName);\r
+    if (RETURN_ERROR (Return)) {\r
+      return EFI_WARN_DELETE_FAILURE;\r
     }\r
+    return EFI_SUCCESS;\r
   } else {\r
-    Status = EFI_WARN_DELETE_FAILURE;\r
+    return EFI_WARN_DELETE_FAILURE;\r
   }\r
-\r
-  return Status;\r
 }\r
 \r
+/**\r
+  Read data from an open file.\r
+\r
+  @param[in]      This        A pointer to the EFI_FILE_PROTOCOL instance that\r
+                              is the file handle to read data from.\r
+  @param[in out]  BufferSize  On input, the size of the Buffer. On output, the\r
+                              amount of data returned in Buffer. In both cases,\r
+                              the size is measured in bytes.\r
+  @param[out]     Buffer      The buffer into which the data is read.\r
+\r
+  @retval  EFI_SUCCESS            The data was read.\r
+  @retval  EFI_DEVICE_ERROR       On entry, the current file position is\r
+                                  beyond the end of the file, or the semi-hosting\r
+                                  interface reported an error while performing the\r
+                                  read operation.\r
+  @retval  EFI_INVALID_PARAMETER  At least one of the three input pointers is NULL.\r
+\r
+**/\r
 EFI_STATUS\r
 FileRead (\r
-  IN     EFI_FILE *File,\r
-  IN OUT UINTN    *BufferSize,\r
-  OUT    VOID     *Buffer\r
+  IN     EFI_FILE  *This,\r
+  IN OUT UINTN     *BufferSize,\r
+  OUT    VOID      *Buffer\r
   )\r
 {\r
-  SEMIHOST_FCB *Fcb = NULL;\r
-  EFI_STATUS   Status;\r
+  SEMIHOST_FCB   *Fcb;\r
+  EFI_STATUS     Status;\r
+  RETURN_STATUS  Return;\r
 \r
-  Fcb = SEMIHOST_FCB_FROM_THIS(File);\r
+  if ((This == NULL) || (BufferSize == NULL) || (Buffer == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
 \r
-  if (Fcb->IsRoot == TRUE) {\r
-    // By design, the Semihosting feature does not allow to list files on the host machine.\r
+  Fcb = SEMIHOST_FCB_FROM_THIS (This);\r
+\r
+  if (Fcb->IsRoot) {\r
+    // The semi-hosting interface does not allow to list files on the host machine.\r
     Status = EFI_UNSUPPORTED;\r
   } else {\r
-    Status = SemihostFileRead (Fcb->SemihostHandle, BufferSize, Buffer);\r
-    if (!EFI_ERROR (Status)) {\r
-      Fcb->Position += *BufferSize;\r
+    Status = EFI_SUCCESS;\r
+    if (Fcb->Position >= Fcb->Info.FileSize) {\r
+      *BufferSize = 0;\r
+      if (Fcb->Position > Fcb->Info.FileSize) {\r
+        Status = EFI_DEVICE_ERROR;\r
+      }\r
+    } else {\r
+      Return = SemihostFileRead (Fcb->SemihostHandle, BufferSize, Buffer);\r
+      if (RETURN_ERROR (Return)) {\r
+        Status = EFI_DEVICE_ERROR;\r
+      } else {\r
+        Fcb->Position += *BufferSize;\r
+      }\r
     }\r
   }\r
 \r
   return Status;\r
 }\r
 \r
+/**\r
+  Worker function that extends the size of an open file.\r
+\r
+  The extension is filled with zeros.\r
+\r
+  @param[in]  Fcb   Internal description of the opened file\r
+  @param[in]  Size  The number of bytes, the file has to be extended.\r
+\r
+  @retval  EFI_SUCCESS       The file was extended.\r
+  @retval  EFI_DEVICE_ERROR  The last issued semi-hosting operation failed.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+ExtendFile (\r
+  IN  SEMIHOST_FCB  *Fcb,\r
+  IN  UINTN         Size\r
+  )\r
+{\r
+  RETURN_STATUS  Return;\r
+  UINTN          Remaining;\r
+  CHAR8          WriteBuffer[128];\r
+  UINTN          WriteNb;\r
+  UINTN          WriteSize;\r
+\r
+  Return = SemihostFileSeek (Fcb->SemihostHandle, Fcb->Info.FileSize);\r
+  if (RETURN_ERROR (Return)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Remaining = Size;\r
+  SetMem (WriteBuffer, 0, sizeof(WriteBuffer));\r
+  while (Remaining > 0) {\r
+    WriteNb = MIN (Remaining, sizeof(WriteBuffer));\r
+    WriteSize = WriteNb;\r
+    Return = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, WriteBuffer);\r
+    if (RETURN_ERROR (Return)) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+    Remaining -= WriteNb;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Write data to an open file.\r
+\r
+  @param[in]      This        A pointer to the EFI_FILE_PROTOCOL instance that\r
+                              is the file handle to write data to.\r
+  @param[in out]  BufferSize  On input, the size of the Buffer. On output, the\r
+                              size of the data actually written. In both cases,\r
+                              the size is measured in bytes.\r
+  @param[in]      Buffer      The buffer of data to write.\r
+\r
+  @retval  EFI_SUCCESS            The data was written.\r
+  @retval  EFI_ACCESS_DENIED      Attempt to write into a read only file or\r
+                                  in a file opened in read only mode.\r
+  @retval  EFI_DEVICE_ERROR       The last issued semi-hosting operation failed.\r
+  @retval  EFI_INVALID_PARAMETER  At least one of the three input pointers is NULL.\r
+\r
+**/\r
 EFI_STATUS\r
 FileWrite (\r
-  IN     EFI_FILE *File,\r
+  IN     EFI_FILE *This,\r
   IN OUT UINTN    *BufferSize,\r
   IN     VOID     *Buffer\r
   )\r
 {\r
-  SEMIHOST_FCB *Fcb    = NULL;\r
-  EFI_STATUS   Status;\r
-  UINTN        WriteSize = *BufferSize;\r
+  SEMIHOST_FCB   *Fcb;\r
+  EFI_STATUS     Status;\r
+  UINTN          WriteSize;\r
+  RETURN_STATUS  Return;\r
+  UINTN          Length;\r
 \r
-  Fcb = SEMIHOST_FCB_FROM_THIS(File);\r
+  if ((This == NULL) || (BufferSize == NULL) || (Buffer == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Fcb = SEMIHOST_FCB_FROM_THIS (This);\r
 \r
   // We cannot write a read-only file\r
   if ((Fcb->Info.Attribute & EFI_FILE_READ_ONLY)\r
@@ -401,72 +625,142 @@ FileWrite (
     return EFI_ACCESS_DENIED;\r
   }\r
 \r
-  Status = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, Buffer);\r
+  //\r
+  // If the position has been set past the end of the file, first grow the\r
+  // file from its current size "Fcb->Info.FileSize" to "Fcb->Position"\r
+  // size, filling the gap with zeros.\r
+  //\r
+  if (Fcb->Position > Fcb->Info.FileSize) {\r
+    Status = ExtendFile (Fcb, Fcb->Position - Fcb->Info.FileSize);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    Fcb->Info.FileSize = Fcb->Position;\r
+  }\r
 \r
-  if (!EFI_ERROR(Status)) {\r
-    // Semihost write return the number of bytes *NOT* written.\r
-    *BufferSize -= WriteSize;\r
-    Fcb->Position += *BufferSize;\r
+  WriteSize = *BufferSize;\r
+  Return = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, Buffer);\r
+  if (RETURN_ERROR (Return)) {\r
+    return EFI_DEVICE_ERROR;\r
   }\r
 \r
-  return Status;\r
+  Fcb->Position += *BufferSize;\r
+  if (Fcb->Position > Fcb->Info.FileSize) {\r
+    Fcb->Info.FileSize = Fcb->Position;\r
+  }\r
+\r
+  Return = SemihostFileLength (Fcb->SemihostHandle, &Length);\r
+  if (RETURN_ERROR (Return)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  Fcb->Info.PhysicalSize = Length;\r
+\r
+  return EFI_SUCCESS;\r
 }\r
 \r
+/**\r
+  Return a file's current position.\r
+\r
+  @param[in]   This      A pointer to the EFI_FILE_PROTOCOL instance that is\r
+                         the file handle to get the current position on.\r
+  @param[out]  Position  The address to return the file's current position value.\r
+\r
+  @retval  EFI_SUCCESS            The position was returned.\r
+  @retval  EFI_INVALID_PARAMETER  The parameter "This" or "Position" is NULL.\r
+\r
+**/\r
 EFI_STATUS\r
 FileGetPosition (\r
-  IN  EFI_FILE    *File,\r
+  IN  EFI_FILE    *This,\r
   OUT UINT64      *Position\r
   )\r
 {\r
-  SEMIHOST_FCB *Fcb = NULL;\r
+  SEMIHOST_FCB *Fcb;\r
 \r
-  if (Position == NULL) {\r
+  if ((This == NULL) || (Position == NULL)) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  Fcb = SEMIHOST_FCB_FROM_THIS(File);\r
+  Fcb = SEMIHOST_FCB_FROM_THIS(This);\r
 \r
   *Position = Fcb->Position;\r
 \r
   return EFI_SUCCESS;\r
 }\r
 \r
+/**\r
+  Set a file's current position.\r
+\r
+  @param[in]  This      A pointer to the EFI_FILE_PROTOCOL instance that is\r
+                        the file handle to set the requested position on.\r
+  @param[in]  Position  The byte position from the start of the file to set.\r
+\r
+  @retval  EFI_SUCCESS       The position was set.\r
+  @retval  EFI_DEVICE_ERROR  The semi-hosting positionning operation failed.\r
+  @retval  EFI_UNSUPPORTED   The seek request for nonzero is not valid on open\r
+                             directories.\r
+  @retval  EFI_INVALID_PARAMETER  The parameter "This" is NULL.\r
+\r
+**/\r
 EFI_STATUS\r
 FileSetPosition (\r
-  IN EFI_FILE *File,\r
+  IN EFI_FILE *This,\r
   IN UINT64   Position\r
   )\r
 {\r
-  SEMIHOST_FCB *Fcb    = NULL;\r
-  UINTN        Length;\r
-  EFI_STATUS   Status;\r
+  SEMIHOST_FCB   *Fcb;\r
+  RETURN_STATUS  Return;\r
 \r
-  Fcb = SEMIHOST_FCB_FROM_THIS(File);\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
 \r
-  if (!Fcb->IsRoot) {\r
-    Status = SemihostFileLength (Fcb->SemihostHandle, &Length);\r
-    if (!EFI_ERROR(Status) && (Length < Position)) {\r
-      Position = Length;\r
-    }\r
+  Fcb = SEMIHOST_FCB_FROM_THIS (This);\r
 \r
-    Status = SemihostFileSeek (Fcb->SemihostHandle, (UINT32)Position);\r
-    if (!EFI_ERROR(Status)) {\r
-      Fcb->Position = Position;\r
+  if (Fcb->IsRoot) {\r
+    if (Position != 0) {\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+  }\r
+  else {\r
+    //\r
+    // UEFI Spec section 12.5:\r
+    // "Seeking to position 0xFFFFFFFFFFFFFFFF causes the current position to\r
+    // be set to the end of the file."\r
+    //\r
+    if (Position == 0xFFFFFFFFFFFFFFFF) {\r
+      Position = Fcb->Info.FileSize;\r
+    }\r
+    Return = SemihostFileSeek (Fcb->SemihostHandle, MIN (Position, Fcb->Info.FileSize));\r
+    if (RETURN_ERROR (Return)) {\r
+      return EFI_DEVICE_ERROR;\r
     }\r
-  } else {\r
-    Fcb->Position = Position;\r
-    Status = EFI_SUCCESS;\r
   }\r
 \r
-  return Status;\r
+  Fcb->Position = Position;\r
+\r
+  return EFI_SUCCESS;\r
 }\r
 \r
+/**\r
+  Return information about a file.\r
+\r
+  @param[in]      Fcb         A pointer to the description of an open file.\r
+  @param[in out]  BufferSize  The size, in bytes, of Buffer.\r
+  @param[out]     Buffer      A pointer to the data buffer to return. Not NULL if\r
+                              "*BufferSize" is greater than 0.\r
+\r
+  @retval  EFI_SUCCESS            The information was returned.\r
+  @retval  EFI_BUFFER_TOO_SMALL   The BufferSize is too small to return the information.\r
+                                  BufferSize has been updated with the size needed to\r
+                                  complete the request.\r
+**/\r
 STATIC\r
 EFI_STATUS\r
 GetFileInfo (\r
   IN     SEMIHOST_FCB  *Fcb,\r
-  IN OUT UINTN        *BufferSize,\r
-  OUT    VOID         *Buffer\r
+  IN OUT UINTN         *BufferSize,\r
+  OUT    VOID          *Buffer\r
   )\r
 {\r
   EFI_FILE_INFO   *Info = NULL;\r
@@ -507,6 +801,22 @@ GetFileInfo (
   return EFI_SUCCESS;\r
 }\r
 \r
+/**\r
+  Return information about a file system.\r
+\r
+  @param[in]      Fcb         A pointer to the description of an open file\r
+                              which belongs to the file system, the information\r
+                              is requested for.\r
+  @param[in out]  BufferSize  The size, in bytes, of Buffer.\r
+  @param[out]     Buffer      A pointer to the data buffer to return. Not NULL if\r
+                              "*BufferSize" is greater than 0.\r
+\r
+  @retval  EFI_SUCCESS            The information was returned.\r
+  @retval  EFI_BUFFER_TOO_SMALL   The BufferSize is too small to return the information.\r
+                                  BufferSize has been updated with the size needed to\r
+                                  complete the request.\r
+\r
+**/\r
 STATIC\r
 EFI_STATUS\r
 GetFilesystemInfo (\r
@@ -515,9 +825,11 @@ GetFilesystemInfo (
   OUT    VOID         *Buffer\r
   )\r
 {\r
-  EFI_FILE_SYSTEM_INFO    *Info = NULL;\r
-  EFI_STATUS              Status;\r
-  UINTN                   ResultSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (mSemihostFsLabel);\r
+  EFI_FILE_SYSTEM_INFO  *Info;\r
+  EFI_STATUS            Status;\r
+  UINTN                 ResultSize;\r
+\r
+  ResultSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (mSemihostFsLabel);\r
 \r
   if (*BufferSize >= ResultSize) {\r
     ZeroMem (Buffer, ResultSize);\r
@@ -540,25 +852,54 @@ GetFilesystemInfo (
   return Status;\r
 }\r
 \r
+/**\r
+  Return information about a file or a file system.\r
+\r
+  @param[in]      This             A pointer to the EFI_FILE_PROTOCOL instance that\r
+                                   is the file handle the requested information is for.\r
+  @param[in]      InformationType  The type identifier for the information being requested :\r
+                                   EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or\r
+                                   EFI_FILE_SYSTEM_VOLUME_LABEL_ID\r
+  @param[in out]  BufferSize       The size, in bytes, of Buffer.\r
+  @param[out]     Buffer           A pointer to the data buffer to return. The type of the\r
+                                   data inside the buffer is indicated by InformationType.\r
+\r
+  @retval  EFI_SUCCESS           The information was returned.\r
+  @retval  EFI_UNSUPPORTED       The InformationType is not known.\r
+  @retval  EFI_BUFFER_TOO_SMALL  The BufferSize is too small to return the information.\r
+                                 BufferSize has been updated with the size needed to\r
+                                 complete the request.\r
+  @retval  EFI_INVALID_PARAMETER  The parameter "This" or "InformationType" or "BufferSize"\r
+                                  is NULL or "Buffer" is NULL and "*Buffersize" is greater\r
+                                  than 0.\r
+\r
+**/\r
 EFI_STATUS\r
 FileGetInfo (\r
-  IN     EFI_FILE *File,\r
-  IN     EFI_GUID *InformationType,\r
-  IN OUT UINTN    *BufferSize,\r
-  OUT    VOID     *Buffer\r
+  IN     EFI_FILE  *This,\r
+  IN     EFI_GUID  *InformationType,\r
+  IN OUT UINTN     *BufferSize,\r
+  OUT    VOID      *Buffer\r
   )\r
 {\r
   SEMIHOST_FCB *Fcb;\r
   EFI_STATUS   Status;\r
   UINTN        ResultSize;\r
 \r
-  Fcb = SEMIHOST_FCB_FROM_THIS(File);\r
+  if ((This == NULL)                         ||\r
+      (InformationType == NULL)              ||\r
+      (BufferSize == NULL)                   ||\r
+      ((Buffer == NULL) && (*BufferSize > 0))  ) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Fcb = SEMIHOST_FCB_FROM_THIS(This);\r
 \r
-  if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) != 0) {\r
+  if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {\r
     Status = GetFilesystemInfo (Fcb, BufferSize, Buffer);\r
-  } else if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) {\r
+  } else if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {\r
     Status = GetFileInfo (Fcb, BufferSize, Buffer);\r
-  } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid) != 0) {\r
+  } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {\r
     ResultSize = StrSize (mSemihostFsLabel);\r
 \r
     if (*BufferSize >= ResultSize) {\r
@@ -576,37 +917,239 @@ FileGetInfo (
   return Status;\r
 }\r
 \r
+/**\r
+  Set information about a file.\r
+\r
+  @param[in]  Fcb   A pointer to the description of the open file.\r
+  @param[in]  Info  A pointer to the file information to write.\r
+\r
+  @retval  EFI_SUCCESS           The information was set.\r
+  @retval  EFI_ACCESS_DENIED     An attempt is made to change the name of a file\r
+                                 to a file that is already present.\r
+  @retval  EFI_ACCESS_DENIED     An attempt is being made to change the\r
+                                 EFI_FILE_DIRECTORY Attribute.\r
+  @retval  EFI_ACCESS_DENIED     The file is a read-only file or has been\r
+                                 opened in read-only mode and an attempt is\r
+                                 being made to modify a field other than\r
+                                 Attribute.\r
+  @retval  EFI_WRITE_PROTECTED   An attempt is being made to modify a\r
+                                 read-only attribute.\r
+  @retval  EFI_DEVICE_ERROR      The last issued semi-hosting operation failed.\r
+  @retval  EFI_OUT_OF_RESOURCES  A allocation needed to process the request failed.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+SetFileInfo (\r
+  IN  SEMIHOST_FCB   *Fcb,\r
+  IN  EFI_FILE_INFO  *Info\r
+  )\r
+{\r
+  EFI_STATUS     Status;\r
+  RETURN_STATUS  Return;\r
+  BOOLEAN        FileSizeIsDifferent;\r
+  BOOLEAN        FileNameIsDifferent;\r
+  BOOLEAN        ReadOnlyIsDifferent;\r
+  CHAR8          *AsciiFileName;\r
+  UINTN          FileSize;\r
+  UINTN          Length;\r
+  UINTN          SemihostHandle;\r
+\r
+  //\r
+  // A directory can not be changed to a file and a file can\r
+  // not be changed to a directory.\r
+  //\r
+  if (((Info->Attribute & EFI_FILE_DIRECTORY) != 0) != Fcb->IsRoot) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  AsciiFileName = AllocatePool (StrLen (Info->FileName) + 1);\r
+  if (AsciiFileName == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  UnicodeStrToAsciiStr (Info->FileName, AsciiFileName);\r
+\r
+  FileSizeIsDifferent = (Info->FileSize != Fcb->Info.FileSize);\r
+  FileNameIsDifferent = (AsciiStrCmp (AsciiFileName, Fcb->FileName) != 0);\r
+  ReadOnlyIsDifferent = CompareMem (\r
+                          &Info->CreateTime,\r
+                          &Fcb->Info.CreateTime,\r
+                          3 * sizeof (EFI_TIME)\r
+                          ) != 0;\r
+\r
+  //\r
+  // For a read-only file or a file opened in read-only mode, only\r
+  // the Attribute field can be modified. As the root directory is\r
+  // read-only (i.e. VolumeOpen()), this protects the root directory\r
+  // description.\r
+  //\r
+  if ((Fcb->OpenMode == EFI_FILE_MODE_READ)     ||\r
+      (Fcb->Info.Attribute & EFI_FILE_READ_ONLY)  ) {\r
+    if (FileSizeIsDifferent || FileNameIsDifferent || ReadOnlyIsDifferent) {\r
+      Status = EFI_ACCESS_DENIED;\r
+      goto Error;\r
+    }\r
+  }\r
+\r
+  if (ReadOnlyIsDifferent) {\r
+    Status = EFI_WRITE_PROTECTED;\r
+    goto Error;\r
+  }\r
+\r
+  Status = EFI_DEVICE_ERROR;\r
+\r
+  if (FileSizeIsDifferent) {\r
+    FileSize = Info->FileSize;\r
+    if (Fcb->Info.FileSize < FileSize) {\r
+      Status = ExtendFile (Fcb, FileSize - Fcb->Info.FileSize);\r
+      if (EFI_ERROR (Status)) {\r
+        goto Error;\r
+      }\r
+      //\r
+      // The read/write position from the host file system point of view\r
+      // is at the end of the file. If the position from this module\r
+      // point of view is smaller than the new file size, then\r
+      // ask the host file system to move to that position.\r
+      //\r
+      if (Fcb->Position < FileSize) {\r
+        FileSetPosition (&Fcb->File, Fcb->Position);\r
+      }\r
+    }\r
+    Fcb->Info.FileSize = FileSize;\r
+\r
+    Return = SemihostFileLength (Fcb->SemihostHandle, &Length);\r
+    if (RETURN_ERROR (Return)) {\r
+      goto Error;\r
+    }\r
+    Fcb->Info.PhysicalSize = Length;\r
+  }\r
+\r
+  //\r
+  // Note down in RAM the Attribute field but we can not ask\r
+  // for its modification to the host file system as the\r
+  // semi-host interface does not provide this feature.\r
+  //\r
+  Fcb->Info.Attribute = Info->Attribute;\r
+\r
+  if (FileNameIsDifferent) {\r
+    Return = SemihostFileOpen (\r
+               AsciiFileName,\r
+               SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY,\r
+               &SemihostHandle\r
+               );\r
+    if (!RETURN_ERROR (Return)) {\r
+      SemihostFileClose (SemihostHandle);\r
+      Status = EFI_ACCESS_DENIED;\r
+      goto Error;\r
+    }\r
+\r
+    Return = SemihostFileRename (Fcb->FileName, AsciiFileName);\r
+    if (RETURN_ERROR (Return)) {\r
+      goto Error;\r
+    }\r
+    FreePool (Fcb->FileName);\r
+    Fcb->FileName = AsciiFileName;\r
+    AsciiFileName = NULL;\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+Error:\r
+  if (AsciiFileName != NULL) {\r
+    FreePool (AsciiFileName);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Set information about a file or a file system.\r
+\r
+  @param[in]  This             A pointer to the EFI_FILE_PROTOCOL instance that\r
+                               is the file handle the information is for.\r
+  @param[in]  InformationType  The type identifier for the information being set :\r
+                               EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or\r
+                               EFI_FILE_SYSTEM_VOLUME_LABEL_ID\r
+  @param[in]  BufferSize       The size, in bytes, of Buffer.\r
+  @param[in]  Buffer           A pointer to the data buffer to write. The type of the\r
+                               data inside the buffer is indicated by InformationType.\r
+\r
+  @retval  EFI_SUCCESS            The information was set.\r
+  @retval  EFI_UNSUPPORTED        The InformationType is not known.\r
+  @retval  EFI_DEVICE_ERROR       The last issued semi-hosting operation failed.\r
+  @retval  EFI_ACCESS_DENIED      An attempt is being made to change the\r
+                                  EFI_FILE_DIRECTORY Attribute.\r
+  @retval  EFI_ACCESS_DENIED      InformationType is EFI_FILE_INFO_ID and\r
+                                  the file is a read-only file or has been\r
+                                  opened in read-only mode and an attempt is\r
+                                  being made to modify a field other than\r
+                                  Attribute.\r
+  @retval  EFI_ACCESS_DENIED      An attempt is made to change the name of a file\r
+                                  to a file that is already present.\r
+  @retval  EFI_WRITE_PROTECTED    An attempt is being made to modify a\r
+                                  read-only attribute.\r
+  @retval  EFI_BAD_BUFFER_SIZE    The size of the buffer is lower than that indicated by\r
+                                  the data inside the buffer.\r
+  @retval  EFI_OUT_OF_RESOURCES   An allocation needed to process the request failed.\r
+  @retval  EFI_INVALID_PARAMETER  At least one of the parameters is invalid.\r
+\r
+**/\r
 EFI_STATUS\r
 FileSetInfo (\r
-  IN EFI_FILE *File,\r
-  IN EFI_GUID *InformationType,\r
-  IN UINTN    BufferSize,\r
-  IN VOID     *Buffer\r
+  IN EFI_FILE  *This,\r
+  IN EFI_GUID  *InformationType,\r
+  IN UINTN     BufferSize,\r
+  IN VOID      *Buffer\r
   )\r
 {\r
-  EFI_STATUS   Status;\r
+  SEMIHOST_FCB          *Fcb;\r
+  EFI_FILE_INFO         *Info;\r
+  EFI_FILE_SYSTEM_INFO  *SystemInfo;\r
+  CHAR16                *VolumeLabel;\r
 \r
-  if (Buffer == NULL) {\r
+  if ((This == NULL) || (InformationType == NULL) || (Buffer == NULL)) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  Status = EFI_UNSUPPORTED;\r
+  Fcb = SEMIHOST_FCB_FROM_THIS (This);\r
+\r
+  if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {\r
+    Info = Buffer;\r
+    if (Info->Size < (SIZE_OF_EFI_FILE_INFO + StrSize (Info->FileName))) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    if (BufferSize < Info->Size) {\r
+      return EFI_BAD_BUFFER_SIZE;\r
+    }\r
+    return SetFileInfo (Fcb, Info);\r
+  } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {\r
+    SystemInfo = Buffer;\r
+    if (SystemInfo->Size <\r
+        (SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (SystemInfo->VolumeLabel))) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    if (BufferSize < SystemInfo->Size) {\r
+      return EFI_BAD_BUFFER_SIZE;\r
+    }\r
+    Buffer = SystemInfo->VolumeLabel;\r
 \r
-  if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) != 0) {\r
-    //Status = SetFilesystemInfo (Fcb, BufferSize, Buffer);\r
-  } else if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) {\r
-    // Semihosting does not give us access to setting file info, but\r
-    // if we fail here we cannot create new files.\r
-    Status = EFI_SUCCESS;\r
-  } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid) != 0) {\r
     if (StrSize (Buffer) > 0) {\r
-      FreePool (mSemihostFsLabel);\r
-      mSemihostFsLabel = AllocateCopyPool (StrSize (Buffer), Buffer);\r
-      Status = EFI_SUCCESS;\r
+      VolumeLabel = AllocateCopyPool (StrSize (Buffer), Buffer);\r
+      if (VolumeLabel != NULL) {\r
+        FreePool (mSemihostFsLabel);\r
+        mSemihostFsLabel = VolumeLabel;\r
+        return EFI_SUCCESS;\r
+      } else {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+    } else {\r
+      return EFI_INVALID_PARAMETER;\r
     }\r
+  } else if (!CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {\r
+    return EFI_UNSUPPORTED;\r
+  } else {\r
+    return EFI_UNSUPPORTED;\r
   }\r
-\r
-  return Status;\r
 }\r
 \r
 EFI_STATUS\r
index c02b36ae5f617a3dccb14622df672f6ca5439873..93395743baaa06b7897a4b7ff9da0d5c05a1a59f 100644 (file)
@@ -57,56 +57,190 @@ FileOpen (
   IN  UINT64    Attributes\r
   );\r
 \r
+/**\r
+  Close a specified file handle.\r
+\r
+  @param[in]  This  A pointer to the EFI_FILE_PROTOCOL instance that is the file\r
+                    handle to close.\r
+\r
+  @retval  EFI_SUCCESS            The file was closed.\r
+  @retval  EFI_INVALID_PARAMETER  The parameter "This" is NULL.\r
+\r
+**/\r
 EFI_STATUS\r
 FileClose (\r
-  IN EFI_FILE *File\r
+  IN EFI_FILE  *This\r
   );\r
 \r
+/**\r
+  Close and delete a file.\r
+\r
+  @param[in]  This  A pointer to the EFI_FILE_PROTOCOL instance that is the file\r
+                    handle to delete.\r
+\r
+  @retval  EFI_SUCCESS              The file was closed and deleted.\r
+  @retval  EFI_WARN_DELETE_FAILURE  The handle was closed, but the file was not deleted.\r
+  @retval  EFI_INVALID_PARAMETER    The parameter "This" is NULL.\r
+\r
+**/\r
 EFI_STATUS\r
-FileDelete(\r
-  IN EFI_FILE *File\r
+FileDelete (\r
+  IN EFI_FILE *This\r
   );\r
 \r
+/**\r
+  Read data from an open file.\r
+\r
+  @param[in]      This        A pointer to the EFI_FILE_PROTOCOL instance that\r
+                              is the file handle to read data from.\r
+  @param[in out]  BufferSize  On input, the size of the Buffer. On output, the\r
+                              amount of data returned in Buffer. In both cases,\r
+                              the size is measured in bytes.\r
+  @param[out]     Buffer      The buffer into which the data is read.\r
+\r
+  @retval  EFI_SUCCESS            The data was read.\r
+  @retval  EFI_DEVICE_ERROR       On entry, the current file position is\r
+                                  beyond the end of the file, or the semi-hosting\r
+                                  interface reported an error while performing the\r
+                                  read operation.\r
+  @retval  EFI_INVALID_PARAMETER  The parameter "This" or the parameter "Buffer"\r
+                                  is NULL.\r
+**/\r
 EFI_STATUS\r
 FileRead (\r
-  IN     EFI_FILE *File,\r
-  IN OUT UINTN    *BufferSize,\r
-  OUT    VOID     *Buffer\r
+  IN     EFI_FILE  *This,\r
+  IN OUT UINTN     *BufferSize,\r
+  OUT    VOID      *Buffer\r
   );\r
 \r
+/**\r
+  Write data to an open file.\r
+\r
+  @param[in]      This        A pointer to the EFI_FILE_PROTOCOL instance that\r
+                              is the file handle to write data to.\r
+  @param[in out]  BufferSize  On input, the size of the Buffer. On output, the\r
+                              size of the data actually written. In both cases,\r
+                              the size is measured in bytes.\r
+  @param[in]      Buffer      The buffer of data to write.\r
+\r
+  @retval  EFI_SUCCESS            The data was written.\r
+  @retval  EFI_ACCESS_DENIED      Attempt to write into a read only file or\r
+                                  in a file opened in read only mode.\r
+  @retval  EFI_DEVICE_ERROR       The last issued semi-hosting operation failed.\r
+  @retval  EFI_INVALID_PARAMETER  The parameter "This" or the parameter "Buffer"\r
+                                  is NULL.\r
+\r
+**/\r
 EFI_STATUS\r
 FileWrite (\r
-  IN     EFI_FILE *File,\r
+  IN     EFI_FILE *This,\r
   IN OUT UINTN    *BufferSize,\r
   IN     VOID     *Buffer\r
   );\r
 \r
+/**\r
+  Return a file's current position.\r
+\r
+  @param[in]   This      A pointer to the EFI_FILE_PROTOCOL instance that is\r
+                         the file handle to get the current position on.\r
+  @param[out]  Position  The address to return the file's current position value.\r
+\r
+  @retval  EFI_SUCCESS            The position was returned.\r
+  @retval  EFI_INVALID_PARAMETER  Position is a NULL pointer.\r
+\r
+**/\r
 EFI_STATUS\r
 FileGetPosition (\r
-  IN  EFI_FILE  *File,\r
-  OUT UINT64    *Position\r
+  IN  EFI_FILE    *File,\r
+  OUT UINT64      *Position\r
   );\r
 \r
+/**\r
+  Set a file's current position.\r
+\r
+  @param[in]  This      A pointer to the EFI_FILE_PROTOCOL instance that is\r
+                        the file handle to set the requested position on.\r
+  @param[in]  Position  The byte position from the start of the file to set.\r
+\r
+  @retval  EFI_SUCCESS       The position was set.\r
+  @retval  EFI_DEVICE_ERROR  The semi-hosting positionning operation failed.\r
+  @retval  EFI_UNSUPPORTED   The seek request for nonzero is not valid on open\r
+                             directories.\r
+\r
+**/\r
 EFI_STATUS\r
 FileSetPosition (\r
   IN EFI_FILE *File,\r
   IN UINT64   Position\r
   );\r
 \r
+/**\r
+  Return information about a file or a file system.\r
+\r
+  @param[in]      This             A pointer to the EFI_FILE_PROTOCOL instance that\r
+                                   is the file handle the requested information is for.\r
+  @param[in]      InformationType  The type identifier for the information being requested :\r
+                                   EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or\r
+                                   EFI_FILE_SYSTEM_VOLUME_LABEL_ID\r
+  @param[in out]  BufferSize       The size, in bytes, of Buffer.\r
+  @param[out]     Buffer           A pointer to the data buffer to return. The type of the\r
+                                   data inside the buffer is indicated by InformationType.\r
+\r
+  @retval  EFI_SUCCESS           The information was returned.\r
+  @retval  EFI_UNSUPPORTED       The InformationType is not known.\r
+  @retval  EFI_BUFFER_TOO_SMALL  The BufferSize is too small to return the information.\r
+                                 BufferSize has been updated with the size needed to\r
+                                 complete the request.\r
+  @retval  EFI_INVALID_PARAMETER  The parameter "This" or the parameter "Buffer"\r
+                                  is NULL.\r
+\r
+**/\r
 EFI_STATUS\r
 FileGetInfo (\r
-  IN     EFI_FILE *File,\r
-  IN     EFI_GUID *InformationType,\r
-  IN OUT UINTN    *BufferSize,\r
-  OUT    VOID     *Buffer\r
+  IN     EFI_FILE  *This,\r
+  IN     EFI_GUID  *InformationType,\r
+  IN OUT UINTN     *BufferSize,\r
+  OUT    VOID      *Buffer\r
   );\r
 \r
+/**\r
+  Set information about a file or a file system.\r
+\r
+  @param[in]  This             A pointer to the EFI_FILE_PROTOCOL instance that\r
+                               is the file handle the information is for.\r
+  @param[in]  InformationType  The type identifier for the information being set :\r
+                               EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or\r
+                               EFI_FILE_SYSTEM_VOLUME_LABEL_ID\r
+  @param[in]  BufferSize       The size, in bytes, of Buffer.\r
+  @param[in]  Buffer           A pointer to the data buffer to write. The type of the\r
+                               data inside the buffer is indicated by InformationType.\r
+\r
+  @retval  EFI_SUCCESS            The information was set.\r
+  @retval  EFI_UNSUPPORTED        The InformationType is not known.\r
+  @retval  EFI_DEVICE_ERROR       The last issued semi-hosting operation failed.\r
+  @retval  EFI_ACCESS_DENIED      An attempt is being made to change the\r
+                                  EFI_FILE_DIRECTORY Attribute.\r
+  @retval  EFI_ACCESS_DENIED      InformationType is EFI_FILE_INFO_ID and\r
+                                  the file is a read-only file or has been\r
+                                  opened in read-only mode and an attempt is\r
+                                  being made to modify a field other than\r
+                                  Attribute.\r
+  @retval  EFI_ACCESS_DENIED      An attempt is made to change the name of a file\r
+                                  to a file that is already present.\r
+  @retval  EFI_WRITE_PROTECTED    An attempt is being made to modify a\r
+                                  read-only attribute.\r
+  @retval  EFI_BAD_BUFFER_SIZE    The size of the buffer is lower than that indicated by\r
+                                  the data inside the buffer.\r
+  @retval  EFI_OUT_OF_RESOURCES   An allocation needed to process the request failed.\r
+  @retval  EFI_INVALID_PARAMETER  At least one of the parameters is invalid.\r
+\r
+**/\r
 EFI_STATUS\r
 FileSetInfo (\r
-  IN EFI_FILE *File,\r
-  IN EFI_GUID *InformationType,\r
-  IN UINTN    BufferSize,\r
-  IN VOID     *Buffer\r
+  IN EFI_FILE  *This,\r
+  IN EFI_GUID  *InformationType,\r
+  IN UINTN     BufferSize,\r
+  IN VOID      *Buffer\r
   );\r
 \r
 EFI_STATUS\r