efi_loader: correct reading of directories

EFI_FILE_PROTOCOL.Read() is used both to read files and directories.

When reaching the end of a directory we always have to return buffer size
zero irrespective of the incoming buffer size. (The described scenario for
a Shim quirk cannot arise because every directory has at least '.' and '..'
as entries.)

Even when the buffer_size is too small multiple times we have to keep a
reference to our last read directory entry.

When we return to the start of the directory via SetPosition() we must
remove the reference to a previously kept directory entry.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
This commit is contained in:
Heinrich Schuchardt 2019-09-07 22:34:07 +02:00
parent 87c4840610
commit 83a74ad143

View file

@ -338,7 +338,7 @@ static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
{
struct efi_file_info *info = buffer;
struct fs_dirent *dent;
unsigned int required_size;
u64 required_size;
u16 *dst;
if (!fh->dirs) {
@ -346,6 +346,7 @@ static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
fh->dirs = fs_opendir(fh->path);
if (!fh->dirs)
return EFI_DEVICE_ERROR;
fh->dent = NULL;
}
/*
@ -356,28 +357,13 @@ static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
*/
if (fh->dent) {
dent = fh->dent;
fh->dent = NULL;
} else {
dent = fs_readdir(fh->dirs);
}
if (!dent) {
/* no more files in directory: */
/* workaround shim.efi bug/quirk.. as find_boot_csv()
* loops through directory contents, it initially calls
* read w/ zero length buffer to find out how much mem
* to allocate for the EFI_FILE_INFO, then allocates,
* and then calls a 2nd time. If we return size of
* zero the first time, it happily passes that to
* AllocateZeroPool(), and when that returns NULL it
* thinks it is EFI_OUT_OF_RESOURCES. So on first
* call return a non-zero size:
*/
if (*buffer_size == 0)
*buffer_size = sizeof(*info);
else
*buffer_size = 0;
/* no more files in directory */
*buffer_size = 0;
return EFI_SUCCESS;
}
@ -389,6 +375,7 @@ static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
fh->dent = dent;
return EFI_BUFFER_TOO_SMALL;
}
fh->dent = NULL;
*buffer_size = required_size;
memset(info, 0, required_size);