From 24089d19e2dde4e72d8c783a3f1a607f5a8e7729 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 22 Apr 2011 19:20:46 +0200 Subject: [PATCH] Add cheatmounting --- grub-core/disk/luks.c | 99 +++++++++++++++++++++++++++++++++-- grub-core/kern/emu/getroot.c | 26 +++++++-- grub-core/kern/emu/hostdisk.c | 75 ++++++++++++++------------ include/grub/emu/hostdisk.h | 6 +++ 4 files changed, 166 insertions(+), 40 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 4d907b4c6..99dd3a8e6 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -25,6 +25,15 @@ #include #include #include +#ifdef GRUB_UTIL +#include +#include +#include +#include +#include +#include +#include +#endif GRUB_MOD_LICENSE ("GPLv3+"); @@ -80,6 +89,10 @@ struct grub_luks unsigned long id, source_id; enum grub_disk_dev_id source_dev_id; char uuid[sizeof (((struct grub_luks_phdr *) 0)->uuid) + 1]; +#ifdef GRUB_UTIL + char *cheat; + int cheat_fd; +#endif struct grub_luks *next; }; typedef struct grub_luks *grub_luks_t; @@ -497,6 +510,12 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) } newdev->source = grub_strdup (name); + if (!newdev->source) + { + grub_free (newdev); + return grub_errno; + } + newdev->source_id = source->id; newdev->source_dev_id = source->dev->id; newdev->next = luks_list; @@ -507,6 +526,48 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) return GRUB_ERR_NONE; } +#ifdef GRUB_UTIL +grub_err_t +grub_luks_cheat_mount (const char *sourcedev, const char *cheat) +{ + grub_err_t err; + struct grub_luks_phdr header; + grub_luks_t newdev; + grub_disk_t source; + + /* Try to open disk. */ + source = grub_disk_open (sourcedev); + if (!source) + return grub_errno; + + /* Read the LUKS header. */ + err = grub_disk_read (source, 0, 0, sizeof (header), &header); + if (err) + return err; + + newdev = configure_ciphers (&header); + grub_disk_close (source); + if (!newdev) + return grub_errno; + + newdev->cheat = grub_strdup (cheat); + newdev->source = grub_strdup (sourcedev); + if (!newdev->source || !newdev->cheat) + { + grub_free (newdev->source); + grub_free (newdev->cheat); + grub_free (newdev); + return grub_errno; + } + newdev->cheat_fd = -1; + newdev->source_id = source->id; + newdev->source_dev_id = source->dev->id; + newdev->next = luks_list; + luks_list = newdev; + return GRUB_ERR_NONE; +} +#endif + static int grub_luks_scan_device (const char *name) { @@ -575,6 +636,17 @@ grub_luks_open (const char *name, grub_disk_t disk, if (!dev) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); +#ifdef GRUB_UTIL + if (dev->cheat) + { + if (dev->cheat_fd == -1) + dev->cheat_fd = open (dev->cheat, O_RDONLY); + if (dev->cheat_fd == -1) + return grub_error (GRUB_ERR_IO, "couldn't open %s: %s", + dev->cheat, strerror (errno)); + } +#endif + if (!dev->source_disk) { grub_dprintf ("luks", "Opening device %s\n", name); @@ -599,11 +671,17 @@ grub_luks_close (grub_disk_t disk) dev->ref--; - if (dev->ref == 0) + if (dev->ref != 0) + return; +#ifdef GRUB_UTIL + if (dev->cheat) { - grub_disk_close (dev->source_disk); - dev->source_disk = NULL; + close (dev->cheat_fd); + dev->cheat_fd = -1; } +#endif + grub_disk_close (dev->source_disk); + dev->source_disk = NULL; } static grub_err_t @@ -612,6 +690,21 @@ grub_luks_read (grub_disk_t disk, grub_disk_addr_t sector, { grub_luks_t dev = (grub_luks_t) disk->data; grub_err_t err; + +#ifdef GRUB_UTIL + if (dev->cheat) + { + err = grub_util_fd_sector_seek (dev->cheat_fd, dev->cheat, sector); + if (err) + return err; + if (grub_util_fd_read (dev->cheat_fd, buf, size << GRUB_DISK_SECTOR_BITS) + != (ssize_t) (size << GRUB_DISK_SECTOR_BITS)) + return grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", + dev->cheat); + return GRUB_ERR_NONE; + } +#endif + grub_dprintf ("luks", "Reading %" PRIuGRUB_SIZE " sectors from sector 0x%" PRIxGRUB_UINT64_T " with offset of %" PRIuGRUB_UINT32_T "\n", diff --git a/grub-core/kern/emu/getroot.c b/grub-core/kern/emu/getroot.c index 6cc745833..c3a971689 100644 --- a/grub-core/kern/emu/getroot.c +++ b/grub-core/kern/emu/getroot.c @@ -865,7 +865,9 @@ out: void grub_util_pull_device (const char *os_dev) { - switch (grub_util_get_dev_abstraction (os_dev)) + int ab; + ab = grub_util_get_dev_abstraction (os_dev); + switch (ab) { case GRUB_DEV_ABSTRACTION_LVM: case GRUB_DEV_ABSTRACTION_LUKS: @@ -875,6 +877,7 @@ grub_util_pull_device (const char *os_dev) struct dm_tree_node *node; struct dm_tree_node *child; void *handle = NULL; + char *lastsubdev = NULL; if (!grub_util_open_dm (os_dev, &tree, &node)) return; @@ -887,9 +890,26 @@ grub_util_pull_device (const char *os_dev) continue; subdev = grub_find_device ("/dev", makedev (dm->major, dm->minor)); if (subdev) - grub_util_pull_device (subdev); + { + lastsubdev = subdev; + grub_util_pull_device (subdev); + } } - dm_tree_free (tree); + if (ab == GRUB_DEV_ABSTRACTION_LUKS && lastsubdev) + { + char *grdev = grub_util_get_grub_dev (lastsubdev); + dm_tree_free (tree); + if (grdev) + { + grub_err_t err; + err = grub_luks_cheat_mount (grdev, os_dev); + if (err) + grub_util_error ("Can't mount LUKS: %s", grub_errmsg); + } + grub_free (grdev); + } + else + dm_tree_free (tree); } #endif return; diff --git a/grub-core/kern/emu/hostdisk.c b/grub-core/kern/emu/hostdisk.c index 9b65d417e..110d3e291 100644 --- a/grub-core/kern/emu/hostdisk.c +++ b/grub-core/kern/emu/hostdisk.c @@ -631,6 +631,37 @@ linux_find_partition (char *dev, unsigned long sector) } #endif /* __linux__ */ +#if defined(__linux__) && (!defined(__GLIBC__) || \ + ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))) + /* Maybe libc doesn't have large file support. */ +grub_err_t +grub_util_fd_sector_seek (int fd, const char *name, grub_disk_addr_t sector) +{ + loff_t offset, result; + static int _llseek (uint filedes, ulong hi, ulong lo, + loff_t *res, uint wh); + _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo, + loff_t *, res, uint, wh); + + offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS; + if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET)) + { + return grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", name); + } + return GRUB_ERR_NONE; +} +#else +grub_err_t +grub_util_fd_sector_seek (int fd, const char *name, grub_disk_addr_t sector) +{ + off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS; + + if (lseek (fd, offset, SEEK_SET) != offset) + return grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", name); + return 0; +} +#endif + static int open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags) { @@ -781,44 +812,19 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags) configure_device_driver (fd); #endif /* defined(__NetBSD__) */ -#if defined(__linux__) && (!defined(__GLIBC__) || \ - ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))) - /* Maybe libc doesn't have large file support. */ - { - loff_t offset, result; - static int _llseek (uint filedes, ulong hi, ulong lo, - loff_t *res, uint wh); - _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo, - loff_t *, res, uint, wh); - - offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS; - if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET)) - { - grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device); - close (fd); - return -1; - } - } -#else - { - off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS; - - if (lseek (fd, offset, SEEK_SET) != offset) - { - grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device); - close (fd); - return -1; - } - } -#endif + if (grub_util_fd_sector_seek (fd, map[disk->id].device, sector)) + { + close (fd); + return -1; + } return fd; } /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an error occurs, otherwise return LEN. */ -static ssize_t -nread (int fd, char *buf, size_t len) +ssize_t +grub_util_fd_read (int fd, char *buf, size_t len) { ssize_t size = len; @@ -901,7 +907,8 @@ grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, sectors that are read together with the MBR in one read. It should only remap the MBR, so we split the read in two parts. -jochen */ - if (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE) + if (grub_util_fd_read (fd, buf, GRUB_DISK_SECTOR_SIZE) + != GRUB_DISK_SECTOR_SIZE) { grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device); return grub_errno; @@ -912,7 +919,7 @@ grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, } #endif /* __linux__ */ - if (nread (fd, buf, size << GRUB_DISK_SECTOR_BITS) + if (grub_util_fd_read (fd, buf, size << GRUB_DISK_SECTOR_BITS) != (ssize_t) (size << GRUB_DISK_SECTOR_BITS)) grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id].device); diff --git a/include/grub/emu/hostdisk.h b/include/grub/emu/hostdisk.h index 803c0f755..c7a794d68 100644 --- a/include/grub/emu/hostdisk.h +++ b/include/grub/emu/hostdisk.h @@ -21,6 +21,7 @@ #define GRUB_BIOSDISK_MACHINE_UTIL_HEADER 1 #include +#include void grub_util_biosdisk_init (const char *dev_map); void grub_util_biosdisk_fini (void); @@ -30,5 +31,10 @@ int grub_util_biosdisk_is_present (const char *name); int grub_util_biosdisk_is_floppy (grub_disk_t disk); grub_err_t grub_util_biosdisk_flush (struct grub_disk *disk); void grub_util_pull_device (const char *osname); +grub_err_t +grub_util_fd_sector_seek (int fd, const char *name, grub_disk_addr_t sector); +ssize_t grub_util_fd_read (int fd, char *buf, size_t len); +grub_err_t +grub_luks_cheat_mount (const char *sourcedev, const char *cheat); #endif /* ! GRUB_BIOSDISK_MACHINE_UTIL_HEADER */