I'm a beginner in UEFI. I'm trying to open a file from my UEFI application. The path of file is
fs1:/myfolder/myfile.txt
The code (With the help of this answer) :
efiStatus = bs->LocateHandleBuffer(ByProtocol,
&sfspGuid,
NULL,
&handleCount,
&handles);
for (index = 0; index < (int)handleCount; ++ index)
{
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* fs = NULL;
efiStatus = bs->HandleProtocol(
handles[index],
&sfspGuid,
(void**)&fs);
EFI_FILE_PROTOCOL* root = NULL;
...
efiStatus = fs->OpenVolume(fs, &root);
EFI_FILE_PROTOCOL* token = NULL;
efiStatus = root->Open(
root,
&token,
L"myfolder\\myfile.txt",
EFI_FILE_MODE_READ,
EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
}
But using this method, I can only go through all the file system handles and open each volume and try opening my file.
But I want to give full path to my file and open it in it's volume.
How can I acheive this?
EDIT:
I tried using Shell APIs for opening the file as suggested by #Alex in comments.
Below is the code. But it hangs in function OpenFileByName .
What is the mistake in this code? (argv[ 1 ] would be my file path fs1:\myfile.txt )
EFI_STATUS
EFIAPI
main (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS status;
UINTN argc;
CHAR16 **argv;
SHELL_FILE_HANDLE Handle;
status = get_args(&argc, &argv);
if (EFI_ERROR(status)) {
Print(L"ERROR: Parsing command line arguments: %d\n", status);
return status;
}
if (argc <= 1){
Print(L"No file name to open\n");
return (EFI_UNSUPPORTED); //need to have at least one parameter
}
Print(L"File to open is: %s\n", argv[1]);
status = gEfiShellProtocol->OpenFileByName (argv[1], &Handle,
EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE);
if (EFI_ERROR(status)) {
Print(L"\nFile Open did not work %s\n", argv[1]);
return (status);
}else{
Print(L"\nFile Open worked %s\n", argv[1]);
gEfiShellProtocol->CloseFile(Handle);
}
return EFI_SUCCESS;
}
And the code hangs even if I try GetCurDir function.
Print(L"Dir: %s \n",gEfiShellProtocol->GetCurDir(NULL));
Any pointers would be helpful.
Answer to the comment how to get EFI_SHELL_PROTOCOL:
The procedure is basically the same as for any Efi protocols. First, grab a handle to the interface:
UINTN BufferSize;
EFI_HANDLE* Buffer;
Status = bs->LocateHandle(ByProtocol, &gEfiShellProtocolGuid, NULL, &BufferSize, Buffer);
Than, allocate and recall with the buffer of the correct size:
Status = bs->AllocatePool(EfiBootServicesData, BufferSize, &Buffer);
Status = bs->LocateHandle(ByProtocol, &gEfiShellProtocolGuid, NULL, &BufferSize, Buffer);
Now, you can grab a handle to the protocol. Remember, it's EFI, there might be multiple protocols installed! That's why we have to iterate through all of them. But in this case most likely there will be just one instance of the SHELL protocol:
UINTN HandleCounter;
for (HandleCounter = 0 ; HandleCounter < (BufferSize/sizeof(EFI_HANDLE)) ; HandleCounter++)
{
Status = bs->OpenProtocol(Buffer[HandleCounter],
&gEfiShellProtocolGuid,
(VOID**)&gEfiShellProtocol,
imageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
And don't forget to check Status for every step!
And of course don't forget:
bs->FreePool(buffer);
As for the protocol itself you don't have to close it. EFI starting from 2.31 doesn't require it anymore.
Related
Hello I am currently programming a UEFI bootloader with GNU-EFI and I am just about to program a small config system I have tested it so far and it works, but now I did not want to have everything in one file and split it into several files. Now I have the problem that in my File.c file in the ReadFile function somehow the buffer is not returned. I already checked if the buffer contains anything at all and t does. Hope someone can help me.
File.c
UINT8 *ReadFile(EFI_FILE_HANDLE Volume, CHAR16 *FileName) {
// Declare variables
EFI_STATUS Status;
EFI_FILE_HANDLE FileHandle;
UINT64 ReadSize;
UINT8 *Buffer;
// Open the file
Status = uefi_call_wrapper(
Volume->Open,
5,
Volume,
&FileHandle,
FileName,
EFI_FILE_MODE_READ,
EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM
);
if(EFI_ERROR(Status)) {
Print(L"Could not open file! Reason: %r\n", Status);
}
// Read the contents of the file
ReadSize = FileSize(FileHandle);
Buffer = AllocatePool(ReadSize);
Status = uefi_call_wrapper(
FileHandle->Read,
3,
FileHandle,
&ReadSize,
Buffer
);
if(EFI_ERROR(Status)) {
Print(L"Could not read file! Reason: %r\n", Status);
}
// Close the file
Status = uefi_call_wrapper(
FileHandle->Close,
1,
FileHandle
);
if(EFI_ERROR(Status)) {
Print(L"Could not close file! Reason: %r\n", Status);
}
return Buffer;
}
Main.c
UINT8 *Buffer = ReadFile(Volume, FileName);
Print(L"File content:\n%a\n", Buffer);
I'm trying to mount a vfat disk image from DOS, using .C
if( mount( "/mypath/disk.img", "/mypath/img/", "vfat", MS_DIRSYNC | MS_SYNCHRONOUS, "utf8" ) ) {
printf( "Error mount %s errno=%d %s\n", dst.c_str(), errno, strerror( errno ) );
}
I get all the times the error "Block device required". Should i add some parameter or flag?
Note:I can mount from the bash the same file in the same target without any error.
UPDATE:
I had some good result using the function to mount an ISO. When I run the program, it remains stacked on the call ioctl(loop_device_fd, LOOP_CLR_FD, 0);. When I exit from the program (ctrl-c), the image is mounted. Is LOOP_CLR_FD necessary to complete all the steps? In additional,it is mounted in read only and seems not possible to change it in read/write.
const auto loop_control = std::fopen( "/dev/loop-control", "r" );
const auto loop_control_fd = fileno(loop_control);
const auto devnr = ioctl(loop_control_fd, LOOP_CTL_GET_FREE);
std::stringstream loopname;
loopname << "/dev/loop" << devnr;
const auto loop_device_name = loopname.str();
const auto loop_device = std::fopen(loop_device_name.c_str(), "r");
const auto loop_device_fd = fileno(loop_device);
const auto image = std::fopen( dst.c_str(), "r" );
const auto image_fd = fileno(image);
//Associate the loop device with the open file whose file descriptor is passed as the (third) ioctl(2) argument.
ioctl(loop_device_fd, LOOP_SET_FD, image_fd);
const auto result = mount(loop_device_name.c_str(), dst_path_img.c_str(), "vfat", MS_RDONLY, NULL);
if( result ) {
printf( "Error mount %s errno=%d %s\n", dst.c_str(), errno, strerror( errno ) );
return;
}
ioctl(loop_device_fd, LOOP_CLR_FD, 0);
The example code from the link above seems to mount your image quite fine, with minor modifications to retrieve free loop device (I'm assuming it is Dos3.3 diskette image from allbootdisks.com):
#include <sys/mount.h>
#include <linux/loop.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main()
{
int control_fd, file_fd, device_fd;
char loop_device[16];
control_fd = open("/dev/loop-control", O_RDWR);
if (control_fd < 0) {
perror("open loop control device failed");
return 1;
}
int loop_id = ioctl(control_fd, LOOP_CTL_GET_FREE);
sprintf(loop_device, "/dev/loop%d", loop_id);
close(control_fd);
printf("using loop device: %s\n", loop_device);
file_fd = open("./Dos3.3.img", O_RDWR);
if (file_fd < 0) {
perror("open backing file failed");
return 1;
}
device_fd = open(loop_device, O_RDWR);
if (device_fd < 0) {
perror("open loop device failed");
close(file_fd);
return 1;
}
if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0) {
perror("ioctl LOOP_SET_FD failed");
close(file_fd);
close(device_fd);
return 1;
}
close(file_fd);
if (mount(loop_device, "./mnt/", "vfat", MS_DIRSYNC | MS_SYNCHRONOUS, "") < 0) {
perror("mount failed");
} else {
printf("mount successful\n");
}
// always free loop device in the end
ioctl(device_fd, LOOP_CLR_FD, 0);
close(device_fd);
}
As the disk image is not really a device, it cannot be directly mounted.
What you are trying to do, is to mount via a loop device.
Command line equivalent is: mount /mypath/disk.img /mypath/img -t vfat -o loop
I'm not sure, but try whether adding the "loop, utf8" as your last parameter would fix the problem.
The problem here is you're trying to mount an image as you you would do with a block device. A block device has more bindings to the operating system than an image file would, so you need to find a way around this.
Try a loopback device! A loopback device can give you the operating system reference to that image /mypath/disk.img as a block device. You can create a loopback device in bash like so:
# set up a block device
losetup -fP /mypath/disk.img
# now list the loopback devices
losetup -a
Anyway, this solution is in bash, but certainly there is a library out there somewhere for c.
I am trying to write a simple program (toy example) that copies a file from a remote host to the local machine.
It works when I try to copy a txt file, but not for files like mp4.
Here is my code, which is basically parts stitched together from the tutorial: https://pastebin.com/0FPrmeDx
This is where the error happens:
int scp_receive(ssh_session session, ssh_scp scp)
{
int rc;
int size, mode;
char *filename, *buffer;
rc = ssh_scp_pull_request(scp);
if (rc != SSH_SCP_REQUEST_NEWFILE)
{
fprintf(stderr, "Error receiving information about file: %s\n",
ssh_get_error(session));
return SSH_ERROR;
}
size = ssh_scp_request_get_size(scp);
filename = strdup(ssh_scp_request_get_filename(scp));
mode = ssh_scp_request_get_permissions(scp);
printf("Receiving file %s, size %d, permisssions 0%o\n",
filename, size, mode);
free(filename);
buffer = malloc(size);
if (buffer == NULL)
{
fprintf(stderr, "Memory allocation error\n");
return SSH_ERROR;
}
ssh_scp_accept_request(scp);
rc = ssh_scp_read(scp, buffer, size);
if (rc == SSH_ERROR)
{
fprintf(stderr, "Error receiving file data: %s\n",
ssh_get_error(session));
free(buffer);
return rc;
}
printf("Done!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
int filedesc = open("/home/user/video.mp4", O_WRONLY | O_CREAT);
if (filedesc < 0) {
return -1;
}
write(filedesc, buffer, size);
free(buffer);
close(filedesc);
rc = ssh_scp_pull_request(scp);
if (rc != SSH_SCP_REQUEST_EOF)
{
fprintf(stderr, "Unexpected request: %s\n",
ssh_get_error(session));
return SSH_ERROR;
}
return SSH_OK;
}
Error is fired with the code:
rc = ssh_scp_pull_request(scp);
if (rc != SSH_SCP_REQUEST_EOF)
{
fprintf(stderr, "Unexpected request: %s\n",
ssh_get_error(session));
return SSH_ERROR;
}
This is the error that I get:
Unexpected request: ssh_scp_pull_request called under invalid state
I tried to figure it out, but I couldn't make any progress on it.
Update 1:
The size of the copied file is exactly the same as of the source file both for txt and mp4 files. However, the copied file seems to be largely empty...
When copied, the permissions are changed from -rwxr-xr-x to --wxr-----.
Update 2:
It seems that the file size plays a major role here. Very small files (10-15kb) are copied without problems. Bigger files are not copied and produce the above mentioned error...
You cannot expect, that ssh_scp_read() reads the whole data in a single call to it. You have to iterate, until no more data is left to read:
int r = 0;
while (r < size) {
int st = ssh_scp_read(scp, buffer+r, size-r);
r += st;
}
Now, a subsequent call to ssh_scp_pull_request(scp) should succeed.
I'm writing an application that works from efi environment (f.e. can be run from EFI shell).
My problem is that I cannot create any file on my machine's volume. I tested it on WMVare and on real machines with ntfs and even on mac with hfs+ (got drivers frim http://efi.akeo.ie/). I can read everything, but when I try to write I got error code 8 (EFI_WRITE_PROTECTED).
Is there any way to avoid this protection? Maybe I should go deeper and work with block devices instead of file systems (don't really want to do this)?
My code. Driver Loading (I think it works fine, just for the case):
Print(L"\nLoading NTFS Driver... ");
Status = BS->LocateHandleBuffer(ByProtocol, &FileSystemProtocol, NULL, &NumHandles, &Handle);
if (EFI_ERROR(Status))
{
PrintStatusError(Status, L"\n Failed to list file systems");
goto exit;
}
for (i = 0; i < NumHandles; i++)
{
// Look for our NTFS driver. Note: the path MUST be specified using backslashes!
DevicePath = FileDevicePath(Handle[i], DriverPath);
if (DevicePath == NULL)
continue;
// Attempt to load the driver. If that fails, it means we weren't on the right partition
Status = BS->LoadImage(FALSE, ImageHandle, DevicePath, NULL, 0, &DriverHandle);
SafeFree(DevicePath);
if (EFI_ERROR(Status))
continue;
// Load was a success - attempt to start the driver
Status = BS->StartImage(DriverHandle, NULL, NULL);
if (EFI_ERROR(Status))
{
PrintStatusError(Status, L"\n Driver did not start");
goto exit;
}
Print(L"LOADED");
break;
}
SafeFree(Handle);
if (i >= NumHandles)
{
Print(L"\n Failed to locate driver. Please check that '%s' exists on the FAT partition", DriverPath);
Status = EFI_NOT_FOUND;
goto exit;
}
File reading/writing:
// Now enumerate all disk handles again
Status = BS->LocateHandleBuffer(ByProtocol, &DiskIoProtocol, NULL, &NumHandles, &Handle);
if (EFI_ERROR(Status))
{
PrintStatusError(Status, L"\n Failed to list disks");
goto exit;
}
// Go through the partitions and find the NTFS one
for (i = 0; i < NumHandles; i++)
{
dev_path = DevicePathFromHandle(Handle[i]);
Print(L"\nVolume [%d]: ", i);
// Calling ConnectController() on a handle starts all the drivers that can service it
Status = BS->ConnectController(Handle[i], NULL, NULL, TRUE);
if (Status == EFI_SUCCESS)
{
Print(L"Driver connected! ");
}
// Open the the volume
Status = BS->HandleProtocol(Handle[i], &FileSystemProtocol, (VOID**)&Volume);
if (EFI_ERROR(Status))
{
PrintStatusError(Status, L"\n Could not find volume");
continue;
}
// Open the root directory
Root = NULL;
Status = Volume->OpenVolume(Volume, &Root);
if ((EFI_ERROR(Status)) || (Root == NULL))
{
PrintStatusError(Status, L"\n Could not open Root directory");
continue;
}
Status = Root->Open(Root, &FileHandle, L"bb.txt", EFI_FILE_MODE_READ, 0);
Print(L"\n Read %d", Status);
if (Status == EFI_SUCCESS)
{
UINTN fsize = 5;
char fdata[5];
Status = FileHandle->Read(FileHandle, &fsize, fdata);
fdata[4] = 0;
Print(L" %d [%x%x%x%x] ", Status, fdata[0], fdata[1], fdata[2], fdata[3]);
FileHandle->Close(FileHandle);
}
Status = Root->Open(Root, &FileHandle, L"aa.txt", EFI_FILE_MODE_CREATE | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ, 0);
Print(L" Create %d", Status);
if (Status == EFI_SUCCESS)
{
UINTN fsize = 4;
Status = FileHandle->Write(FileHandle, &fsize, file_content);
Print(L" Wr %d", Status);
Status = FileHandle->Flush(FileHandle);
Print(L" Fl %d", Status);
Status = FileHandle->Close(FileHandle);
Print(L" Cl %d", Status);
}
}
<---------------- after a bit of investigation ------------------------>
It turned out that the only thing we can do with machine's file system is to change existing file content. Seems like it is the only possible way without monthes of research. But still, its not easy to do:
I have "EFI_FILE *fileHandle" variable and can read mine pre-created file. How to find corresponding LBA (or offset for DiskIO)?
I looked to EFI sources (from AMI bios sources) and found that there are hidden data placed right after EFI_FILE struct:
typedef struct _FILE_HANDLE_INSTANCE {
EFI_FILE_PROTOCOL FileHandle; // Should be the first entry in the structure
EFI_STATUS HandleInstanceStatus;
FILE_HANDLE *pFH;
UINT64 OpenMode; // Open Modes
UINT64 Position; //
UINT32 CurrentCluster; // will point to sector number where the position is pointing currently
UINT32 CurrentClusterOffset; // will point to sector number where the position is pointing currently
BOOLEAN MEDIA_NOT_VALID; // Will be true if for any reason current instances cannot use the volume Eg: Change in Media
DLINK ViFILink; // This link is for the DLIST OpenFIs in the VOLUME_INFO
} FILE_HANDLE_INSTANCE;
And they operate with it like this:
void example_read_inside_efi(EFI_FILE *FileHandle, ...)
{
FILE_HANDLE_INSTANCE *fhi = (FILE_HANDLE_INSTANCE *)FileHandle;
...
But when I try to read that struct, it don't seem that this struct has right field values (f.e. FILE_HANDLE pointer is not valid). I don't know what to do with this next. Have no ideas..
So, the question is: how can i find corresponding LBA (or offset for diskIO) if I have valid EFI_FILE pointer?
I am trying to use zlib and minizip. When I build 6 projects in one soltuion which are included in archive I downloaded everything works and project will create exe files (minizip and miniunz). Here is the problem, I don't know how to use miniunz and minizip source codes in my application and google isn't helping. Can somebody, who has experience with these libs, provide step by step tutorial how to include these lib in my application?
Or if you have other libraries to work with password protected files and can provide some tutorial how to include it in project that will help too, I tried to find something but there was no tutorial how to install them to projects
Thanks
Code based on minizip unzip.c.
include stdio.h zip.h unzip.h
First, create a zip with a file inside with a password.
The zip file must be in the same directory as the executable.
Run the program from a prompt in the directory of the generated program.
This example only extracts the first file!
/*-----------start-------------- */
/*Tries to open the zip in the current directory.*/
unzFile zfile = unzOpen("teste.zip");
if(zfile==NULL)
{
printf("Error!");
return;
}
printf("OK Zip teste.zip opened...\n");
int err = unzGoToFirstFile(zfile);/*go to first file in zip*/
if (err != UNZ_OK)
{
printf("error %d with zipfile in unzGoToFirstFile\n", err);
unzClose(zfile);/*close zip*/
}
/*At this point zfile points to the first file contained in the zip*/
char filename_inzip[256] = {0};/* The file name will be returned here */
unz_file_info file_info = {0};/*strcuture with info of the first file*/
err = unzGetCurrentFileInfo(zfile, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
if (err != UNZ_OK)
{
printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
}
else
{
int len = 8192;/*size of chunk*/
char buffer[8192]={0};/*buffer used to save uncompressed data*/
printf("name of first file is :%s\n",filename_inzip);
printf("uncompressed_size = %d\n",file_info.uncompressed_size);
/*Use your password here, the same one used to create your zip */
err = unzOpenCurrentFilePassword(zfile, "yourpassword");
if (err != UNZ_OK)
printf("error %d with zipfile in unzOpenCurrentFilePassword\n", err);
else
printf("password ok\n");
FILE *fp = fopen(filename_inzip, "wb");/*file for data binary type*/
if (fp != NULL)
{
do
{/*read the current file returned by unzGoToFirstFile to buffer in chunks of the 8192*/
err = unzReadCurrentFile(zfile, &buffer, len );
if (err < 0)
{
printf("error %d with zipfile in unzReadCurrentFile\n", err);
break;
}
if (err == 0)
break;
/*Save the chunk read to the file*/
if (fwrite(&buffer, err, 1, fp) != 1)/*if error break*/
{
printf("error %d in writing extracted file\n", errno);
err = UNZ_ERRNO;
break;
}/*else continue*/
}
while (err > 0);
/*close file*/
fclose(fp);
}
}
unzClose(zfile);