[edk2] [PATCH v3 3/6] MdeModulePkg: Initial UDF/ECMA-167 file system support

Paulo Alcantara posted 6 patches 7 years, 4 months ago
There is a newer version of this series
[edk2] [PATCH v3 3/6] MdeModulePkg: Initial UDF/ECMA-167 file system support
Posted by Paulo Alcantara 7 years, 4 months ago
This patch introduces UDF file system support in EDK2. All block devices
that support BlockIo and DiskIo protocols and contain a valid UDF file
system - as specified by OSTA Universal Disk Format (revisions 1.02
through 2.60) - will be installed EFI_SIMPLE_FILE_SYSTEM_PROTOCOL to
provide access to underlying file system.

File system operations on regular, directory and symlink files are
supported.

Cc: Star Zeng <star.zeng@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 MdeModulePkg/Universal/Disk/UdfDxe/ComponentName.c |  185 ++
 MdeModulePkg/Universal/Disk/UdfDxe/File.c          |  903 ++++++++
 MdeModulePkg/Universal/Disk/UdfDxe/FileName.c      |  195 ++
 .../Universal/Disk/UdfDxe/FileSystemOperations.c   | 2447 ++++++++++++++++++++
 MdeModulePkg/Universal/Disk/UdfDxe/Udf.c           |  344 +++
 MdeModulePkg/Universal/Disk/UdfDxe/Udf.h           | 1244 ++++++++++
 MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf      |   66 +
 7 files changed, 5384 insertions(+)
 create mode 100644 MdeModulePkg/Universal/Disk/UdfDxe/ComponentName.c
 create mode 100644 MdeModulePkg/Universal/Disk/UdfDxe/File.c
 create mode 100644 MdeModulePkg/Universal/Disk/UdfDxe/FileName.c
 create mode 100644 MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c
 create mode 100644 MdeModulePkg/Universal/Disk/UdfDxe/Udf.c
 create mode 100644 MdeModulePkg/Universal/Disk/UdfDxe/Udf.h
 create mode 100644 MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf

diff --git a/MdeModulePkg/Universal/Disk/UdfDxe/ComponentName.c b/MdeModulePkg/Universal/Disk/UdfDxe/ComponentName.c
new file mode 100644
index 0000000000..c58bc940b9
--- /dev/null
+++ b/MdeModulePkg/Universal/Disk/UdfDxe/ComponentName.c
@@ -0,0 +1,185 @@
+/** @file
+  UEFI Component Name protocol for UDF/ECMA-167 file system driver.
+
+  Copyright (C) 2014-2017 Paulo Alcantara <pcacjr@zytor.com>
+
+  This program and the accompanying materials are licensed and made available
+  under the terms and conditions of the BSD License which 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 "Udf.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gUdfComponentName = {
+  UdfComponentNameGetDriverName,
+  UdfComponentNameGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUdfComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) UdfComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) UdfComponentNameGetControllerName,
+  "en"
+};
+
+//
+// Driver name table for Udf module.
+// It is shared by the implementation of ComponentName & ComponentName2 Protocol.
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUdfDriverNameTable[] = {
+  {
+    "eng;en",
+    L"UDF File System Driver"
+  },
+  {
+    NULL,
+    NULL
+  }
+};
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfComponentNameGetDriverName (
+  IN   EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN   CHAR8                        *Language,
+  OUT  CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+    Language,
+    This->SupportedLanguages,
+    mUdfDriverNameTable,
+    DriverName,
+    (BOOLEAN)(This == &gUdfComponentName)
+    );
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfComponentNameGetControllerName (
+  IN   EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN   EFI_HANDLE                   ControllerHandle,
+  IN   EFI_HANDLE                   ChildHandle OPTIONAL,
+  IN   CHAR8                        *Language,
+  OUT  CHAR16                       **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}
diff --git a/MdeModulePkg/Universal/Disk/UdfDxe/File.c b/MdeModulePkg/Universal/Disk/UdfDxe/File.c
new file mode 100644
index 0000000000..8ad14fe594
--- /dev/null
+++ b/MdeModulePkg/Universal/Disk/UdfDxe/File.c
@@ -0,0 +1,903 @@
+/** @file
+  Handle operations in files and directories from UDF/ECMA-167 file systems.
+
+  Copyright (C) 2014-2017 Paulo Alcantara <pcacjr@zytor.com>
+
+  This program and the accompanying materials are licensed and made available
+  under the terms and conditions of the BSD License which 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 "Udf.h"
+
+EFI_FILE_PROTOCOL gUdfFileIoOps = {
+  EFI_FILE_PROTOCOL_REVISION,
+  UdfOpen,
+  UdfClose,
+  UdfDelete,
+  UdfRead,
+  UdfWrite,
+  UdfGetPosition,
+  UdfSetPosition,
+  UdfGetInfo,
+  UdfSetInfo,
+  UdfFlush,
+  NULL,
+  NULL,
+  NULL,
+  NULL
+};
+
+#define _ROOT_FILE(_PrivData) (_PrivData)->Root
+#define _PARENT_FILE(_PrivData) \
+  ((_PrivData)->IsRootDirectory ? (_PrivData)->Root : &(_PrivData)->File)
+#define _FILE(_PrivData) _PARENT_FILE(_PrivData)
+
+/**
+  Open the root directory on a volume.
+
+  @param  This Protocol instance pointer.
+  @param  Root Returns an Open file handle for the root directory
+
+  @retval EFI_SUCCESS          The device was opened.
+  @retval EFI_UNSUPPORTED      This volume does not support the file system.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_ACCESS_DENIED    The service denied access to the file.
+  @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfOpenVolume (
+  IN   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *This,
+  OUT  EFI_FILE_PROTOCOL                **Root
+  )
+{
+  EFI_TPL                     OldTpl;
+  EFI_STATUS                  Status;
+  PRIVATE_UDF_SIMPLE_FS_DATA  *PrivFsData;
+  PRIVATE_UDF_FILE_DATA       *PrivFileData;
+
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+  if (This == NULL || Root == NULL) {
+    Status = EFI_INVALID_PARAMETER;
+    goto Error_Invalid_Params;
+  }
+
+  PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (This);
+
+  if (PrivFsData->OpenFiles == 0) {
+    //
+    // There is no more open files. Read volume information again since it was
+    // cleaned up on the last UdfClose() call.
+    //
+    Status = ReadUdfVolumeInformation (
+      PrivFsData->BlockIo,
+      PrivFsData->DiskIo,
+      &PrivFsData->Volume
+      );
+    if (EFI_ERROR (Status)) {
+      goto Error_Read_Udf_Volume;
+    }
+  }
+
+  CleanupFileInformation (&PrivFsData->Root);
+
+  //
+  // Find root directory file.
+  //
+  Status = FindRootDirectory (
+    PrivFsData->BlockIo,
+    PrivFsData->DiskIo,
+    &PrivFsData->Volume,
+    &PrivFsData->Root
+    );
+  if (EFI_ERROR (Status)) {
+    goto Error_Find_Root_Dir;
+  }
+
+  PrivFileData =
+    (PRIVATE_UDF_FILE_DATA *) AllocateZeroPool (sizeof (PRIVATE_UDF_FILE_DATA));
+  if (PrivFileData == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Error_Alloc_Priv_File_Data;
+  }
+
+  PrivFileData->Signature        = PRIVATE_UDF_FILE_DATA_SIGNATURE;
+  PrivFileData->SimpleFs         = This;
+  PrivFileData->Root             = &PrivFsData->Root;
+  PrivFileData->IsRootDirectory  = TRUE;
+
+  CopyMem ((VOID *)&PrivFileData->FileIo, (VOID *)&gUdfFileIoOps,
+           sizeof (EFI_FILE_PROTOCOL));
+
+  *Root = &PrivFileData->FileIo;
+
+  PrivFsData->OpenFiles++;
+
+  gBS->RestoreTPL (OldTpl);
+
+  return EFI_SUCCESS;
+
+Error_Alloc_Priv_File_Data:
+  CleanupFileInformation (&PrivFsData->Root);
+
+Error_Find_Root_Dir:
+  CleanupVolumeInformation (&PrivFsData->Volume);
+
+Error_Read_Udf_Volume:
+Error_Invalid_Params:
+  gBS->RestoreTPL (OldTpl);
+
+  return Status;
+}
+
+/**
+  Opens a new file relative to the source file's location.
+
+  @param  This       The protocol instance pointer.
+  @param  NewHandle  Returns File Handle for FileName.
+  @param  FileName   Null terminated string. "\", ".", and ".." are supported.
+  @param  OpenMode   Open mode for file.
+  @param  Attributes Only used for EFI_FILE_MODE_CREATE.
+
+  @retval EFI_SUCCESS          The device was opened.
+  @retval EFI_NOT_FOUND        The specified file could not be found on the
+                               device.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_MEDIA_CHANGED    The media has changed.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_ACCESS_DENIED    The service denied access to the file.
+  @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of
+                               resources.
+  @retval EFI_VOLUME_FULL      The volume is full.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfOpen (
+  IN   EFI_FILE_PROTOCOL  *This,
+  OUT  EFI_FILE_PROTOCOL  **NewHandle,
+  IN   CHAR16             *FileName,
+  IN   UINT64             OpenMode,
+  IN   UINT64             Attributes
+  )
+{
+  EFI_TPL                     OldTpl;
+  EFI_STATUS                  Status;
+  PRIVATE_UDF_FILE_DATA       *PrivFileData;
+  PRIVATE_UDF_SIMPLE_FS_DATA  *PrivFsData;
+  CHAR16                      FilePath[UDF_PATH_LENGTH] = { 0 };
+  UDF_FILE_INFO               File;
+  PRIVATE_UDF_FILE_DATA       *NewPrivFileData;
+  CHAR16                      *TempFileName;
+
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+  if (This == NULL || NewHandle == NULL || FileName == NULL) {
+    Status = EFI_INVALID_PARAMETER;
+    goto Error_Invalid_Params;
+  }
+
+  if (OpenMode != EFI_FILE_MODE_READ) {
+    Status = EFI_WRITE_PROTECTED;
+    goto Error_Invalid_Params;
+  }
+
+  PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
+
+  PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (PrivFileData->SimpleFs);
+
+  //
+  // Build full path
+  //
+  if (*FileName == L'\\') {
+    StrCpyS (FilePath, UDF_PATH_LENGTH, FileName);
+  } else {
+    StrCpyS (FilePath, UDF_PATH_LENGTH, PrivFileData->AbsoluteFileName);
+    StrCatS (FilePath, UDF_PATH_LENGTH, L"\\");
+    StrCatS (FilePath, UDF_PATH_LENGTH, FileName);
+  }
+
+  MangleFileName (FilePath);
+  if (FilePath[0] == L'\0') {
+    Status = EFI_NOT_FOUND;
+    goto Error_Bad_FileName;
+  }
+
+  Status = FindFile (
+    PrivFsData->BlockIo,
+    PrivFsData->DiskIo,
+    &PrivFsData->Volume,
+    FilePath,
+    _ROOT_FILE (PrivFileData),
+    _PARENT_FILE (PrivFileData),
+    &_PARENT_FILE(PrivFileData)->FileIdentifierDesc->Icb,
+    &File
+    );
+  if (EFI_ERROR (Status)) {
+    goto Error_Find_File;
+  }
+
+  NewPrivFileData =
+    (PRIVATE_UDF_FILE_DATA *)AllocateZeroPool (sizeof (PRIVATE_UDF_FILE_DATA));
+  if (NewPrivFileData == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Error_Alloc_New_Priv_File_Data;
+  }
+
+  CopyMem ((VOID *)NewPrivFileData, (VOID *)PrivFileData,
+           sizeof (PRIVATE_UDF_FILE_DATA));
+  CopyMem ((VOID *)&NewPrivFileData->File, &File, sizeof (UDF_FILE_INFO));
+
+  NewPrivFileData->IsRootDirectory = FALSE;
+
+  StrCpyS (NewPrivFileData->AbsoluteFileName, UDF_PATH_LENGTH, FilePath);
+  FileName = NewPrivFileData->AbsoluteFileName;
+
+  while ((TempFileName = StrStr (FileName, L"\\")) != NULL) {
+    FileName = TempFileName + 1;
+  }
+
+  StrCpyS (NewPrivFileData->FileName, UDF_PATH_LENGTH, FileName);
+
+  Status = GetFileSize (
+    PrivFsData->BlockIo,
+    PrivFsData->DiskIo,
+    &PrivFsData->Volume,
+    &NewPrivFileData->File,
+    &NewPrivFileData->FileSize
+    );
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    goto Error_Get_File_Size;
+  }
+
+  NewPrivFileData->FilePosition = 0;
+  ZeroMem ((VOID *)&NewPrivFileData->ReadDirInfo,
+           sizeof (UDF_READ_DIRECTORY_INFO));
+
+  *NewHandle = &NewPrivFileData->FileIo;
+
+  PrivFsData->OpenFiles++;
+
+  gBS->RestoreTPL (OldTpl);
+
+  return Status;
+
+Error_Get_File_Size:
+  FreePool ((VOID *)NewPrivFileData);
+
+Error_Alloc_New_Priv_File_Data:
+  CleanupFileInformation (&File);
+
+Error_Find_File:
+Error_Bad_FileName:
+Error_Invalid_Params:
+  gBS->RestoreTPL (OldTpl);
+
+  return Status;
+}
+
+/**
+  Read data from the file.
+
+  @param  This       Protocol instance pointer.
+  @param  BufferSize On input size of buffer, on output amount of data in
+                     buffer.
+  @param  Buffer     The buffer in which data is read.
+
+  @retval EFI_SUCCESS          Data was read.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_BUFFER_TO_SMALL  BufferSize is too small. BufferSize contains
+                               required size.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfRead (
+  IN      EFI_FILE_PROTOCOL  *This,
+  IN OUT  UINTN              *BufferSize,
+  OUT     VOID               *Buffer
+  )
+{
+  EFI_TPL                         OldTpl;
+  EFI_STATUS                      Status;
+  PRIVATE_UDF_FILE_DATA           *PrivFileData;
+  PRIVATE_UDF_SIMPLE_FS_DATA      *PrivFsData;
+  UDF_VOLUME_INFO                 *Volume;
+  UDF_FILE_INFO                   *Parent;
+  UDF_READ_DIRECTORY_INFO         *ReadDirInfo;
+  EFI_BLOCK_IO_PROTOCOL           *BlockIo;
+  EFI_DISK_IO_PROTOCOL            *DiskIo;
+  UDF_FILE_INFO                   FoundFile;
+  UDF_FILE_IDENTIFIER_DESCRIPTOR  *NewFileIdentifierDesc;
+  VOID                            *NewFileEntryData;
+  CHAR16                          FileName[UDF_FILENAME_LENGTH] = { 0 };
+  UINT64                          FileSize;
+
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+  if (This == NULL || BufferSize == NULL || (*BufferSize != 0 &&
+                                             Buffer == NULL)) {
+    Status = EFI_INVALID_PARAMETER;
+    goto Error_Invalid_Params;
+  }
+
+  PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
+  PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (PrivFileData->SimpleFs);
+
+  BlockIo                = PrivFsData->BlockIo;
+  DiskIo                 = PrivFsData->DiskIo;
+  Volume                 = &PrivFsData->Volume;
+  ReadDirInfo            = &PrivFileData->ReadDirInfo;
+  NewFileIdentifierDesc  = NULL;
+  NewFileEntryData       = NULL;
+
+  Parent = _PARENT_FILE (PrivFileData);
+
+  Status = EFI_VOLUME_CORRUPTED;
+
+  if (IS_FID_NORMAL_FILE (Parent->FileIdentifierDesc)) {
+    if (PrivFileData->FilePosition > PrivFileData->FileSize) {
+      //
+      // File's position is beyond the EOF
+      //
+      Status = EFI_DEVICE_ERROR;
+      goto Error_File_Beyond_The_Eof;
+    }
+
+    if (PrivFileData->FilePosition == PrivFileData->FileSize) {
+      *BufferSize = 0;
+      Status = EFI_SUCCESS;
+      goto Done;
+    }
+
+    Status = ReadFileData (
+      BlockIo,
+      DiskIo,
+      Volume,
+      Parent,
+      PrivFileData->FileSize,
+      &PrivFileData->FilePosition,
+      Buffer,
+      BufferSize
+      );
+  } else if (IS_FID_DIRECTORY_FILE (Parent->FileIdentifierDesc)) {
+    if (ReadDirInfo->FidOffset == 0 && PrivFileData->FilePosition > 0) {
+      Status = EFI_DEVICE_ERROR;
+      *BufferSize = 0;
+      goto Done;
+    }
+
+    for (;;) {
+      Status = ReadDirectoryEntry (
+        BlockIo,
+        DiskIo,
+        Volume,
+        &Parent->FileIdentifierDesc->Icb,
+        Parent->FileEntry,
+        ReadDirInfo,
+        &NewFileIdentifierDesc
+        );
+      if (EFI_ERROR (Status)) {
+        if (Status == EFI_DEVICE_ERROR) {
+          FreePool (ReadDirInfo->DirectoryData);
+          ZeroMem ((VOID *)ReadDirInfo, sizeof (UDF_READ_DIRECTORY_INFO));
+
+          *BufferSize = 0;
+          Status = EFI_SUCCESS;
+        }
+
+        goto Done;
+      }
+
+      if (!IS_FID_PARENT_FILE (NewFileIdentifierDesc)) {
+        break;
+      }
+
+      FreePool ((VOID *)NewFileIdentifierDesc);
+    }
+
+    Status = FindFileEntry (
+      BlockIo,
+      DiskIo,
+      Volume,
+      &NewFileIdentifierDesc->Icb,
+      &NewFileEntryData
+      );
+    if (EFI_ERROR (Status)) {
+      goto Error_Find_Fe;
+    }
+
+    if (IS_FE_SYMLINK (NewFileEntryData)) {
+      Status = ResolveSymlink (
+        BlockIo,
+        DiskIo,
+        Volume,
+        Parent,
+        NewFileEntryData,
+        &FoundFile
+        );
+      if (EFI_ERROR (Status)) {
+        goto Error_Resolve_Symlink;
+      }
+
+      FreePool ((VOID *)NewFileEntryData);
+      NewFileEntryData = FoundFile.FileEntry;
+
+      Status = GetFileNameFromFid (NewFileIdentifierDesc, FileName);
+      if (EFI_ERROR (Status)) {
+        FreePool ((VOID *)FoundFile.FileIdentifierDesc);
+        goto Error_Get_FileName;
+      }
+
+      FreePool ((VOID *)NewFileIdentifierDesc);
+      NewFileIdentifierDesc = FoundFile.FileIdentifierDesc;
+    } else {
+      FoundFile.FileIdentifierDesc  = NewFileIdentifierDesc;
+      FoundFile.FileEntry           = NewFileEntryData;
+
+      Status = GetFileNameFromFid (FoundFile.FileIdentifierDesc, FileName);
+      if (EFI_ERROR (Status)) {
+        goto Error_Get_FileName;
+      }
+    }
+
+    Status = GetFileSize (
+      BlockIo,
+      DiskIo,
+      Volume,
+      &FoundFile,
+      &FileSize
+      );
+    if (EFI_ERROR (Status)) {
+      goto Error_Get_File_Size;
+    }
+
+    Status = SetFileInfo (
+      &FoundFile,
+      FileSize,
+      FileName,
+      BufferSize,
+      Buffer
+      );
+    if (EFI_ERROR (Status)) {
+      goto Error_Set_File_Info;
+    }
+
+    PrivFileData->FilePosition++;
+    Status = EFI_SUCCESS;
+  } else if (IS_FID_DELETED_FILE (Parent->FileIdentifierDesc)) {
+    Status = EFI_DEVICE_ERROR;
+  }
+
+Error_Set_File_Info:
+Error_Get_File_Size:
+Error_Get_FileName:
+Error_Resolve_Symlink:
+  if (NewFileEntryData != NULL) {
+    FreePool (NewFileEntryData);
+  }
+
+Error_Find_Fe:
+  if (NewFileIdentifierDesc != NULL) {
+    FreePool ((VOID *)NewFileIdentifierDesc);
+  }
+
+Done:
+Error_File_Beyond_The_Eof:
+Error_Invalid_Params:
+  gBS->RestoreTPL (OldTpl);
+
+  return Status;
+}
+
+/**
+  Close the file handle.
+
+  @param  This Protocol instance pointer.
+
+  @retval EFI_SUCCESS The file was closed.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfClose (
+  IN EFI_FILE_PROTOCOL *This
+  )
+{
+  EFI_TPL                     OldTpl;
+  EFI_STATUS                  Status;
+  PRIVATE_UDF_FILE_DATA       *PrivFileData;
+  PRIVATE_UDF_SIMPLE_FS_DATA  *PrivFsData;
+
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+  Status = EFI_SUCCESS;
+
+  if (This == NULL) {
+    Status = EFI_INVALID_PARAMETER;
+    goto Exit;
+  }
+
+  PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
+
+  PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (PrivFileData->SimpleFs);
+
+  if (!PrivFileData->IsRootDirectory) {
+    CleanupFileInformation (&PrivFileData->File);
+
+    if (PrivFileData->ReadDirInfo.DirectoryData != NULL) {
+      FreePool (PrivFileData->ReadDirInfo.DirectoryData);
+    }
+  }
+
+  if (--PrivFsData->OpenFiles == 0) {
+    CleanupVolumeInformation (&PrivFsData->Volume);
+  }
+
+  FreePool ((VOID *)PrivFileData);
+
+Exit:
+  gBS->RestoreTPL (OldTpl);
+
+  return Status;
+}
+
+/**
+  Close and delete the file handle.
+
+  @param  This                     Protocol instance pointer.
+
+  @retval EFI_SUCCESS              The file was closed and deleted.
+  @retval EFI_WARN_DELETE_FAILURE  The handle was closed but the file was not
+                                   deleted.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfDelete (
+  IN EFI_FILE_PROTOCOL  *This
+  )
+{
+  PRIVATE_UDF_FILE_DATA *PrivFileData;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
+
+  (VOID)PrivFileData->FileIo.Close(This);
+
+  return EFI_WARN_DELETE_FAILURE;
+}
+
+/**
+  Write data to a file.
+
+  @param  This       Protocol instance pointer.
+  @param  BufferSize On input size of buffer, on output amount of data in
+                     buffer.
+  @param  Buffer     The buffer in which data to write.
+
+  @retval EFI_SUCCESS          Data was written.
+  @retval EFI_UNSUPPORTED      Writes to Open directory are not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_DEVICE_ERROR     An attempt was made to write to a deleted file.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_WRITE_PROTECTED  The device is write protected.
+  @retval EFI_ACCESS_DENIED    The file was open for read only.
+  @retval EFI_VOLUME_FULL      The volume is full.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfWrite (
+  IN      EFI_FILE_PROTOCOL  *This,
+  IN OUT  UINTN              *BufferSize,
+  IN      VOID               *Buffer
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Get file's current position.
+
+  @param  This      Protocol instance pointer.
+  @param  Position  Byte position from the start of the file.
+
+  @retval EFI_SUCCESS      Position was updated.
+  @retval EFI_UNSUPPORTED  Seek request for directories is not valid.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfGetPosition (
+  IN   EFI_FILE_PROTOCOL  *This,
+  OUT  UINT64             *Position
+  )
+{
+  PRIVATE_UDF_FILE_DATA *PrivFileData;
+
+  if (This == NULL || Position == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
+
+  //
+  // As per UEFI spec, if the file handle is a directory, then the current file
+  // position has no meaning and the operation is not supported.
+  //
+  if (IS_FID_DIRECTORY_FILE (&PrivFileData->File.FileIdentifierDesc)) {
+    return  EFI_UNSUPPORTED;
+  }
+
+  //
+  // The file is not a directory. So, return its position.
+  //
+  *Position = PrivFileData->FilePosition;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Set file's current position.
+
+  @param  This      Protocol instance pointer.
+  @param  Position  Byte position from the start of the file.
+
+  @retval EFI_SUCCESS      Position was updated.
+  @retval EFI_UNSUPPORTED  Seek request for non-zero is not valid on open.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfSetPosition (
+  IN EFI_FILE_PROTOCOL  *This,
+  IN UINT64             Position
+  )
+{
+  EFI_STATUS                      Status;
+  PRIVATE_UDF_FILE_DATA           *PrivFileData;
+  UDF_FILE_IDENTIFIER_DESCRIPTOR  *FileIdentifierDesc;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = EFI_UNSUPPORTED;
+
+  PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
+
+  FileIdentifierDesc = PrivFileData->File.FileIdentifierDesc;
+  if (IS_FID_DIRECTORY_FILE (FileIdentifierDesc)) {
+    //
+    // If the file handle is a directory, the _only_ position that may be set is
+    // zero. This has no effect of starting the read proccess of the directory
+    // entries over.
+    //
+    if (Position == 0) {
+      PrivFileData->FilePosition = Position;
+      PrivFileData->ReadDirInfo.FidOffset = 0;
+      Status = EFI_SUCCESS;
+    }
+  } else if (IS_FID_NORMAL_FILE (FileIdentifierDesc)) {
+    //
+    // Seeking to position 0xFFFFFFFFFFFFFFFF causes the current position to be
+    // set to the EOF.
+    //
+    if (Position == 0xFFFFFFFFFFFFFFFF) {
+      PrivFileData->FilePosition = PrivFileData->FileSize - 1;
+    } else {
+      PrivFileData->FilePosition = Position;
+    }
+
+    Status = EFI_SUCCESS;
+  }
+
+  return Status;
+}
+
+/**
+  Get information about a file.
+
+  @param  This            Protocol instance pointer.
+  @param  InformationType Type of information to return in Buffer.
+  @param  BufferSize      On input size of buffer, on output amount of data in
+                          buffer.
+  @param  Buffer          The buffer to return data.
+
+  @retval EFI_SUCCESS          Data was returned.
+  @retval EFI_UNSUPPORTED      InformationType is not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_WRITE_PROTECTED  The device is write protected.
+  @retval EFI_ACCESS_DENIED    The file was open for read only.
+  @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in
+                               BufferSize.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfGetInfo (
+  IN      EFI_FILE_PROTOCOL  *This,
+  IN      EFI_GUID           *InformationType,
+  IN OUT  UINTN              *BufferSize,
+  OUT     VOID               *Buffer
+  )
+{
+  EFI_STATUS                  Status;
+  PRIVATE_UDF_FILE_DATA       *PrivFileData;
+  PRIVATE_UDF_SIMPLE_FS_DATA  *PrivFsData;
+  EFI_FILE_SYSTEM_INFO        *FileSystemInfo;
+  UINTN                       FileSystemInfoLength;
+  CHAR16                      *String;
+  UDF_FILE_SET_DESCRIPTOR     *FileSetDesc;
+  UINTN                       Index;
+  UINT8                       *OstaCompressed;
+  UINT8                       CompressionId;
+  UINT64                      VolumeSize;
+  UINT64                      FreeSpaceSize;
+  CHAR16                      VolumeLabel[64];
+
+  if (This == NULL || InformationType == NULL || BufferSize == NULL ||
+      (*BufferSize != 0 && Buffer == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
+
+  PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (PrivFileData->SimpleFs);
+
+  Status = EFI_UNSUPPORTED;
+
+  if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
+    Status = SetFileInfo (
+      _FILE (PrivFileData),
+      PrivFileData->FileSize,
+      PrivFileData->FileName,
+      BufferSize,
+      Buffer
+      );
+  } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
+    String = VolumeLabel;
+
+    FileSetDesc = PrivFsData->Volume.FileSetDescs[0];
+
+    OstaCompressed = &FileSetDesc->LogicalVolumeIdentifier[0];
+
+    CompressionId = OstaCompressed[0];
+    if (!IS_VALID_COMPRESSION_ID (CompressionId)) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
+    for (Index = 1; Index < 128; Index++) {
+      if (CompressionId == 16) {
+        *String = *(UINT8 *)(OstaCompressed + Index) << 8;
+        Index++;
+      } else {
+        *String = 0;
+      }
+
+      if (Index < 128) {
+        *String |= *(UINT8 *)(OstaCompressed + Index);
+      }
+
+      //
+      // Unlike FID Identifiers, Logical Volume Identifier is stored in a
+      // NULL-terminated OSTA compressed format, so we must check for the NULL
+      // character.
+      //
+      if (*String == L'\0') {
+        break;
+      }
+
+      String++;
+    }
+
+    *String = L'\0';
+
+    FileSystemInfoLength = StrSize (VolumeLabel) +
+                           sizeof (EFI_FILE_SYSTEM_INFO);
+    if (*BufferSize < FileSystemInfoLength) {
+      *BufferSize = FileSystemInfoLength;
+      return EFI_BUFFER_TOO_SMALL;
+    }
+
+    FileSystemInfo = (EFI_FILE_SYSTEM_INFO *)Buffer;
+    StrCpyS (FileSystemInfo->VolumeLabel, ARRAY_SIZE (VolumeLabel),
+             VolumeLabel);
+    Status = GetVolumeSize (
+      PrivFsData->BlockIo,
+      PrivFsData->DiskIo,
+      &PrivFsData->Volume,
+      &VolumeSize,
+      &FreeSpaceSize
+      );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    FileSystemInfo->Size        = FileSystemInfoLength;
+    FileSystemInfo->ReadOnly    = TRUE;
+    FileSystemInfo->BlockSize   =
+      LV_BLOCK_SIZE (&PrivFsData->Volume, UDF_DEFAULT_LV_NUM);
+    FileSystemInfo->VolumeSize  = VolumeSize;
+    FileSystemInfo->FreeSpace   = FreeSpaceSize;
+
+    *BufferSize = FileSystemInfoLength;
+    Status = EFI_SUCCESS;
+  }
+
+  return Status;
+}
+
+/**
+  Set information about a file.
+
+  @param  File            Protocol instance pointer.
+  @param  InformationType Type of information in Buffer.
+  @param  BufferSize      Size of buffer.
+  @param  Buffer          The data to write.
+
+  @retval EFI_SUCCESS          Data was set.
+  @retval EFI_UNSUPPORTED      InformationType is not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_WRITE_PROTECTED  The device is write protected.
+  @retval EFI_ACCESS_DENIED    The file was open for read only.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfSetInfo (
+  IN EFI_FILE_PROTOCOL  *This,
+  IN EFI_GUID           *InformationType,
+  IN UINTN              BufferSize,
+  IN VOID               *Buffer
+  )
+{
+  return EFI_WRITE_PROTECTED;
+}
+
+/**
+  Flush data back for the file handle.
+
+  @param  This Protocol instance pointer.
+
+  @retval EFI_SUCCESS          Data was flushed.
+  @retval EFI_UNSUPPORTED      Writes to Open directory are not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_WRITE_PROTECTED  The device is write protected.
+  @retval EFI_ACCESS_DENIED    The file was open for read only.
+  @retval EFI_VOLUME_FULL      The volume is full.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfFlush (
+  IN EFI_FILE_PROTOCOL *This
+  )
+{
+  return EFI_WRITE_PROTECTED;
+}
diff --git a/MdeModulePkg/Universal/Disk/UdfDxe/FileName.c b/MdeModulePkg/Universal/Disk/UdfDxe/FileName.c
new file mode 100644
index 0000000000..f73793320d
--- /dev/null
+++ b/MdeModulePkg/Universal/Disk/UdfDxe/FileName.c
@@ -0,0 +1,195 @@
+/** @file
+  Helper functions for mangling file names in UDF/ECMA-167 file systems.
+
+  Copyright (C) 2014-2017 Paulo Alcantara <pcacjr@zytor.com>
+
+  This program and the accompanying materials are licensed and made available
+  under the terms and conditions of the BSD License which 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 "Udf.h"
+
+CHAR16 *
+TrimString (
+  IN CHAR16    *String
+  )
+{
+  CHAR16       *TempString;
+
+  for ( ; *String != L'\0' && *String == L' '; String++) {
+    ;
+  }
+
+  TempString = String + StrLen (String) - 1;
+  while ((TempString >= String) && (*TempString == L' ')) {
+    TempString--;
+  }
+
+  *(TempString + 1) = L'\0';
+
+  return String;
+}
+
+VOID
+ReplaceLeft (
+  IN CHAR16         *Destination,
+  IN CONST CHAR16   *Source
+  )
+{
+  CONST CHAR16      *EndString;
+
+  EndString = Source + StrLen (Source);
+  while (Source <= EndString) {
+    *Destination++ = *Source++;
+  }
+}
+
+CHAR16 *
+ExcludeTrailingBackslashes (
+  IN CHAR16                    *String
+  )
+{
+  CHAR16                       *TempString;
+
+  switch (*(String + 1)) {
+  case L'\\':
+    break;
+  case L'\0':
+  default:
+    String++;
+    goto Exit;
+  }
+
+  TempString = String;
+  while (*TempString != L'\0' && *TempString == L'\\') {
+    TempString++;
+  }
+
+  if (TempString - 1 > String) {
+    ReplaceLeft (String + 1, TempString);
+  }
+
+  String++;
+
+Exit:
+  return String;
+}
+
+/**
+  Mangle a filename by cutting off trailing whitespaces, "\\", "." and "..".
+
+  @param[in] FileName Filename.
+
+  @retval @p FileName Filename mangled.
+
+**/
+CHAR16 *
+MangleFileName (
+  IN CHAR16        *FileName
+  )
+{
+  CHAR16           *FileNameSavedPointer;
+  CHAR16           *TempFileName;
+  UINTN            BackslashesNo;
+
+  if (FileName == NULL || *FileName == L'\0') {
+    FileName = NULL;
+    goto Exit;
+  }
+
+  FileName = TrimString (FileName);
+  if (*FileName == L'\0') {
+    goto Exit;
+  }
+
+  if ((StrLen (FileName) > 1) && (FileName[StrLen (FileName) - 1] == L'\\')) {
+    FileName[StrLen (FileName) - 1] = L'\0';
+  }
+
+  FileNameSavedPointer = FileName;
+
+  if (FileName[0] == L'.') {
+    if (FileName[1] == L'.') {
+      if (FileName[2] == L'\0') {
+        goto Exit;
+      } else {
+        FileName += 2;
+      }
+    } else if (FileName[1] == L'\0') {
+      goto Exit;
+    }
+  }
+
+  while (*FileName != L'\0') {
+    if (*FileName == L'\\') {
+      FileName = ExcludeTrailingBackslashes (FileName);
+    } else if (*FileName == L'.') {
+      switch (*(FileName + 1)) {
+      case L'\0':
+        *FileName = L'\0';
+        break;
+      case L'\\':
+        TempFileName = FileName + 1;
+        TempFileName = ExcludeTrailingBackslashes (TempFileName);
+        ReplaceLeft (FileName, TempFileName);
+        break;
+      case '.':
+        if ((*(FileName - 1) != L'\\') && ((*(FileName + 2) != L'\\') ||
+                                           (*(FileName + 2) != L'\0'))) {
+          FileName++;
+          continue;
+        }
+
+        BackslashesNo = 0;
+        TempFileName = FileName - 1;
+        while (TempFileName >= FileNameSavedPointer) {
+          if (*TempFileName == L'\\') {
+            if (++BackslashesNo == 2) {
+              break;
+            }
+          }
+
+          TempFileName--;
+        }
+
+        TempFileName++;
+
+        if ((*TempFileName == L'.') && (*(TempFileName + 1) == L'.')) {
+          FileName += 2;
+        } else {
+          if (*(FileName + 2) != L'\0') {
+            ReplaceLeft (TempFileName, FileName + 3);
+            if (*(TempFileName - 1) == L'\\') {
+              FileName = TempFileName;
+              ExcludeTrailingBackslashes (TempFileName - 1);
+              TempFileName = FileName;
+            }
+          } else {
+            *TempFileName = L'\0';
+          }
+
+          FileName = TempFileName;
+        }
+
+        break;
+      default:
+        FileName++;
+      }
+    } else {
+      FileName++;
+    }
+  }
+
+  FileName = FileNameSavedPointer;
+  if ((StrLen (FileName) > 1) && (FileName [StrLen (FileName) - 1] == L'\\')) {
+    FileName [StrLen (FileName) - 1] = L'\0';
+  }
+
+Exit:
+  return FileName;
+}
diff --git a/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c b/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c
new file mode 100644
index 0000000000..7d7f722188
--- /dev/null
+++ b/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c
@@ -0,0 +1,2447 @@
+/** @file
+  Handle on-disk format and volume structures in UDF/ECMA-167 file systems.
+
+  Copyright (C) 2014-2017 Paulo Alcantara <pcacjr@zytor.com>
+
+  This program and the accompanying materials are licensed and made available
+  under the terms and conditions of the BSD License which 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 "Udf.h"
+
+EFI_STATUS
+FindAnchorVolumeDescriptorPointer (
+  IN   EFI_BLOCK_IO_PROTOCOL                 *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL                  *DiskIo,
+  OUT  UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER  *AnchorPoint
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      BlockSize = BlockIo->Media->BlockSize;
+  EFI_LBA     EndLBA = BlockIo->Media->LastBlock;
+  EFI_LBA     DescriptorLBAs[] = { 256, EndLBA - 256, EndLBA, 512 };
+  UINTN       Index;
+
+  for (Index = 0; Index < ARRAY_SIZE (DescriptorLBAs); Index++) {
+    Status = DiskIo->ReadDisk (
+      DiskIo,
+      BlockIo->Media->MediaId,
+      MultU64x32 (DescriptorLBAs[Index], BlockSize),
+      sizeof (UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER),
+      (VOID *)AnchorPoint
+      );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    //
+    // Check if read LBA has a valid AVDP descriptor.
+    //
+    if (IS_AVDP (AnchorPoint)) {
+      return EFI_SUCCESS;
+    }
+  }
+  //
+  // No AVDP found.
+  //
+  return EFI_VOLUME_CORRUPTED;
+}
+
+EFI_STATUS
+StartMainVolumeDescriptorSequence (
+  IN   EFI_BLOCK_IO_PROTOCOL                 *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL                  *DiskIo,
+  IN   UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER  *AnchorPoint,
+  OUT  UDF_VOLUME_INFO                       *Volume
+  )
+{
+  EFI_STATUS                     Status;
+  UINT32                         BlockSize;
+  UDF_EXTENT_AD                  *ExtentAd;
+  UINT64                         StartingLsn;
+  UINT64                         EndingLsn;
+  VOID                           *Buffer;
+  UDF_LOGICAL_VOLUME_DESCRIPTOR  *LogicalVolDesc;
+  UDF_PARTITION_DESCRIPTOR       *PartitionDesc;
+  UINTN                          Index;
+  UINT32                         LogicalBlockSize;
+
+  //
+  // We've already found an ADVP on the volume. It contains the extent
+  // (MainVolumeDescriptorSequenceExtent) where the Main Volume Descriptor
+  // Sequence starts. Therefore, we'll look for Logical Volume Descriptors and
+  // Partitions Descriptors and save them in memory, accordingly.
+  //
+  // Note also that each descriptor will be aligned on a block size (BlockSize)
+  // boundary, so we need to read one block at a time.
+  //
+  BlockSize    = BlockIo->Media->BlockSize;
+  ExtentAd     = &AnchorPoint->MainVolumeDescriptorSequenceExtent;
+  StartingLsn  = (UINT64)ExtentAd->ExtentLocation;
+  EndingLsn    = StartingLsn + DivU64x32 (
+                                     (UINT64)ExtentAd->ExtentLength,
+                                     BlockSize
+                                     );
+
+  Volume->LogicalVolDescs =
+    (UDF_LOGICAL_VOLUME_DESCRIPTOR **)AllocateZeroPool (ExtentAd->ExtentLength);
+  if (Volume->LogicalVolDescs == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Volume->PartitionDescs =
+    (UDF_PARTITION_DESCRIPTOR **)AllocateZeroPool (ExtentAd->ExtentLength);
+  if (Volume->PartitionDescs == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Error_Alloc_Pds;
+  }
+
+  Buffer = AllocateZeroPool (BlockSize);
+  if (Buffer == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Error_Alloc_Buf;
+  }
+
+  Volume->LogicalVolDescsNo  = 0;
+  Volume->PartitionDescsNo   = 0;
+
+  while (StartingLsn <= EndingLsn) {
+    Status = DiskIo->ReadDisk (
+      DiskIo,
+      BlockIo->Media->MediaId,
+      MultU64x32 (StartingLsn, BlockSize),
+      BlockSize,
+      Buffer
+      );
+    if (EFI_ERROR (Status)) {
+      goto Error_Read_Disk_Blk;
+    }
+
+    if (IS_TD (Buffer)) {
+      //
+      // Found a Terminating Descriptor. Stop the sequence then.
+      //
+      break;
+    }
+
+    if (IS_LVD (Buffer)) {
+      //
+      // Found a Logical Volume Descriptor.
+      //
+      LogicalVolDesc =
+        (UDF_LOGICAL_VOLUME_DESCRIPTOR *)
+        AllocateZeroPool (sizeof (UDF_LOGICAL_VOLUME_DESCRIPTOR));
+      if (LogicalVolDesc == NULL) {
+        Status = EFI_OUT_OF_RESOURCES;
+        goto Error_Alloc_Lvd;
+      }
+
+      CopyMem ((VOID *)LogicalVolDesc, Buffer,
+               sizeof (UDF_LOGICAL_VOLUME_DESCRIPTOR));
+      Volume->LogicalVolDescs[Volume->LogicalVolDescsNo++] = LogicalVolDesc;
+    } else if (IS_PD (Buffer)) {
+      //
+      // Found a Partition Descriptor.
+      //
+      PartitionDesc =
+        (UDF_PARTITION_DESCRIPTOR *)
+        AllocateZeroPool (sizeof (UDF_PARTITION_DESCRIPTOR));
+      if (PartitionDesc == NULL) {
+        Status = EFI_OUT_OF_RESOURCES;
+        goto Error_Alloc_Pd;
+      }
+
+      CopyMem ((VOID *)PartitionDesc, Buffer,
+               sizeof (UDF_PARTITION_DESCRIPTOR));
+      Volume->PartitionDescs[Volume->PartitionDescsNo++] = PartitionDesc;
+    }
+
+    StartingLsn++;
+  }
+
+  //
+  // When an UDF volume (revision 2.00 or higher) contains a File Entry rather
+  // than an Extended File Entry (which is not recommended as per spec), we need
+  // to make sure the size of a FE will be _at least_ 2048
+  // (UDF_LOGICAL_SECTOR_SIZE) bytes long to keep backward compatibility.
+  //
+  LogicalBlockSize = LV_BLOCK_SIZE (Volume, UDF_DEFAULT_LV_NUM);
+  if (LogicalBlockSize >= UDF_LOGICAL_SECTOR_SIZE) {
+    Volume->FileEntrySize = LogicalBlockSize;
+  } else {
+    Volume->FileEntrySize = UDF_LOGICAL_SECTOR_SIZE;
+  }
+
+  FreePool (Buffer);
+
+  return EFI_SUCCESS;
+
+Error_Alloc_Pd:
+Error_Alloc_Lvd:
+  for (Index = 0; Index < Volume->PartitionDescsNo; Index++) {
+    FreePool ((VOID *)Volume->PartitionDescs[Index]);
+  }
+
+  for (Index = 0; Index < Volume->LogicalVolDescsNo; Index++) {
+    FreePool ((VOID *)Volume->LogicalVolDescs[Index]);
+  }
+
+Error_Read_Disk_Blk:
+  FreePool (Buffer);
+
+Error_Alloc_Buf:
+  FreePool ((VOID *)Volume->PartitionDescs);
+  Volume->PartitionDescs = NULL;
+
+Error_Alloc_Pds:
+  FreePool ((VOID *)Volume->LogicalVolDescs);
+  Volume->LogicalVolDescs = NULL;
+
+  return Status;
+}
+
+//
+// Return a Partition Descriptor given a Long Allocation Descriptor. This is
+// necessary to calculate the right extent (LongAd) offset which is added up
+// with partition's starting location.
+//
+UDF_PARTITION_DESCRIPTOR *
+GetPdFromLongAd (
+  IN UDF_VOLUME_INFO                 *Volume,
+  IN UDF_LONG_ALLOCATION_DESCRIPTOR  *LongAd
+  )
+{
+  UDF_LOGICAL_VOLUME_DESCRIPTOR  *LogicalVolDesc;
+  UINTN                          Index;
+  UDF_PARTITION_DESCRIPTOR       *PartitionDesc;
+  UINT16                         PartitionNum;
+
+  LogicalVolDesc = Volume->LogicalVolDescs[UDF_DEFAULT_LV_NUM];
+
+  switch (LV_UDF_REVISION (LogicalVolDesc)) {
+  case 0x0102:
+    //
+    // As per UDF 1.02 specification:
+    //
+    // There shall be exactly one prevailing Logical Volume Descriptor recorded
+    // per Volume Set. The Partition Maps field shall contain only Type 1
+    // Partition Maps.
+    //
+    PartitionNum = *(UINT16 *)((UINTN)&LogicalVolDesc->PartitionMaps[4]);
+    break;
+  case 0x0150:
+    //
+    // Ensure Type 1 Partition map. Other types aren't supported in this
+    // implementation.
+    //
+    if (LogicalVolDesc->PartitionMaps[0] != 1 ||
+        LogicalVolDesc->PartitionMaps[1] != 6) {
+      return NULL;
+    }
+    PartitionNum = *(UINT16 *)((UINTN)&LogicalVolDesc->PartitionMaps[4]);
+    break;
+  case 0x0260:
+    //
+    // Fall through.
+    //
+  default:
+    PartitionNum = LongAd->ExtentLocation.PartitionReferenceNumber;
+    break;
+  }
+
+  for (Index = 0; Index < Volume->PartitionDescsNo; Index++) {
+    PartitionDesc = Volume->PartitionDescs[Index];
+    if (PartitionDesc->PartitionNumber == PartitionNum) {
+      return PartitionDesc;
+    }
+  }
+
+  return NULL;
+}
+
+//
+// Return logical sector number of a given Long Allocation Descriptor.
+//
+UINT64
+GetLongAdLsn (
+  IN UDF_VOLUME_INFO                 *Volume,
+  IN UDF_LONG_ALLOCATION_DESCRIPTOR  *LongAd
+  )
+{
+  UDF_PARTITION_DESCRIPTOR *PartitionDesc;
+
+  PartitionDesc = GetPdFromLongAd (Volume, LongAd);
+  ASSERT (PartitionDesc != NULL);
+
+  return (UINT64)PartitionDesc->PartitionStartingLocation +
+                 LongAd->ExtentLocation.LogicalBlockNumber;
+}
+
+//
+// Return logical sector number of a given Short Allocation Descriptor.
+//
+UINT64
+GetShortAdLsn (
+  IN UDF_PARTITION_DESCRIPTOR         *PartitionDesc,
+  IN UDF_SHORT_ALLOCATION_DESCRIPTOR  *ShortAd
+  )
+{
+  return (UINT64)PartitionDesc->PartitionStartingLocation +
+    ShortAd->ExtentPosition;
+}
+
+//
+// Find File Set Descriptor of a given Logical Volume Descriptor.
+//
+// The found FSD will contain the extent (LogicalVolumeContentsUse) where our
+// root directory is.
+//
+EFI_STATUS
+FindFileSetDescriptor (
+  IN   EFI_BLOCK_IO_PROTOCOL    *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL     *DiskIo,
+  IN   UDF_VOLUME_INFO          *Volume,
+  IN   UINTN                    LogicalVolDescNum,
+  OUT  UDF_FILE_SET_DESCRIPTOR  *FileSetDesc
+  )
+{
+  EFI_STATUS                     Status;
+  UINT64                         Lsn;
+  UDF_LOGICAL_VOLUME_DESCRIPTOR  *LogicalVolDesc;
+
+  LogicalVolDesc = Volume->LogicalVolDescs[LogicalVolDescNum];
+  Lsn = GetLongAdLsn (Volume, &LogicalVolDesc->LogicalVolumeContentsUse);
+
+  //
+  // Read extent (Long Ad).
+  //
+  Status = DiskIo->ReadDisk (
+    DiskIo,
+    BlockIo->Media->MediaId,
+    MultU64x32 (Lsn, LogicalVolDesc->LogicalBlockSize),
+    sizeof (UDF_FILE_SET_DESCRIPTOR),
+    (VOID *)FileSetDesc
+    );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Check if the read extent contains a valid FSD's tag identifier.
+  //
+  if (!IS_FSD (FileSetDesc)) {
+    return EFI_VOLUME_CORRUPTED;
+  }
+
+  return EFI_SUCCESS;
+}
+
+//
+// Get all File Set Descriptors for each Logical Volume Descriptor.
+//
+EFI_STATUS
+GetFileSetDescriptors (
+  IN      EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN      EFI_DISK_IO_PROTOCOL   *DiskIo,
+  IN OUT  UDF_VOLUME_INFO        *Volume
+  )
+{
+  EFI_STATUS               Status;
+  UINTN                    Index;
+  UDF_FILE_SET_DESCRIPTOR  *FileSetDesc;
+  UINTN                    Count;
+
+  Volume->FileSetDescs =
+    (UDF_FILE_SET_DESCRIPTOR **)AllocateZeroPool (
+      Volume->LogicalVolDescsNo * sizeof (UDF_FILE_SET_DESCRIPTOR));
+  if (Volume->FileSetDescs == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  for (Index = 0; Index < Volume->LogicalVolDescsNo; Index++) {
+    FileSetDesc = AllocateZeroPool (sizeof (UDF_FILE_SET_DESCRIPTOR));
+    if (FileSetDesc == NULL) {
+      Status = EFI_OUT_OF_RESOURCES;
+      goto Error_Alloc_Fsd;
+    }
+
+    //
+    // Find a FSD for this LVD.
+    //
+    Status = FindFileSetDescriptor (
+      BlockIo,
+      DiskIo,
+      Volume,
+      Index,
+      FileSetDesc
+      );
+    if (EFI_ERROR (Status)) {
+      goto Error_Find_Fsd;
+    }
+
+    //
+    // Got one. Save it.
+    //
+    Volume->FileSetDescs[Index] = FileSetDesc;
+  }
+
+  Volume->FileSetDescsNo = Volume->LogicalVolDescsNo;
+  return EFI_SUCCESS;
+
+Error_Find_Fsd:
+  Count = Index + 1;
+  for (Index = 0; Index < Count; Index++) {
+    FreePool ((VOID *)Volume->FileSetDescs[Index]);
+  }
+
+  FreePool ((VOID *)Volume->FileSetDescs);
+  Volume->FileSetDescs = NULL;
+
+Error_Alloc_Fsd:
+  return Status;
+}
+
+//
+// Read Volume and File Structure on an UDF file system.
+//
+EFI_STATUS
+ReadVolumeFileStructure (
+  IN   EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL   *DiskIo,
+  OUT  UDF_VOLUME_INFO        *Volume
+  )
+{
+  EFI_STATUS                            Status;
+  UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER  AnchorPoint;
+
+  //
+  // Find an AVDP.
+  //
+  Status = FindAnchorVolumeDescriptorPointer (
+    BlockIo,
+    DiskIo,
+    &AnchorPoint
+    );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // AVDP has been found. Start MVDS.
+  //
+  Status = StartMainVolumeDescriptorSequence (
+    BlockIo,
+    DiskIo,
+    &AnchorPoint,
+    Volume
+    );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return Status;
+}
+
+//
+// Calculate length of a given File Identifier Descriptor.
+//
+UINT64
+GetFidDescriptorLength (
+  IN UDF_FILE_IDENTIFIER_DESCRIPTOR  *FileIdentifierDesc
+  )
+{
+  return (UINT64)(
+             (INTN)((OFFSET_OF (UDF_FILE_IDENTIFIER_DESCRIPTOR, Data[0]) + 3 +
+             FileIdentifierDesc->LengthOfFileIdentifier +
+             FileIdentifierDesc->LengthOfImplementationUse) >> 2) << 2
+             );
+}
+
+//
+// Duplicate a given File Identifier Descriptor.
+//
+VOID
+DuplicateFid (
+  IN   UDF_FILE_IDENTIFIER_DESCRIPTOR  *FileIdentifierDesc,
+  OUT  UDF_FILE_IDENTIFIER_DESCRIPTOR  **NewFileIdentifierDesc
+  )
+{
+  *NewFileIdentifierDesc =
+    (UDF_FILE_IDENTIFIER_DESCRIPTOR *)AllocateCopyPool (
+      GetFidDescriptorLength (FileIdentifierDesc), FileIdentifierDesc);
+}
+
+//
+// Duplicate either a given File Entry or a given Extended File Entry.
+//
+VOID
+DuplicateFe (
+  IN   EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN   UDF_VOLUME_INFO        *Volume,
+  IN   VOID                   *FileEntry,
+  OUT  VOID                   **NewFileEntry
+  )
+{
+  *NewFileEntry = AllocateCopyPool (Volume->FileEntrySize, FileEntry);
+}
+
+//
+// Get raw data + length of a given File Entry or Extended File Entry.
+//
+// The file's recorded data can contain either real file content (inline) or
+// a sequence of extents (or Allocation Descriptors) which tells where file's
+// content is stored in.
+//
+// NOTE: The FE/EFE can be thought it was an inode.
+//
+VOID
+GetFileEntryData (
+  IN   VOID    *FileEntryData,
+  OUT  VOID    **Data,
+  OUT  UINT64  *Length
+  )
+{
+  UDF_EXTENDED_FILE_ENTRY  *ExtendedFileEntry;
+  UDF_FILE_ENTRY           *FileEntry;
+
+  if (IS_EFE (FileEntryData)) {
+    ExtendedFileEntry = (UDF_EXTENDED_FILE_ENTRY *)FileEntryData;
+
+    *Length  = ExtendedFileEntry->InformationLength;
+    *Data    = (VOID *)((UINT8 *)ExtendedFileEntry->Data +
+                        ExtendedFileEntry->LengthOfExtendedAttributes);
+  } else if (IS_FE (FileEntryData)) {
+    FileEntry = (UDF_FILE_ENTRY *)FileEntryData;
+
+    *Length  = FileEntry->InformationLength;
+    *Data    = (VOID *)((UINT8 *)FileEntry->Data +
+                        FileEntry->LengthOfExtendedAttributes);
+  }
+}
+
+//
+// Get Allocation Descriptors' data information from a given FE/EFE.
+//
+VOID
+GetAdsInformation (
+  IN   VOID    *FileEntryData,
+  OUT  VOID    **AdsData,
+  OUT  UINT64  *Length
+  )
+{
+  UDF_EXTENDED_FILE_ENTRY  *ExtendedFileEntry;
+  UDF_FILE_ENTRY           *FileEntry;
+
+  if (IS_EFE (FileEntryData)) {
+    ExtendedFileEntry = (UDF_EXTENDED_FILE_ENTRY *)FileEntryData;
+
+    *Length = ExtendedFileEntry->LengthOfAllocationDescriptors;
+    *AdsData = (VOID *)((UINT8 *)ExtendedFileEntry->Data +
+                        ExtendedFileEntry->LengthOfExtendedAttributes);
+  } else if (IS_FE (FileEntryData)) {
+    FileEntry = (UDF_FILE_ENTRY *)FileEntryData;
+
+    *Length = FileEntry->LengthOfAllocationDescriptors;
+    *AdsData = (VOID *)((UINT8 *)FileEntry->Data +
+                        FileEntry->LengthOfExtendedAttributes);
+  }
+}
+
+//
+// Read next Long Allocation Descriptor from a given file's data.
+//
+EFI_STATUS
+GetLongAdFromAds (
+  IN      VOID                            *Data,
+  IN OUT  UINT64                          *Offset,
+  IN      UINT64                          Length,
+  OUT     UDF_LONG_ALLOCATION_DESCRIPTOR  **FoundLongAd
+  )
+{
+  UDF_LONG_ALLOCATION_DESCRIPTOR  *LongAd;
+  UDF_EXTENT_FLAGS                ExtentFlags;
+
+  for (;;) {
+    if (*Offset >= Length) {
+      //
+      // No more Long Allocation Descriptors.
+      //
+      return EFI_DEVICE_ERROR;
+    }
+
+    LongAd =
+      (UDF_LONG_ALLOCATION_DESCRIPTOR *)((UINT8 *)Data + *Offset);
+
+    //
+    // If it's either an indirect AD (Extended Alllocation Descriptor) or an
+    // allocated AD, then return it.
+    //
+    ExtentFlags = GET_EXTENT_FLAGS (LONG_ADS_SEQUENCE, LongAd);
+    if (ExtentFlags == EXTENT_IS_NEXT_EXTENT ||
+        ExtentFlags == EXTENT_RECORDED_AND_ALLOCATED) {
+      break;
+    }
+
+    //
+    // This AD is either not recorded but allocated, or not recorded and not
+    // allocated. Skip it.
+    //
+    *Offset += AD_LENGTH (LONG_ADS_SEQUENCE);
+  }
+
+  *FoundLongAd = LongAd;
+
+  return EFI_SUCCESS;
+}
+
+//
+// Read next Short Allocation Descriptor from a given file's data.
+//
+EFI_STATUS
+GetShortAdFromAds (
+  IN      VOID                             *Data,
+  IN OUT  UINT64                           *Offset,
+  IN      UINT64                           Length,
+  OUT     UDF_SHORT_ALLOCATION_DESCRIPTOR  **FoundShortAd
+  )
+{
+  UDF_SHORT_ALLOCATION_DESCRIPTOR *ShortAd;
+  UDF_EXTENT_FLAGS                ExtentFlags;
+
+  for (;;) {
+    if (*Offset >= Length) {
+      //
+      // No more Short Allocation Descriptors.
+      //
+      return EFI_DEVICE_ERROR;
+    }
+
+    ShortAd =
+      (UDF_SHORT_ALLOCATION_DESCRIPTOR *)((UINT8 *)Data + *Offset);
+
+    //
+    // If it's either an indirect AD (Extended Alllocation Descriptor) or an
+    // allocated AD, then return it.
+    //
+    ExtentFlags = GET_EXTENT_FLAGS (SHORT_ADS_SEQUENCE, ShortAd);
+    if (ExtentFlags == EXTENT_IS_NEXT_EXTENT ||
+        ExtentFlags == EXTENT_RECORDED_AND_ALLOCATED) {
+      break;
+    }
+
+    //
+    // This AD is either not recorded but allocated, or not recorded and not
+    // allocated. Skip it.
+    //
+    *Offset += AD_LENGTH (SHORT_ADS_SEQUENCE);
+  }
+
+  *FoundShortAd = ShortAd;
+
+  return EFI_SUCCESS;
+}
+
+//
+// Get either a Short Allocation Descriptor or a Long Allocation Descriptor from
+// file's data.
+//
+EFI_STATUS
+GetAllocationDescriptor (
+  IN      UDF_FE_RECORDING_FLAGS  RecordingFlags,
+  IN      VOID                    *Data,
+  IN OUT  UINT64                  *Offset,
+  IN      UINT64                  Length,
+  OUT     VOID                    **FoundAd
+  )
+{
+  if (RecordingFlags == LONG_ADS_SEQUENCE) {
+    return GetLongAdFromAds (
+      Data,
+      Offset,
+      Length,
+      (UDF_LONG_ALLOCATION_DESCRIPTOR **)FoundAd
+      );
+  } else if (RecordingFlags == SHORT_ADS_SEQUENCE) {
+    return GetShortAdFromAds (
+      Data,
+      Offset,
+      Length,
+      (UDF_SHORT_ALLOCATION_DESCRIPTOR **)FoundAd
+      );
+  }
+
+  return EFI_DEVICE_ERROR;
+}
+
+//
+// Return logical sector number of either Short or Long Allocation Descriptor.
+//
+UINT64
+GetAllocationDescriptorLsn (
+  IN UDF_FE_RECORDING_FLAGS          RecordingFlags,
+  IN UDF_VOLUME_INFO                 *Volume,
+  IN UDF_LONG_ALLOCATION_DESCRIPTOR  *ParentIcb,
+  IN VOID                            *Ad
+  )
+{
+  if (RecordingFlags == LONG_ADS_SEQUENCE) {
+    return GetLongAdLsn (Volume, (UDF_LONG_ALLOCATION_DESCRIPTOR *)Ad);
+  } else if (RecordingFlags == SHORT_ADS_SEQUENCE) {
+    return GetShortAdLsn (
+      GetPdFromLongAd (Volume, ParentIcb),
+      (UDF_SHORT_ALLOCATION_DESCRIPTOR *)Ad
+      );
+  }
+
+  return 0;
+}
+
+//
+// Return offset + length of a given indirect Allocation Descriptor (AED).
+//
+EFI_STATUS
+GetAedAdsOffset (
+  IN   EFI_BLOCK_IO_PROTOCOL           *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL            *DiskIo,
+  IN   UDF_VOLUME_INFO                 *Volume,
+  IN   UDF_LONG_ALLOCATION_DESCRIPTOR  *ParentIcb,
+  IN   UDF_FE_RECORDING_FLAGS          RecordingFlags,
+  IN   VOID                            *Ad,
+  OUT  UINT64                          *Offset,
+  OUT  UINT64                          *Length
+  )
+{
+  EFI_STATUS                        Status;
+  UINT32                            ExtentLength;
+  UINT64                            Lsn;
+  VOID                              *Data;
+  UINT32                            LogicalBlockSize;
+  UDF_ALLOCATION_EXTENT_DESCRIPTOR  *AllocExtDesc;
+
+  ExtentLength  = GET_EXTENT_LENGTH (RecordingFlags, Ad);
+  Lsn           = GetAllocationDescriptorLsn (RecordingFlags,
+                                              Volume,
+                                              ParentIcb,
+                                              Ad);
+
+  Data = AllocatePool (ExtentLength);
+  if (Data == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  LogicalBlockSize = LV_BLOCK_SIZE (Volume, UDF_DEFAULT_LV_NUM);
+
+  //
+  // Read extent.
+  //
+  Status = DiskIo->ReadDisk (
+    DiskIo,
+    BlockIo->Media->MediaId,
+    MultU64x32 (Lsn, LogicalBlockSize),
+    ExtentLength,
+    Data
+    );
+  if (EFI_ERROR (Status)) {
+    goto Exit;
+  }
+
+  //
+  // Check if read extent contains a valid tag identifier for AED.
+  //
+  AllocExtDesc = (UDF_ALLOCATION_EXTENT_DESCRIPTOR *)Data;
+  if (!IS_AED (AllocExtDesc)) {
+    Status = EFI_VOLUME_CORRUPTED;
+    goto Exit;
+  }
+
+  //
+  // Get AED's block offset and its length.
+  //
+  *Offset = MultU64x32 (Lsn, LogicalBlockSize) +
+    sizeof (UDF_ALLOCATION_EXTENT_DESCRIPTOR);
+  *Length = AllocExtDesc->LengthOfAllocationDescriptors;
+
+Exit:
+  FreePool (Data);
+
+  return Status;
+}
+
+//
+// Read Allocation Extent Descriptor into memory.
+//
+EFI_STATUS
+GetAedAdsData (
+  IN   EFI_BLOCK_IO_PROTOCOL           *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL            *DiskIo,
+  IN   UDF_VOLUME_INFO                 *Volume,
+  IN   UDF_LONG_ALLOCATION_DESCRIPTOR  *ParentIcb,
+  IN   UDF_FE_RECORDING_FLAGS          RecordingFlags,
+  IN   VOID                            *Ad,
+  OUT  VOID                            **Data,
+  OUT  UINT64                          *Length
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Offset;
+
+  //
+  // Get AED's offset + length.
+  //
+  Status = GetAedAdsOffset (
+    BlockIo,
+    DiskIo,
+    Volume,
+    ParentIcb,
+    RecordingFlags,
+    Ad,
+    &Offset,
+    Length
+    );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Allocate buffer to read in AED's data.
+  //
+  *Data = AllocatePool (*Length);
+  if (*Data == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  return DiskIo->ReadDisk (
+    DiskIo,
+    BlockIo->Media->MediaId,
+    Offset,
+    *Length,
+    *Data
+    );
+}
+
+//
+// Function used to serialise reads of Allocation Descriptors.
+//
+EFI_STATUS
+GrowUpBufferToNextAd (
+  IN      UDF_FE_RECORDING_FLAGS  RecordingFlags,
+  IN      VOID                    *Ad,
+  IN OUT  VOID                    **Buffer,
+  IN      UINT64                  Length
+  )
+{
+  UINT32 ExtentLength;
+
+  ExtentLength = GET_EXTENT_LENGTH (RecordingFlags, Ad);
+
+  if (*Buffer == NULL) {
+    *Buffer = AllocatePool (ExtentLength);
+    if (*Buffer == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+  } else {
+    *Buffer = ReallocatePool (Length, Length + ExtentLength, *Buffer);
+    if (*Buffer == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+//
+// Read data or size of either a File Entry or an Extended File Entry.
+//
+EFI_STATUS
+ReadFile (
+  IN      EFI_BLOCK_IO_PROTOCOL           *BlockIo,
+  IN      EFI_DISK_IO_PROTOCOL            *DiskIo,
+  IN      UDF_VOLUME_INFO                 *Volume,
+  IN      UDF_LONG_ALLOCATION_DESCRIPTOR  *ParentIcb,
+  IN      VOID                            *FileEntryData,
+  IN OUT  UDF_READ_FILE_INFO              *ReadFileInfo
+  )
+{
+  EFI_STATUS              Status;
+  UINT32                  LogicalBlockSize;
+  VOID                    *Data;
+  UINT64                  Length;
+  VOID                    *Ad;
+  UINT64                  AdOffset;
+  UINT64                  Lsn;
+  BOOLEAN                 DoFreeAed;
+  UINT64                  FilePosition;
+  UINT64                  Offset;
+  UINT64                  DataOffset;
+  UINT64                  BytesLeft;
+  UINT64                  DataLength;
+  BOOLEAN                 FinishedSeeking;
+  UINT32                  ExtentLength;
+  UDF_FE_RECORDING_FLAGS  RecordingFlags;
+
+  LogicalBlockSize  = LV_BLOCK_SIZE (Volume, UDF_DEFAULT_LV_NUM);
+  DoFreeAed         = FALSE;
+
+  switch (ReadFileInfo->Flags) {
+  case READ_FILE_GET_FILESIZE:
+  case READ_FILE_ALLOCATE_AND_READ:
+    //
+    // Initialise ReadFileInfo structure for either getting file size, or
+    // reading file's recorded data.
+    //
+    ReadFileInfo->ReadLength = 0;
+    ReadFileInfo->FileData = NULL;
+    break;
+  case READ_FILE_SEEK_AND_READ:
+    //
+    // About to seek a file and/or read its data.
+    //
+    Length = ReadFileInfo->FileSize - ReadFileInfo->FilePosition;
+    if (ReadFileInfo->FileDataSize > Length) {
+      //
+      // About to read beyond the EOF -- truncate it.
+      //
+      ReadFileInfo->FileDataSize = Length;
+    }
+
+    //
+    // Initialise data to start seeking and/or reading a file.
+    //
+    BytesLeft = ReadFileInfo->FileDataSize;
+    DataOffset = 0;
+    FilePosition = 0;
+    FinishedSeeking = FALSE;
+
+    break;
+  }
+
+  RecordingFlags = GET_FE_RECORDING_FLAGS (FileEntryData);
+  switch (RecordingFlags) {
+  case INLINE_DATA:
+    //
+    // There are no extents for this FE/EFE. All data is inline.
+    //
+    GetFileEntryData (FileEntryData, &Data, &Length);
+
+    if (ReadFileInfo->Flags == READ_FILE_GET_FILESIZE) {
+      ReadFileInfo->ReadLength = Length;
+    } else if (ReadFileInfo->Flags == READ_FILE_ALLOCATE_AND_READ) {
+      //
+      // Allocate buffer for starting read data.
+      //
+      ReadFileInfo->FileData = AllocatePool (Length);
+      if (ReadFileInfo->FileData == NULL) {
+        return EFI_OUT_OF_RESOURCES;
+      }
+
+      //
+      // Read all inline data into ReadFileInfo->FileData
+      //
+      CopyMem (ReadFileInfo->FileData, Data, Length);
+      ReadFileInfo->ReadLength = Length;
+    } else if (ReadFileInfo->Flags == READ_FILE_SEEK_AND_READ) {
+      //
+      // If FilePosition is non-zero, seek file to FilePosition, read
+      // FileDataSize bytes and then updates FilePosition.
+      //
+      CopyMem (
+        ReadFileInfo->FileData,
+        (VOID *)((UINT8 *)Data + ReadFileInfo->FilePosition),
+        ReadFileInfo->FileDataSize
+        );
+
+      ReadFileInfo->FilePosition += ReadFileInfo->FileDataSize;
+    }
+
+    break;
+  case LONG_ADS_SEQUENCE:
+  case SHORT_ADS_SEQUENCE:
+    //
+    // This FE/EFE contains a run of Allocation Descriptors. Get data + size
+    // for start reading them out.
+    //
+    GetAdsInformation (FileEntryData, &Data, &Length);
+    AdOffset = 0;
+
+    for (;;) {
+      //
+      // Read AD.
+      //
+      Status = GetAllocationDescriptor (
+        RecordingFlags,
+        Data,
+        &AdOffset,
+        Length,
+        &Ad
+        );
+      if (Status == EFI_DEVICE_ERROR) {
+        Status = EFI_SUCCESS;
+        goto Done;
+      }
+
+      //
+      // Check if AD is an indirect AD. If so, read Allocation Extent
+      // Descriptor and its extents (ADs).
+      //
+      if (GET_EXTENT_FLAGS (RecordingFlags, Ad) == EXTENT_IS_NEXT_EXTENT) {
+        if (!DoFreeAed) {
+          DoFreeAed = TRUE;
+        } else {
+          FreePool (Data);
+        }
+
+        Status = GetAedAdsData (
+          BlockIo,
+          DiskIo,
+          Volume,
+          ParentIcb,
+          RecordingFlags,
+          Ad,
+          &Data,
+          &Length
+          );
+        if (EFI_ERROR (Status)) {
+          goto Error_Get_Aed;
+        }
+
+        AdOffset = 0;
+        continue;
+      }
+
+      ExtentLength = GET_EXTENT_LENGTH (RecordingFlags, Ad);
+
+      Lsn = GetAllocationDescriptorLsn (RecordingFlags,
+                                        Volume,
+                                        ParentIcb,
+                                        Ad);
+
+      switch (ReadFileInfo->Flags) {
+      case READ_FILE_GET_FILESIZE:
+        ReadFileInfo->ReadLength += ExtentLength;
+        break;
+      case READ_FILE_ALLOCATE_AND_READ:
+        //
+        // Increase FileData (if necessary) to read next extent.
+        //
+        Status = GrowUpBufferToNextAd (
+          RecordingFlags,
+          Ad,
+          &ReadFileInfo->FileData,
+          ReadFileInfo->ReadLength
+          );
+        if (EFI_ERROR (Status)) {
+          goto Error_Alloc_Buffer_To_Next_Ad;
+        }
+
+        //
+        // Read extent's data into FileData.
+        //
+        Status = DiskIo->ReadDisk (
+          DiskIo,
+          BlockIo->Media->MediaId,
+          MultU64x32 (Lsn, LogicalBlockSize),
+          ExtentLength,
+          (VOID *)((UINT8 *)ReadFileInfo->FileData +
+                   ReadFileInfo->ReadLength)
+          );
+        if (EFI_ERROR (Status)) {
+          goto Error_Read_Disk_Blk;
+        }
+
+        ReadFileInfo->ReadLength += ExtentLength;
+        break;
+      case READ_FILE_SEEK_AND_READ:
+        //
+        // Seek file first before reading in its data.
+        //
+        if (FinishedSeeking) {
+          Offset = 0;
+          goto Skip_File_Seek;
+        }
+
+        if (FilePosition + ExtentLength < ReadFileInfo->FilePosition) {
+          FilePosition += ExtentLength;
+          goto Skip_Ad;
+        }
+
+        if (FilePosition + ExtentLength > ReadFileInfo->FilePosition) {
+          Offset = ReadFileInfo->FilePosition - FilePosition;
+          if (Offset < 0) {
+            Offset = -(Offset);
+          }
+        } else {
+          Offset = 0;
+        }
+
+        //
+        // Done with seeking file. Start reading its data.
+        //
+        FinishedSeeking = TRUE;
+
+      Skip_File_Seek:
+        //
+        // Make sure we don't read more data than really wanted.
+        //
+        if (ExtentLength - Offset > BytesLeft) {
+          DataLength = BytesLeft;
+        } else {
+          DataLength = ExtentLength - Offset;
+        }
+
+        //
+        // Read extent's data into FileData.
+        //
+        Status = DiskIo->ReadDisk (
+          DiskIo,
+          BlockIo->Media->MediaId,
+          Offset + MultU64x32 (Lsn, LogicalBlockSize),
+          DataLength,
+          (VOID *)((UINT8 *)ReadFileInfo->FileData +
+                   DataOffset)
+          );
+        if (EFI_ERROR (Status)) {
+          goto Error_Read_Disk_Blk;
+        }
+
+        //
+        // Update current file's position.
+        //
+        DataOffset += DataLength;
+        ReadFileInfo->FilePosition += DataLength;
+
+        BytesLeft -= DataLength;
+        if (BytesLeft == 0) {
+          //
+          // There is no more file data to read.
+          //
+          Status = EFI_SUCCESS;
+          goto Done;
+        }
+
+        break;
+      }
+
+    Skip_Ad:
+      //
+      // Point to the next AD (extent).
+      //
+      AdOffset += AD_LENGTH (RecordingFlags);
+    }
+
+    break;
+  case EXTENDED_ADS_SEQUENCE:
+     // FIXME: Not supported. Got no volume with it, yet.
+    ASSERT (FALSE);
+    Status = EFI_UNSUPPORTED;
+    break;
+  }
+
+Done:
+  if (DoFreeAed) {
+    FreePool (Data);
+  }
+
+  return Status;
+
+Error_Read_Disk_Blk:
+Error_Alloc_Buffer_To_Next_Ad:
+  if (ReadFileInfo->Flags != READ_FILE_SEEK_AND_READ) {
+    FreePool (ReadFileInfo->FileData);
+  }
+
+  if (DoFreeAed) {
+    FreePool (Data);
+  }
+
+Error_Get_Aed:
+  return Status;
+}
+
+//
+// Find a file by its filename from a given Parent file.
+//
+EFI_STATUS
+InternalFindFile (
+  IN   EFI_BLOCK_IO_PROTOCOL           *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL            *DiskIo,
+  IN   UDF_VOLUME_INFO                 *Volume,
+  IN   CHAR16                          *FileName,
+  IN   UDF_FILE_INFO                   *Parent,
+  IN   UDF_LONG_ALLOCATION_DESCRIPTOR  *Icb,
+  OUT  UDF_FILE_INFO                   *File
+  )
+{
+  EFI_STATUS                      Status;
+  UDF_FILE_IDENTIFIER_DESCRIPTOR  *FileIdentifierDesc;
+  UDF_READ_DIRECTORY_INFO         ReadDirInfo;
+  BOOLEAN                         Found;
+  CHAR16                          FoundFileName[UDF_FILENAME_LENGTH];
+  VOID                            *CompareFileEntry;
+
+  //
+  // Check if parent file is really directory.
+  //
+  if (!IS_FE_DIRECTORY (Parent->FileEntry)) {
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // If FileName is current file or working directory, just duplicate Parent's
+  // FE/EFE and FID descriptors.
+  //
+  if (StrCmp (FileName, L".") == 0) {
+    DuplicateFe (BlockIo, Volume, Parent->FileEntry, &File->FileEntry);
+    DuplicateFid (Parent->FileIdentifierDesc, &File->FileIdentifierDesc);
+
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Start directory listing.
+  //
+  ZeroMem ((VOID *)&ReadDirInfo, sizeof (UDF_READ_DIRECTORY_INFO));
+  Found = FALSE;
+
+  for (;;) {
+    Status = ReadDirectoryEntry (
+      BlockIo,
+      DiskIo,
+      Volume,
+      Parent->FileIdentifierDesc ?
+      &Parent->FileIdentifierDesc->Icb :
+      Icb,
+      Parent->FileEntry,
+      &ReadDirInfo,
+      &FileIdentifierDesc
+      );
+    if (EFI_ERROR (Status)) {
+      if (Status == EFI_DEVICE_ERROR) {
+        Status = EFI_NOT_FOUND;
+      }
+
+      break;
+    }
+
+    if (IS_FID_PARENT_FILE (FileIdentifierDesc)) {
+      //
+      // This FID contains the location (FE/EFE) of the parent directory of this
+      // directory (Parent), and if FileName is either ".." or "\\", then it's
+      // the expected FID.
+      //
+      if (StrCmp (FileName, L"..") == 0 || StrCmp (FileName, L"\\") == 0) {
+        Found = TRUE;
+        break;
+      }
+    } else {
+      Status = GetFileNameFromFid (FileIdentifierDesc, FoundFileName);
+      if (EFI_ERROR (Status)) {
+        break;
+      }
+
+      if (StrCmp (FileName, FoundFileName) == 0) {
+        //
+        // FID has been found. Prepare to find its respective FE/EFE.
+        //
+        Found = TRUE;
+        break;
+      }
+    }
+
+    FreePool ((VOID *)FileIdentifierDesc);
+  }
+
+  if (ReadDirInfo.DirectoryData != NULL) {
+    //
+    // Free all allocated resources for the directory listing.
+    //
+    FreePool (ReadDirInfo.DirectoryData);
+  }
+
+  if (Found) {
+    Status = EFI_SUCCESS;
+
+    File->FileIdentifierDesc = FileIdentifierDesc;
+
+    //
+    // If the requested file is root directory, then the FE/EFE was already
+    // retrieved in UdfOpenVolume() function, thus no need to find it again.
+    //
+    // Otherwise, find FE/EFE from the respective FID.
+    //
+    if (StrCmp (FileName, L"\\") != 0) {
+      Status = FindFileEntry (
+        BlockIo,
+        DiskIo,
+        Volume,
+        &FileIdentifierDesc->Icb,
+        &CompareFileEntry
+        );
+      if (EFI_ERROR (Status)) {
+        goto Error_Find_Fe;
+      }
+
+      //
+      // Make sure that both Parent's FE/EFE and found FE/EFE are not equal.
+      //
+      if (CompareMem ((VOID *)Parent->FileEntry, (VOID *)CompareFileEntry,
+                      Volume->FileEntrySize) != 0) {
+        File->FileEntry = CompareFileEntry;
+      } else {
+        FreePool ((VOID *)FileIdentifierDesc);
+        FreePool ((VOID *)CompareFileEntry);
+        Status = EFI_NOT_FOUND;
+      }
+    }
+  }
+
+  return Status;
+
+Error_Find_Fe:
+  FreePool ((VOID *)FileIdentifierDesc);
+
+  return Status;
+}
+
+/**
+  Read volume information on a medium which contains a valid UDF file system.
+
+  @param[in]   BlockIo  BlockIo interface.
+  @param[in]   DiskIo   DiskIo interface.
+  @param[out]  Volume   UDF volume information structure.
+
+  @retval EFI_SUCCESS          Volume information read.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The volume was not read due to lack of resources.
+
+**/
+EFI_STATUS
+ReadUdfVolumeInformation (
+  IN   EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL   *DiskIo,
+  OUT  UDF_VOLUME_INFO        *Volume
+  )
+{
+  EFI_STATUS Status;
+
+  Status = ReadVolumeFileStructure (
+    BlockIo,
+    DiskIo,
+    Volume
+    );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = GetFileSetDescriptors (
+    BlockIo,
+    DiskIo,
+    Volume
+    );
+  if (EFI_ERROR (Status)) {
+    CleanupVolumeInformation (Volume);
+  }
+
+  return Status;
+}
+
+/**
+  Find the root directory on an UDF volume.
+
+  @param[in]   BlockIo  BlockIo interface.
+  @param[in]   DiskIo   DiskIo interface.
+  @param[in]   Volume   UDF volume information structure.
+  @param[out]  File     Root directory file.
+
+  @retval EFI_SUCCESS          Root directory found.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The root directory was not found due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+FindRootDirectory (
+  IN   EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL   *DiskIo,
+  IN   UDF_VOLUME_INFO        *Volume,
+  OUT  UDF_FILE_INFO          *File
+  )
+{
+  EFI_STATUS     Status;
+  UDF_FILE_INFO  Parent;
+
+  Status = FindFileEntry (
+    BlockIo,
+    DiskIo,
+    Volume,
+    &Volume->FileSetDescs[0]->RootDirectoryIcb,
+    &File->FileEntry
+    );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Parent.FileEntry = File->FileEntry;
+  Parent.FileIdentifierDesc = NULL;
+
+  Status = FindFile (
+    BlockIo,
+    DiskIo,
+    Volume,
+    L"\\",
+    NULL,
+    &Parent,
+    &Volume->FileSetDescs[0]->RootDirectoryIcb,
+    File
+    );
+  if (EFI_ERROR (Status)) {
+    FreePool (File->FileEntry);
+  }
+
+  return Status;
+}
+
+/**
+  Find either a File Entry or a Extended File Entry from a given ICB.
+
+  @param[in]   BlockIo    BlockIo interface.
+  @param[in]   DiskIo     DiskIo interface.
+  @param[in]   Volume     UDF volume information structure.
+  @param[in]   Icb        ICB of the FID.
+  @param[out]  FileEntry  File Entry or Extended File Entry.
+
+  @retval EFI_SUCCESS          File Entry or Extended File Entry found.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The FE/EFE entry was not found due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+FindFileEntry (
+  IN   EFI_BLOCK_IO_PROTOCOL           *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL            *DiskIo,
+  IN   UDF_VOLUME_INFO                 *Volume,
+  IN   UDF_LONG_ALLOCATION_DESCRIPTOR  *Icb,
+  OUT  VOID                            **FileEntry
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Lsn;
+  UINT32      LogicalBlockSize;
+
+  Lsn               = GetLongAdLsn (Volume, Icb);
+  LogicalBlockSize  = LV_BLOCK_SIZE (Volume, UDF_DEFAULT_LV_NUM);
+
+  *FileEntry = AllocateZeroPool (Volume->FileEntrySize);
+  if (*FileEntry == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Read extent.
+  //
+  Status = DiskIo->ReadDisk (
+    DiskIo,
+    BlockIo->Media->MediaId,
+    MultU64x32 (Lsn, LogicalBlockSize),
+    Volume->FileEntrySize,
+    *FileEntry
+    );
+  if (EFI_ERROR (Status)) {
+    goto Error_Read_Disk_Blk;
+  }
+
+  //
+  // Check if the read extent contains a valid Tag Identifier for the expected
+  // FE/EFE.
+  //
+  if (!IS_FE (*FileEntry) && !IS_EFE (*FileEntry)) {
+    Status = EFI_VOLUME_CORRUPTED;
+    goto Error_Invalid_Fe;
+  }
+
+  return EFI_SUCCESS;
+
+Error_Invalid_Fe:
+Error_Read_Disk_Blk:
+  FreePool (*FileEntry);
+
+  return Status;
+}
+
+/**
+  Find a file given its absolute path on an UDF volume.
+
+  @param[in]   BlockIo   BlockIo interface.
+  @param[in]   DiskIo    DiskIo interface.
+  @param[in]   Volume    UDF volume information structure.
+  @param[in]   FilePath  File's absolute path.
+  @param[in]   Root      Root directory file.
+  @param[in]   Parent    Parent directory file.
+  @param[out]  File      Found file.
+
+  @retval EFI_SUCCESS          @p FilePath was found.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The @p FilePath file was not found due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+FindFile (
+  IN   EFI_BLOCK_IO_PROTOCOL           *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL            *DiskIo,
+  IN   UDF_VOLUME_INFO                 *Volume,
+  IN   CHAR16                          *FilePath,
+  IN   UDF_FILE_INFO                   *Root,
+  IN   UDF_FILE_INFO                   *Parent,
+  IN   UDF_LONG_ALLOCATION_DESCRIPTOR  *Icb,
+  OUT  UDF_FILE_INFO                   *File
+  )
+{
+  EFI_STATUS     Status;
+  CHAR16         FileName[UDF_FILENAME_LENGTH];
+  CHAR16         *FileNamePointer;
+  UDF_FILE_INFO  PreviousFile;
+  VOID           *FileEntry;
+
+  Status = EFI_NOT_FOUND;
+
+  CopyMem ((VOID *)&PreviousFile, (VOID *)Parent, sizeof (UDF_FILE_INFO));
+  while (*FilePath != L'\0') {
+    FileNamePointer = FileName;
+    while (*FilePath != L'\0' && *FilePath != L'\\') {
+      *FileNamePointer++ = *FilePath++;
+    }
+
+    *FileNamePointer = L'\0';
+    if (FileName[0] == L'\0') {
+      //
+      // Open root directory.
+      //
+      if (Root == NULL) {
+        //
+        // There is no file found for the root directory yet. So, find only its
+        // FID by now.
+        //
+        // See UdfOpenVolume() function.
+        //
+        Status = InternalFindFile (BlockIo,
+                                   DiskIo,
+                                   Volume,
+                                   L"\\",
+                                   &PreviousFile,
+                                   Icb,
+                                   File);
+      } else {
+        //
+        // We've already a file pointer (Root) for the root directory. Duplicate
+        // its FE/EFE and FID descriptors.
+        //
+        DuplicateFe (BlockIo, Volume, Root->FileEntry, &File->FileEntry);
+        DuplicateFid (Root->FileIdentifierDesc, &File->FileIdentifierDesc);
+        Status = EFI_SUCCESS;
+      }
+    } else {
+      //
+      // No root directory. Find filename from the current directory.
+      //
+      Status = InternalFindFile (BlockIo,
+                                 DiskIo,
+                                 Volume,
+                                 FileName,
+                                 &PreviousFile,
+                                 Icb,
+                                 File);
+    }
+
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    //
+    // If the found file is a symlink, then find its respective FE/EFE and
+    // FID descriptors.
+    //
+    if (IS_FE_SYMLINK (File->FileEntry)) {
+      FreePool ((VOID *)File->FileIdentifierDesc);
+
+      FileEntry = File->FileEntry;
+
+      Status = ResolveSymlink (BlockIo,
+                               DiskIo,
+                               Volume,
+                               &PreviousFile,
+                               FileEntry,
+                               File);
+
+      FreePool (FileEntry);
+
+      if (EFI_ERROR (Status)) {
+        return Status;
+      }
+    }
+
+    if (CompareMem ((VOID *)&PreviousFile, (VOID *)Parent,
+                    sizeof (UDF_FILE_INFO)) != 0) {
+      CleanupFileInformation (&PreviousFile);
+    }
+
+    CopyMem ((VOID *)&PreviousFile, (VOID *)File, sizeof (UDF_FILE_INFO));
+    if (*FilePath != L'\0' && *FilePath == L'\\') {
+      FilePath++;
+    }
+  }
+
+  return Status;
+}
+
+/**
+  Read a directory entry at a time on an UDF volume.
+
+  @param[in]      BlockIo        BlockIo interface.
+  @param[in]      DiskIo         DiskIo interface.
+  @param[in]      Volume         UDF volume information structure.
+  @param[in]      ParentIcb      ICB of the parent file.
+  @param[in]      FileEntryData  FE/EFE of the parent file.
+  @param[in out]  ReadDirInfo    Next read directory listing structure
+                                 information.
+  @param[out]     FoundFid       File Identifier Descriptor pointer.
+
+  @retval EFI_SUCCESS          Directory entry read.
+  @retval EFI_UNSUPPORTED      Extended Allocation Descriptors not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The directory entry was not read due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+ReadDirectoryEntry (
+  IN      EFI_BLOCK_IO_PROTOCOL           *BlockIo,
+  IN      EFI_DISK_IO_PROTOCOL            *DiskIo,
+  IN      UDF_VOLUME_INFO                 *Volume,
+  IN      UDF_LONG_ALLOCATION_DESCRIPTOR  *ParentIcb,
+  IN      VOID                            *FileEntryData,
+  IN OUT  UDF_READ_DIRECTORY_INFO         *ReadDirInfo,
+  OUT     UDF_FILE_IDENTIFIER_DESCRIPTOR  **FoundFid
+  )
+{
+  EFI_STATUS                      Status;
+  UDF_READ_FILE_INFO              ReadFileInfo;
+  UDF_FILE_IDENTIFIER_DESCRIPTOR  *FileIdentifierDesc;
+
+  if (ReadDirInfo->DirectoryData == NULL) {
+    //
+    // The directory's recorded data has not been read yet. So let's cache it
+    // into memory and the next calls won't need to read it again.
+    //
+    ReadFileInfo.Flags = READ_FILE_ALLOCATE_AND_READ;
+
+    Status = ReadFile (
+      BlockIo,
+      DiskIo,
+      Volume,
+      ParentIcb,
+      FileEntryData,
+      &ReadFileInfo
+      );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    //
+    // Fill in ReadDirInfo structure with the read directory's data information.
+    //
+    ReadDirInfo->DirectoryData = ReadFileInfo.FileData;
+    ReadDirInfo->DirectoryLength = ReadFileInfo.ReadLength;
+  }
+
+  do {
+    if (ReadDirInfo->FidOffset >= ReadDirInfo->DirectoryLength) {
+      //
+      // There are no longer FIDs for this directory. By returning
+      // EFI_DEVICE_ERROR to the callee will indicate end of directory
+      // listening.
+      //
+      return EFI_DEVICE_ERROR;
+    }
+
+    //
+    // Get FID for this entry.
+    //
+    FileIdentifierDesc = GET_FID_FROM_ADS (ReadDirInfo->DirectoryData,
+                                           ReadDirInfo->FidOffset);
+    //
+    // Update FidOffset to point to next FID.
+    //
+    ReadDirInfo->FidOffset += GetFidDescriptorLength (FileIdentifierDesc);
+  } while (IS_FID_DELETED_FILE (FileIdentifierDesc));
+
+  DuplicateFid (FileIdentifierDesc, FoundFid);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Get a filename (encoded in OSTA-compressed format) from a File Identifier
+  Descriptor on an UDF volume.
+
+  @param[in]   FileIdentifierDesc  File Identifier Descriptor pointer.
+  @param[out]  FileName            Decoded filename.
+
+  @retval EFI_SUCCESS           Filename decoded and read.
+  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
+**/
+EFI_STATUS
+GetFileNameFromFid (
+  IN   UDF_FILE_IDENTIFIER_DESCRIPTOR  *FileIdentifierDesc,
+  OUT  CHAR16                          *FileName
+  )
+{
+  UINT8 *OstaCompressed;
+  UINT8 CompressionId;
+  UINT8 Length;
+  UINTN Index;
+
+  OstaCompressed =
+    (UINT8 *)(
+      (UINT8 *)FileIdentifierDesc->Data +
+      FileIdentifierDesc->LengthOfImplementationUse
+      );
+
+  CompressionId = OstaCompressed[0];
+  if (!IS_VALID_COMPRESSION_ID (CompressionId)) {
+    return EFI_VOLUME_CORRUPTED;
+  }
+
+  //
+  // Decode filename.
+  //
+  Length = FileIdentifierDesc->LengthOfFileIdentifier;
+  for (Index = 1; Index < Length; Index++) {
+    if (CompressionId == 16) {
+      *FileName = OstaCompressed[Index++] << 8;
+    } else {
+      *FileName = 0;
+    }
+
+    if (Index < Length) {
+      *FileName |= OstaCompressed[Index];
+    }
+
+    FileName++;
+  }
+
+  *FileName = L'\0';
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Resolve a symlink file on an UDF volume.
+
+  @param[in]   BlockIo        BlockIo interface.
+  @param[in]   DiskIo         DiskIo interface.
+  @param[in]   Volume         UDF volume information structure.
+  @param[in]   Parent         Parent file.
+  @param[in]   FileEntryData  FE/EFE structure pointer.
+  @param[out]  File           Resolved file.
+
+  @retval EFI_SUCCESS          Symlink file resolved.
+  @retval EFI_UNSUPPORTED      Extended Allocation Descriptors not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The symlink file was not resolved due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+ResolveSymlink (
+  IN   EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL   *DiskIo,
+  IN   UDF_VOLUME_INFO        *Volume,
+  IN   UDF_FILE_INFO          *Parent,
+  IN   VOID                   *FileEntryData,
+  OUT  UDF_FILE_INFO          *File
+  )
+{
+  EFI_STATUS          Status;
+  UDF_READ_FILE_INFO  ReadFileInfo;
+  UINT8               *Data;
+  UINT64              Length;
+  UINT8               *EndData;
+  UDF_PATH_COMPONENT  *PathComp;
+  UINT8               PathCompLength;
+  CHAR16              FileName[UDF_FILENAME_LENGTH];
+  CHAR16              *C;
+  UINTN               Index;
+  UINT8               CompressionId;
+  UDF_FILE_INFO       PreviousFile;
+
+  //
+  // Symlink files on UDF volumes do not contain so much data other than
+  // Path Components which resolves to real filenames, so it's OK to read in
+  // all its data here -- usually the data will be inline with the FE/EFE for
+  // lower filenames.
+  //
+  ReadFileInfo.Flags = READ_FILE_ALLOCATE_AND_READ;
+
+  Status = ReadFile (
+    BlockIo,
+    DiskIo,
+    Volume,
+    &Parent->FileIdentifierDesc->Icb,
+    FileEntryData,
+    &ReadFileInfo
+    );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Length = ReadFileInfo.ReadLength;
+
+  Data = (UINT8 *)ReadFileInfo.FileData;
+  EndData = Data + Length;
+
+  CopyMem ((VOID *)&PreviousFile, (VOID *)Parent, sizeof (UDF_FILE_INFO));
+
+  for (;;) {
+    PathComp = (UDF_PATH_COMPONENT *)Data;
+
+    PathCompLength = PathComp->LengthOfComponentIdentifier;
+
+    switch (PathComp->ComponentType) {
+    case 1:
+      //
+      // This Path Component specifies the root directory hierarchy subject to
+      // agreement between the originator and recipient of the medium. Skip it.
+      //
+      // Fall through.
+      //
+    case 2:
+      //
+      // "\\." of the current directory. Read next Path Component.
+      //
+      goto Next_Path_Component;
+    case 3:
+      //
+      // ".." (parent directory). Go to it.
+      //
+      CopyMem ((VOID *)FileName, L"..", 6);
+      break;
+    case 4:
+      //
+      // "." (current file). Duplicate both FE/EFE and FID of this file.
+      //
+      DuplicateFe (BlockIo, Volume, PreviousFile.FileEntry, &File->FileEntry);
+      DuplicateFid (PreviousFile.FileIdentifierDesc,
+                    &File->FileIdentifierDesc);
+      goto Next_Path_Component;
+    case 5:
+      //
+      // This Path Component identifies an object, either a file or a
+      // directory or an alias.
+      //
+      // Decode it from the compressed data in ComponentIdentifier and find
+      // respective path.
+      //
+      CompressionId = PathComp->ComponentIdentifier[0];
+      if (!IS_VALID_COMPRESSION_ID (CompressionId)) {
+        return EFI_VOLUME_CORRUPTED;
+      }
+
+      C = FileName;
+      for (Index = 1; Index < PathCompLength; Index++) {
+        if (CompressionId == 16) {
+          *C = *(UINT8 *)((UINT8 *)PathComp->ComponentIdentifier +
+                          Index) << 8;
+          Index++;
+        } else {
+          *C = 0;
+        }
+
+        if (Index < Length) {
+          *C |= *(UINT8 *)((UINT8 *)PathComp->ComponentIdentifier + Index);
+        }
+
+        C++;
+      }
+
+      *C = L'\0';
+      break;
+    }
+
+    //
+    // Find file from the read filename in symlink's file data.
+    //
+    Status = InternalFindFile (
+      BlockIo,
+      DiskIo,
+      Volume,
+      FileName,
+      &PreviousFile,
+      NULL,
+      File
+      );
+    if (EFI_ERROR (Status)) {
+      goto Error_Find_File;
+    }
+
+  Next_Path_Component:
+    Data += sizeof (UDF_PATH_COMPONENT) + PathCompLength;
+    if (Data >= EndData) {
+      break;
+    }
+
+    if (CompareMem ((VOID *)&PreviousFile, (VOID *)Parent,
+                    sizeof (UDF_FILE_INFO)) != 0) {
+      CleanupFileInformation (&PreviousFile);
+    }
+
+    CopyMem ((VOID *)&PreviousFile, (VOID *)File, sizeof (UDF_FILE_INFO));
+  }
+
+  //
+  // Unmap the symlink file.
+  //
+  FreePool (ReadFileInfo.FileData);
+
+  return EFI_SUCCESS;
+
+Error_Find_File:
+  if (CompareMem ((VOID *)&PreviousFile, (VOID *)Parent,
+                  sizeof (UDF_FILE_INFO)) != 0) {
+    CleanupFileInformation (&PreviousFile);
+  }
+
+  FreePool (ReadFileInfo.FileData);
+
+  return Status;
+}
+
+/**
+  Clean up in-memory UDF volume information.
+
+  @param[in] Volume Volume information pointer.
+
+**/
+VOID
+CleanupVolumeInformation (
+  IN UDF_VOLUME_INFO *Volume
+  )
+{
+  UINTN Index;
+
+  if (Volume->LogicalVolDescs != NULL) {
+    for (Index = 0; Index < Volume->LogicalVolDescsNo; Index++) {
+      FreePool ((VOID *)Volume->LogicalVolDescs[Index]);
+    }
+    FreePool ((VOID *)Volume->LogicalVolDescs);
+  }
+
+  if (Volume->PartitionDescs != NULL) {
+    for (Index = 0; Index < Volume->PartitionDescsNo; Index++) {
+      FreePool ((VOID *)Volume->PartitionDescs[Index]);
+    }
+    FreePool ((VOID *)Volume->PartitionDescs);
+  }
+
+  if (Volume->FileSetDescs != NULL) {
+    for (Index = 0; Index < Volume->FileSetDescsNo; Index++) {
+      FreePool ((VOID *)Volume->FileSetDescs[Index]);
+    }
+    FreePool ((VOID *)Volume->FileSetDescs);
+  }
+
+  ZeroMem ((VOID *)Volume, sizeof (UDF_VOLUME_INFO));
+}
+
+/**
+  Clean up in-memory UDF file information.
+
+  @param[in] File File information pointer.
+
+**/
+VOID
+CleanupFileInformation (
+  IN UDF_FILE_INFO *File
+  )
+{
+  if (File->FileEntry != NULL) {
+    FreePool (File->FileEntry);
+  }
+  if (File->FileIdentifierDesc != NULL) {
+    FreePool ((VOID *)File->FileIdentifierDesc);
+  }
+
+  ZeroMem ((VOID *)File, sizeof (UDF_FILE_INFO));
+}
+
+/**
+  Find a file from its absolute path on an UDF volume.
+
+  @param[in]   BlockIo  BlockIo interface.
+  @param[in]   DiskIo   DiskIo interface.
+  @param[in]   Volume   UDF volume information structure.
+  @param[in]   File     File information structure.
+  @param[out]  Size     Size of the file.
+
+  @retval EFI_SUCCESS          File size calculated and set in @p Size.
+  @retval EFI_UNSUPPORTED      Extended Allocation Descriptors not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The file size was not calculated due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+GetFileSize (
+  IN   EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL   *DiskIo,
+  IN   UDF_VOLUME_INFO        *Volume,
+  IN   UDF_FILE_INFO          *File,
+  OUT  UINT64                 *Size
+  )
+{
+  EFI_STATUS          Status;
+  UDF_READ_FILE_INFO  ReadFileInfo;
+
+  ReadFileInfo.Flags = READ_FILE_GET_FILESIZE;
+
+  Status = ReadFile (
+    BlockIo,
+    DiskIo,
+    Volume,
+    &File->FileIdentifierDesc->Icb,
+    File->FileEntry,
+    &ReadFileInfo
+    );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  *Size = ReadFileInfo.ReadLength;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Set information about a file on an UDF volume.
+
+  @param[in]      File        File pointer.
+  @param[in]      FileSize    Size of the file.
+  @param[in]      FileName    Filename of the file.
+  @param[in out]  BufferSize  Size of the returned file infomation.
+  @param[out]     Buffer      Data of the returned file information.
+
+  @retval EFI_SUCCESS          File information set.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The file information was not set due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+SetFileInfo (
+  IN      UDF_FILE_INFO  *File,
+  IN      UINT64         FileSize,
+  IN      CHAR16         *FileName,
+  IN OUT  UINTN          *BufferSize,
+  OUT     VOID           *Buffer
+  )
+{
+  UINTN                    FileInfoLength;
+  EFI_FILE_INFO            *FileInfo;
+  UDF_FILE_ENTRY           *FileEntry;
+  UDF_EXTENDED_FILE_ENTRY  *ExtendedFileEntry;
+
+  //
+  // Calculate the needed size for the EFI_FILE_INFO structure.
+  //
+  FileInfoLength = sizeof (EFI_FILE_INFO) + (FileName ?
+                                             StrSize (FileName) :
+                                             sizeof (CHAR16));
+  if (*BufferSize < FileInfoLength) {
+    //
+    // The given Buffer has no size enough for EFI_FILE_INFO structure.
+    //
+    *BufferSize = FileInfoLength;
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  //
+  // Buffer now contains room enough to store EFI_FILE_INFO structure.
+  // Now, fill it in with all necessary information about the file.
+  //
+  FileInfo = (EFI_FILE_INFO *)Buffer;
+  FileInfo->Size         = FileInfoLength;
+  FileInfo->Attribute    &= ~EFI_FILE_VALID_ATTR;
+  FileInfo->Attribute    |= EFI_FILE_READ_ONLY;
+
+  if (IS_FID_DIRECTORY_FILE (File->FileIdentifierDesc)) {
+    FileInfo->Attribute |= EFI_FILE_DIRECTORY;
+  } else if (IS_FID_NORMAL_FILE (File->FileIdentifierDesc)) {
+    FileInfo->Attribute |= EFI_FILE_ARCHIVE;
+  }
+
+  if (IS_FID_HIDDEN_FILE (File->FileIdentifierDesc)) {
+    FileInfo->Attribute |= EFI_FILE_HIDDEN;
+  }
+
+  if (IS_FE (File->FileEntry)) {
+    FileEntry = (UDF_FILE_ENTRY *)File->FileEntry;
+
+    //
+    // Check if FE has the system attribute set.
+    //
+    if (FileEntry->IcbTag.Flags & (1 << 10)) {
+      FileInfo->Attribute |= EFI_FILE_SYSTEM;
+    }
+
+    FileInfo->FileSize      = FileSize;
+    FileInfo->PhysicalSize  = FileSize;
+
+    FileInfo->CreateTime.Year        = FileEntry->AccessTime.Year;
+    FileInfo->CreateTime.Month       = FileEntry->AccessTime.Month;
+    FileInfo->CreateTime.Day         = FileEntry->AccessTime.Day;
+    FileInfo->CreateTime.Hour        = FileEntry->AccessTime.Hour;
+    FileInfo->CreateTime.Minute      = FileEntry->AccessTime.Second;
+    FileInfo->CreateTime.Second      = FileEntry->AccessTime.Second;
+    FileInfo->CreateTime.Nanosecond  =
+                                   FileEntry->AccessTime.HundredsOfMicroseconds;
+
+    FileInfo->LastAccessTime.Year        =
+                                   FileEntry->AccessTime.Year;
+    FileInfo->LastAccessTime.Month       =
+                                   FileEntry->AccessTime.Month;
+    FileInfo->LastAccessTime.Day         =
+                                   FileEntry->AccessTime.Day;
+    FileInfo->LastAccessTime.Hour        =
+                                   FileEntry->AccessTime.Hour;
+    FileInfo->LastAccessTime.Minute      =
+                                   FileEntry->AccessTime.Minute;
+    FileInfo->LastAccessTime.Second      =
+                                   FileEntry->AccessTime.Second;
+    FileInfo->LastAccessTime.Nanosecond  =
+                                   FileEntry->AccessTime.HundredsOfMicroseconds;
+  } else if (IS_EFE (File->FileEntry)) {
+    ExtendedFileEntry = (UDF_EXTENDED_FILE_ENTRY *)File->FileEntry;
+
+    //
+    // Check if EFE has the system attribute set.
+    //
+    if (ExtendedFileEntry->IcbTag.Flags & (1 << 10)) {
+      FileInfo->Attribute |= EFI_FILE_SYSTEM;
+    }
+
+    FileInfo->FileSize      = FileSize;
+    FileInfo->PhysicalSize  = FileSize;
+
+    FileInfo->CreateTime.Year        = ExtendedFileEntry->CreationTime.Year;
+    FileInfo->CreateTime.Month       = ExtendedFileEntry->CreationTime.Month;
+    FileInfo->CreateTime.Day         = ExtendedFileEntry->CreationTime.Day;
+    FileInfo->CreateTime.Hour        = ExtendedFileEntry->CreationTime.Hour;
+    FileInfo->CreateTime.Minute      = ExtendedFileEntry->CreationTime.Second;
+    FileInfo->CreateTime.Second      = ExtendedFileEntry->CreationTime.Second;
+    FileInfo->CreateTime.Nanosecond  =
+                           ExtendedFileEntry->AccessTime.HundredsOfMicroseconds;
+
+    FileInfo->LastAccessTime.Year        =
+                           ExtendedFileEntry->AccessTime.Year;
+    FileInfo->LastAccessTime.Month       =
+                           ExtendedFileEntry->AccessTime.Month;
+    FileInfo->LastAccessTime.Day         =
+                           ExtendedFileEntry->AccessTime.Day;
+    FileInfo->LastAccessTime.Hour        =
+                           ExtendedFileEntry->AccessTime.Hour;
+    FileInfo->LastAccessTime.Minute      =
+                           ExtendedFileEntry->AccessTime.Minute;
+    FileInfo->LastAccessTime.Second      =
+                           ExtendedFileEntry->AccessTime.Second;
+    FileInfo->LastAccessTime.Nanosecond  =
+                           ExtendedFileEntry->AccessTime.HundredsOfMicroseconds;
+  }
+
+  FileInfo->CreateTime.TimeZone      = EFI_UNSPECIFIED_TIMEZONE;
+  FileInfo->CreateTime.Daylight      = EFI_TIME_ADJUST_DAYLIGHT;
+  FileInfo->LastAccessTime.TimeZone  = EFI_UNSPECIFIED_TIMEZONE;
+  FileInfo->LastAccessTime.Daylight  = EFI_TIME_ADJUST_DAYLIGHT;
+
+  CopyMem ((VOID *)&FileInfo->ModificationTime,
+           (VOID *)&FileInfo->LastAccessTime,
+           sizeof (EFI_TIME));
+
+  if (FileName != NULL) {
+    StrCpyS (FileInfo->FileName, StrLen (FileName) + 1, FileName);
+  } else {
+    FileInfo->FileName[0] = '\0';
+  }
+
+  *BufferSize = FileInfoLength;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Get volume and free space size information of an UDF volume.
+
+  @param[in]   BlockIo        BlockIo interface.
+  @param[in]   DiskIo         DiskIo interface.
+  @param[in]   Volume         UDF volume information structure.
+  @param[out]  VolumeSize     Volume size.
+  @param[out]  FreeSpaceSize  Free space size.
+
+  @retval EFI_SUCCESS          Volume and free space size calculated.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The volume and free space size were not
+                               calculated due to lack of resources.
+
+**/
+EFI_STATUS
+GetVolumeSize (
+  IN   EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL   *DiskIo,
+  IN   UDF_VOLUME_INFO        *Volume,
+  OUT  UINT64                 *VolumeSize,
+  OUT  UINT64                 *FreeSpaceSize
+  )
+{
+  UDF_EXTENT_AD                 ExtentAd;
+  UINT32                        LogicalBlockSize;
+  UINT64                        Lsn;
+  EFI_STATUS                    Status;
+  UDF_LOGICAL_VOLUME_INTEGRITY  *LogicalVolInt;
+  UINTN                         Index;
+  UINTN                         Length;
+  UINT32                        LsnsNo;
+
+  *VolumeSize     = 0;
+  *FreeSpaceSize  = 0;
+
+  for (Index = 0; Index < Volume->LogicalVolDescsNo; Index++) {
+    CopyMem ((VOID *)&ExtentAd,
+             (VOID *)&Volume->LogicalVolDescs[Index]->IntegritySequenceExtent,
+             sizeof (UDF_EXTENT_AD));
+    if (ExtentAd.ExtentLength == 0) {
+      continue;
+    }
+
+    LogicalBlockSize = LV_BLOCK_SIZE (Volume, Index);
+
+  Read_Next_Sequence:
+    LogicalVolInt = (UDF_LOGICAL_VOLUME_INTEGRITY *)
+      AllocatePool (ExtentAd.ExtentLength);
+    if (LogicalVolInt == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    Lsn = (UINT64)ExtentAd.ExtentLocation;
+
+    Status = DiskIo->ReadDisk (
+      DiskIo,
+      BlockIo->Media->MediaId,
+      MultU64x32 (Lsn, LogicalBlockSize),
+      ExtentAd.ExtentLength,
+      (VOID *)LogicalVolInt
+      );
+    if (EFI_ERROR (Status)) {
+      FreePool ((VOID *)LogicalVolInt);
+      return Status;
+    }
+
+    if (!IS_LVID (LogicalVolInt)) {
+      FreePool ((VOID *)LogicalVolInt);
+      return EFI_VOLUME_CORRUPTED;
+    }
+
+    Length = LogicalVolInt->NumberOfPartitions;
+    for (Index = 0; Index < Length; Index += sizeof (UINT32)) {
+      LsnsNo = *(UINT32 *)((UINT8 *)LogicalVolInt->Data + Index);
+      if (LsnsNo == 0xFFFFFFFFUL) {
+        //
+        // Size not specified.
+        //
+        continue;
+      }
+
+      *FreeSpaceSize += MultU64x32 ((UINT64)LsnsNo, LogicalBlockSize);
+    }
+
+    Length = (LogicalVolInt->NumberOfPartitions * sizeof (UINT32)) << 1;
+    for (; Index < Length; Index += sizeof (UINT32)) {
+      LsnsNo = *(UINT32 *)((UINT8 *)LogicalVolInt->Data + Index);
+      if (LsnsNo == 0xFFFFFFFFUL) {
+        //
+        // Size not specified.
+        //
+        continue;
+      }
+
+      *VolumeSize += MultU64x32 ((UINT64)LsnsNo, LogicalBlockSize);
+    }
+
+    CopyMem ((VOID *)&ExtentAd,(VOID *)&LogicalVolInt->NextIntegrityExtent,
+             sizeof (UDF_EXTENT_AD));
+    if (ExtentAd.ExtentLength > 0) {
+      FreePool ((VOID *)LogicalVolInt);
+      goto Read_Next_Sequence;
+    }
+
+    FreePool ((VOID *)LogicalVolInt);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Seek a file and read its data into memory on an UDF volume.
+
+  @param[in]      BlockIo       BlockIo interface.
+  @param[in]      DiskIo        DiskIo interface.
+  @param[in]      Volume        UDF volume information structure.
+  @param[in]      File          File information structure.
+  @param[in]      FileSize      Size of the file.
+  @param[in out]  FilePosition  File position.
+  @param[in out]  Buffer        File data.
+  @param[in out]  BufferSize    Read size.
+
+  @retval EFI_SUCCESS          File seeked and read.
+  @retval EFI_UNSUPPORTED      Extended Allocation Descriptors not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The file's recorded data was not read due to lack
+                               of resources.
+
+**/
+EFI_STATUS
+ReadFileData (
+  IN      EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN      EFI_DISK_IO_PROTOCOL   *DiskIo,
+  IN      UDF_VOLUME_INFO        *Volume,
+  IN      UDF_FILE_INFO          *File,
+  IN      UINT64                 FileSize,
+  IN OUT  UINT64                 *FilePosition,
+  IN OUT  VOID                   *Buffer,
+  IN OUT  UINT64                 *BufferSize
+  )
+{
+  EFI_STATUS          Status;
+  UDF_READ_FILE_INFO  ReadFileInfo;
+
+  ReadFileInfo.Flags         = READ_FILE_SEEK_AND_READ;
+  ReadFileInfo.FilePosition  = *FilePosition;
+  ReadFileInfo.FileData      = Buffer;
+  ReadFileInfo.FileDataSize  = *BufferSize;
+  ReadFileInfo.FileSize      = FileSize;
+
+  Status = ReadFile (
+                 BlockIo,
+                 DiskIo,
+                 Volume,
+                 &File->FileIdentifierDesc->Icb,
+                 File->FileEntry,
+                 &ReadFileInfo
+                 );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  *BufferSize    = ReadFileInfo.FileDataSize;
+  *FilePosition  = ReadFileInfo.FilePosition;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Check if ControllerHandle supports an UDF file system.
+
+  @param[in]  This                Protocol instance pointer.
+  @param[in]  ControllerHandle    Handle of device to test.
+
+  @retval EFI_SUCCESS             UDF file system found.
+  @retval EFI_UNSUPPORTED         UDF file system not found.
+
+**/
+EFI_STATUS
+SupportUdfFileSystem (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle
+  )
+{
+  EFI_STATUS                Status;
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePathNode;
+  EFI_DEVICE_PATH_PROTOCOL  *LastDevicePathNode;
+  EFI_GUID                  *VendorDefinedGuid;
+  EFI_GUID                  UdfDevPathGuid = EFI_UDF_DEVICE_PATH_GUID;
+
+  //
+  // Open Device Path protocol on ControllerHandle
+  //
+  Status = gBS->OpenProtocol (
+    ControllerHandle,
+    &gEfiDevicePathProtocolGuid,
+    (VOID **)&DevicePath,
+    This->DriverBindingHandle,
+    ControllerHandle,
+    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+    );
+  if (EFI_ERROR (Status)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  Status = EFI_UNSUPPORTED;
+
+  //
+  // Get last Device Path node
+  //
+  LastDevicePathNode = NULL;
+  DevicePathNode = DevicePath;
+  while (!IsDevicePathEnd (DevicePathNode)) {
+    LastDevicePathNode = DevicePathNode;
+    DevicePathNode = NextDevicePathNode (DevicePathNode);
+  }
+  //
+  // Check if last Device Path node contains a Vendor-Defined Media Device Path
+  // of an UDF file system.
+  //
+  if (LastDevicePathNode != NULL &&
+      DevicePathType (LastDevicePathNode) == MEDIA_DEVICE_PATH &&
+      DevicePathSubType (LastDevicePathNode) == MEDIA_VENDOR_DP) {
+    VendorDefinedGuid = (EFI_GUID *)((UINTN)LastDevicePathNode +
+                                     OFFSET_OF (VENDOR_DEVICE_PATH, Guid));
+    if (CompareGuid (VendorDefinedGuid, &UdfDevPathGuid)) {
+      Status = EFI_SUCCESS;
+    }
+  }
+
+  //
+  // Close Device Path protocol on ControllerHandle
+  //
+  gBS->CloseProtocol (
+    ControllerHandle,
+    &gEfiDevicePathProtocolGuid,
+    This->DriverBindingHandle,
+    ControllerHandle
+    );
+
+  return Status;
+}
diff --git a/MdeModulePkg/Universal/Disk/UdfDxe/Udf.c b/MdeModulePkg/Universal/Disk/UdfDxe/Udf.c
new file mode 100644
index 0000000000..9f10c78ca9
--- /dev/null
+++ b/MdeModulePkg/Universal/Disk/UdfDxe/Udf.c
@@ -0,0 +1,344 @@
+/** @file
+  UDF/ECMA-167 file system driver.
+
+  Copyright (C) 2014-2017 Paulo Alcantara <pcacjr@zytor.com>
+
+  This program and the accompanying materials are licensed and made available
+  under the terms and conditions of the BSD License which 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 "Udf.h"
+
+//
+// UDF filesystem driver's Global Variables.
+//
+EFI_DRIVER_BINDING_PROTOCOL gUdfDriverBinding = {
+  UdfDriverBindingSupported,
+  UdfDriverBindingStart,
+  UdfDriverBindingStop,
+  0x10,
+  NULL,
+  NULL
+};
+
+EFI_SIMPLE_FILE_SYSTEM_PROTOCOL gUdfSimpleFsTemplate = {
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION,
+  UdfOpenVolume
+};
+
+/**
+  Test to see if this driver supports ControllerHandle. Any ControllerHandle
+  than contains a BlockIo and DiskIo protocol or a BlockIo2 protocol can be
+  supported.
+
+  @param[in]  This                Protocol instance pointer.
+  @param[in]  ControllerHandle    Handle of device to test.
+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific
+                                  child device to start.
+
+  @retval EFI_SUCCESS         This driver supports this device.
+  @retval EFI_ALREADY_STARTED This driver is already running on this device.
+  @retval other               This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_STATUS            Status;
+  EFI_DISK_IO_PROTOCOL  *DiskIo;
+
+  //
+  // Open DiskIo protocol on ControllerHandle
+  //
+  Status = gBS->OpenProtocol (
+    ControllerHandle,
+    &gEfiDiskIoProtocolGuid,
+    (VOID **)&DiskIo,
+    This->DriverBindingHandle,
+    ControllerHandle,
+    EFI_OPEN_PROTOCOL_BY_DRIVER
+    );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Close DiskIo protocol on ControllerHandle
+  //
+  gBS->CloseProtocol (
+    ControllerHandle,
+    &gEfiDiskIoProtocolGuid,
+    This->DriverBindingHandle,
+    ControllerHandle
+    );
+
+  //
+  // Test whether ControllerHandle supports BlockIo protocol
+  //
+  Status = gBS->OpenProtocol (
+    ControllerHandle,
+    &gEfiBlockIoProtocolGuid,
+    NULL,
+    This->DriverBindingHandle,
+    ControllerHandle,
+    EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+    );
+
+  return Status;
+}
+
+/**
+  Start this driver on ControllerHandle by opening a Block IO or a Block IO2
+  or both, and Disk IO protocol, reading Device Path, and creating a child
+  handle with a Disk IO and device path protocol.
+
+  @param[in]  This                 Protocol instance pointer.
+  @param[in]  ControllerHandle     Handle of device to bind driver to
+  @param[in]  RemainingDevicePath  Optional parameter use to pick a specific
+                                   child device to start.
+
+  @retval EFI_SUCCESS          This driver is added to ControllerHandle.
+  @retval EFI_ALREADY_STARTED  This driver is already running on
+                               ControllerHandle.
+  @retval other                This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_TPL                     OldTpl;
+  EFI_STATUS                  Status;
+  EFI_BLOCK_IO_PROTOCOL       *BlockIo;
+  EFI_DISK_IO_PROTOCOL        *DiskIo;
+  PRIVATE_UDF_SIMPLE_FS_DATA  *PrivFsData;
+
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+  //
+  // Open BlockIo protocol on ControllerHandle
+  //
+  Status = gBS->OpenProtocol (
+    ControllerHandle,
+    &gEfiBlockIoProtocolGuid,
+    (VOID **)&BlockIo,
+    This->DriverBindingHandle,
+    ControllerHandle,
+    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+    );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Open DiskIo protocol on ControllerHandle
+  //
+  Status = gBS->OpenProtocol (
+    ControllerHandle,
+    &gEfiDiskIoProtocolGuid,
+    (VOID **)&DiskIo,
+    This->DriverBindingHandle,
+    ControllerHandle,
+    EFI_OPEN_PROTOCOL_BY_DRIVER
+    );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Check if ControllerHandle supports an UDF file system
+  //
+  Status = SupportUdfFileSystem (This, ControllerHandle);
+  if (EFI_ERROR (Status)) {
+    goto Exit;
+  }
+
+  //
+  // Initialize private file system structure
+  //
+  PrivFsData =
+    (PRIVATE_UDF_SIMPLE_FS_DATA *)
+    AllocateZeroPool (sizeof (PRIVATE_UDF_SIMPLE_FS_DATA));
+  if (PrivFsData == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Exit;
+  }
+
+  //
+  // Create new child handle
+  //
+  PrivFsData->Signature = PRIVATE_UDF_SIMPLE_FS_DATA_SIGNATURE;
+  PrivFsData->BlockIo   = BlockIo;
+  PrivFsData->DiskIo    = DiskIo;
+  PrivFsData->Handle    = ControllerHandle;
+
+  //
+  // Set up SimpleFs protocol
+  //
+  CopyMem ((VOID *)&PrivFsData->SimpleFs, (VOID *)&gUdfSimpleFsTemplate,
+           sizeof (EFI_SIMPLE_FILE_SYSTEM_PROTOCOL));
+
+  //
+  // Install child handle
+  //
+  Status = gBS->InstallMultipleProtocolInterfaces (
+    &PrivFsData->Handle,
+    &gEfiSimpleFileSystemProtocolGuid,
+    &PrivFsData->SimpleFs,
+    NULL
+    );
+
+Exit:
+  if (EFI_ERROR (Status)) {
+    //
+    // Close DiskIo protocol on ControllerHandle
+    //
+    gBS->CloseProtocol (
+      ControllerHandle,
+      &gEfiDiskIoProtocolGuid,
+      This->DriverBindingHandle,
+      ControllerHandle
+      );
+    //
+    // Close BlockIo protocol on ControllerHandle
+    //
+    gBS->CloseProtocol (
+      ControllerHandle,
+      &gEfiBlockIoProtocolGuid,
+      This->DriverBindingHandle,
+      ControllerHandle
+      );
+  }
+
+  gBS->RestoreTPL (OldTpl);
+
+  return Status;
+}
+
+/**
+  Stop this driver on ControllerHandle. Support stopping any child handles
+  created by this driver.
+
+  @param  This              Protocol instance pointer.
+  @param  ControllerHandle  Handle of device to stop driver on
+  @param  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
+                            children is zero stop the entire bus driver.
+  @param  ChildHandleBuffer List of Child Handles to Stop.
+
+  @retval EFI_SUCCESS       This driver is removed ControllerHandle
+  @retval other             This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+UdfDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL   *This,
+  IN  EFI_HANDLE                    ControllerHandle,
+  IN  UINTN                         NumberOfChildren,
+  IN  EFI_HANDLE                    *ChildHandleBuffer
+  )
+{
+  PRIVATE_UDF_SIMPLE_FS_DATA        *PrivFsData;
+  EFI_STATUS                        Status;
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL   *SimpleFs;
+
+  //
+  // Open SimpleFs protocol on ControllerHandle
+  //
+  Status = gBS->OpenProtocol (
+    ControllerHandle,
+    &gEfiSimpleFileSystemProtocolGuid,
+    (VOID **)&SimpleFs,
+    This->DriverBindingHandle,
+    ControllerHandle,
+    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+    );
+  if (!EFI_ERROR (Status)) {
+    PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (This);
+
+    //
+    // Uninstall child handle
+    //
+    Status = gBS->UninstallMultipleProtocolInterfaces (
+      PrivFsData->Handle,
+      &gEfiSimpleFileSystemProtocolGuid,
+      &PrivFsData->SimpleFs,
+      NULL
+      );
+
+    //
+    // Check if there's any open file. If so, clean them up.
+    //
+    if (PrivFsData->OpenFiles > 0) {
+      CleanupVolumeInformation (&PrivFsData->Volume);
+    }
+
+    FreePool ((VOID *)PrivFsData);
+  }
+
+  if (!EFI_ERROR (Status)) {
+    //
+    // Close DiskIo protocol on ControllerHandle
+    //
+    gBS->CloseProtocol (
+      ControllerHandle,
+      &gEfiDiskIoProtocolGuid,
+      This->DriverBindingHandle,
+      ControllerHandle
+      );
+    //
+    // Close BlockIo protocol on ControllerHandle
+    //
+    gBS->CloseProtocol (
+      ControllerHandle,
+      &gEfiBlockIoProtocolGuid,
+      This->DriverBindingHandle,
+      ControllerHandle
+      );
+  }
+
+  return Status;
+}
+
+/**
+  The user Entry Point for UDF file system driver. The user code starts with
+  this function.
+
+  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
+  @param[in] SystemTable    A pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS       The entry point is executed successfully.
+  @retval other             Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeUdf (
+  IN EFI_HANDLE           ImageHandle,
+  IN EFI_SYSTEM_TABLE     *SystemTable
+  )
+{
+  EFI_STATUS              Status;
+
+  Status = EfiLibInstallDriverBindingComponentName2 (
+    ImageHandle,
+    SystemTable,
+    &gUdfDriverBinding,
+    ImageHandle,
+    &gUdfComponentName,
+    &gUdfComponentName2
+    );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/MdeModulePkg/Universal/Disk/UdfDxe/Udf.h b/MdeModulePkg/Universal/Disk/UdfDxe/Udf.h
new file mode 100644
index 0000000000..240d420ff5
--- /dev/null
+++ b/MdeModulePkg/Universal/Disk/UdfDxe/Udf.h
@@ -0,0 +1,1244 @@
+/** @file
+  UDF/ECMA-167 file system driver.
+
+  Copyright (C) 2014-2017 Paulo Alcantara <pcacjr@zytor.com>
+
+  This program and the accompanying materials are licensed and made available
+  under the terms and conditions of the BSD License which 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.
+**/
+
+#ifndef _UDF_H_
+#define _UDF_H_
+
+#include <Uefi.h>
+#include <Base.h>
+
+#include <Protocol/BlockIo.h>
+#include <Protocol/ComponentName.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/DiskIo.h>
+#include <Protocol/SimpleFileSystem.h>
+
+#include <Guid/FileInfo.h>
+#include <Guid/FileSystemInfo.h>
+#include <Guid/FileSystemVolumeLabelInfo.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DevicePathLib.h>
+
+#include <IndustryStandard/ElTorito.h>
+#include <IndustryStandard/Udf.h>
+
+//
+// C5BD4D42-1A76-4996-8956-73CDA326CD0A
+//
+#define EFI_UDF_DEVICE_PATH_GUID                        \
+  { 0xC5BD4D42, 0x1A76, 0x4996,                         \
+    { 0x89, 0x56, 0x73, 0xCD, 0xA3, 0x26, 0xCD, 0x0A }  \
+  }
+
+#define UDF_DEFAULT_LV_NUM 0
+
+#define IS_PVD(_Pointer) \
+  ((BOOLEAN)(_GET_TAG_ID (_Pointer) == 1))
+#define IS_PD(_Pointer) \
+  ((BOOLEAN)(_GET_TAG_ID (_Pointer) == 5))
+#define IS_LVD(_Pointer) \
+  ((BOOLEAN)(_GET_TAG_ID (_Pointer) == 6))
+#define IS_TD(_Pointer) \
+  ((BOOLEAN)(_GET_TAG_ID (_Pointer) == 8))
+#define IS_FSD(_Pointer) \
+  ((BOOLEAN)(_GET_TAG_ID (_Pointer) == 256))
+#define IS_FE(_Pointer) \
+  ((BOOLEAN)(_GET_TAG_ID (_Pointer) == 261))
+#define IS_EFE(_Pointer) \
+  ((BOOLEAN)(_GET_TAG_ID (_Pointer) == 266))
+#define IS_FID(_Pointer) \
+  ((BOOLEAN)(_GET_TAG_ID (_Pointer) == 257))
+#define IS_AED(_Pointer) \
+  ((BOOLEAN)(_GET_TAG_ID (_Pointer) == 258))
+#define IS_LVID(_Pointer) \
+  ((BOOLEAN)(_GET_TAG_ID (_Pointer) == 9))
+
+#define _GET_FILETYPE(_Pointer) \
+  (IS_FE (_Pointer) ? \
+   (((UDF_FILE_ENTRY *)(_Pointer))->IcbTag.FileType) \
+   : \
+   (((UDF_EXTENDED_FILE_ENTRY *)(_Pointer))->IcbTag.FileType))
+
+#define IS_FE_DIRECTORY(_Pointer) \
+  ((BOOLEAN)(_GET_FILETYPE (_Pointer) == 4))
+#define IS_FE_STANDARD_FILE(_Pointer) \
+  ((BOOLEAN)(_GET_FILETYPE (_Pointer) == 5))
+#define IS_FE_SYMLINK(_Pointer) \
+  ((BOOLEAN)(_GET_FILETYPE (_Pointer) == 12))
+
+#define HIDDEN_FILE     (1 << 0)
+#define DIRECTORY_FILE  (1 << 1)
+#define DELETED_FILE    (1 << 2)
+#define PARENT_FILE     (1 << 3)
+
+#define _GET_FILE_CHARS(_Pointer) \
+  (((UDF_FILE_IDENTIFIER_DESCRIPTOR *)(_Pointer))->FileCharacteristics)
+
+#define IS_FID_HIDDEN_FILE(_Pointer) \
+  ((BOOLEAN)(_GET_FILE_CHARS (_Pointer) & HIDDEN_FILE))
+#define IS_FID_DIRECTORY_FILE(_Pointer) \
+  ((BOOLEAN)(_GET_FILE_CHARS (_Pointer) & DIRECTORY_FILE))
+#define IS_FID_DELETED_FILE(_Pointer) \
+  ((BOOLEAN)(_GET_FILE_CHARS (_Pointer) & DELETED_FILE))
+#define IS_FID_PARENT_FILE(_Pointer) \
+  ((BOOLEAN)(_GET_FILE_CHARS (_Pointer) & PARENT_FILE))
+#define IS_FID_NORMAL_FILE(_Pointer) \
+  ((BOOLEAN)(!IS_FID_DIRECTORY_FILE (_Pointer) && \
+         !IS_FID_PARENT_FILE (_Pointer)))
+
+typedef enum {
+  SHORT_ADS_SEQUENCE,
+  LONG_ADS_SEQUENCE,
+  EXTENDED_ADS_SEQUENCE,
+  INLINE_DATA
+} UDF_FE_RECORDING_FLAGS;
+
+#define GET_FE_RECORDING_FLAGS(_Fe) \
+  ((UDF_FE_RECORDING_FLAGS)((UDF_ICB_TAG *)( \
+                  (UINT8 *)(_Fe) + \
+                  sizeof (UDF_DESCRIPTOR_TAG)))->Flags & 0x07)
+
+typedef enum {
+  EXTENT_RECORDED_AND_ALLOCATED,
+  EXTENT_NOT_RECORDED_BUT_ALLOCATED,
+  EXTENT_NOT_RECORDED_NOT_ALLOCATED,
+  EXTENT_IS_NEXT_EXTENT,
+} UDF_EXTENT_FLAGS;
+
+#define AD_LENGTH(_RecFlags) \
+  ((_RecFlags) == SHORT_ADS_SEQUENCE ? \
+   ((UINT64)(sizeof (UDF_SHORT_ALLOCATION_DESCRIPTOR))) : \
+   ((UINT64)(sizeof (UDF_LONG_ALLOCATION_DESCRIPTOR))))
+
+#define GET_EXTENT_FLAGS(_RecFlags, _Ad) \
+  ((_RecFlags) == SHORT_ADS_SEQUENCE ? \
+   ((UDF_EXTENT_FLAGS)((((UDF_SHORT_ALLOCATION_DESCRIPTOR *)(_Ad))->ExtentLength >> \
+            30) & 0x3)) : \
+   ((UDF_EXTENT_FLAGS)((((UDF_LONG_ALLOCATION_DESCRIPTOR *)(_Ad))->ExtentLength >> \
+            30) & 0x3)))
+
+#define GET_EXTENT_LENGTH(_RecFlags, _Ad) \
+  ((_RecFlags) == SHORT_ADS_SEQUENCE ? \
+   ((UINT32)((((UDF_SHORT_ALLOCATION_DESCRIPTOR *)(_Ad))->ExtentLength & \
+          ~0xC0000000UL))) : \
+   ((UINT32)((((UDF_LONG_ALLOCATION_DESCRIPTOR *)(_Ad))->ExtentLength & \
+          ~0xC0000000UL))))
+
+#define UDF_FILENAME_LENGTH  128
+#define UDF_PATH_LENGTH      512
+
+#define GET_FID_FROM_ADS(_Data, _Offs) \
+  ((UDF_FILE_IDENTIFIER_DESCRIPTOR *)((UINT8 *)(_Data) + (_Offs)))
+
+#define IS_VALID_COMPRESSION_ID(_CompId) \
+  ((BOOLEAN)((_CompId) == 8 || (_CompId) == 16))
+
+#define LV_BLOCK_SIZE(_Vol, _LvNum) \
+  (_Vol)->LogicalVolDescs[(_LvNum)]->LogicalBlockSize
+
+#define UDF_STANDARD_IDENTIFIER_LENGTH   5
+
+#define LV_UDF_REVISION(_Lv) \
+  *(UINT16 *)(UINTN)(_Lv)->DomainIdentifier.IdentifierSuffix
+
+#pragma pack(1)
+
+typedef struct {
+  UINT8 StandardIdentifier[UDF_STANDARD_IDENTIFIER_LENGTH];
+} UDF_STANDARD_IDENTIFIER;
+
+#pragma pack()
+
+typedef enum {
+  READ_FILE_GET_FILESIZE,
+  READ_FILE_ALLOCATE_AND_READ,
+  READ_FILE_SEEK_AND_READ,
+} UDF_READ_FILE_FLAGS;
+
+typedef struct {
+  VOID                 *FileData;
+  UDF_READ_FILE_FLAGS  Flags;
+  UINT64               FileDataSize;
+  UINT64               FilePosition;
+  UINT64               FileSize;
+  UINT64               ReadLength;
+} UDF_READ_FILE_INFO;
+
+#pragma pack(1)
+
+typedef struct {
+  UINT8           CharacterSetType;
+  UINT8           CharacterSetInfo[63];
+} UDF_CHAR_SPEC;
+
+typedef struct {
+  UINT8           Flags;
+  UINT8           Identifier[23];
+  UINT8           IdentifierSuffix[8];
+} UDF_ENTITY_ID;
+
+typedef struct {
+  UINT16          TypeAndTimezone;
+  INT16           Year;
+  UINT8           Month;
+  UINT8           Day;
+  UINT8           Hour;
+  UINT8           Minute;
+  UINT8           Second;
+  UINT8           Centiseconds;
+  UINT8           HundredsOfMicroseconds;
+  UINT8           Microseconds;
+} UDF_TIMESTAMP;
+
+typedef struct {
+  UINT32        LogicalBlockNumber;
+  UINT16        PartitionReferenceNumber;
+} UDF_LB_ADDR;
+
+typedef struct {
+  UINT32                           ExtentLength;
+  UDF_LB_ADDR                      ExtentLocation;
+  UINT8                            ImplementationUse[6];
+} UDF_LONG_ALLOCATION_DESCRIPTOR;
+
+typedef struct {
+  UDF_DESCRIPTOR_TAG                 DescriptorTag;
+  UINT32                             PrevAllocationExtentDescriptor;
+  UINT32                             LengthOfAllocationDescriptors;
+} UDF_ALLOCATION_EXTENT_DESCRIPTOR;
+
+typedef struct {
+  UINT8                   StructureType;
+  UINT8                   StandardIdentifier[UDF_STANDARD_IDENTIFIER_LENGTH];
+  UINT8                   StructureVersion;
+  UINT8                   Reserved;
+  UINT8                   StructureData[2040];
+} UDF_VOLUME_DESCRIPTOR;
+
+typedef struct {
+  UDF_DESCRIPTOR_TAG         DescriptorTag;
+  UINT32                     VolumeDescriptorSequenceNumber;
+  UINT16                     PartitionFlags;
+  UINT16                     PartitionNumber;
+  UDF_ENTITY_ID              PartitionContents;
+  UINT8                      PartitionContentsUse[128];
+  UINT32                     AccessType;
+  UINT32                     PartitionStartingLocation;
+  UINT32                     PartitionLength;
+  UDF_ENTITY_ID              ImplementationIdentifier;
+  UINT8                      ImplementationUse[128];
+  UINT8                      Reserved[156];
+} UDF_PARTITION_DESCRIPTOR;
+
+typedef struct {
+  UDF_DESCRIPTOR_TAG              DescriptorTag;
+  UINT32                          VolumeDescriptorSequenceNumber;
+  UDF_CHAR_SPEC                   DescriptorCharacterSet;
+  UINT8                           LogicalVolumeIdentifier[128];
+  UINT32                          LogicalBlockSize;
+  UDF_ENTITY_ID                   DomainIdentifier;
+  UDF_LONG_ALLOCATION_DESCRIPTOR  LogicalVolumeContentsUse;
+  UINT32                          MapTableLength;
+  UINT32                          NumberOfPartitionMaps;
+  UDF_ENTITY_ID                   ImplementationIdentifier;
+  UINT8                           ImplementationUse[128];
+  UDF_EXTENT_AD                   IntegritySequenceExtent;
+  UINT8                           PartitionMaps[6];
+} UDF_LOGICAL_VOLUME_DESCRIPTOR;
+
+typedef struct {
+  UDF_DESCRIPTOR_TAG             DescriptorTag;
+  UDF_TIMESTAMP                  RecordingDateTime;
+  UINT32                         IntegrityType;
+  UDF_EXTENT_AD                  NextIntegrityExtent;
+  UINT8                          LogicalVolumeContentsUse[32];
+  UINT32                         NumberOfPartitions;
+  UINT32                         LengthOfImplementationUse;
+  UINT8                          Data[0];
+} UDF_LOGICAL_VOLUME_INTEGRITY;
+
+typedef struct {
+  UDF_DESCRIPTOR_TAG              DescriptorTag;
+  UDF_TIMESTAMP                   RecordingDateAndTime;
+  UINT16                          InterchangeLevel;
+  UINT16                          MaximumInterchangeLevel;
+  UINT32                          CharacterSetList;
+  UINT32                          MaximumCharacterSetList;
+  UINT32                          FileSetNumber;
+  UINT32                          FileSetDescriptorNumber;
+  UDF_CHAR_SPEC                   LogicalVolumeIdentifierCharacterSet;
+  UINT8                           LogicalVolumeIdentifier[128];
+  UDF_CHAR_SPEC                   FileSetCharacterSet;
+  UINT8                           FileSetIdentifier[32];
+  UINT8                           CopyrightFileIdentifier[32];
+  UINT8                           AbstractFileIdentifier[32];
+  UDF_LONG_ALLOCATION_DESCRIPTOR  RootDirectoryIcb;
+  UDF_ENTITY_ID                   DomainIdentifier;
+  UDF_LONG_ALLOCATION_DESCRIPTOR  NextExtent;
+  UDF_LONG_ALLOCATION_DESCRIPTOR  SystemStreamDirectoryIcb;
+  UINT8                           Reserved[32];
+} UDF_FILE_SET_DESCRIPTOR;
+
+typedef struct {
+  UINT32                            ExtentLength;
+  UINT32                            ExtentPosition;
+} UDF_SHORT_ALLOCATION_DESCRIPTOR;
+
+typedef struct {
+  UDF_DESCRIPTOR_TAG               DescriptorTag;
+  UINT16                           FileVersionNumber;
+  UINT8                            FileCharacteristics;
+  UINT8                            LengthOfFileIdentifier;
+  UDF_LONG_ALLOCATION_DESCRIPTOR   Icb;
+  UINT16                           LengthOfImplementationUse;
+  UINT8                            Data[0];
+} UDF_FILE_IDENTIFIER_DESCRIPTOR;
+
+typedef struct {
+  UINT32        PriorRecordNumberOfDirectEntries;
+  UINT16        StrategyType;
+  UINT16        StrategyParameter;
+  UINT16        MaximumNumberOfEntries;
+  UINT8         Reserved;
+  UINT8         FileType;
+  UDF_LB_ADDR   ParentIcbLocation;
+  UINT16        Flags;
+} UDF_ICB_TAG;
+
+typedef struct {
+  UDF_DESCRIPTOR_TAG              DescriptorTag;
+  UDF_ICB_TAG                     IcbTag;
+  UINT32                          Uid;
+  UINT32                          Gid;
+  UINT32                          Permissions;
+  UINT16                          FileLinkCount;
+  UINT8                           RecordFormat;
+  UINT8                           RecordDisplayAttributes;
+  UINT32                          RecordLength;
+  UINT64                          InformationLength;
+  UINT64                          LogicalBlocksRecorded;
+  UDF_TIMESTAMP                   AccessTime;
+  UDF_TIMESTAMP                   ModificationTime;
+  UDF_TIMESTAMP                   AttributeTime;
+  UINT32                          CheckPoint;
+  UDF_LONG_ALLOCATION_DESCRIPTOR  ExtendedAttributeIcb;
+  UDF_ENTITY_ID                   ImplementationIdentifier;
+  UINT64                          UniqueId;
+  UINT32                          LengthOfExtendedAttributes;
+  UINT32                          LengthOfAllocationDescriptors;
+  UINT8                           Data[0]; // L_EA + L_AD
+} UDF_FILE_ENTRY;
+
+typedef struct {
+  UDF_DESCRIPTOR_TAG              DescriptorTag;
+  UDF_ICB_TAG                     IcbTag;
+  UINT32                          Uid;
+  UINT32                          Gid;
+  UINT32                          Permissions;
+  UINT16                          FileLinkCount;
+  UINT8                           RecordFormat;
+  UINT8                           RecordDisplayAttributes;
+  UINT32                          RecordLength;
+  UINT64                          InformationLength;
+  UINT64                          ObjectSize;
+  UINT64                          LogicalBlocksRecorded;
+  UDF_TIMESTAMP                   AccessTime;
+  UDF_TIMESTAMP                   ModificationTime;
+  UDF_TIMESTAMP                   CreationTime;
+  UDF_TIMESTAMP                   AttributeTime;
+  UINT32                          CheckPoint;
+  UINT32                          Reserved;
+  UDF_LONG_ALLOCATION_DESCRIPTOR  ExtendedAttributeIcb;
+  UDF_LONG_ALLOCATION_DESCRIPTOR  StreamDirectoryIcb;
+  UDF_ENTITY_ID                   ImplementationIdentifier;
+  UINT64                          UniqueId;
+  UINT32                          LengthOfExtendedAttributes;
+  UINT32                          LengthOfAllocationDescriptors;
+  UINT8                           Data[0]; // L_EA + L_AD
+} UDF_EXTENDED_FILE_ENTRY;
+
+typedef struct {
+  UINT8                ComponentType;
+  UINT8                LengthOfComponentIdentifier;
+  UINT16               ComponentFileVersionNumber;
+  UINT8                ComponentIdentifier[0];
+} UDF_PATH_COMPONENT;
+
+#pragma pack()
+
+//
+// UDF filesystem driver's private data
+//
+typedef struct {
+  UDF_LOGICAL_VOLUME_DESCRIPTOR  **LogicalVolDescs;
+  UINTN                          LogicalVolDescsNo;
+  UDF_PARTITION_DESCRIPTOR       **PartitionDescs;
+  UINTN                          PartitionDescsNo;
+  UDF_FILE_SET_DESCRIPTOR        **FileSetDescs;
+  UINTN                          FileSetDescsNo;
+  UINTN                          FileEntrySize;
+} UDF_VOLUME_INFO;
+
+typedef struct {
+  VOID                            *FileEntry;
+  UDF_FILE_IDENTIFIER_DESCRIPTOR  *FileIdentifierDesc;
+} UDF_FILE_INFO;
+
+typedef struct {
+  VOID                      *DirectoryData;
+  UINT64                    DirectoryLength;
+  UINT64                    FidOffset;
+} UDF_READ_DIRECTORY_INFO;
+
+#define PRIVATE_UDF_FILE_DATA_SIGNATURE SIGNATURE_32 ('U', 'd', 'f', 'f')
+
+#define PRIVATE_UDF_FILE_DATA_FROM_THIS(a) \
+  CR ( \
+      a, \
+      PRIVATE_UDF_FILE_DATA, \
+      FileIo, \
+      PRIVATE_UDF_FILE_DATA_SIGNATURE \
+      )
+
+typedef struct {
+  UINTN                            Signature;
+  BOOLEAN                          IsRootDirectory;
+  UDF_FILE_INFO                    *Root;
+  UDF_FILE_INFO                    File;
+  UDF_READ_DIRECTORY_INFO          ReadDirInfo;
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *SimpleFs;
+  EFI_FILE_PROTOCOL                FileIo;
+  CHAR16                           AbsoluteFileName[UDF_PATH_LENGTH];
+  CHAR16                           FileName[UDF_FILENAME_LENGTH];
+  UINT64                           FileSize;
+  UINT64                           FilePosition;
+} PRIVATE_UDF_FILE_DATA;
+
+#define PRIVATE_UDF_SIMPLE_FS_DATA_SIGNATURE SIGNATURE_32 ('U', 'd', 'f', 's')
+
+#define PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS(a) \
+  CR ( \
+      a, \
+      PRIVATE_UDF_SIMPLE_FS_DATA, \
+      SimpleFs, \
+      PRIVATE_UDF_SIMPLE_FS_DATA_SIGNATURE \
+      )
+
+typedef struct {
+  UINTN                            Signature;
+  EFI_BLOCK_IO_PROTOCOL            *BlockIo;
+  EFI_DISK_IO_PROTOCOL             *DiskIo;
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  SimpleFs;
+  UDF_VOLUME_INFO                  Volume;
+  UDF_FILE_INFO                    Root;
+  UINTN                            OpenFiles;
+  EFI_HANDLE                       Handle;
+} PRIVATE_UDF_SIMPLE_FS_DATA;
+
+//
+// Global Variables
+//
+extern EFI_DRIVER_BINDING_PROTOCOL   gUdfDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL   gUdfComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL  gUdfComponentName2;
+
+//
+// Function Prototypes
+//
+
+/**
+  Open the root directory on a volume.
+
+  @param  This Protocol instance pointer.
+  @param  Root Returns an Open file handle for the root directory
+
+  @retval EFI_SUCCESS          The device was opened.
+  @retval EFI_UNSUPPORTED      This volume does not support the file system.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_ACCESS_DENIED    The service denied access to the file.
+  @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfOpenVolume (
+  IN   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *This,
+  OUT  EFI_FILE_PROTOCOL                **Root
+  );
+
+/**
+  Opens a new file relative to the source file's location.
+
+  @param  This       The protocol instance pointer.
+  @param  NewHandle  Returns File Handle for FileName.
+  @param  FileName   Null terminated string. "\", ".", and ".." are supported.
+  @param  OpenMode   Open mode for file.
+  @param  Attributes Only used for EFI_FILE_MODE_CREATE.
+
+  @retval EFI_SUCCESS          The device was opened.
+  @retval EFI_NOT_FOUND        The specified file could not be found on the device.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_MEDIA_CHANGED    The media has changed.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_ACCESS_DENIED    The service denied access to the file.
+  @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources.
+  @retval EFI_VOLUME_FULL      The volume is full.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfOpen (
+  IN   EFI_FILE_PROTOCOL  *This,
+  OUT  EFI_FILE_PROTOCOL  **NewHandle,
+  IN   CHAR16             *FileName,
+  IN   UINT64             OpenMode,
+  IN   UINT64             Attributes
+  );
+
+/**
+  Read data from the file.
+
+  @param  This       Protocol instance pointer.
+  @param  BufferSize On input size of buffer, on output amount of data in buffer.
+  @param  Buffer     The buffer in which data is read.
+
+  @retval EFI_SUCCESS          Data was read.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_BUFFER_TO_SMALL  BufferSize is too small. BufferSize contains required size.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfRead (
+  IN      EFI_FILE_PROTOCOL  *This,
+  IN OUT  UINTN              *BufferSize,
+  OUT     VOID               *Buffer
+  );
+
+/**
+  Close the file handle.
+
+  @param  This Protocol instance pointer.
+
+  @retval EFI_SUCCESS The file was closed.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfClose (
+  IN EFI_FILE_PROTOCOL *This
+  );
+
+/**
+  Close and delete the file handle.
+
+  @param  This Protocol instance pointer.
+
+  @retval EFI_SUCCESS              The file was closed and deleted.
+  @retval EFI_WARN_DELETE_FAILURE  The handle was closed but the file was not
+                                   deleted.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfDelete (
+  IN EFI_FILE_PROTOCOL  *This
+  );
+
+/**
+  Write data to a file.
+
+  @param  This       Protocol instance pointer.
+  @param  BufferSize On input size of buffer, on output amount of data in buffer.
+  @param  Buffer     The buffer in which data to write.
+
+  @retval EFI_SUCCESS          Data was written.
+  @retval EFI_UNSUPPORTED      Writes to Open directory are not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_DEVICE_ERROR     An attempt was made to write to a deleted file.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_WRITE_PROTECTED  The device is write protected.
+  @retval EFI_ACCESS_DENIED    The file was open for read only.
+  @retval EFI_VOLUME_FULL      The volume is full.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfWrite (
+  IN      EFI_FILE_PROTOCOL  *This,
+  IN OUT  UINTN              *BufferSize,
+  IN      VOID               *Buffer
+  );
+
+/**
+  Get file's current position.
+
+  @param  This      Protocol instance pointer.
+  @param  Position  Byte position from the start of the file.
+
+  @retval EFI_SUCCESS     Position was updated.
+  @retval EFI_UNSUPPORTED Seek request for directories is not valid.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfGetPosition (
+  IN   EFI_FILE_PROTOCOL  *This,
+  OUT  UINT64             *Position
+  );
+
+/**
+  Set file's current position.
+
+  @param  This      Protocol instance pointer.
+  @param  Position  Byte position from the start of the file.
+
+  @retval EFI_SUCCESS      Position was updated.
+  @retval EFI_UNSUPPORTED  Seek request for non-zero is not valid on open.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfSetPosition (
+  IN EFI_FILE_PROTOCOL  *This,
+  IN UINT64             Position
+  );
+
+/**
+  Get information about a file.
+
+  @param  This            Protocol instance pointer.
+  @param  InformationType Type of information to return in Buffer.
+  @param  BufferSize      On input size of buffer, on output amount of data in buffer.
+  @param  Buffer          The buffer to return data.
+
+  @retval EFI_SUCCESS          Data was returned.
+  @retval EFI_UNSUPPORTED      InformationType is not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_WRITE_PROTECTED  The device is write protected.
+  @retval EFI_ACCESS_DENIED    The file was open for read only.
+  @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in BufferSize.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfGetInfo (
+  IN      EFI_FILE_PROTOCOL  *This,
+  IN      EFI_GUID           *InformationType,
+  IN OUT  UINTN              *BufferSize,
+  OUT     VOID               *Buffer
+  );
+
+/**
+  Set information about a file.
+
+  @param  File            Protocol instance pointer.
+  @param  InformationType Type of information in Buffer.
+  @param  BufferSize      Size of buffer.
+  @param  Buffer          The data to write.
+
+  @retval EFI_SUCCESS          Data was set.
+  @retval EFI_UNSUPPORTED      InformationType is not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_WRITE_PROTECTED  The device is write protected.
+  @retval EFI_ACCESS_DENIED    The file was open for read only.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfSetInfo (
+  IN EFI_FILE_PROTOCOL  *This,
+  IN EFI_GUID           *InformationType,
+  IN UINTN              BufferSize,
+  IN VOID               *Buffer
+  );
+
+/**
+  Flush data back for the file handle.
+
+  @param  This Protocol instance pointer.
+
+  @retval EFI_SUCCESS          Data was flushed.
+  @retval EFI_UNSUPPORTED      Writes to Open directory are not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_WRITE_PROTECTED  The device is write protected.
+  @retval EFI_ACCESS_DENIED    The file was open for read only.
+  @retval EFI_VOLUME_FULL      The volume is full.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfFlush (
+  IN EFI_FILE_PROTOCOL *This
+  );
+
+/**
+  Read volume information on a medium which contains a valid UDF file system.
+
+  @param[in]   BlockIo  BlockIo interface.
+  @param[in]   DiskIo   DiskIo interface.
+  @param[out]  Volume   UDF volume information structure.
+
+  @retval EFI_SUCCESS          Volume information read.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The volume was not read due to lack of resources.
+
+**/
+EFI_STATUS
+ReadUdfVolumeInformation (
+  IN   EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL   *DiskIo,
+  OUT  UDF_VOLUME_INFO        *Volume
+  );
+
+/**
+  Find the root directory on an UDF volume.
+
+  @param[in]   BlockIo  BlockIo interface.
+  @param[in]   DiskIo   DiskIo interface.
+  @param[in]   Volume   UDF volume information structure.
+  @param[out]  File     Root directory file.
+
+  @retval EFI_SUCCESS          Root directory found.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The root directory was not found due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+FindRootDirectory (
+  IN   EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL   *DiskIo,
+  IN   UDF_VOLUME_INFO        *Volume,
+  OUT  UDF_FILE_INFO          *File
+  );
+
+/**
+  Find either a File Entry or a Extended File Entry from a given ICB.
+
+  @param[in]   BlockIo    BlockIo interface.
+  @param[in]   DiskIo     DiskIo interface.
+  @param[in]   Volume     UDF volume information structure.
+  @param[in]   Icb        ICB of the FID.
+  @param[out]  FileEntry  File Entry or Extended File Entry.
+
+  @retval EFI_SUCCESS          File Entry or Extended File Entry found.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The FE/EFE entry was not found due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+FindFileEntry (
+  IN   EFI_BLOCK_IO_PROTOCOL           *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL            *DiskIo,
+  IN   UDF_VOLUME_INFO                 *Volume,
+  IN   UDF_LONG_ALLOCATION_DESCRIPTOR  *Icb,
+  OUT  VOID                            **FileEntry
+  );
+
+/**
+  Find a file given its absolute path on an UDF volume.
+
+  @param[in]   BlockIo   BlockIo interface.
+  @param[in]   DiskIo    DiskIo interface.
+  @param[in]   Volume    UDF volume information structure.
+  @param[in]   FilePath  File's absolute path.
+  @param[in]   Root      Root directory file.
+  @param[in]   Parent    Parent directory file.
+  @param[out]  File      Found file.
+
+  @retval EFI_SUCCESS          @p FilePath was found.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The @p FilePath file was not found due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+FindFile (
+  IN   EFI_BLOCK_IO_PROTOCOL           *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL            *DiskIo,
+  IN   UDF_VOLUME_INFO                 *Volume,
+  IN   CHAR16                          *FilePath,
+  IN   UDF_FILE_INFO                   *Root,
+  IN   UDF_FILE_INFO                   *Parent,
+  IN   UDF_LONG_ALLOCATION_DESCRIPTOR  *Icb,
+  OUT  UDF_FILE_INFO                   *File
+  );
+
+/**
+  Read a directory entry at a time on an UDF volume.
+
+  @param[in]      BlockIo        BlockIo interface.
+  @param[in]      DiskIo         DiskIo interface.
+  @param[in]      Volume         UDF volume information structure.
+  @param[in]      ParentIcb      ICB of the parent file.
+  @param[in]      FileEntryData  FE/EFE of the parent file.
+  @param[in out]  ReadDirInfo    Next read directory listing structure
+                                 information.
+  @param[out]     FoundFid       File Identifier Descriptor pointer.
+
+  @retval EFI_SUCCESS          Directory entry read.
+  @retval EFI_UNSUPPORTED      Extended Allocation Descriptors not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The directory entry was not read due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+ReadDirectoryEntry (
+  IN      EFI_BLOCK_IO_PROTOCOL           *BlockIo,
+  IN      EFI_DISK_IO_PROTOCOL            *DiskIo,
+  IN      UDF_VOLUME_INFO                 *Volume,
+  IN      UDF_LONG_ALLOCATION_DESCRIPTOR  *ParentIcb,
+  IN      VOID                            *FileEntryData,
+  IN OUT  UDF_READ_DIRECTORY_INFO         *ReadDirInfo,
+  OUT     UDF_FILE_IDENTIFIER_DESCRIPTOR  **FoundFid
+  );
+
+/**
+  Get a filename (encoded in OSTA-compressed format) from a File Identifier
+  Descriptor on an UDF volume.
+
+  @param[in]   FileIdentifierDesc  File Identifier Descriptor pointer.
+  @param[out]  FileName            Decoded filename.
+
+  @retval EFI_SUCCESS           Filename decoded and read.
+  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
+**/
+EFI_STATUS
+GetFileNameFromFid (
+  IN   UDF_FILE_IDENTIFIER_DESCRIPTOR  *FileIdentifierDesc,
+  OUT  CHAR16                          *FileName
+  );
+
+/**
+  Resolve a symlink file on an UDF volume.
+
+  @param[in]   BlockIo        BlockIo interface.
+  @param[in]   DiskIo         DiskIo interface.
+  @param[in]   Volume         UDF volume information structure.
+  @param[in]   Parent         Parent file.
+  @param[in]   FileEntryData  FE/EFE structure pointer.
+  @param[out]  File           Resolved file.
+
+  @retval EFI_SUCCESS          Symlink file resolved.
+  @retval EFI_UNSUPPORTED      Extended Allocation Descriptors not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The symlink file was not resolved due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+ResolveSymlink (
+  IN   EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL   *DiskIo,
+  IN   UDF_VOLUME_INFO        *Volume,
+  IN   UDF_FILE_INFO          *Parent,
+  IN   VOID                   *FileEntryData,
+  OUT  UDF_FILE_INFO          *File
+  );
+
+/**
+  Clean up in-memory UDF volume information.
+
+  @param[in] Volume Volume information pointer.
+
+**/
+VOID
+CleanupVolumeInformation (
+  IN UDF_VOLUME_INFO *Volume
+  );
+
+/**
+  Clean up in-memory UDF file information.
+
+  @param[in] File File information pointer.
+
+**/
+VOID
+CleanupFileInformation (
+  IN UDF_FILE_INFO *File
+  );
+
+/**
+  Find a file from its absolute path on an UDF volume.
+
+  @param[in]   BlockIo  BlockIo interface.
+  @param[in]   DiskIo   DiskIo interface.
+  @param[in]   Volume   UDF volume information structure.
+  @param[in]   File     File information structure.
+  @param[out]  Size     Size of the file.
+
+  @retval EFI_SUCCESS          File size calculated and set in @p Size.
+  @retval EFI_UNSUPPORTED      Extended Allocation Descriptors not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The file size was not calculated due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+GetFileSize (
+  IN   EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL   *DiskIo,
+  IN   UDF_VOLUME_INFO        *Volume,
+  IN   UDF_FILE_INFO          *File,
+  OUT  UINT64                 *Size
+  );
+
+/**
+  Set information about a file on an UDF volume.
+
+  @param[in]      File        File pointer.
+  @param[in]      FileSize    Size of the file.
+  @param[in]      FileName    Filename of the file.
+  @param[in out]  BufferSize  Size of the returned file infomation.
+  @param[out]     Buffer      Data of the returned file information.
+
+  @retval EFI_SUCCESS          File information set.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The file information was not set due to lack of
+                               resources.
+
+**/
+EFI_STATUS
+SetFileInfo (
+  IN      UDF_FILE_INFO  *File,
+  IN      UINT64         FileSize,
+  IN      CHAR16         *FileName,
+  IN OUT  UINTN          *BufferSize,
+  OUT     VOID           *Buffer
+  );
+
+/**
+  Get volume and free space size information of an UDF volume.
+
+  @param[in]   BlockIo        BlockIo interface.
+  @param[in]   DiskIo         DiskIo interface.
+  @param[in]   Volume         UDF volume information structure.
+  @param[out]  VolumeSize     Volume size.
+  @param[out]  FreeSpaceSize  Free space size.
+
+  @retval EFI_SUCCESS          Volume and free space size calculated.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The volume and free space size were not
+                               calculated due to lack of resources.
+
+**/
+EFI_STATUS
+GetVolumeSize (
+  IN   EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN   EFI_DISK_IO_PROTOCOL   *DiskIo,
+  IN   UDF_VOLUME_INFO        *Volume,
+  OUT  UINT64                 *VolumeSize,
+  OUT  UINT64                 *FreeSpaceSize
+  );
+
+/**
+  Seek a file and read its data into memory on an UDF volume.
+
+  @param[in]      BlockIo       BlockIo interface.
+  @param[in]      DiskIo        DiskIo interface.
+  @param[in]      Volume        UDF volume information structure.
+  @param[in]      File          File information structure.
+  @param[in]      FileSize      Size of the file.
+  @param[in out]  FilePosition  File position.
+  @param[in out]  Buffer        File data.
+  @param[in out]  BufferSize    Read size.
+
+  @retval EFI_SUCCESS          File seeked and read.
+  @retval EFI_UNSUPPORTED      Extended Allocation Descriptors not supported.
+  @retval EFI_NO_MEDIA         The device has no media.
+  @retval EFI_DEVICE_ERROR     The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+  @retval EFI_OUT_OF_RESOURCES The file's recorded data was not read due to lack
+                               of resources.
+
+**/
+EFI_STATUS
+ReadFileData (
+  IN      EFI_BLOCK_IO_PROTOCOL  *BlockIo,
+  IN      EFI_DISK_IO_PROTOCOL   *DiskIo,
+  IN      UDF_VOLUME_INFO        *Volume,
+  IN      UDF_FILE_INFO          *File,
+  IN      UINT64                 FileSize,
+  IN OUT  UINT64                 *FilePosition,
+  IN OUT  VOID                   *Buffer,
+  IN OUT  UINT64                 *BufferSize
+  );
+
+/**
+  Check if ControllerHandle supports an UDF file system.
+
+  @param[in]  This                Protocol instance pointer.
+  @param[in]  ControllerHandle    Handle of device to test.
+
+  @retval EFI_SUCCESS             UDF file system found.
+  @retval EFI_UNSUPPORTED         UDF file system not found.
+
+**/
+EFI_STATUS
+SupportUdfFileSystem (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle
+  );
+
+/**
+  Mangle a filename by cutting off trailing whitespaces, "\\", "." and "..".
+
+  @param[in] FileName Filename.
+
+  @retval @p FileName Filename mangled.
+
+**/
+CHAR16 *
+MangleFileName (
+  IN CHAR16        *FileName
+  );
+
+/**
+  Test to see if this driver supports ControllerHandle. Any ControllerHandle
+  than contains a BlockIo and DiskIo protocol can be supported.
+
+  @param  This                Protocol instance pointer.
+  @param  ControllerHandle    Handle of device to test
+  @param  RemainingDevicePath Optional parameter use to pick a specific child
+                              device to start.
+
+  @retval EFI_SUCCESS         This driver supports this device
+  @retval EFI_ALREADY_STARTED This driver is already running on this device
+  @retval other               This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+UdfDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+/**
+  Start this driver on ControllerHandle by opening a Block IO and Disk IO
+  protocol, reading Device Path, and creating a child handle with a
+  Disk IO and device path protocol.
+
+  @param  This                 Protocol instance pointer.
+  @param  ControllerHandle     Handle of device to bind driver to
+  @param  RemainingDevicePath  Optional parameter use to pick a specific child
+                               device to start.
+
+  @retval EFI_SUCCESS          This driver is added to ControllerHandle
+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle
+  @retval other                This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+UdfDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+/**
+  Stop this driver on ControllerHandle. Support stopping any child handles
+  created by this driver.
+
+  @param  This              Protocol instance pointer.
+  @param  ControllerHandle  Handle of device to stop driver on
+  @param  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
+                            children is zero stop the entire bus driver.
+  @param  ChildHandleBuffer List of Child Handles to Stop.
+
+  @retval EFI_SUCCESS       This driver is removed ControllerHandle
+  @retval other             This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+UdfDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL   *This,
+  IN  EFI_HANDLE                    ControllerHandle,
+  IN  UINTN                         NumberOfChildren,
+  IN  EFI_HANDLE                    *ChildHandleBuffer
+  );
+
+//
+// EFI Component Name Functions
+//
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  );
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UdfComponentNameGetControllerName (
+  IN   EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN   EFI_HANDLE                   ControllerHandle,
+  IN   EFI_HANDLE                   ChildHandle OPTIONAL,
+  IN   CHAR8                        *Language,
+  OUT  CHAR16                       **ControllerName
+  );
+
+#endif // _UDF_H_
diff --git a/MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf b/MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
new file mode 100644
index 0000000000..7fea6bd9dc
--- /dev/null
+++ b/MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
@@ -0,0 +1,66 @@
+## @file
+#  UDF/ECMA-167 file system driver.
+#
+#  Copyright (C) 2014-2017 Paulo Alcantara <pcacjr@zytor.com>
+#
+#  This program and the accompanying materials
+#  are licensed and made available under the terms and conditions of the BSD License
+#  which 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.
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = UdfDxe
+  FILE_GUID                      = 905f13b0-8f91-4b0a-bd76-e1e78f9422e4
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = InitializeUdf
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC
+#
+#  DRIVER_BINDING                = gUdfDriverBinding
+#  COMPONENT_NAME                = gUdfComponentName
+#  COMPONENT_NAME2               = gUdfComponentName2
+#
+
+[Sources]
+  ComponentName.c
+  FileSystemOperations.c
+  FileName.c
+  File.c
+  Udf.c
+  Udf.h
+
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+
+[LibraryClasses]
+  DevicePathLib
+  UefiBootServicesTableLib
+  MemoryAllocationLib
+  BaseMemoryLib
+  UefiLib
+  BaseLib
+  UefiDriverEntryPoint
+  DebugLib
+
+
+[Guids]
+  gEfiFileInfoGuid                              ## SOMETIMES_CONSUMES ## Protocol
+  gEfiFileSystemInfoGuid                        ## SOMETIMES_CONSUMES ## Protocol
+
+
+[Protocols]
+  gEfiSimpleFileSystemProtocolGuid              ## BY_START
+  gEfiDevicePathProtocolGuid                    ## BY_START
+  gEfiBlockIoProtocolGuid                       ## TO_START
+  gEfiDiskIoProtocolGuid                        ## TO_START
-- 
2.11.0

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel