Discussion:
[edk2] Directory/File system traversal example
John Smith
2014-12-11 17:35:27 UTC
Permalink
Anyone know where I can get an example of walking through the filesystem
with a UEFI application?

More generally, is there a set of skeleton code examples like Microsoft
does for kernel development?

V/R

JRS
Andrew Fish
2014-12-11 17:59:02 UTC
Permalink
Anyone know where I can get an example of walking through the filesystem with a UEFI application?
You could get some ideas from this code: https://svn.code.sf.net/p/edk2/code/trunk/edk2/EmbeddedPkg/Library/EfiFileLib/EfiFileLib.c

It has Open/Close/Seek/Tell/Read/Write. The library abstracts more than just EFI Simple File System, but it should be a good example.
Supported Device Names:
A0x1234:0x12 - A memory buffer starting at address 0x1234 for 0x12 bytes
l1: - EFI LoadFile device one.
B0: - EFI BlockIo zero.
fs3: - EFI Simple File System device 3
Fv2: - EFI Firmware VOlume device 2
10.0.1.102: - TFTP service IP followed by the file name
Note: There is no concept of Volume names in EFI. This library makes up a namespace, the shell makes up a namespace, etc. In EFI you just have a set of EFI Simple File System protocols on handles, and the device paths represent the name/location of the file system.
More generally, is there a set of skeleton code examples like Microsoft does for kernel development?
There is a lot of open source code to look at: https://svn.code.sf.net/p/edk2/code/trunk/edk2/ <https://svn.code.sf.net/p/edk2/code/trunk/edk2/>

Thanks,

Andrew Fish
V/R
JRS
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
John Smith
2014-12-11 19:10:38 UTC
Permalink
Post by John Smith
Anyone know where I can get an example of walking through the filesystem
with a UEFI application?
https://svn.code.sf.net/p/edk2/code/trunk/edk2/EmbeddedPkg/Library/EfiFileLib/EfiFileLib.c
Thanks, I'll look at that.
Post by John Smith
It has Open/Close/Seek/Tell/Read/Write. The library abstracts more than
just EFI Simple File System, but it should be a good example.
A0x1234:0x12 - A memory buffer starting at address 0x1234 for 0x12 bytes
l1: - EFI LoadFile device one.
B0: - EFI BlockIo zero.
fs3: - EFI Simple File System device 3
Fv2: - EFI Firmware VOlume device 210.0.1.102: - TFTP service IP followed by the file name
Note: There is no concept of Volume names in EFI.
Well that explains why FileSystemInfoBuf->VolumeLabel doesn't really make
sense in my NT32 example.
Post by John Smith
This library makes up a namespace, the shell makes up a namespace, etc. In
EFI you just have a set of EFI Simple File System protocols on handles, and
the device paths represent the name/location of the file system.
More generally, is there a set of skeleton code examples like Microsoft
does for kernel development?
https://svn.code.sf.net/p/edk2/code/trunk/edk2/
The core difference is that the MS kernel code is organized around what
general types of activity you're going to be doing (e.g. network) and then
there's example code for all the different levels of the stack that they
support people writing code at (WSK, NDIS protocol, NDIS IM, NDIS
miniport). Saying "use grep to search through an entire mega-LOC repository
for random keywords you think of" isn't exactly the most productive way to
bootstrap people, in my experience. Well organized example skeletons are.

Good to see someone on the UEFI Forum board of directors answering
questions though :-)

V/R

JRS
Post by John Smith
Thanks,
Andrew Fish
V/R
JRS
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
Thomas Rognon
2014-12-11 19:02:38 UTC
Permalink
Not skeleton code, but look at ls in ShellPkg

https://svn.code.sf.net/p/edk2/code/trunk/edk2/ShellPkg/Library/UefiShellLevel2CommandsLib/Ls.c
Post by John Smith
Anyone know where I can get an example of walking through the filesystem
with a UEFI application?
More generally, is there a set of skeleton code examples like Microsoft
does for kernel development?
V/R
JRS
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
John Smith
2014-12-11 19:31:13 UTC
Permalink
Thank you, that looks like it will get me very close. I had went into the
ShellPkg at one point while wandering the directory structure, but went
into Application rather than Library and then finding nothing I backed out
and went elsewhere (BDS).

V/R

JRS
Post by Thomas Rognon
Not skeleton code, but look at ls in ShellPkg
https://svn.code.sf.net/p/edk2/code/trunk/edk2/ShellPkg/Library/UefiShellLevel2CommandsLib/Ls.c
Post by John Smith
Anyone know where I can get an example of walking through the filesystem
with a UEFI application?
More generally, is there a set of skeleton code examples like Microsoft
does for kernel development?
V/R
JRS
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
Thomas Rognon
2014-12-11 19:47:26 UTC
Permalink
I was bored and feeling in high spirits, so I wrote some code for you.
Also, I agree it would be nice to have example snippets on the tianocore
website for common tasks. If someone sets that up, I'd contribute.

I didn't compile this, so please forgive any mistakes.

/**
loop through all volumes and recurse through all files in
each volume and call a user defined function to do
some processing on each file
*/
EFI_STATUS
EFIAPI
ProcessFiles (
IN EFI_HANDLE AgentHandle,
IN EFI_HANDLE FsHandle,
IN PROCESS_FILES_CALLOUT Callout,
IN VOID *Context
)
{
EFI_STATUS Status;
UINTN NumHandles;
EFI_HANDLE *Handles;
UINTN Index;

// gets all handles with simple file system installed
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumHandles,
&Handles
);
if (EFI_ERROR (Status)) {
return Status;
}

// loop through all handles we just got
for (Index = 0; Index < NumHandles; Index++) {
EFI_FILE_HANDLE Root;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
EFI_DEVICE_PATH *Dp;

// get simple file system protocol instance
// from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiSimpleFileSystemProtocolGuid,
&Fs,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on
handle.\n"));
continue;
}

// get device path instance from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiDevicePathProtocolGuid,
&Dp,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_DEVICE_PATH_PROTOCOL on
handle.\n"));
continue;
}

// open root dir from current simple file system
Status = Fs->OpenVolume (Fs, &Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Unable to open volume.\n"));
continue;
}

// recursively process files in root dir
Status = ProcessFilesInDir (
Root,
Dp,
Callout,
Context
);
Root->Close (Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "ProcessFilesInDir error. Continuing with next
volume...\n"));
continue;
}
}

return EFI_SUCCESS;
}

/**
recurse through directory, calling a user defined
function for each file
*/
EFI_STATUS
EFIAPI
ProcessFilesInDir (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH CONST *DirDp,
IN PROCESS_FILES_CALLOUT Callout,
IN VOID *Context
)
{
EFI_STATUS Status;
EFI_FILE_INFO *FileInfo;
CHAR16 *FileName;
UINTN FileInfoSize;
EFI_DEVICE_PATH *Dp;
EFI_FILE_HANDLE File;

// big enough to hold EFI_FILE_INFO struct and
// the whole file path
FileInfo = AllocatePool (MAX_FILE_INFO_SIZE);
if (FileInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}

for (;;) {
// get the next file's info. there's an internal position
// that gets incremented when you read from a directory
// so that subsequent reads gets the next file's info
FileInfoSize = MAX_FILE_INFO_SIZE;
Status = Dir->Read (Dir, &FileInfoSize, (VOID *) FileInfo);
if (EFI_ERROR (Status) || FileInfoSize == 0) {
FreePool (FileInfo);
return Status;
}

FileName = FileInfo->FileName;

// skip files named . or ..
if (StrCmp (FileName, L".") == 0 || StrCmp (FileName, L"..") == 0) {
continue;
}

// so we have absolute device path to child file/dir
Dp = AppendFileDevicePath (DirDp, FileName);
if (Dp == NULL) {
FreePool (FileInfo);
return EFI_OUT_OF_RESOURCES;
}

// read the file into a buffer
Status = Dir->Open (Dir, &File, FileName, EFI_FILE_MODE_READ, 0);
if (EFI_ERROR (Status)) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
// reset position just in case
File->SetPosition (File, 0);

// send all this stuff to a callout for processing
Callout (Dp, FileInfo, File, Context);

Dir->Close (File);

if (FileInfo->Attribute & EFI_FILE_DIRECTORY) {
//
// recurse
//

EFI_FILE_HANDLE NewDir;

Status = Dir->Open (Dir, &NewDir, FileName, EFI_FILE_MODE_READ, 0);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
NewDir->SetPosition (NewDir, 0);
Status = ProcessFilesInDir (
NewDir,
Dp,
Callout,
Context
);
Dir->Close (NewDir);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
}

FreePool (Dp);
}
}
Post by John Smith
Thank you, that looks like it will get me very close. I had went into the
ShellPkg at one point while wandering the directory structure, but went
into Application rather than Library and then finding nothing I backed out
and went elsewhere (BDS).
V/R
JRS
Post by Thomas Rognon
Not skeleton code, but look at ls in ShellPkg
https://svn.code.sf.net/p/edk2/code/trunk/edk2/ShellPkg/Library/UefiShellLevel2CommandsLib/Ls.c
Post by John Smith
Anyone know where I can get an example of walking through the filesystem
with a UEFI application?
More generally, is there a set of skeleton code examples like Microsoft
does for kernel development?
V/R
JRS
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
Thomas Rognon
2014-12-11 19:50:32 UTC
Permalink
oops ignore FsHandle parameter in ProcessFiles()
Post by Thomas Rognon
I was bored and feeling in high spirits, so I wrote some code for you.
Also, I agree it would be nice to have example snippets on the tianocore
website for common tasks. If someone sets that up, I'd contribute.
I didn't compile this, so please forgive any mistakes.
/**
loop through all volumes and recurse through all files in
each volume and call a user defined function to do
some processing on each file
*/
EFI_STATUS
EFIAPI
ProcessFiles (
IN EFI_HANDLE AgentHandle,
IN EFI_HANDLE FsHandle,
IN PROCESS_FILES_CALLOUT Callout,
IN VOID *Context
)
{
EFI_STATUS Status;
UINTN NumHandles;
EFI_HANDLE *Handles;
UINTN Index;
// gets all handles with simple file system installed
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumHandles,
&Handles
);
if (EFI_ERROR (Status)) {
return Status;
}
// loop through all handles we just got
for (Index = 0; Index < NumHandles; Index++) {
EFI_FILE_HANDLE Root;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
EFI_DEVICE_PATH *Dp;
// get simple file system protocol instance
// from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiSimpleFileSystemProtocolGuid,
&Fs,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on
handle.\n"));
continue;
}
// get device path instance from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiDevicePathProtocolGuid,
&Dp,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_DEVICE_PATH_PROTOCOL on
handle.\n"));
continue;
}
// open root dir from current simple file system
Status = Fs->OpenVolume (Fs, &Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Unable to open volume.\n"));
continue;
}
// recursively process files in root dir
Status = ProcessFilesInDir (
Root,
Dp,
Callout,
Context
);
Root->Close (Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "ProcessFilesInDir error. Continuing with next
volume...\n"));
continue;
}
}
return EFI_SUCCESS;
}
/**
recurse through directory, calling a user defined
function for each file
*/
EFI_STATUS
EFIAPI
ProcessFilesInDir (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH CONST *DirDp,
IN PROCESS_FILES_CALLOUT Callout,
IN VOID *Context
)
{
EFI_STATUS Status;
EFI_FILE_INFO *FileInfo;
CHAR16 *FileName;
UINTN FileInfoSize;
EFI_DEVICE_PATH *Dp;
EFI_FILE_HANDLE File;
// big enough to hold EFI_FILE_INFO struct and
// the whole file path
FileInfo = AllocatePool (MAX_FILE_INFO_SIZE);
if (FileInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
for (;;) {
// get the next file's info. there's an internal position
// that gets incremented when you read from a directory
// so that subsequent reads gets the next file's info
FileInfoSize = MAX_FILE_INFO_SIZE;
Status = Dir->Read (Dir, &FileInfoSize, (VOID *) FileInfo);
if (EFI_ERROR (Status) || FileInfoSize == 0) {
FreePool (FileInfo);
return Status;
}
FileName = FileInfo->FileName;
// skip files named . or ..
if (StrCmp (FileName, L".") == 0 || StrCmp (FileName, L"..") == 0) {
continue;
}
// so we have absolute device path to child file/dir
Dp = AppendFileDevicePath (DirDp, FileName);
if (Dp == NULL) {
FreePool (FileInfo);
return EFI_OUT_OF_RESOURCES;
}
// read the file into a buffer
Status = Dir->Open (Dir, &File, FileName, EFI_FILE_MODE_READ, 0);
if (EFI_ERROR (Status)) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
// reset position just in case
File->SetPosition (File, 0);
// send all this stuff to a callout for processing
Callout (Dp, FileInfo, File, Context);
Dir->Close (File);
if (FileInfo->Attribute & EFI_FILE_DIRECTORY) {
//
// recurse
//
EFI_FILE_HANDLE NewDir;
Status = Dir->Open (Dir, &NewDir, FileName, EFI_FILE_MODE_READ, 0);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
NewDir->SetPosition (NewDir, 0);
Status = ProcessFilesInDir (
NewDir,
Dp,
Callout,
Context
);
Dir->Close (NewDir);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
}
FreePool (Dp);
}
}
Post by John Smith
Thank you, that looks like it will get me very close. I had went into the
ShellPkg at one point while wandering the directory structure, but went
into Application rather than Library and then finding nothing I backed out
and went elsewhere (BDS).
V/R
JRS
Post by Thomas Rognon
Not skeleton code, but look at ls in ShellPkg
https://svn.code.sf.net/p/edk2/code/trunk/edk2/ShellPkg/Library/UefiShellLevel2CommandsLib/Ls.c
Post by John Smith
Anyone know where I can get an example of walking through the
filesystem with a UEFI application?
More generally, is there a set of skeleton code examples like Microsoft
does for kernel development?
V/R
JRS
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
John Smith
2014-12-13 18:43:14 UTC
Permalink
As a thanks to Thomas, and to pay it forward for everyone who comes after
me, here's a slightly modified compiling version of what he wrote. There
are some parts (like continuously re-reading the directory file) which I
found counter-intuitive and I doubt I would have figured out on my own by
reading the spec, which is why skeleton code is so important. It would be
great if someone started a repository

////////////////////////////////////////////////////////////////////
/** @file
Brief Description of UEFI MyHelloWorld
Detailed Description of UEFI MyHelloWorld
Copyright for UEFI MyHelloWorld
License for UEFI MyHelloWorld
**/

#pragma warning( disable : 4090 ) //couldn't find a way around this...maybe
someone can post the fix

//relative to MyWorkspace/MdePkg/Include/...
#include <Uefi.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiLib.h>
#include <Protocol/SimpleFileSystem.h>
#include <Library/MemoryAllocationLib.h>
#include <Guid/FileInfo.h>
#include <Library/DebugLib.h>
#include <Library/DevicePathLib.h> //for FileDevicePath

EFI_GUID gEfiSimpleFileSystemProtocolGuid =
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
EFI_BOOT_SERVICES *gBS;

EFI_STATUS
EFIAPI
ProcessFilesInDir (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH CONST *DirDp
);

EFI_STATUS
EFIAPI PerFileFunc (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH *DirDp,
IN EFI_FILE_INFO *FileInfo,
IN EFI_DEVICE_PATH *Dp
);

/**
The entry point for the application.

@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.

loop through all volumes and recurse through all files in
each volume and call a user defined function to do
some processing on each file
*/
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_HANDLE AgentHandle;
EFI_STATUS Status;
UINTN NumHandles;
EFI_HANDLE *Handles;
UINTN Index;
VOID *Context;

AgentHandle = ImageHandle;

// gets all handles with simple file system installed
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumHandles,
&Handles
);
if (EFI_ERROR (Status)) {
return Status;
}

// loop through all handles we just got
for (Index = 0; Index < NumHandles; Index++) {
EFI_FILE_HANDLE Root;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
EFI_DEVICE_PATH *Dp;

// get simple file system protocol instance
// from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiSimpleFileSystemProtocolGuid,
&Fs,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on
handle.\n"));
continue;
}

// get device path instance from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiDevicePathProtocolGuid,
&Dp,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_DEVICE_PATH_PROTOCOL on
handle.\n"));
continue;
}

// open root dir from current simple file system
Status = Fs->OpenVolume (Fs, &Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Unable to open volume.\n"));
continue;
}

// recursively process files in root dir
Context = NULL;
Status = ProcessFilesInDir (Root, Dp);
Root->Close (Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "ProcessFilesInDir error. Continuing with next
volume...\n"));
continue;
}

}

Print(L"Done successfully\n");

return EFI_SUCCESS;
}

//I have no idea what the max length of a file path is in EFI
#define MAX_FILE_INFO_SIZE 1024

/**
recurse through directory, calling a user defined
function for each file
*/
EFI_STATUS
EFIAPI
ProcessFilesInDir (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH CONST *DirDp
)
{
EFI_STATUS Status;
EFI_FILE_INFO *FileInfo;
CHAR16 *FileName;
UINTN FileInfoSize;
EFI_DEVICE_PATH *Dp;

// big enough to hold EFI_FILE_INFO struct and
// the whole file path
FileInfo = AllocatePool (MAX_FILE_INFO_SIZE);
if (FileInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}

for (;;) {
// get the next file's info. there's an internal position
// that gets incremented when you read from a directory
// so that subsequent reads gets the next file's info
FileInfoSize = MAX_FILE_INFO_SIZE;
Status = Dir->Read (Dir, &FileInfoSize, (VOID *) FileInfo);
if (EFI_ERROR (Status) || FileInfoSize == 0) { //this is how we
eventually exit this function when we run out of files
if (Status == EFI_BUFFER_TOO_SMALL) {
Print (L"EFI_FILE_INFO > MAX_FILE_INFO_SIZE. Increase the size\n");
}
FreePool (FileInfo);
return Status;
}

FileName = FileInfo->FileName;

// skip files named . or ..
if (StrCmp (FileName, L".") == 0 || StrCmp (FileName, L"..") == 0) {
continue;
}

// so we have absolute device path to child file/dir
Dp = FileDevicePath (DirDp, FileName);
if (Dp == NULL) {
FreePool (FileInfo);
return EFI_OUT_OF_RESOURCES;
}

// Do whatever processing on the file
PerFileFunc (Dir, DirDp, FileInfo, Dp);

if (FileInfo->Attribute & EFI_FILE_DIRECTORY) {
//
// recurse
//

EFI_FILE_HANDLE NewDir;

Status = Dir->Open (Dir, &NewDir, FileName, EFI_FILE_MODE_READ, 0);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
NewDir->SetPosition (NewDir, 0);

Status = ProcessFilesInDir (NewDir,Dp);
Dir->Close (NewDir);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
}

FreePool (Dp);
}
}

EFI_STATUS
EFIAPI PerFileFunc (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH *DirDp,
IN EFI_FILE_INFO *FileInfo,
IN EFI_DEVICE_PATH *Dp
)
{
EFI_STATUS Status;
EFI_FILE_HANDLE File;

Print (L"Path = %s FileName = %s\n", ConvertDevicePathToText(DirDp, TRUE,
TRUE), FileInfo->FileName);

// read the file into a buffer
Status = Dir->Open (Dir, &File, FileInfo->FileName, EFI_FILE_MODE_READ,
0);
if (EFI_ERROR (Status)) {
return Status;
}

// reset position just in case
File->SetPosition (File, 0);

// ****Do stuff on the file here****

Dir->Close (File);

return EFI_SUCCESS;
}

////////////////////////////////////////////////////////////////////

V/R

JRS
Post by Thomas Rognon
oops ignore FsHandle parameter in ProcessFiles()
Post by Thomas Rognon
I was bored and feeling in high spirits, so I wrote some code for you.
Also, I agree it would be nice to have example snippets on the tianocore
website for common tasks. If someone sets that up, I'd contribute.
I didn't compile this, so please forgive any mistakes.
/**
loop through all volumes and recurse through all files in
each volume and call a user defined function to do
some processing on each file
*/
EFI_STATUS
EFIAPI
ProcessFiles (
IN EFI_HANDLE AgentHandle,
IN EFI_HANDLE FsHandle,
IN PROCESS_FILES_CALLOUT Callout,
IN VOID *Context
)
{
EFI_STATUS Status;
UINTN NumHandles;
EFI_HANDLE *Handles;
UINTN Index;
// gets all handles with simple file system installed
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumHandles,
&Handles
);
if (EFI_ERROR (Status)) {
return Status;
}
// loop through all handles we just got
for (Index = 0; Index < NumHandles; Index++) {
EFI_FILE_HANDLE Root;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
EFI_DEVICE_PATH *Dp;
// get simple file system protocol instance
// from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiSimpleFileSystemProtocolGuid,
&Fs,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on
handle.\n"));
continue;
}
// get device path instance from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiDevicePathProtocolGuid,
&Dp,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_DEVICE_PATH_PROTOCOL on
handle.\n"));
continue;
}
// open root dir from current simple file system
Status = Fs->OpenVolume (Fs, &Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Unable to open volume.\n"));
continue;
}
// recursively process files in root dir
Status = ProcessFilesInDir (
Root,
Dp,
Callout,
Context
);
Root->Close (Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "ProcessFilesInDir error. Continuing with next
volume...\n"));
continue;
}
}
return EFI_SUCCESS;
}
/**
recurse through directory, calling a user defined
function for each file
*/
EFI_STATUS
EFIAPI
ProcessFilesInDir (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH CONST *DirDp,
IN PROCESS_FILES_CALLOUT Callout,
IN VOID *Context
)
{
EFI_STATUS Status;
EFI_FILE_INFO *FileInfo;
CHAR16 *FileName;
UINTN FileInfoSize;
EFI_DEVICE_PATH *Dp;
EFI_FILE_HANDLE File;
// big enough to hold EFI_FILE_INFO struct and
// the whole file path
FileInfo = AllocatePool (MAX_FILE_INFO_SIZE);
if (FileInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
for (;;) {
// get the next file's info. there's an internal position
// that gets incremented when you read from a directory
// so that subsequent reads gets the next file's info
FileInfoSize = MAX_FILE_INFO_SIZE;
Status = Dir->Read (Dir, &FileInfoSize, (VOID *) FileInfo);
if (EFI_ERROR (Status) || FileInfoSize == 0) {
FreePool (FileInfo);
return Status;
}
FileName = FileInfo->FileName;
// skip files named . or ..
if (StrCmp (FileName, L".") == 0 || StrCmp (FileName, L"..") == 0) {
continue;
}
// so we have absolute device path to child file/dir
Dp = AppendFileDevicePath (DirDp, FileName);
if (Dp == NULL) {
FreePool (FileInfo);
return EFI_OUT_OF_RESOURCES;
}
// read the file into a buffer
Status = Dir->Open (Dir, &File, FileName, EFI_FILE_MODE_READ, 0);
if (EFI_ERROR (Status)) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
// reset position just in case
File->SetPosition (File, 0);
// send all this stuff to a callout for processing
Callout (Dp, FileInfo, File, Context);
Dir->Close (File);
if (FileInfo->Attribute & EFI_FILE_DIRECTORY) {
//
// recurse
//
EFI_FILE_HANDLE NewDir;
Status = Dir->Open (Dir, &NewDir, FileName, EFI_FILE_MODE_READ, 0);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
NewDir->SetPosition (NewDir, 0);
Status = ProcessFilesInDir (
NewDir,
Dp,
Callout,
Context
);
Dir->Close (NewDir);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
}
FreePool (Dp);
}
}
Post by John Smith
Thank you, that looks like it will get me very close. I had went into
the ShellPkg at one point while wandering the directory structure, but went
into Application rather than Library and then finding nothing I backed out
and went elsewhere (BDS).
V/R
JRS
Post by Thomas Rognon
Not skeleton code, but look at ls in ShellPkg
https://svn.code.sf.net/p/edk2/code/trunk/edk2/ShellPkg/Library/UefiShellLevel2CommandsLib/Ls.c
On Thu, Dec 11, 2014 at 11:35 AM, John Smith <
Post by John Smith
Anyone know where I can get an example of walking through the
filesystem with a UEFI application?
More generally, is there a set of skeleton code examples like
Microsoft does for kernel development?
V/R
JRS
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
Andrew Fish
2014-12-13 19:16:29 UTC
Permalink
As a thanks to Thomas, and to pay it forward for everyone who comes after me, here's a slightly modified compiling version of what he wrote. There are some parts (like continuously re-reading the directory file) which I found counter-intuitive and I doubt I would have figured out on my own by reading the spec, which is why skeleton code is so important. It would be great if someone started a repository
Not sure about how to start a new repository, but you need to contribute under a license structure.

The edk2 is BSD + TianoCore Contribution Agreement. Technically speaking you should add your copyright (required to enforce the license), and the BDS license to the code. The TianoCore Contribution Agreement is only required in the check in message, or an email like this to make a contribution (The Contribution Agreement is required as BSD is to ambiguous about IP rights)

Contributed-under: TianoCore Contribution Agreement 1.0

Thanks,

Andrew Fish
////////////////////////////////////////////////////////////////////
Brief Description of UEFI MyHelloWorld
Detailed Description of UEFI MyHelloWorld
Copyright for UEFI MyHelloWorld
License for UEFI MyHelloWorld
**/
#pragma warning( disable : 4090 ) //couldn't find a way around this...maybe someone can post the fix
You are converting between CONST and non-CONST device paths without casting. That changes the compilers view of the variable type, so you have to cast to be explicit.
//relative to MyWorkspace/MdePkg/Include/...
#include <Uefi.h>
#include <Library/UefiApplicationEntryPoint.h>
You don’t need this include.
#include <Library/UefiLib.h>
#include <Protocol/SimpleFileSystem.h>
#include <Library/MemoryAllocationLib.h>
#include <Guid/FileInfo.h>
#include <Library/DebugLib.h>
#include <Library/DevicePathLib.h> //for FileDevicePath
EFI_GUID gEfiSimpleFileSystemProtocolGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
You don’t need this, just add it to the INF [Protocols] section and the global will exist when you link, the extern is in <Protocol/SimpleFileSystem.h>
EFI_BOOT_SERVICES *gBS;
You don’t need this, and it will likely break other compilers (Duplicate definition).
You should just add:
#include <Library/UefiBootServicesTableLib.h>

and this to the INF file [LibraryClasses] section
UefiBootServicesTableLib
EFI_STATUS
EFIAPI
ProcessFilesInDir (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH CONST *DirDp
);
EFI_STATUS
EFIAPI PerFileFunc (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH *DirDp,
IN EFI_FILE_INFO *FileInfo,
IN EFI_DEVICE_PATH *Dp
);
/**
The entry point for the application.
@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.
loop through all volumes and recurse through all files in
each volume and call a user defined function to do
some processing on each file
*/
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_HANDLE AgentHandle;
EFI_STATUS Status;
UINTN NumHandles;
EFI_HANDLE *Handles;
UINTN Index;
VOID *Context;
AgentHandle = ImageHandle;
// gets all handles with simple file system installed
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumHandles,
&Handles
);
if (EFI_ERROR (Status)) {
return Status;
}
// loop through all handles we just got
for (Index = 0; Index < NumHandles; Index++) {
EFI_FILE_HANDLE Root;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
EFI_DEVICE_PATH *Dp;
// get simple file system protocol instance
// from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiSimpleFileSystemProtocolGuid,
&Fs,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on handle.\n"));
continue;
}
// get device path instance from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiDevicePathProtocolGuid,
&Dp,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_DEVICE_PATH_PROTOCOL on handle.\n"));
continue;
}
// open root dir from current simple file system
Status = Fs->OpenVolume (Fs, &Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Unable to open volume.\n"));
continue;
}
// recursively process files in root dir
Context = NULL;
Status = ProcessFilesInDir (Root, Dp);
Root->Close (Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "ProcessFilesInDir error. Continuing with next volume...\n"));
continue;
}
}
Print(L"Done successfully\n");
return EFI_SUCCESS;
}
//I have no idea what the max length of a file path is in EFI
#define MAX_FILE_INFO_SIZE 1024
/**
recurse through directory, calling a user defined
function for each file
*/
EFI_STATUS
EFIAPI
ProcessFilesInDir (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH CONST *DirDp
)
{
EFI_STATUS Status;
EFI_FILE_INFO *FileInfo;
CHAR16 *FileName;
UINTN FileInfoSize;
EFI_DEVICE_PATH *Dp;
// big enough to hold EFI_FILE_INFO struct and
// the whole file path
FileInfo = AllocatePool (MAX_FILE_INFO_SIZE);
if (FileInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
for (;;) {
// get the next file's info. there's an internal position
// that gets incremented when you read from a directory
// so that subsequent reads gets the next file's info
FileInfoSize = MAX_FILE_INFO_SIZE;
Status = Dir->Read (Dir, &FileInfoSize, (VOID *) FileInfo);
if (EFI_ERROR (Status) || FileInfoSize == 0) { //this is how we eventually exit this function when we run out of files
if (Status == EFI_BUFFER_TOO_SMALL) {
Print (L"EFI_FILE_INFO > MAX_FILE_INFO_SIZE. Increase the size\n");
}
FreePool (FileInfo);
return Status;
}
FileName = FileInfo->FileName;
// skip files named . or ..
if (StrCmp (FileName, L".") == 0 || StrCmp (FileName, L"..") == 0) {
continue;
}
// so we have absolute device path to child file/dir
Dp = FileDevicePath (DirDp, FileName);
if (Dp == NULL) {
FreePool (FileInfo);
return EFI_OUT_OF_RESOURCES;
}
// Do whatever processing on the file
PerFileFunc (Dir, DirDp, FileInfo, Dp);
if (FileInfo->Attribute & EFI_FILE_DIRECTORY) {
//
// recurse
//
EFI_FILE_HANDLE NewDir;
Status = Dir->Open (Dir, &NewDir, FileName, EFI_FILE_MODE_READ, 0);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
NewDir->SetPosition (NewDir, 0);
Status = ProcessFilesInDir (NewDir,Dp);
Dir->Close (NewDir);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
}
FreePool (Dp);
}
}
EFI_STATUS
EFIAPI PerFileFunc (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH *DirDp,
IN EFI_FILE_INFO *FileInfo,
IN EFI_DEVICE_PATH *Dp
)
{
EFI_STATUS Status;
EFI_FILE_HANDLE File;
Print (L"Path = %s FileName = %s\n", ConvertDevicePathToText(DirDp, TRUE, TRUE), FileInfo->FileName);
// read the file into a buffer
Status = Dir->Open (Dir, &File, FileInfo->FileName, EFI_FILE_MODE_READ, 0);
if (EFI_ERROR (Status)) {
return Status;
}
// reset position just in case
File->SetPosition (File, 0);
// ****Do stuff on the file here****
Dir->Close (File);
return EFI_SUCCESS;
}
////////////////////////////////////////////////////////////////////
V/R
JRS
oops ignore FsHandle parameter in ProcessFiles()
I was bored and feeling in high spirits, so I wrote some code for you. Also, I agree it would be nice to have example snippets on the tianocore website for common tasks. If someone sets that up, I'd contribute.
I didn't compile this, so please forgive any mistakes.
/**
loop through all volumes and recurse through all files in
each volume and call a user defined function to do
some processing on each file
*/
EFI_STATUS
EFIAPI
ProcessFiles (
IN EFI_HANDLE AgentHandle,
IN EFI_HANDLE FsHandle,
IN PROCESS_FILES_CALLOUT Callout,
IN VOID *Context
)
{
EFI_STATUS Status;
UINTN NumHandles;
EFI_HANDLE *Handles;
UINTN Index;
// gets all handles with simple file system installed
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumHandles,
&Handles
);
if (EFI_ERROR (Status)) {
return Status;
}
// loop through all handles we just got
for (Index = 0; Index < NumHandles; Index++) {
EFI_FILE_HANDLE Root;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
EFI_DEVICE_PATH *Dp;
// get simple file system protocol instance
// from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiSimpleFileSystemProtocolGuid,
&Fs,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on handle.\n"));
continue;
}
// get device path instance from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiDevicePathProtocolGuid,
&Dp,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_DEVICE_PATH_PROTOCOL on handle.\n"));
continue;
}
// open root dir from current simple file system
Status = Fs->OpenVolume (Fs, &Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Unable to open volume.\n"));
continue;
}
// recursively process files in root dir
Status = ProcessFilesInDir (
Root,
Dp,
Callout,
Context
);
Root->Close (Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "ProcessFilesInDir error. Continuing with next volume...\n"));
continue;
}
}
return EFI_SUCCESS;
}
/**
recurse through directory, calling a user defined
function for each file
*/
EFI_STATUS
EFIAPI
ProcessFilesInDir (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH CONST *DirDp,
IN PROCESS_FILES_CALLOUT Callout,
IN VOID *Context
)
{
EFI_STATUS Status;
EFI_FILE_INFO *FileInfo;
CHAR16 *FileName;
UINTN FileInfoSize;
EFI_DEVICE_PATH *Dp;
EFI_FILE_HANDLE File;
// big enough to hold EFI_FILE_INFO struct and
// the whole file path
FileInfo = AllocatePool (MAX_FILE_INFO_SIZE);
if (FileInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
for (;;) {
// get the next file's info. there's an internal position
// that gets incremented when you read from a directory
// so that subsequent reads gets the next file's info
FileInfoSize = MAX_FILE_INFO_SIZE;
Status = Dir->Read (Dir, &FileInfoSize, (VOID *) FileInfo);
if (EFI_ERROR (Status) || FileInfoSize == 0) {
FreePool (FileInfo);
return Status;
}
FileName = FileInfo->FileName;
// skip files named . or ..
if (StrCmp (FileName, L".") == 0 || StrCmp (FileName, L"..") == 0) {
continue;
}
// so we have absolute device path to child file/dir
Dp = AppendFileDevicePath (DirDp, FileName);
if (Dp == NULL) {
FreePool (FileInfo);
return EFI_OUT_OF_RESOURCES;
}
// read the file into a buffer
Status = Dir->Open (Dir, &File, FileName, EFI_FILE_MODE_READ, 0);
if (EFI_ERROR (Status)) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
// reset position just in case
File->SetPosition (File, 0);
// send all this stuff to a callout for processing
Callout (Dp, FileInfo, File, Context);
Dir->Close (File);
if (FileInfo->Attribute & EFI_FILE_DIRECTORY) {
//
// recurse
//
EFI_FILE_HANDLE NewDir;
Status = Dir->Open (Dir, &NewDir, FileName, EFI_FILE_MODE_READ, 0);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
NewDir->SetPosition (NewDir, 0);
Status = ProcessFilesInDir (
NewDir,
Dp,
Callout,
Context
);
Dir->Close (NewDir);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
}
FreePool (Dp);
}
}
Thank you, that looks like it will get me very close. I had went into the ShellPkg at one point while wandering the directory structure, but went into Application rather than Library and then finding nothing I backed out and went elsewhere (BDS).
V/R
JRS
Not skeleton code, but look at ls in ShellPkg
https://svn.code.sf.net/p/edk2/code/trunk/edk2/ShellPkg/Library/UefiShellLevel2CommandsLib/Ls.c <https://svn.code.sf.net/p/edk2/code/trunk/edk2/ShellPkg/Library/UefiShellLevel2CommandsLib/Ls.c>
Anyone know where I can get an example of walking through the filesystem with a UEFI application?
More generally, is there a set of skeleton code examples like Microsoft does for kernel development?
V/R
JRS
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk <http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk>
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel <https://lists.sourceforge.net/lists/listinfo/edk2-devel>
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk <http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk>
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel <https://lists.sourceforge.net/lists/listinfo/edk2-devel>
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk <http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk>
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel <https://lists.sourceforge.net/lists/listinfo/edk2-devel>
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk <http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk>
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel <https://lists.sourceforge.net/lists/listinfo/edk2-devel>
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
John Smith
2014-12-14 00:18:57 UTC
Permalink
Post by John Smith
As a thanks to Thomas, and to pay it forward for everyone who comes after
me, here's a slightly modified compiling version of what he wrote. There
are some parts (like continuously re-reading the directory file) which I
found counter-intuitive and I doubt I would have figured out on my own by
reading the spec, which is why skeleton code is so important. It would be
great if someone started a repository
Not sure about how to start a new repository, but you need to contribute
under a license structure.
The edk2 is BSD + TianoCore Contribution Agreement. Technically speaking
you should add your copyright (required to enforce the license), and the
BDS license to the code. The TianoCore Contribution Agreement is only
required in the check in message, or an email like this to make a
contribution (The Contribution Agreement is required as BSD is to
ambiguous about IP rights)
Contributed-under: TianoCore Contribution Agreement 1.0
Thanks,
Andrew Fish
////////////////////////////////////////////////////////////////////
Brief Description of UEFI MyHelloWorld
Detailed Description of UEFI MyHelloWorld
Copyright for UEFI MyHelloWorld
License for UEFI MyHelloWorld
**/
#pragma warning( disable : 4090 ) //couldn't find a way around
this...maybe someone can post the fix
You are converting between CONST and non-CONST device paths without
casting. That changes the compilers view of the variable type, so you have
to cast to be explicit.
That's what was annoying. I added a cast and it still didn't go away. Had
my wife (also a low level programmer look at it, and she didn't see
anything wrong either)

Thanks for the other comments too

V/R

JRS
Post by John Smith
//relative to MyWorkspace/MdePkg/Include/...
#include <Uefi.h>
#include <Library/UefiApplicationEntryPoint.h>
You don’t need this include.
#include <Library/UefiLib.h>
#include <Protocol/SimpleFileSystem.h>
#include <Library/MemoryAllocationLib.h>
#include <Guid/FileInfo.h>
#include <Library/DebugLib.h>
#include <Library/DevicePathLib.h> //for FileDevicePath
EFI_GUID gEfiSimpleFileSystemProtocolGuid =
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
You don’t need this, just add it to the INF [Protocols] section and the
global will exist when you link, the extern is in
<Protocol/SimpleFileSystem.h>
EFI_BOOT_SERVICES *gBS;
You don’t need this, and it will likely break other compilers (Duplicate definition).
#include <Library/UefiBootServicesTableLib.h>
and this to the INF file [LibraryClasses] section
UefiBootServicesTableLib
EFI_STATUS
EFIAPI
ProcessFilesInDir (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH CONST *DirDp
);
EFI_STATUS
EFIAPI PerFileFunc (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH *DirDp,
IN EFI_FILE_INFO *FileInfo,
IN EFI_DEVICE_PATH *Dp
);
/**
The entry point for the application.
@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.
loop through all volumes and recurse through all files in
each volume and call a user defined function to do
some processing on each file
*/
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_HANDLE AgentHandle;
EFI_STATUS Status;
UINTN NumHandles;
EFI_HANDLE *Handles;
UINTN Index;
VOID *Context;
AgentHandle = ImageHandle;
// gets all handles with simple file system installed
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumHandles,
&Handles
);
if (EFI_ERROR (Status)) {
return Status;
}
// loop through all handles we just got
for (Index = 0; Index < NumHandles; Index++) {
EFI_FILE_HANDLE Root;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
EFI_DEVICE_PATH *Dp;
// get simple file system protocol instance
// from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiSimpleFileSystemProtocolGuid,
&Fs,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on handle.\n"));
continue;
}
// get device path instance from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiDevicePathProtocolGuid,
&Dp,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_DEVICE_PATH_PROTOCOL on handle.\n"));
continue;
}
// open root dir from current simple file system
Status = Fs->OpenVolume (Fs, &Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Unable to open volume.\n"));
continue;
}
// recursively process files in root dir
Context = NULL;
Status = ProcessFilesInDir (Root, Dp);
Root->Close (Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "ProcessFilesInDir error. Continuing with next volume...\n"));
continue;
}
}
Print(L"Done successfully\n");
return EFI_SUCCESS;
}
//I have no idea what the max length of a file path is in EFI
#define MAX_FILE_INFO_SIZE 1024
/**
recurse through directory, calling a user defined
function for each file
*/
EFI_STATUS
EFIAPI
ProcessFilesInDir (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH CONST *DirDp
)
{
EFI_STATUS Status;
EFI_FILE_INFO *FileInfo;
CHAR16 *FileName;
UINTN FileInfoSize;
EFI_DEVICE_PATH *Dp;
// big enough to hold EFI_FILE_INFO struct and
// the whole file path
FileInfo = AllocatePool (MAX_FILE_INFO_SIZE);
if (FileInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
for (;;) {
// get the next file's info. there's an internal position
// that gets incremented when you read from a directory
// so that subsequent reads gets the next file's info
FileInfoSize = MAX_FILE_INFO_SIZE;
Status = Dir->Read (Dir, &FileInfoSize, (VOID *) FileInfo);
if (EFI_ERROR (Status) || FileInfoSize == 0) { //this is how we
eventually exit this function when we run out of files
if (Status == EFI_BUFFER_TOO_SMALL) {
Print (L"EFI_FILE_INFO > MAX_FILE_INFO_SIZE. Increase the size\n");
}
FreePool (FileInfo);
return Status;
}
FileName = FileInfo->FileName;
// skip files named . or ..
if (StrCmp (FileName, L".") == 0 || StrCmp (FileName, L"..") == 0) {
continue;
}
// so we have absolute device path to child file/dir
Dp = FileDevicePath (DirDp, FileName);
if (Dp == NULL) {
FreePool (FileInfo);
return EFI_OUT_OF_RESOURCES;
}
// Do whatever processing on the file
PerFileFunc (Dir, DirDp, FileInfo, Dp);
if (FileInfo->Attribute & EFI_FILE_DIRECTORY) {
//
// recurse
//
EFI_FILE_HANDLE NewDir;
Status = Dir->Open (Dir, &NewDir, FileName, EFI_FILE_MODE_READ, 0);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
NewDir->SetPosition (NewDir, 0);
Status = ProcessFilesInDir (NewDir,Dp);
Dir->Close (NewDir);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
}
FreePool (Dp);
}
}
EFI_STATUS
EFIAPI PerFileFunc (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH *DirDp,
IN EFI_FILE_INFO *FileInfo,
IN EFI_DEVICE_PATH *Dp
)
{
EFI_STATUS Status;
EFI_FILE_HANDLE File;
Print (L"Path = %s FileName = %s\n", ConvertDevicePathToText(DirDp,
TRUE, TRUE), FileInfo->FileName);
// read the file into a buffer
Status = Dir->Open (Dir, &File, FileInfo->FileName, EFI_FILE_MODE_READ, 0);
if (EFI_ERROR (Status)) {
return Status;
}
// reset position just in case
File->SetPosition (File, 0);
// ****Do stuff on the file here****
Dir->Close (File);
return EFI_SUCCESS;
}
////////////////////////////////////////////////////////////////////
V/R
JRS
Post by Thomas Rognon
oops ignore FsHandle parameter in ProcessFiles()
Post by Thomas Rognon
I was bored and feeling in high spirits, so I wrote some code for you.
Also, I agree it would be nice to have example snippets on the tianocore
website for common tasks. If someone sets that up, I'd contribute.
I didn't compile this, so please forgive any mistakes.
/**
loop through all volumes and recurse through all files in
each volume and call a user defined function to do
some processing on each file
*/
EFI_STATUS
EFIAPI
ProcessFiles (
IN EFI_HANDLE AgentHandle,
IN EFI_HANDLE FsHandle,
IN PROCESS_FILES_CALLOUT Callout,
IN VOID *Context
)
{
EFI_STATUS Status;
UINTN NumHandles;
EFI_HANDLE *Handles;
UINTN Index;
// gets all handles with simple file system installed
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumHandles,
&Handles
);
if (EFI_ERROR (Status)) {
return Status;
}
// loop through all handles we just got
for (Index = 0; Index < NumHandles; Index++) {
EFI_FILE_HANDLE Root;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
EFI_DEVICE_PATH *Dp;
// get simple file system protocol instance
// from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiSimpleFileSystemProtocolGuid,
&Fs,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on handle.\n"));
continue;
}
// get device path instance from current handle
Status = gBS->OpenProtocol (
Handles[Index],
&gEfiDevicePathProtocolGuid,
&Dp,
NULL,
AgentHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Missing EFI_DEVICE_PATH_PROTOCOL on handle.\n"));
continue;
}
// open root dir from current simple file system
Status = Fs->OpenVolume (Fs, &Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Unable to open volume.\n"));
continue;
}
// recursively process files in root dir
Status = ProcessFilesInDir (
Root,
Dp,
Callout,
Context
);
Root->Close (Root);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "ProcessFilesInDir error. Continuing with
next volume...\n"));
continue;
}
}
return EFI_SUCCESS;
}
/**
recurse through directory, calling a user defined
function for each file
*/
EFI_STATUS
EFIAPI
ProcessFilesInDir (
IN EFI_FILE_HANDLE Dir,
IN EFI_DEVICE_PATH CONST *DirDp,
IN PROCESS_FILES_CALLOUT Callout,
IN VOID *Context
)
{
EFI_STATUS Status;
EFI_FILE_INFO *FileInfo;
CHAR16 *FileName;
UINTN FileInfoSize;
EFI_DEVICE_PATH *Dp;
EFI_FILE_HANDLE File;
// big enough to hold EFI_FILE_INFO struct and
// the whole file path
FileInfo = AllocatePool (MAX_FILE_INFO_SIZE);
if (FileInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
for (;;) {
// get the next file's info. there's an internal position
// that gets incremented when you read from a directory
// so that subsequent reads gets the next file's info
FileInfoSize = MAX_FILE_INFO_SIZE;
Status = Dir->Read (Dir, &FileInfoSize, (VOID *) FileInfo);
if (EFI_ERROR (Status) || FileInfoSize == 0) {
FreePool (FileInfo);
return Status;
}
FileName = FileInfo->FileName;
// skip files named . or ..
if (StrCmp (FileName, L".") == 0 || StrCmp (FileName, L"..") == 0) {
continue;
}
// so we have absolute device path to child file/dir
Dp = AppendFileDevicePath (DirDp, FileName);
if (Dp == NULL) {
FreePool (FileInfo);
return EFI_OUT_OF_RESOURCES;
}
// read the file into a buffer
Status = Dir->Open (Dir, &File, FileName, EFI_FILE_MODE_READ, 0);
if (EFI_ERROR (Status)) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
// reset position just in case
File->SetPosition (File, 0);
// send all this stuff to a callout for processing
Callout (Dp, FileInfo, File, Context);
Dir->Close (File);
if (FileInfo->Attribute & EFI_FILE_DIRECTORY) {
//
// recurse
//
EFI_FILE_HANDLE NewDir;
Status = Dir->Open (Dir, &NewDir, FileName, EFI_FILE_MODE_READ, 0);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
NewDir->SetPosition (NewDir, 0);
Status = ProcessFilesInDir (
NewDir,
Dp,
Callout,
Context
);
Dir->Close (NewDir);
if (Status != EFI_SUCCESS) {
FreePool (FileInfo);
FreePool (Dp);
return Status;
}
}
FreePool (Dp);
}
}
Post by John Smith
Thank you, that looks like it will get me very close. I had went into
the ShellPkg at one point while wandering the directory structure, but went
into Application rather than Library and then finding nothing I backed out
and went elsewhere (BDS).
V/R
JRS
Post by Thomas Rognon
Not skeleton code, but look at ls in ShellPkg
https://svn.code.sf.net/p/edk2/code/trunk/edk2/ShellPkg/Library/UefiShellLevel2CommandsLib/Ls.c
On Thu, Dec 11, 2014 at 11:35 AM, John Smith <
Post by John Smith
Anyone know where I can get an example of walking through the
filesystem with a UEFI application?
More generally, is there a set of skeleton code examples like
Microsoft does for kernel development?
V/R
JRS
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
https://lists.sourceforge.net/lists/listinfo/edk2-devel
Loading...