Allow referencing a filesystem by a file it contains From: Pavel Roskin ChangeLog: * conf/common.rmk: Add fs_file.mod. * disk/fs_file.c: New file. * include/grub/disk.h (enum grub_disk_dev_id): Add GRUB_DISK_DEVICE_FILE_ID. --- conf/common.rmk | 9 +++ disk/fs_file.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/grub/disk.h | 1 3 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 disk/fs_file.c diff --git a/conf/common.rmk b/conf/common.rmk index d0c142b..fbca2e4 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -349,8 +349,8 @@ scsi_mod_LDFLAGS = $(COMMON_LDFLAGS) # Commands. pkglib_MODULES += minicmd.mod extcmd.mod hello.mod handler.mod \ - ls.mod cmp.mod cat.mod help.mod search.mod \ - loopback.mod fs_uuid.mod configfile.mod echo.mod \ + ls.mod cmp.mod cat.mod help.mod search.mod loopback.mod \ + fs_file.mod fs_uuid.mod configfile.mod echo.mod \ terminfo.mod test.mod blocklist.mod hexdump.mod \ read.mod sleep.mod loadenv.mod crc.mod parttool.mod \ pcpart.mod memrw.mod normal.mod sh.mod lua.mod \ @@ -431,6 +431,11 @@ loopback_mod_SOURCES = disk/loopback.c loopback_mod_CFLAGS = $(COMMON_CFLAGS) loopback_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For fs_file.mod +fs_file_mod_SOURCES = disk/fs_file.c +fs_file_mod_CFLAGS = $(COMMON_CFLAGS) +fs_file_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For fs_uuid.mod fs_uuid_mod_SOURCES = disk/fs_uuid.c fs_uuid_mod_CFLAGS = $(COMMON_CFLAGS) diff --git a/disk/fs_file.c b/disk/fs_file.c new file mode 100644 index 0000000..d81a971 --- /dev/null +++ b/disk/fs_file.c @@ -0,0 +1,136 @@ +/* fs_file.c - Access partition by a file it contains. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +static grub_device_t +search_fs_file (const char *key, unsigned long *count) +{ + char *filename = NULL; + grub_device_t ret = NULL; + *count = 0; + + auto int iterate_device (const char *name); + int iterate_device (const char *name) + { + int len; + grub_file_t file; + + (*count)++; + + len = grub_strlen (name) + 2 + grub_strlen (key) + 1; + filename = grub_realloc (filename, len); + if (! filename) + return 1; + + grub_sprintf (filename, "(%s)%s", name, key); + file = grub_file_open (filename); + if (file) + { + grub_file_close (file); + ret = grub_device_open (name); + return 1; + } + + grub_errno = GRUB_ERR_NONE; + return 0; + } + + grub_device_iterate (iterate_device); + grub_free (filename); + + return ret; +} + +static grub_err_t +grub_fs_file_open (const char *name, grub_disk_t disk) +{ + grub_device_t dev; + + if (grub_strncmp (name, "FILE=", sizeof ("FILE=") - 1)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a FILE virtual volume"); + + dev = search_fs_file (name + sizeof ("FILE=") - 1, &disk->id); + if (! dev) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching file found"); + + disk->total_sectors = dev->disk->total_sectors; + disk->has_partitions = 0; + if (dev->disk->partition) + { + disk->partition = grub_malloc (sizeof (*disk->partition)); + if (disk->partition) + grub_memcpy (disk->partition, dev->disk->partition, + sizeof (*disk->partition)); + } + else + disk->partition = NULL; + + disk->data = dev; + + return GRUB_ERR_NONE; +} + +static void +grub_fs_file_close (grub_disk_t disk) +{ + grub_device_t parent = disk->data; + grub_device_close (parent); +} + +static grub_err_t +grub_fs_file_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_device_t parent = disk->data; + return parent->disk->dev->read (parent->disk, sector, size, buf); +} + +static grub_err_t +grub_fs_file_write (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, const char *buf) +{ + grub_device_t parent = disk->data; + return parent->disk->dev->write (parent->disk, sector, size, buf); +} + +static struct grub_disk_dev grub_fs_file_dev = { + .name = "fs_file", + .id = GRUB_DISK_DEVICE_FILE_ID, + .open = grub_fs_file_open, + .close = grub_fs_file_close, + .read = grub_fs_file_read, + .write = grub_fs_file_write, + .next = 0 +}; + +GRUB_MOD_INIT (fs_file) +{ + grub_disk_dev_register (&grub_fs_file_dev); +} + +GRUB_MOD_FINI (fs_file) +{ + grub_disk_dev_unregister (&grub_fs_file_dev); +} diff --git a/include/grub/disk.h b/include/grub/disk.h index a0cc642..1f96111 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -38,6 +38,7 @@ enum grub_disk_dev_id GRUB_DISK_DEVICE_ATA_ID, GRUB_DISK_DEVICE_MEMDISK_ID, GRUB_DISK_DEVICE_NAND_ID, + GRUB_DISK_DEVICE_FILE_ID, GRUB_DISK_DEVICE_UUID_ID, GRUB_DISK_DEVICE_PXE_ID, GRUB_DISK_DEVICE_SCSI_ID,