Step to UEFI (33) ----- FindFile 枚举目录下所有文件

研究一下如何枚举目录下的全部文件,查到了2个函数 ShellFindFirstFile 和 ShellFindNextFile。下面就研究如何使用这两个函数。

这两个函数在 \ShellPkg\Include\Library\ShellLib.h 中有定义

/** Retrieve first entry from a directory.

  This function takes an open directory handle and gets information from the
  first entry in the directory.  A buffer is allocated to contain
  the information and a pointer to the buffer is returned in *Buffer.  The
  caller can use ShellFindNextFile() to get subsequent directory entries.

  The buffer will be freed by ShellFindNextFile() when the last directory
  entry is read.  Otherwise, the caller must free the buffer, using FreePool,
  when finished with it.

  @param[in]  DirHandle         The file handle of the directory to search.
  @param[out] Buffer            The pointer to the buffer for the file's information.

  @retval EFI_SUCCESS           Found the first file.
  @retval EFI_NOT_FOUND         Cannot find the directory.
  @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.
  @return Others                Status of ShellGetFileInfo, ShellSetFilePosition,
                                or ShellReadFile.

  @sa ShellReadFile
**/
EFI_STATUS
EFIAPI
ShellFindFirstFile (
  IN      SHELL_FILE_HANDLE       DirHandle,
     OUT  EFI_FILE_INFO         **Buffer
  );


/** Retrieve next entries from a directory.

  To use this function, the caller must first call the ShellFindFirstFile()
  function to get the first directory entry.  Subsequent directory entries are
  retrieved by using the ShellFindNextFile() function.  This function can
  be called several times to get each entry from the directory.  If the call of
  ShellFindNextFile() retrieved the last directory entry, the next call of
  this function will set *NoFile to TRUE and free the buffer.

  @param[in]  DirHandle         The file handle of the directory.
  @param[out] Buffer            The pointer to buffer for file's information.
  @param[out] NoFile            The pointer to boolean when last file is found.

  @retval EFI_SUCCESS           Found the next file, or reached last file
  @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.
**/
EFI_STATUS
EFIAPI
ShellFindNextFile(
  IN SHELL_FILE_HANDLE                      DirHandle,
  OUT EFI_FILE_INFO              *Buffer,
  OUT BOOLEAN                    *NoFile
  )
{
  //
  // pass to file handle lib
  //
  return (FileHandleFindNextFile(DirHandle, Buffer, NoFile));
}

 

阅读说明可以发现,调用之后会分配一个Buffer,然后由调用者来负责释放。

开始写程序之后很快问题就来了,下面这样很简单的程序会让我的模拟器一次次崩溃。

int
EFIAPI
main (
  IN int Argc,
  IN char **Argv
  )
{
  EFI_FILE_HANDLE   DirHandle;
  RETURN_STATUS     Status;
  EFI_FILE_INFO     *FileInfo = NULL;
  BOOLEAN			NoFile=FALSE;
  
  Status = ShellOpenFileByName(L"fsnt0:\\testz", (SHELL_FILE_HANDLE *)&DirHandle,
                               EFI_FILE_MODE_READ , 0);

  if(Status != RETURN_SUCCESS) {
        Print(L"OpenFile failed!\n");
		return EFI_SUCCESS;
      }							   

  ShellFindFirstFile(DirHandle,&FileInfo);
  PrintFileInfo(FileInfo);
  free(FileInfo);
  while (FALSE==NoFile) 
    {
		Status=ShellFindNextFile(DirHandle,FileInfo,&NoFile);
		if (EFI_SUCCESS == Status)
			{
			}
		Print(L"No File[%d]\n",NoFile);	
	}
 
  return EFI_SUCCESS;
}

 

百思不得其解,在程序中加入输出函数,跟踪一下。出错的部分在ShellFindNextFile(DirHandle,FileInfo,&NoFile); 中。追踪进入 \ShellPkg\Library\UefiFileHandleLib\UefiFileHandleLib.c 的 FileHandleFindNextFile。

/** Retrieve next entries from a directory.

  To use this function, the caller must first call the FileHandleFindFirstFile()
  function to get the first directory entry.  Subsequent directory entries are
  retrieved by using the FileHandleFindNextFile() function.  This function can
  be called several times to get each entry from the directory.  If the call of
  FileHandleFindNextFile() retrieved the last directory entry, the next call of
  this function will set *NoFile to TRUE and free the buffer.

  @param[in]  DirHandle         The file handle of the directory.
  @param[out] Buffer            The pointer to buffer for file's information.
  @param[out] NoFile            The pointer to boolean when last file is found.

  @retval EFI_SUCCESS           Found the next file, or reached last file
  @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.
**/
EFI_STATUS
EFIAPI
FileHandleFindNextFile(
  IN EFI_FILE_HANDLE          DirHandle,
  OUT EFI_FILE_INFO          *Buffer,
  OUT BOOLEAN                *NoFile
  )
{
  EFI_STATUS    Status;
  UINTN         BufferSize;

  //
  // ASSERTs for DirHandle or Buffer or NoFile poitners being NULL
  //
  ASSERT (DirHandle != NULL);
  ASSERT (Buffer    != NULL);
  ASSERT (NoFile    != NULL);

  //
  // This BufferSize MUST stay equal to the originally allocated one in GetFirstFile
  //
  BufferSize = FIND_XXXXX_FILE_BUFFER_SIZE;

  //
  // read in the info about the next file
  //
  Status = FileHandleRead (DirHandle, &BufferSize, Buffer);
  ASSERT(Status != EFI_BUFFER_TOO_SMALL);
  if (EFI_ERROR(Status)) {
    return (Status);
  }

  //
  // If we read 0 bytes (but did not have erros) we already read in the last file.
  //
  if (BufferSize == 0) {
    FreePool(Buffer);
    *NoFile = TRUE;
  }

  return (EFI_SUCCESS);
}

 

确定是 FreePool(Buffer); 导致的。看到这个代码感觉很奇怪,明明是让 caller 负责释放,为什么他在里面要干这事情?再阅读函数说明“ If the call of
FileHandleFindNextFile() retrieved the last directory entry, the next call of
this function will set *NoFile to TRUE and free the buffer.” 原来这个函数设计的意思是:调用FileHandleFindNextFile 不断取得文件时,如果你中间停止了,那么请自行释放Buffer;但是如果你一直调用到了最后一个文件,那么函数本身会帮你释放掉而不需要你来做。

最终的程序不复杂

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>

#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

extern EFI_SHELL_PROTOCOL        *gEfiShellProtocol;

void PrintFileInfo(EFI_FILE_INFO     *FileInfo)
{
  Print(L"Filesize [%ld] bytes\n",FileInfo-> FileSize);
  Print(L"PhysicalSize [%ld] bytes\n",FileInfo-> PhysicalSize);  

  Print(L"File Create date [%d-%d-%d %d-%d-%d]\n",
		FileInfo-> CreateTime.Year,
		FileInfo-> CreateTime.Month,
		FileInfo-> CreateTime.Day,
		FileInfo-> CreateTime.Hour,
		FileInfo-> CreateTime.Minute,
		FileInfo-> CreateTime.Second);    
		
  Print(L"File last accessed date [%d-%d-%d %d-%d-%d]\n",
		FileInfo-> LastAccessTime.Year,
		FileInfo-> LastAccessTime.Month,
		FileInfo-> LastAccessTime.Day,
		FileInfo-> LastAccessTime.Hour,
		FileInfo-> LastAccessTime.Minute,
		FileInfo-> LastAccessTime.Second);   		
		
  Print(L"File last modification date [%d-%d-%d %d-%d-%d]\n",
		FileInfo-> ModificationTime.Year,
		FileInfo-> ModificationTime.Month,
		FileInfo-> ModificationTime.Day,
		FileInfo-> ModificationTime.Hour,
		FileInfo-> ModificationTime.Minute,
		FileInfo-> ModificationTime.Second);   		

  Print(L"File Name [%s]\n",&FileInfo->FileName[0]);  
}

int
EFIAPI
main (
  IN int Argc,
  IN char **Argv
  )
{
  EFI_FILE_HANDLE   DirHandle;
  RETURN_STATUS     Status;
  EFI_FILE_INFO     *FileInfo = NULL;
  BOOLEAN			NoFile=FALSE;
  
  Status = ShellOpenFileByName(L"fsnt0:", (SHELL_FILE_HANDLE *)&DirHandle,
                               EFI_FILE_MODE_READ , 0);

  if(Status != RETURN_SUCCESS) {
        Print(L"OpenFile failed!\n");
		return EFI_SUCCESS;
      }							   

  ShellFindFirstFile(DirHandle,&FileInfo);
  while (TRUE!=NoFile) 
    {
	    PrintFileInfo(FileInfo);
		Status=ShellFindNextFile(DirHandle,FileInfo,&NoFile);
	}
  return EFI_SUCCESS;
}

 

运行结果

findfile

完整代码下载

FindFile

《Step to UEFI (33) ----- FindFile 枚举目录下所有文件》有9个想法

  1. 请问你的efi文件是怎么编译出来的,《UEFI原理与编程》中说使用main 函数的应用程序工程模块在AppPkg 环境下才能成功编译,但我没找到AppPkg

  2. thank so much

    i'll try Another method
    uefiman entry point .....

    EFI FILE PROTOCOL * ROOT
    Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, Root);

    Status = Root ->Open(
    Root,
    & SystemFile,
    (CHAR16*)L"TEST.TXT",
    EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
    0
    );

    fs0:\ file only accessible

    fs2:\test.txt file access method please......

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注