#include <Library/EblNetworkLib.h>
+CHAR8 *gCwd = NULL;
+
+CONST EFI_GUID gZeroGuid = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
+
#define EFI_OPEN_FILE_GUARD_HEADER 0x4B4D4641
#define EFI_OPEN_FILE_GUARD_FOOTER 0x444D5A56
return Status;
}
+ // Get FVB Info about the handle
+ Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
+ if (!EFI_ERROR (Status)) {
+ Status = Fvb->GetPhysicalAddress (Fvb, &File->FvStart);
+ if (!EFI_ERROR (Status)) {
+ for (Lba = 0, File->FvSize = 0; ; File->FvSize += (BlockSize * NumberOfBlocks), Lba += NumberOfBlocks) {
+ Status = Fvb->GetBlockSize (Fvb, Lba, &BlockSize, &NumberOfBlocks);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+ }
+ }
+
+
DevicePath = DevicePathFromHandle (File->EfiHandle);
if (*FileName == '\0') {
File->DevicePath = DuplicateDevicePath (DevicePath);
+ File->Size = File->FvSize;
+ File->MaxPosition = File->Size;
} else {
Key = 0;
do {
File->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&DevicePathNode);
}
-
- // Get FVB Info about the handle
- Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
- if (!EFI_ERROR (Status)) {
- Status = Fvb->GetPhysicalAddress (Fvb, &File->FvStart);
- if (!EFI_ERROR (Status)) {
- for (Lba = 0, File->FvSize = 0; ; File->FvSize += (BlockSize * NumberOfBlocks), Lba += NumberOfBlocks) {
- Status = Fvb->GetBlockSize (Fvb, Lba, &BlockSize, &NumberOfBlocks);
- if (EFI_ERROR (Status)) {
- break;
- }
- }
- }
- }
-
+
// FVB not required if FV was soft loaded...
return EFI_SUCCESS;
}
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
UINTN Size;
EFI_IP_ADDRESS Ip;
+ CHAR8 *CwdPlusPathName;
EblUpdateDeviceLists ();
File->FvSectionType = SectionType;
StrLen = AsciiStrSize (PathName);
- if (StrLen <= 2) {
+ if (StrLen <= 1) {
// Smallest valid path is 1 char and a null
return NULL;
}
}
}
- if (FileStart == 0) {
- // We could add a current working diretory concept
- return NULL;
- }
-
//
// Matching volume name has precedence over handle based names
//
VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber);
if (!VolumeNameMatch) {
+ if (FileStart == StrLen) {
+ // No Volume name or device name, so try Current Working Directory
+ if (gCwd == NULL) {
+ // No CWD
+ return NULL;
+ }
+
+ // We could add a current working diretory concept
+ CwdPlusPathName = AllocatePool (AsciiStrSize (gCwd) + AsciiStrSize (PathName));
+ if (CwdPlusPathName == NULL) {
+ return NULL;
+ }
+
+ if ((PathName[0] == '/') || (PathName[0] == '\\')) {
+ // PathName starts in / so this means we go to the root of the device in the CWD.
+ CwdPlusPathName[0] = '\0';
+ for (FileStart = 0; gCwd[FileStart] != '\0'; FileStart++) {
+ CwdPlusPathName[FileStart] = gCwd[FileStart];
+ if (gCwd[FileStart] == ':') {
+ FileStart++;
+ CwdPlusPathName[FileStart] = '\0';
+ break;
+ }
+ }
+ } else {
+ AsciiStrCpy (CwdPlusPathName, gCwd);
+ StrLen = AsciiStrLen (gCwd);
+ if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) {
+ AsciiStrCat (CwdPlusPathName, "\\");
+ }
+ }
+
+ AsciiStrCat (CwdPlusPathName, PathName);
+ if (AsciiStrStr (CwdPlusPathName, ":") == NULL) {
+ // Extra error check to make sure we don't recusre and blow stack
+ return NULL;
+ }
+
+ File = EfiOpen (CwdPlusPathName, OpenMode, SectionType);
+ FreePool (CwdPlusPathName);
+ return File;
+ }
+
DevNumber = EblConvertDevStringToNumber ((CHAR8 *)PathName);
}
AsciiStrCpy (File->DeviceName, PathName);
File->DeviceName[FileStart - 1] = '\0';
File->FileName = &File->DeviceName[FileStart];
+ if (File->FileName[0] == '\0') {
+ // if it is just a file name use / as root
+ File->FileName = "\\";
+ }
//
// Use best match algorithm on the dev names so we only need to look at the
return 0;
}
- File->Size = BufferSize;
+ File->Size = (UINTN)BufferSize;
File->MaxPosition = File->Size;
}
}
if (File->Type == EfiOpenLoadFile || File->Type == EfiOpenFirmwareVolume) {
- // LoadFile and FV do not support Seek
- return EFI_UNSUPPORTED;
+ if (!CompareGuid (&File->FvNameGuid, &gZeroGuid)) {
+ // LoadFile and FV do not support Seek
+ // You can seek on a raw FV device
+ return EFI_UNSUPPORTED;
+ }
}
CurrentPosition = File->CurrentPosition;
break;
case EfiOpenFirmwareVolume:
- if (File->FvSectionType == EFI_SECTION_ALL) {
- Status = File->Fv->ReadFile (
- File->Fv,
- &File->FvNameGuid,
- &Buffer,
- BufferSize,
- &File->FvType,
- &File->FvAttributes,
- &AuthenticationStatus
- );
+ if (CompareGuid (&File->FvNameGuid, &gZeroGuid)) {
+ // This is the entire FV device, so treat like a memory buffer
+ CopyMem (Buffer, (VOID *)(UINTN)(File->FvStart + File->CurrentPosition), *BufferSize);
+ File->CurrentPosition += *BufferSize;
+ Status = EFI_SUCCESS;
} else {
- Status = File->Fv->ReadSection (
- File->Fv,
- &File->FvNameGuid,
- File->FvSectionType,
- 0,
- &Buffer,
- BufferSize,
- &AuthenticationStatus
- );
+ if (File->FvSectionType == EFI_SECTION_ALL) {
+ Status = File->Fv->ReadFile (
+ File->Fv,
+ &File->FvNameGuid,
+ &Buffer,
+ BufferSize,
+ &File->FvType,
+ &File->FvAttributes,
+ &AuthenticationStatus
+ );
+ } else {
+ Status = File->Fv->ReadSection (
+ File->Fv,
+ &File->FvNameGuid,
+ File->FvSectionType,
+ 0,
+ &Buffer,
+ BufferSize,
+ &AuthenticationStatus
+ );
+ }
}
break;
TempBuffer = File->Buffer;
- File->Buffer = AllocatePool (File->CurrentPosition + *BufferSize);
+ File->Buffer = AllocatePool ((UINTN)(File->CurrentPosition + *BufferSize));
if (File->Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
FreePool (TempBuffer);
- File->Size = File->CurrentPosition + *BufferSize;
- File->MaxPosition = File->Size;
+ File->Size = (UINTN)(File->CurrentPosition + *BufferSize);
+ File->MaxPosition = (UINT64)File->Size;
}
// Copy in the requested data
return Status;
}
+
+/**
+ Given Cwd expand Path to remove .. and replace them with real
+ directory names.
+
+ @param Cwd Current Working Directory
+ @param Path Path to expand
+
+ @return NULL Cwd or Path are not valid
+ @return 'other' Path with .. expanded
+
+**/
+CHAR8 *
+ExpandPath (
+ IN CHAR8 *Cwd,
+ IN CHAR8 *Path
+ )
+{
+ CHAR8 *NewPath;
+ CHAR8 *Work, *Start, *End;
+ UINTN StrLen;
+ UINTN i;
+
+ if (Cwd == NULL || Path == NULL) {
+ return NULL;
+ }
+
+ StrLen = AsciiStrSize (Cwd);
+ if (StrLen <= 2) {
+ // Smallest valid path is 1 char and a null
+ return NULL;
+ }
+
+ StrLen = AsciiStrSize (Path);
+ NewPath = AllocatePool (AsciiStrSize (Cwd) + StrLen + 1);
+ if (NewPath == NULL) {
+ return NULL;
+ }
+ AsciiStrCpy (NewPath, Cwd);
+
+ End = Path + StrLen;
+ for (Start = Path ;;) {
+ Work = AsciiStrStr (Start, "..") ;
+ if (Work == NULL) {
+ // Remaining part of Path contains no more ..
+ break;
+ }
+
+ // append path prior to ..
+ AsciiStrnCat (NewPath, Start, Work - Start);
+ StrLen = AsciiStrLen (NewPath);
+ for (i = StrLen; i >= 0; i--) {
+ if (NewPath[i] == ':') {
+ // too many ..
+ return NULL;
+ }
+ if (NewPath[i] == '/' || NewPath[i] == '\\') {
+ if ((i > 0) && (NewPath[i-1] == ':')) {
+ // leave the / before a :
+ NewPath[i+1] = '\0';
+ } else {
+ // replace / will Null to remove trailing file/dir reference
+ NewPath[i] = '\0';
+ }
+ break;
+ }
+ }
+
+ Start = Work + 3;
+ }
+
+ // Handle the path that remains after the ..
+ AsciiStrnCat (NewPath, Start, End - Start);
+
+ return NewPath;
+}
+
+
+/**
+ Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
+ the path does not contain a device name, The CWD is prepended to the path.
+
+ @param Cwd Current Working Directory to set
+
+
+ @return EFI_SUCCESS CWD is set
+ @return EFI_INVALID_PARAMETER Cwd is not a valid device:path
+
+**/
+EFI_STATUS
+EfiSetCwd (
+ IN CHAR8 *Cwd
+ )
+{
+ EFI_OPEN_FILE *File;
+ UINTN Len;
+ CHAR8 *Path;
+
+ if (Cwd == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (AsciiStrCmp (Cwd, ".") == 0) {
+ // cd . is a no-op
+ return EFI_SUCCESS;
+ }
+
+ Path = Cwd;
+ if (AsciiStrStr (Cwd, "..") != NULL) {
+ if (gCwd == NULL) {
+ // no parent
+ return EFI_SUCCESS;
+ }
+
+ Len = AsciiStrLen (gCwd);
+ if ((gCwd[Len-2] == ':') && ((gCwd[Len-1] == '/') || (gCwd[Len-1] == '\\'))) {
+ // parent is device so nothing to do
+ return EFI_SUCCESS;
+ }
+
+ // Expand .. in Cwd, given we know current working directory
+ Path = ExpandPath (gCwd, Cwd);
+ if (Path == NULL) {
+ return EFI_NOT_FOUND;
+ }
+ }
+
+ File = EfiOpen (Path, EFI_FILE_MODE_READ, 0);
+ if (File == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (gCwd != NULL) {
+ FreePool (gCwd);
+ }
+
+ // Use the info returned from EfiOpen as it can add in CWD if needed. So Cwd could be
+ // relative to the current gCwd or not.
+ gCwd = AllocatePool (AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 10);
+ if (gCwd == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ AsciiStrCpy (gCwd, File->DeviceName);
+ if (File->FileName == NULL) {
+ AsciiStrCat (gCwd, ":\\");
+ } else {
+ AsciiStrCat (gCwd, ":");
+ AsciiStrCat (gCwd, File->FileName);
+ }
+
+ EfiClose (File);
+ if (Path != Cwd) {
+ FreePool (Path);
+ }
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
+ the path does not contain a device name, The CWD is prepended to the path.
+ The CWD buffer is only valid until a new call is made to EfiSetCwd(). After
+ a call to EfiSetCwd() it is not legal to use the pointer returned by
+ this funciton.
+
+ @param Cwd Current Working Directory
+
+
+ @return "" No CWD set
+ @return 'other' Returns buffer that contains CWD.
+
+**/
+CHAR8 *
+EfiGetCwd (
+ VOID
+ )
+{
+ if (gCwd == NULL) {
+ return "";
+ }
+ return gCwd;
+}
+
+