Step to UEFI (34) ----- FindFile2 查找特定文件

前面介绍了如何枚举全部文件,这里介绍一下如何枚举特定的问题。比如,用 “M*.*” 匹配全部 M开头的文件。

参考 touch 命令的Source Code 很快有了方案,使用:ShellOpenFileMetaArg

对应的头文件在 \ShellPkg\Include\Library\ShellLib.h

/**
  Opens a group of files based on a path.

  This function uses the Arg to open all the matching files. Each matched
  file has a SHELL_FILE_ARG structure to record the file information. These
  structures are placed on the list ListHead. Users can get the SHELL_FILE_ARG
  structures from ListHead to access each file. This function supports wildcards
  and will process '?' and '*' as such.  The list must be freed with a call to
  ShellCloseFileMetaArg().

  If you are NOT appending to an existing list *ListHead must be NULL.  If
  *ListHead is NULL then it must be callee freed.

  @param[in] Arg                 The pointer to path string.
  @param[in] OpenMode            Mode to open files with.
  @param[in, out] ListHead       Head of linked list of results.

  @retval EFI_SUCCESS           The operation was sucessful and the list head
                                contains the list of opened files.
  @retval != EFI_SUCCESS        The operation failed.

  @sa InternalShellConvertFileListType
**/
EFI_STATUS
EFIAPI
ShellOpenFileMetaArg (
  IN CHAR16                     *Arg,
  IN UINT64                     OpenMode,
  IN OUT EFI_SHELL_FILE_INFO    **ListHead
  );

 

从介绍上来看,最主要的参数有2个:Arg输入要查找的路径,可以使用通配符。ListHead 返回结果。结果实际上是两部分一部分是 SHELL_FILE_ARG 结构体,另外一部分是 EFI_SHELL_FILE_INFO 结构体。前者只有一个,后者有一个或者很多个。他们使用链表结构联系在一起。上面这样的结构体感觉上很奇怪,不过确实是这样的。可以在Touch的 Source Code中看到。他使用 GetFirstNode 来跳过第一个不需要的结构体。这个函数可以在 \MdePkg\Library\BaseLib\LinkedList.c 里面看到

/**
  Retrieves the first node of a doubly-linked list.

  Returns the first node of a doubly-linked list.  List must have been 
  initialized with INTIALIZE_LIST_HEAD_VARIABLE() or InitializeListHead().
  If List is empty, then List is returned.

  If List is NULL, then ASSERT().
  If List was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or 
  InitializeListHead(), then ASSERT().
  If PcdMaximumLinkedListLenth is not zero, and the number of nodes
  in List, including the List node, is greater than or equal to
  PcdMaximumLinkedListLength, then ASSERT().

  @param  List  A pointer to the head node of a doubly-linked list.

  @return The first node of a doubly-linked list.
  @retval NULL  The list is empty.

**/
LIST_ENTRY *
EFIAPI
GetFirstNode (
  IN      CONST LIST_ENTRY          *List
  )
{
  //
  // ASSERT List not too long
  //
  ASSERT (InternalBaseLibIsNodeInList (List, List, FALSE));

  return List->ForwardLink;
}

 

最终编写程序如下

#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 PrintShellFileInfo(EFI_SHELL_FILE_INFO     *ShellFileInfo)
{
  //Print(L"Status [%d]\n",ShellFileInfo-> Status);
  Print(L"FullName [%s]\n",ShellFileInfo-> FullName);
  //Print(L"FileName [%s]\n",ShellFileInfo-> FileName);
  //Print(L"Handle [%d]\n",ShellFileInfo->Handle);  
}

int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
  //EFI_FILE_HANDLE   DirHandle;
  RETURN_STATUS     Status;
  EFI_SHELL_FILE_INFO *FileList=NULL;
  EFI_SHELL_FILE_INFO *Node;
  
  Status = ShellOpenFileMetaArg(L"fsnt0:\\a*.*", EFI_FILE_MODE_READ, &FileList);
  if(Status != RETURN_SUCCESS) {
        Print(L"OpenFile failed!\n");
		return EFI_SUCCESS;
  }							   
  
  //Print(L"Signature [%X]\n",((SHELL_FILE_ARG *)&FileList)->Signature);  
  //Print(L"Status []\n",((SHELL_FILE_ARG *)&FileList)->Status);  
  //Print(L"ParentName [%s]\n",((SHELL_FILE_ARG *)&FileList)->ParentName);  
  //Print(L"FullName [%s]\n",((SHELL_FILE_ARG *)&FileList)->FullName);  
  //Print(L"FileName [%s]\n",((SHELL_FILE_ARG *)&FileList)->FileName);  

  
  // check that we have at least 1 file
  //
  if (FileList == NULL || IsListEmpty(&FileList->Link)) {
                Print(L"No Files Found!\n");
  } else {
		//
		// loop through the list and make sure we are not aborting...
		//
		for ( Node = (EFI_SHELL_FILE_INFO*)GetFirstNode(&FileList->Link)
			; !IsNull(&FileList->Link, &Node->Link) && !ShellGetExecutionBreakFlag()
			; Node = (EFI_SHELL_FILE_INFO*)GetNextNode(&FileList->Link, &Node->Link)){
					PrintShellFileInfo(Node);			   
				//
				// make sure the file opened ok
				//
				if (EFI_ERROR(Node->Status)){
					Print(L"OpenFile Error!\n");
				}

        }
    }
		  
   //
   // Free the fileList
   //
   if (FileList != NULL && !IsListEmpty(&FileList->Link)) {
       Status = ShellCloseFileMetaArg(&FileList);
   }
   FileList = NULL;
		
  return EFI_SUCCESS;
}

 

运行结果

FindFIle2

可以看出能够正常查找到我们需要的 fsnt0:x下面a开头的文件。

代码下载

FindFile2

===============================================================================
最后有一个问题:
前面说过,ShellOpenFileMetaArg 输出的结果是2部分组成的,我的程序只是输出了后面的那个结构体,那么前面的那个结构体呢?

  Status = ShellOpenFileMetaArg(L"fsnt0:\\a*.*", EFI_FILE_MODE_READ, &FileList);
  if(Status != RETURN_SUCCESS) {
        Print(L"OpenFile failed!\n");
		return EFI_SUCCESS;
  }							   
  
  Print(L"Signature [%X]\n",((SHELL_FILE_ARG *)&FileList)->Signature);  
  Print(L"Status []\n",((SHELL_FILE_ARG *)&FileList)->Status);  
  Print(L"ParentName [%s]\n",((SHELL_FILE_ARG *)&FileList)->ParentName);  
  Print(L"FullName [%s]\n",((SHELL_FILE_ARG *)&FileList)->FullName);  
  Print(L"FileName [%s]\n",((SHELL_FILE_ARG *)&FileList)->FileName); 

 

结果出来之后非常奇怪的是 Signature 的输出并非固定的数值,而根据 EfiShellEnvironment2.h 来看,这个似乎应该是一个固定的值。

不知道是我理解有偏差还是输出方法不对。有懂的朋友请指教一下。谢谢!

发表评论

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