diff --git a/conf/common.rmk b/conf/common.rmk index 3d674a6..dee5d54 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -35,6 +35,7 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ \ kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + disk/mdraid.c \ grub_fstest_init.c # For the parser. @@ -257,7 +258,7 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) # Special disk structures -pkglib_MODULES += raid.mod lvm.mod +pkglib_MODULES += raid.mod lvm.mod mdraid.mod # For raid.mod raid_mod_SOURCES = disk/raid.c @@ -269,6 +270,11 @@ lvm_mod_SOURCES = disk/lvm.c lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For mdraid.mod +mdraid_mod_SOURCES = disk/mdraid.c +mdraid_mod_CFLAGS = $(COMMON_CFLAGS) +mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) + # Commands. pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod \ cmp.mod cat.mod help.mod font.mod search.mod \ diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index c8fd142..b40bb13 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -121,6 +121,7 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->sectors = 32; total_sectors = 9000000; /* TODO: get the correct size. */ + disk->size_invalid = 1; } else if (drive & 0x80) { diff --git a/disk/mdraid.c b/disk/mdraid.c new file mode 100644 index 0000000..0552436 --- /dev/null +++ b/disk/mdraid.c @@ -0,0 +1,245 @@ +/* mdraid.c - module to handle mdraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,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 + +/* Linux RAID on disk structures and constants, + copied from include/linux/raid/md_p.h. */ + +#define GRUB_RAID_RESERVED_BYTES (64 * 1024) +#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) + +#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ + - GRUB_RAID_RESERVED_SECTORS) + +#define GRUB_RAID_SB_BYTES 4096 +#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) +#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define GRUB_RAID_SB_GENERIC_OFFSET 0 + +#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 +#define GRUB_RAID_SB_DISKS_OFFSET 128 +#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 + +#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 +#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 +#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ + + GRUB_RAID_SB_GENERIC_STATE_WORDS) +#define GRUB_RAID_SB_PERSONALITY_WORDS 64 +#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 +#define GRUB_RAID_SB_DISKS 27 +#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ + - GRUB_RAID_SB_PERSONALITY_WORDS \ + - GRUB_RAID_SB_DISKS_WORDS \ + - GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ + + GRUB_RAID_SB_PERSONALITY_WORDS \ + + GRUB_RAID_SB_DISKS_WORDS) + +/* + * Device "operational" state bits + */ +#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ +#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ +#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ +#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ + +#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. + * read requests will only be sent here in + * dire need + */ + + +#define GRUB_RAID_SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define GRUB_RAID_SB_CLEAN 0 +#define GRUB_RAID_SB_ERRORS 1 + +#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + +struct grub_raid_disk_09 { + grub_uint32_t number; /* 0 Device number in the entire set */ + grub_uint32_t major; /* 1 Device major number */ + grub_uint32_t minor; /* 2 Device minor number */ + grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ + grub_uint32_t state; /* 4 Operational state */ + grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; +}; + +struct grub_raid_super_09 { + /* + * Constant generic information + */ + grub_uint32_t md_magic; /* 0 MD identifier */ + grub_uint32_t major_version; /* 1 major version to which the set conforms */ + grub_uint32_t minor_version; /* 2 minor version ... */ + grub_uint32_t patch_version; /* 3 patchlevel version ... */ + grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ + grub_uint32_t set_uuid0; /* 5 Raid set identifier */ + grub_uint32_t ctime; /* 6 Creation time */ + grub_uint32_t level; /* 7 Raid personality */ + grub_uint32_t size; /* 8 Apparent size of each individual disk */ + grub_uint32_t nr_disks; /* 9 total disks in the raid set */ + grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ + grub_uint32_t md_minor; /* 11 preferred MD minor device number */ + grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ + grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ + grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ + grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ + grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; + + /* + * Generic state information + */ + grub_uint32_t utime; /* 0 Superblock update time */ + grub_uint32_t state; /* 1 State bits (clean, ...) */ + grub_uint32_t active_disks; /* 2 Number of currently active disks */ + grub_uint32_t working_disks; /* 3 Number of working disks */ + grub_uint32_t failed_disks; /* 4 Number of failed disks */ + grub_uint32_t spare_disks; /* 5 Number of spare disks */ + grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ +#ifdef GRUB_HOST_WORDS_BIGENDIAN + grub_uint32_t events_hi; /* 7 high-order of superblock update count */ + grub_uint32_t events_lo; /* 8 low-order of superblock update count */ + grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ + grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ +#else + grub_uint32_t events_lo; /* 7 low-order of superblock update count */ + grub_uint32_t events_hi; /* 8 high-order of superblock update count */ + grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ + grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ +#endif + grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ + grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; + + /* + * Personality information + */ + grub_uint32_t layout; /* 0 the array's physical layout */ + grub_uint32_t chunk_size; /* 1 chunk size in bytes */ + grub_uint32_t root_pv; /* 2 LV root PV */ + grub_uint32_t root_block; /* 3 LV root block */ + grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; + + /* + * Disks information + */ + struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; + + /* + * Reserved + */ + grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + struct grub_raid_disk_09 this_disk; +}; + +static grub_err_t +grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + grub_uint64_t size; + struct grub_raid_super_09 sb; + grub_uint32_t *uuid; + + /* The sector where the RAID superblock is stored, if available. */ + size = grub_disk_get_size (disk); + sector = GRUB_RAID_NEW_SIZE_SECTORS(size); + + if (grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb)) + return grub_errno; + + /* Look whether there is a RAID superblock. */ + if (sb.md_magic != GRUB_RAID_SB_MAGIC) + return grub_error (GRUB_ERR_BAD_FS, "not raid"); + + /* FIXME: Also support version 1.0. */ + if (sb.major_version != 0 || sb.minor_version != 90) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID version: %d.%d", + sb.major_version, sb.minor_version); + + /* FIXME: Check the checksum. */ + + /* FIXME: Support all RAID levels. */ + if (sb.level != 0 && sb.level != 1 && sb.level != 5) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", + sb.level); + + /* FIXME: Support all layouts. */ + if (sb.level == 5 && sb.layout != 2) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID5 layout: %d", + sb.layout); + + array->number = sb.md_minor; + array->level = sb.level; + array->layout = sb.layout; + array->total_devs = sb.nr_disks; + array->disk_size = sb.size * 2; + array->chunk_size = sb.chunk_size >> 9; + array->index = sb.this_disk.number; + array->uuid_len = 16; + array->uuid = grub_malloc (16); + if (! array->uuid) + return grub_errno; + + uuid = (grub_uint32_t *) array->uuid; + uuid[0] = sb.set_uuid0; + uuid[1] = sb.set_uuid1; + uuid[2] = sb.set_uuid2; + uuid[3] = sb.set_uuid3; + + return 0; +} + +static struct grub_raid grub_mdraid_dev = +{ + .name = "md", + .detect = grub_mdraid_detect, + .next = 0 +}; + +GRUB_MOD_INIT(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); + grub_raid_rescan (); +} + +GRUB_MOD_FINI(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} diff --git a/disk/raid.c b/disk/raid.c index 731bf1f..bdfb03d 100644 --- a/disk/raid.c +++ b/disk/raid.c @@ -113,7 +113,17 @@ grub_raid_open (const char *name, grub_disk_t disk) { case 0: /* FIXME: RAID0 disks can have different sizes! */ - disk->total_sectors = array->total_devs * array->disk_size; + if (array->disk_size) + disk->total_sectors = array->total_devs * array->disk_size; + else + { + unsigned i; + + disk->total_sectors = 0; + for (i = 0; i < array->total_devs; i++) + disk->total_sectors += array->device[i]->total_sectors; + } + break; case 1: @@ -348,167 +358,122 @@ grub_raid_write (grub_disk_t disk __attribute ((unused)), return GRUB_ERR_NOT_IMPLEMENTED_YET; } -static int -grub_raid_scan_device (const char *name) -{ - grub_err_t err; - grub_disk_t disk; - grub_disk_addr_t sector; - grub_uint64_t size; - struct grub_raid_super_09 sb; - struct grub_raid_array *p, *array = NULL; - - grub_dprintf ("raid", "Scanning for RAID devices\n"); - - disk = grub_disk_open (name); - if (!disk) - return 0; - - /* The sector where the RAID superblock is stored, if available. */ - size = grub_disk_get_size (disk); - sector = GRUB_RAID_NEW_SIZE_SECTORS(size); +static grub_raid_t grub_raid_list; - err = grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb); - grub_disk_close (disk); - if (err) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - - /* Look whether there is a RAID superblock. */ - if (sb.md_magic != GRUB_RAID_SB_MAGIC) - return 0; - - /* FIXME: Also support version 1.0. */ - if (sb.major_version != 0 || sb.minor_version != 90) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID version: %d.%d", - sb.major_version, sb.minor_version); - return 0; - } +void +grub_raid_register (grub_raid_t raid) +{ + raid->next = grub_raid_list; + grub_raid_list = raid; +} - /* FIXME: Check the checksum. */ +void +grub_raid_unregister (grub_raid_t raid) +{ + grub_raid_t *p, q; - /* FIXME: Support all RAID levels. */ - if (sb.level != 0 && sb.level != 1 && sb.level != 5) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID level: %d", - sb.level); - return 0; - } + for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next) + if (q == raid) + { + *p = q->next; + break; + } +} - /* FIXME: Support all layouts. */ - if (sb.level == 5 && sb.layout != 2) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID5 layout: %d", - sb.layout); - return 0; - } +static grub_err_t +insert_array (grub_disk_t disk, struct grub_raid_array *new_array, + const char *device_name) +{ + struct grub_raid_array *array = 0, *p; /* See whether the device is part of an array we have already seen a device from. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->uuid[0] == sb.set_uuid0 && p->uuid[1] == sb.set_uuid1 - && p->uuid[2] == sb.set_uuid2 && p->uuid[3] == sb.set_uuid3) - { - array = p; - break; - } - } - - /* Do some checks before adding the device to the array. */ - if (array) - { - /* FIXME: Check whether the update time of the superblocks are - the same. */ - - if (array->total_devs == array->nr_devs) - { - /* We found more members of the array than the array - actually has according to its superblock. This shouldn't - happen normally, but what is the sanest things to do in such - a case? */ - - grub_error (GRUB_ERR_BAD_NUMBER, - "array->nr_devs > array->total_devs (%d)?!?", - array->total_devs); - - return 0; - } - - if (array->device[sb.this_disk.number] != NULL) - /* We found multiple devices with the same number. Again, - this shouldn't happen.*/ - grub_dprintf ("raid", "Found two disks with the number %d?!?", - sb.this_disk.number); - } + if ((p->uuid_len == new_array->uuid_len) && + (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len))) + { + grub_free (new_array->uuid); + array = p; + + /* Do some checks before adding the device to the array. */ + + /* FIXME: Check whether the update time of the superblocks are + the same. */ + + if (array->total_devs == array->nr_devs) + /* We found more members of the array than the array + actually has according to its superblock. This shouldn't + happen normally, but what is the sanest things to do in such + a case? */ + return grub_error (GRUB_ERR_BAD_NUMBER, + "array->nr_devs > array->total_devs (%d)?!?", + array->total_devs); + + if (array->device[new_array->index] != NULL) + /* We found multiple devices with the same number. Again, + this shouldn't happen.*/ + return grub_error (GRUB_ERR_BAD_NUMBER, + "Found two disks with the number %d?!?", + new_array->number); + + break; + } /* Add an array to the list if we didn't find any. */ if (!array) { array = grub_malloc (sizeof (*array)); if (!array) - return 0; - grub_memset (array, 0, sizeof (*array)); - array->number = sb.md_minor; - array->version = sb.major_version; - array->level = sb.level; - array->layout = sb.layout; - array->total_devs = sb.nr_disks; + { + grub_free (new_array->uuid); + return grub_errno; + } + + *array = *new_array; array->nr_devs = 0; - array->uuid[0] = sb.set_uuid0; - array->uuid[1] = sb.set_uuid1; - array->uuid[2] = sb.set_uuid2; - array->uuid[3] = sb.set_uuid3; - /* The superblock specifies the size in 1024-byte sectors. */ - array->disk_size = sb.size * 2; - array->chunk_size = sb.chunk_size / 512; + grub_memset (&array->device, 0, sizeof (array->device)); /* Check whether we don't have multiple arrays with the same number. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->number == array->number) - break; - } + { + if (p->number == array->number) + break; + } if (p) - { - /* The number is already in use, so we need to find an new number. */ - int i = 0; - - while (1) - { - for (p = array_list; p != NULL; p = p->next) - { - if (p->number == i) - break; - } - - if (!p) - { - /* We found an unused number. */ - array->number = i; - break; - } - - i++; - } - } - - array->name = grub_malloc (13); + { + /* The number is already in use, so we need to find an new number. */ + int i = 0; + + while (1) + { + for (p = array_list; p != NULL; p = p->next) + { + if (p->number == i) + break; + } + + if (!p) + { + /* We found an unused number. */ + array->number = i; + break; + } + + i++; + } + } + + array->name = grub_malloc (grub_strlen (device_name) + 6); if (! array->name) - { - grub_free (array); + { + grub_free (array->uuid); + grub_free (array); - return 0; - } + return grub_errno; + } - grub_sprintf (array->name, "md%d", array->number); + grub_sprintf (array->name, "%s%d", device_name, array->number); grub_dprintf ("raid", "Found array: %s\n", array->name); @@ -518,49 +483,85 @@ grub_raid_scan_device (const char *name) } /* Add the device to the array. */ - array->device[sb.this_disk.number] = grub_disk_open (name); + array->device[new_array->index] = disk; + array->nr_devs++; + + return 0; +} + +static int +grub_raid_scan_device (const char *name) +{ + grub_disk_t disk; + struct grub_raid_array array; + struct grub_raid *p; + + grub_dprintf ("raid", "Scanning for RAID devices\n"); - if (array->disk_size != array->device[sb.this_disk.number]->total_sectors) + disk = grub_disk_open (name); + if (!disk) + return 0; + + if (disk->size_invalid) { - if (array->total_devs == 1) - { - grub_dprintf ("raid", "Array contains only one disk, but its size (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } - else if (array->level == 1) - { - grub_dprintf ("raid", "Array is RAID level 1, but the size of disk %d (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - sb.this_disk.number, - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } + grub_disk_close (disk); + return 0; } - if (! array->device[sb.this_disk.number]) + for (p = grub_raid_list; p; p = p->next) { - /* Remove array from the list if we have just added it. */ - if (array->nr_devs == 0) - { - array_list = array->next; - grub_free (array->name); - grub_free (array); - } + if (! p->detect (disk, &array)) + { + if (! insert_array (disk, &array, p->name)) + return 0; - return 0; + break; + } + + grub_errno = GRUB_ERR_NONE; } - array->nr_devs++; + grub_disk_close (disk); return 0; } +static void +free_array (void) +{ + struct grub_raid_array *array; + + array = array_list; + while (array) + { + struct grub_raid_array *p; + int i; + + p = array; + array = array->next; + + for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++) + if (p->device[i]) + grub_disk_close (p->device[i]); + + grub_free (p->uuid); + grub_free (p->name); + grub_free (p); + } + + array_list = 0; +} + +void +grub_raid_rescan (void) +{ + if (grub_raid_list) + { + free_array (); + grub_device_iterate (&grub_raid_scan_device); + } +} + static struct grub_disk_dev grub_raid_dev = { .name = "raid", @@ -579,12 +580,11 @@ static struct grub_disk_dev grub_raid_dev = GRUB_MOD_INIT(raid) { - grub_device_iterate (&grub_raid_scan_device); grub_disk_dev_register (&grub_raid_dev); } GRUB_MOD_FINI(raid) { grub_disk_dev_unregister (&grub_raid_dev); - /* FIXME: free the array list. */ + free_array (); } diff --git a/include/grub/disk.h b/include/grub/disk.h index 049cc91..758cdda 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -93,6 +93,9 @@ struct grub_disk /* The underlying disk device. */ grub_disk_dev_t dev; + /* If total_sectors is invalid. */ + int size_invalid; + /* The total number of sectors. */ grub_uint64_t total_sectors; diff --git a/include/grub/raid.h b/include/grub/raid.h index 4af97f1..ca2edb7 100644 --- a/include/grub/raid.h +++ b/include/grub/raid.h @@ -22,165 +22,43 @@ #include +#define GRUB_RAID_MAX_DEVICES 32 + struct grub_raid_array { int number; /* The device number, taken from md_minor so we are consistent with the device name in Linux. */ - int version; /* 0 = 0.90, 1 = 1.0 */ int level; /* RAID levels, only 0, 1 or 5 at the moment. */ int layout; /* Only for RAID 5. */ unsigned int total_devs; /* Total number of devices in the array. */ - unsigned int nr_devs; /* The number of devices we've found so far. */ - grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ - grub_uint32_t uuid[4]; /* The UUID of the device. */ - char *name; /* That will be "md". */ + grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ grub_uint64_t disk_size; /* Size of an individual disk, in 512 byte sectors. */ - grub_disk_t device[32]; /* Array of total_devs devices. */ + int index; /* Index of current device. */ + int uuid_len; /* The length of uuid. */ + char *uuid; /* The UUID of the device. */ + + /* The following field is setup by the caller. */ + char *name; /* That will be "md". */ + unsigned int nr_devs; /* The number of devices we've found so far. */ + grub_disk_t device[GRUB_RAID_MAX_DEVICES]; /* Array of total_devs devices. */ struct grub_raid_array *next; }; -/* Linux RAID on disk structures and constants, - copied from include/linux/raid/md_p.h. */ - -#define GRUB_RAID_RESERVED_BYTES (64 * 1024) -#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) - -#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ - - GRUB_RAID_RESERVED_SECTORS) - -#define GRUB_RAID_SB_BYTES 4096 -#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) -#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) - -/* - * The following are counted in 32-bit words - */ -#define GRUB_RAID_SB_GENERIC_OFFSET 0 - -#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 -#define GRUB_RAID_SB_DISKS_OFFSET 128 -#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 - -#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 -#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 -#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ - + GRUB_RAID_SB_GENERIC_STATE_WORDS) -#define GRUB_RAID_SB_PERSONALITY_WORDS 64 -#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 -#define GRUB_RAID_SB_DISKS 27 -#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ - - GRUB_RAID_SB_PERSONALITY_WORDS \ - - GRUB_RAID_SB_DISKS_WORDS \ - - GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ - + GRUB_RAID_SB_PERSONALITY_WORDS \ - + GRUB_RAID_SB_DISKS_WORDS) - -/* - * Device "operational" state bits - */ -#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ -#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ -#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ -#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ - -#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. - * read requests will only be sent here in - * dire need - */ - - -#define GRUB_RAID_SB_MAGIC 0xa92b4efc - -/* - * Superblock state bits - */ -#define GRUB_RAID_SB_CLEAN 0 -#define GRUB_RAID_SB_ERRORS 1 +struct grub_raid +{ + const char *name; -#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + grub_err_t (*detect) (grub_disk_t disk, struct grub_raid_array *array); -struct grub_raid_disk_09 { - grub_uint32_t number; /* 0 Device number in the entire set */ - grub_uint32_t major; /* 1 Device major number */ - grub_uint32_t minor; /* 2 Device minor number */ - grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ - grub_uint32_t state; /* 4 Operational state */ - grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; + struct grub_raid *next; }; +typedef struct grub_raid *grub_raid_t; -struct grub_raid_super_09 { - /* - * Constant generic information - */ - grub_uint32_t md_magic; /* 0 MD identifier */ - grub_uint32_t major_version; /* 1 major version to which the set conforms */ - grub_uint32_t minor_version; /* 2 minor version ... */ - grub_uint32_t patch_version; /* 3 patchlevel version ... */ - grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ - grub_uint32_t set_uuid0; /* 5 Raid set identifier */ - grub_uint32_t ctime; /* 6 Creation time */ - grub_uint32_t level; /* 7 Raid personality */ - grub_uint32_t size; /* 8 Apparent size of each individual disk */ - grub_uint32_t nr_disks; /* 9 total disks in the raid set */ - grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ - grub_uint32_t md_minor; /* 11 preferred MD minor device number */ - grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ - grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ - grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ - grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ - grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; - - /* - * Generic state information - */ - grub_uint32_t utime; /* 0 Superblock update time */ - grub_uint32_t state; /* 1 State bits (clean, ...) */ - grub_uint32_t active_disks; /* 2 Number of currently active disks */ - grub_uint32_t working_disks; /* 3 Number of working disks */ - grub_uint32_t failed_disks; /* 4 Number of failed disks */ - grub_uint32_t spare_disks; /* 5 Number of spare disks */ - grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ -#ifdef GRUB_HOST_WORDS_BIGENDIAN - grub_uint32_t events_hi; /* 7 high-order of superblock update count */ - grub_uint32_t events_lo; /* 8 low-order of superblock update count */ - grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ - grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ -#else - grub_uint32_t events_lo; /* 7 low-order of superblock update count */ - grub_uint32_t events_hi; /* 8 high-order of superblock update count */ - grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ - grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ -#endif - grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ - grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; - - /* - * Personality information - */ - grub_uint32_t layout; /* 0 the array's physical layout */ - grub_uint32_t chunk_size; /* 1 chunk size in bytes */ - grub_uint32_t root_pv; /* 2 LV root PV */ - grub_uint32_t root_block; /* 3 LV root block */ - grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; +void grub_raid_register (grub_raid_t raid); +void grub_raid_unregister (grub_raid_t raid); - /* - * Disks information - */ - struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; - - /* - * Reserved - */ - grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; - - /* - * Active descriptor - */ - struct grub_raid_disk_09 this_disk; -}; +void grub_raid_rescan (void); #endif /* ! GRUB_RAID_H */ diff --git a/kern/disk.c b/kern/disk.c index ed82506..804ba5f 100644 --- a/kern/disk.c +++ b/kern/disk.c @@ -227,6 +227,7 @@ grub_disk_open (const char *name) disk->read_hook = 0; disk->partition = 0; disk->data = 0; + disk->size_invalid = 0; disk->name = grub_strdup (name); if (! disk->name) goto fail; diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 35af6a5..29234ac 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -141,7 +142,6 @@ grub_unregister_command (const char *name __attribute__ ((unused))) #define BUF_SIZE 32256 static grub_off_t skip, leng; -static char *part; static void read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) @@ -273,32 +273,61 @@ cmd_hex (char *pathname) return 0; } - read_file (pathname, hex_hook); + if (pathname) + read_file (pathname, hex_hook); + else + { + char buf[BUF_SIZE]; + grub_device_t dev; + + dev = grub_device_open (0); + if ((! dev) || (! dev->disk)) + grub_util_error ("Can\'t open device"); + + if (! leng) + leng = GRUB_DISK_SECTOR_SIZE; + + while (leng) + { + grub_size_t len; + + len = (leng > BUF_SIZE) ? BUF_SIZE : leng; + + if (grub_disk_read (dev->disk, 0, skip, len, buf)) + grub_util_error ("Disk read fails at offset %lld, length %d\n", + skip, len); + + hexdump (skip, buf, len); + + skip += len; + leng -= len; + } + + grub_device_close (dev); + } } static void -fstest (char *image_path, int cmd, int n, char **args) +fstest (char **images, int num_disks, int cmd, int n, char **args) { - char host_file[7 + grub_strlen (image_path) + 1]; - char device_name[(part) ? (6 + grub_strlen (part)) : 5]; - char *argv[3] = { "-p", "loop", host_file }; - - - grub_sprintf (host_file, "(host)/%s", image_path); + char host_file[128]; + char loop_name[8]; + char *argv[3] = { "-p", loop_name, host_file}; + int i; - if (execute_command (&cmd_loopback, 3, argv)) + for (i = 0; i < num_disks; i++) { - grub_util_error ("loopback command fails.\n"); - goto fail; - } + if (grub_strlen (images[i]) + 7 > sizeof (host_file)) + grub_util_error ("Pathname %s too long", images[i]); - if (part) - grub_sprintf (device_name, "loop,%s", part); - else - grub_strcpy (device_name, "loop"); + grub_sprintf (loop_name, "loop%d", i); + grub_sprintf (host_file, "(host)%s", images[i]); - grub_env_set ("root", device_name); + if (execute_command (&cmd_loopback, 3, argv)) + grub_util_error ("loopback command fails.\n"); + } + grub_raid_rescan (); switch (cmd) { case CMD_LS: @@ -311,31 +340,31 @@ fstest (char *image_path, int cmd, int n, char **args) cmd_cmp (args[0], args[1]); break; case CMD_HEX: - cmd_hex (args[0]); + cmd_hex ((n == 0) ? 0 : args[0]); break; case CMD_BLOCKLIST: execute_command (&cmd_blocklist, n, args); grub_printf ("\n"); } -fail: - argv[0] = "-d"; - execute_command (&cmd_loopback, 2, argv); + for (i = 0; i < num_disks; i++) + { + grub_sprintf (loop_name, "loop%d", i); + execute_command (&cmd_loopback, 2, argv); + } } static struct option options[] = { - {"part", required_argument, 0, 'p'}, + {"root", required_argument, 0, 'r'}, {"skip", required_argument, 0, 's'}, {"length", required_argument, 0, 'n'}, + {"diskcount", required_argument, 0, 'c'}, {"debug", required_argument, 0, 'd'}, - {"raw", no_argument, 0, 'r'}, - {"long", no_argument, 0, 'l'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0} }; @@ -353,15 +382,14 @@ Debug tool for filesystem driver.\n\ ls PATH list files in PATH\n\ cp SRC DEST copy file to local system\n\ cmp SRC DEST compare files\n\ - hex FILE hex dump FILE\n\ + hex [FILE] Hex dump FILE\n\ blocklist FILE display blocklist of FILE\n\ \nOptions:\n\ - -p, --part=NUM select partition NUM\n\ + -r, --root=DEVICE_NAME set root device\n\ -s, --skip=N skip N bytes from output file\n\ -n, --length=N handle N bytes in output file\n\ + -c, --diskcount=N N input files\n\ -d, --debug=S Set debug environment variable\n\ - -r, --raw disable auto decompression\n\ - -l, --long show long directory list\n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ -v, --verbose print verbose messages\n\ @@ -374,45 +402,66 @@ Report bugs to <%s>.\n", PACKAGE_BUGREPORT); int main (int argc, char *argv[]) { - char *image_path, *debug_str = 0; - int cmd, is_raw = 0, is_long = 0; + char *debug_str = 0, *root = 0, *default_root, *alloc_root; + int i, cmd, num_opts, image_index, num_disks = 1; progname = "grub-fstest"; + /* Find the first non option entry. */ + for (num_opts = 1; num_opts < argc; num_opts++) + if (argv[num_opts][0] == '-') + { + if ((argv[num_opts][2] == 0) && (num_opts < argc - 1) && + ((argv[num_opts][1] == 'r') || + (argv[num_opts][1] == 's') || + (argv[num_opts][1] == 'n') || + (argv[num_opts][1] == 'c') || + (argv[num_opts][1] == 'd'))) + num_opts++; + } + else + break; + /* Check for options. */ while (1) { - int c = getopt_long (argc, argv, "p:s:n:d:rlhVv", options, 0); + int c = getopt_long (num_opts, argv, "r:s:n:c:d:hVv", options, 0); + char *p; if (c == -1) break; else switch (c) { - case 'p': - part = optarg; + case 'r': + root = optarg; break; case 's': - skip = grub_strtoul (optarg, NULL, 0); + skip = grub_strtoul (optarg, &p, 0); + if (*p == 's') + skip <<= GRUB_DISK_SECTOR_BITS; break; case 'n': - leng = grub_strtoul (optarg, NULL, 0); + leng = grub_strtoul (optarg, &p, 0); + if (*p == 's') + leng <<= GRUB_DISK_SECTOR_BITS; break; + case 'c': + num_disks = grub_strtoul (optarg, NULL, 0); + if (num_disks < 1) + { + fprintf (stderr, "Invalid disk count.\n"); + usage (1); + } + break; + case 'd': debug_str = optarg; break; - case 'r': - is_raw = 1; - break; - - case 'l': - is_long = 1; - break; - case 'h': usage (0); break; @@ -432,35 +481,29 @@ main (int argc, char *argv[]) } /* Obtain PATH. */ - if (optind >= argc) - { - fprintf (stderr, "No path is specified.\n"); - usage (1); - } - - image_path = argv[optind]; - - if (*image_path != '/') + if (optind + num_disks - 1 >= argc) { - fprintf (stderr, "Must use absolute path.\n"); + fprintf (stderr, "Not enough pathname.\n"); usage (1); } - optind++; + image_index = optind; + for (i = 0; i < num_disks; i++, optind++) + if (argv[optind][0] != '/') + { + fprintf (stderr, "Must use absolute path.\n"); + usage (1); + } cmd = 0; if (optind < argc) { - int nparm = 1; + int nparm = 0; if (!grub_strcmp (argv[optind], "ls")) - { - cmd = CMD_LS; - if (is_long) - argv[optind--] = "-l"; - else - nparm = 0; - } + { + cmd = CMD_LS; + } else if (!grub_strcmp (argv[optind], "cp")) { cmd = CMD_CP; @@ -478,6 +521,7 @@ main (int argc, char *argv[]) else if (!grub_strcmp (argv[optind], "blocklist")) { cmd = CMD_BLOCKLIST; + nparm = 1; } else { @@ -503,14 +547,31 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); - if (is_raw) - grub_env_set ("filehook", "0"); - if (debug_str) grub_env_set ("debug", debug_str); + default_root = (num_disks == 1) ? "loop0" : "md0"; + alloc_root = 0; + if (root) + { + if ((*root >= '0') && (*root <= '9')) + { + alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2); + + sprintf (alloc_root, "%s,%s", default_root, root); + root = alloc_root; + } + } + else + root = default_root; + + grub_env_set ("root", root); + + if (alloc_root) + free (alloc_root); + /* Do it. */ - fstest (image_path + 1, cmd, argc - optind, argv + optind); + fstest (argv + image_index, num_disks, cmd, argc - optind, argv + optind); /* Free resources. */ grub_fini_all ();