/* grub-setup.c - make GRUB usable */ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include void grub_install_get_blocklist (grub_device_t root_dev, const char *core_path, const char *core_img __attribute__ ((unused)), size_t core_size, void (*callback) (grub_disk_addr_t sector, unsigned offset, unsigned length, void *data), void *hook_data) { grub_partition_t container = root_dev->disk->partition; grub_uint64_t container_start = grub_partition_get_start (container); struct fiemap fie1; int fd; /* Write the first two sectors of the core image onto the disk. */ grub_util_info ("opening the core image `%s'", core_path); fd = open (core_path, O_RDONLY); if (fd < 0) grub_util_error (_("cannot open `%s': %s"), core_path, strerror (errno)); grub_memset (&fie1, 0, sizeof (fie1)); fie1.fm_length = core_size; fie1.fm_flags = FIEMAP_FLAG_SYNC; if (ioctl (fd, FS_IOC_FIEMAP, &fie1) < 0) { int nblocks, i, j; int bsize; int mul; grub_util_info ("FIEMAP failed. Reverting to FIBMAP"); if (ioctl (fd, FIGETBSZ, &bsize) < 0) grub_util_error (_("can't retrieve blocklists: %s"), strerror (errno)); if (bsize & (GRUB_DISK_SECTOR_SIZE - 1)) grub_util_error ("%s", _("blocksize is not divisible by 512")); mul = bsize >> GRUB_DISK_SECTOR_BITS; nblocks = (core_size + bsize - 1) / bsize; if (mul == 0 || nblocks == 0) grub_util_error ("%s", _("can't retrieve blocklists")); for (i = 0; i < nblocks; i++) { unsigned blk = i; if (ioctl (fd, FIBMAP, &blk) < 0) grub_util_error (_("can't retrieve blocklists: %s"), strerror (errno)); for (j = 0; j < mul; j++) { int rest = core_size - ((i * mul + j) << GRUB_DISK_SECTOR_BITS); if (rest <= 0) break; if (rest > GRUB_DISK_SECTOR_SIZE) rest = GRUB_DISK_SECTOR_SIZE; callback (((grub_uint64_t) blk) * mul + j + container_start, 0, rest, hook_data); } } } else { struct fiemap *fie2; int i, j; fie2 = xmalloc (sizeof (*fie2) + fie1.fm_mapped_extents * sizeof (fie1.fm_extents[1])); memset (fie2, 0, sizeof (*fie2) + fie1.fm_mapped_extents * sizeof (fie2->fm_extents[1])); fie2->fm_length = core_size; fie2->fm_flags = FIEMAP_FLAG_SYNC; fie2->fm_extent_count = fie1.fm_mapped_extents; if (ioctl (fd, FS_IOC_FIEMAP, fie2) < 0) grub_util_error (_("can't retrieve blocklists: %s"), strerror (errno)); for (i = 0; i < fie2->fm_mapped_extents; i++) { for (j = 0; j < ((fie2->fm_extents[i].fe_length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS); j++) { size_t len = (fie2->fm_extents[i].fe_length - j * GRUB_DISK_SECTOR_SIZE); if (len > GRUB_DISK_SECTOR_SIZE) len = GRUB_DISK_SECTOR_SIZE; callback ((fie2->fm_extents[i].fe_physical >> GRUB_DISK_SECTOR_BITS) + j + container_start, fie2->fm_extents[i].fe_physical & (GRUB_DISK_SECTOR_SIZE - 1), len, hook_data); } } } close (fd); }