diff --git a/conf/common.rmk b/conf/common.rmk
index 9ca03d4..7586383 100644
--- a/conf/common.rmk
+++ b/conf/common.rmk
@@ -21,7 +21,7 @@ grub_probe_SOURCES = util/grub-probe.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 grub_probe_init.c
+ disk/lvm.c disk/raid.c disk/mdraid_linux.c grub_probe_init.c
ifeq ($(enable_grub_fstest), yes)
bin_UTILITIES += grub-fstest
@@ -32,7 +32,7 @@ util/grub-fstest.c_DEPENDENCIES = grub_fstest_init.h
grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \
kern/file.c kern/device.c kern/disk.c kern/err.c kern/misc.c \
disk/host.c disk/loopback.c normal/arg.c normal/misc.c \
- io/gzio.c lib/hexdump.c commands/blocklist.c commands/ls.c \
+ lib/hexdump.c lib/crc.c commands/blocklist.c commands/ls.c \
\
fs/affs.c fs/cpio.c fs/ext2.c fs/fat.c fs/hfs.c \
fs/hfsplus.c fs/iso9660.c fs/udf.c fs/jfs.c fs/minix.c \
@@ -40,7 +40,9 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \
fs/ufs.c fs/xfs.c fs/afs.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 \
+ kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \
+ disk/raid5_recover.c disk/raid6_recover.c \
+ disk/mdraid_linux.c disk/dmraid_nvidia.c \
grub_fstest_init.c
# For the parser.
@@ -270,14 +272,35 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS)
# Special disk structures
-pkglib_MODULES += raid.mod lvm.mod
+pkglib_MODULES += raid.mod raid5rec.mod raid6rec.mod mdraid.mod dm_nv.mod \
+ lvm.mod
# For raid.mod
raid_mod_SOURCES = disk/raid.c
raid_mod_CFLAGS = $(COMMON_CFLAGS)
raid_mod_LDFLAGS = $(COMMON_LDFLAGS)
-# For raid.mod
+# For raid5rec.mod
+raid5rec_mod_SOURCES = disk/raid5_recover.c
+raid5rec_mod_CFLAGS = $(COMMON_CFLAGS)
+raid5rec_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For raid6rec.mod
+raid6rec_mod_SOURCES = disk/raid6_recover.c
+raid6rec_mod_CFLAGS = $(COMMON_CFLAGS)
+raid6rec_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For mdraid.mod
+mdraid_mod_SOURCES = disk/mdraid_linux.c
+mdraid_mod_CFLAGS = $(COMMON_CFLAGS)
+mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For dm_nv.mod
+dm_nv_mod_SOURCES = disk/dmraid_nvidia.c
+dm_nv_mod_CFLAGS = $(COMMON_CFLAGS)
+dm_nv_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For lvm.mod
lvm_mod_SOURCES = disk/lvm.c
lvm_mod_CFLAGS = $(COMMON_CFLAGS)
lvm_mod_LDFLAGS = $(COMMON_LDFLAGS)
diff --git a/conf/i386-coreboot.rmk b/conf/i386-coreboot.rmk
index 34b3b40..f3eac0d 100644
--- a/conf/i386-coreboot.rmk
+++ b/conf/i386-coreboot.rmk
@@ -85,7 +85,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \
util/biosdisk.c util/getroot.c \
util/i386/pc/misc.c \
\
- disk/raid.c disk/lvm.c \
+ disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \
+ disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \
grub_emu_init.c
grub_emu_LDFLAGS = $(LIBCURSES)
diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk
index ef7d8a2..0b9611f 100644
--- a/conf/i386-efi.rmk
+++ b/conf/i386-efi.rmk
@@ -62,7 +62,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \
util/biosdisk.c util/getroot.c \
util/i386/pc/misc.c \
\
- disk/raid.c disk/lvm.c \
+ disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \
+ disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \
grub_emu_init.c
grub_emu_LDFLAGS = $(LIBCURSES)
diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk
index d62a656..6477a6f 100644
--- a/conf/i386-ieee1275.rmk
+++ b/conf/i386-ieee1275.rmk
@@ -83,7 +83,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \
util/biosdisk.c util/getroot.c \
util/i386/pc/misc.c \
\
- disk/raid.c disk/lvm.c \
+ disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \
+ disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \
grub_emu_init.c
grub_emu_LDFLAGS = $(LIBCURSES)
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index a2ab94d..d2773dc 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -103,7 +103,7 @@ grub_setup_SOURCES = util/i386/pc/grub-setup.c util/biosdisk.c \
\
partmap/pc.c partmap/gpt.c \
\
- disk/raid.c disk/lvm.c \
+ disk/raid.c disk/mdraid_linux.c disk/lvm.c \
util/raid.c util/lvm.c \
grub_setup_init.c
@@ -143,7 +143,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \
util/biosdisk.c util/getroot.c \
util/i386/pc/misc.c \
\
- disk/raid.c disk/lvm.c \
+ disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \
+ disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \
grub_emu_init.c
grub_emu_LDFLAGS = $(LIBCURSES)
diff --git a/conf/powerpc-ieee1275.rmk b/conf/powerpc-ieee1275.rmk
index 7e4aaa9..d97207d 100644
--- a/conf/powerpc-ieee1275.rmk
+++ b/conf/powerpc-ieee1275.rmk
@@ -67,7 +67,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \
util/biosdisk.c util/getroot.c \
util/powerpc/ieee1275/misc.c \
\
- disk/raid.c disk/lvm.c \
+ disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \
+ disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \
grub_script.tab.c grub_emu_init.c
grub_emu_LDFLAGS = $(LIBCURSES)
diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk
index 473c34e..66e9e85 100644
--- a/conf/x86_64-efi.rmk
+++ b/conf/x86_64-efi.rmk
@@ -64,7 +64,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \
util/biosdisk.c util/getroot.c \
util/i386/pc/misc.c \
\
- disk/raid.c disk/lvm.c \
+ disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \
+ disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \
grub_emu_init.c
grub_emu_LDFLAGS = $(LIBCURSES)
diff --git a/disk/dmraid_nvidia.c b/disk/dmraid_nvidia.c
new file mode 100644
index 0000000..c2d5ed3
--- /dev/null
+++ b/disk/dmraid_nvidia.c
@@ -0,0 +1,166 @@
+/* dmraid_nvidia.c - module to handle Nvidia fakeraid. */
+/*
+ * 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
+
+#define NV_SIGNATURES 4
+
+#define NV_IDLE 0
+#define NV_SCDB_INIT_RAID 2
+#define NV_SCDB_REBUILD_RAID 3
+#define NV_SCDB_UPGRADE_RAID 4
+#define NV_SCDB_SYNC_RAID 5
+
+#define NV_LEVEL_UNKNOWN 0x00
+#define NV_LEVEL_JBOD 0xFF
+#define NV_LEVEL_0 0x80
+#define NV_LEVEL_1 0x81
+#define NV_LEVEL_3 0x83
+#define NV_LEVEL_5 0x85
+#define NV_LEVEL_10 0x8a
+#define NV_LEVEL_1_0 0x8180
+
+#define NV_ARRAY_FLAG_BOOT 1 /* BIOS use only. */
+#define NV_ARRAY_FLAG_ERROR 2 /* Degraded or offling. */
+#define NV_ARRAY_FLAG_PARITY_VALID 4 /* RAID-3/5 parity valid. */
+
+struct grub_nv_array
+{
+ grub_uint32_t version;
+ grub_uint32_t signature[NV_SIGNATURES];
+ grub_uint8_t raid_job_code;
+ grub_uint8_t stripe_width;
+ grub_uint8_t total_volumes;
+ grub_uint8_t original_width;
+ grub_uint32_t raid_level;
+ grub_uint32_t stripe_block_size;
+ grub_uint32_t stripe_block_size_bytes;
+ grub_uint32_t stripe_block_size_log2;
+ grub_uint32_t stripe_mask;
+ grub_uint32_t stripe_size;
+ grub_uint32_t stripe_size_bytes;
+ grub_uint32_t raid_job_mask;
+ grub_uint32_t original_capacity;
+ grub_uint32_t flags;
+};
+
+#define NV_ID_LEN 8
+#define NV_ID_STRING "NVIDIA"
+#define NV_VERSION 100
+
+#define NV_PRODID_LEN 16
+#define NV_PRODREV_LEN 4
+
+struct grub_nv_super
+{
+ char vendor[NV_ID_LEN]; /* 0x00 - 0x07 ID string. */
+ grub_uint32_t size; /* 0x08 - 0x0B Size of metadata in dwords. */
+ grub_uint32_t chksum; /* 0x0C - 0x0F Checksum of this struct. */
+ grub_uint16_t version; /* 0x10 - 0x11 NV version. */
+ grub_uint8_t unit_number; /* 0x12 Disk index in array. */
+ grub_uint8_t reserved; /* 0x13. */
+ grub_uint32_t capacity; /* 0x14 - 0x17 Array capacity in sectors. */
+ grub_uint32_t sector_size; /* 0x18 - 0x1B Sector size. */
+ char prodid[NV_PRODID_LEN]; /* 0x1C - 0x2B Array product ID. */
+ char prodrev[NV_PRODREV_LEN]; /* 0x2C - 0x2F Array product revision */
+ grub_uint32_t unit_flags; /* 0x30 - 0x33 Flags for this disk */
+ struct grub_nv_array array; /* Array information */
+} __attribute__ ((packed));
+
+static grub_err_t
+grub_dmraid_nv_detect (grub_disk_t disk, struct grub_raid_array *array)
+{
+ grub_disk_addr_t sector;
+ struct grub_nv_super sb;
+ grub_uint32_t *uuid;
+
+ if (disk->partition)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "skip partition");
+
+ sector = grub_disk_get_size (disk) - 2;
+
+ if (grub_disk_read (disk, sector, 0, sizeof (sb), (char *) &sb))
+ return grub_errno;
+
+ if (grub_memcmp (sb.vendor, NV_ID_STRING, 6))
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid");
+
+ if (sb.version != NV_VERSION)
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "Unknown version: %d.%d", sb.version);
+
+ switch (sb.array.raid_level)
+ {
+ case NV_LEVEL_0:
+ array->level = 0;
+ array->disk_size = sb.capacity / sb.array.total_volumes;
+ break;
+
+ case NV_LEVEL_1:
+ array->level = 1;
+ array->disk_size = sb.capacity;
+ break;
+
+ case NV_LEVEL_5:
+ array->level = 5;
+ array->layout = GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC;
+ array->disk_size = sb.capacity / (sb.array.total_volumes - 1);
+ break;
+
+ default:
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "Unsupported RAID level: %d", sb.array.raid_level);
+ }
+
+ array->number = 0;
+ array->total_devs = sb.array.total_volumes;
+ array->chunk_size = sb.array.stripe_block_size;
+ array->index = sb.unit_number;
+ array->uuid_len = sizeof (sb.array.signature);
+ array->uuid = grub_malloc (sizeof (sb.array.signature));
+ if (! array->uuid)
+ return grub_errno;
+
+ grub_memcpy (array->uuid, (char *) &sb.array.signature,
+ sizeof (sb.array.signature));
+
+ return 0;
+}
+
+static struct grub_raid grub_dmraid_nv_dev =
+{
+ .name = "dmraid_nv",
+ .detect = grub_dmraid_nv_detect,
+ .next = 0
+};
+
+GRUB_MOD_INIT(dm_nv)
+{
+ grub_raid_register (&grub_dmraid_nv_dev);
+}
+
+GRUB_MOD_FINI(dm_nv)
+{
+ grub_raid_register (&grub_dmraid_nv_dev);
+}
diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c
index c8fd142..8b7f5ed 100644
--- a/disk/i386/pc/biosdisk.c
+++ b/disk/i386/pc/biosdisk.c
@@ -120,7 +120,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. */
+ total_sectors = ULONG_MAX; /* TODO: get the correct size. */
}
else if (drive & 0x80)
{
diff --git a/disk/mdraid_linux.c b/disk/mdraid_linux.c
new file mode 100644
index 0000000..163fab7
--- /dev/null
+++ b/disk/mdraid_linux.c
@@ -0,0 +1,233 @@
+/* mdraid_linux.c - module to handle linux softraid. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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 RESERVED_BYTES (64 * 1024)
+#define RESERVED_SECTORS (RESERVED_BYTES / 512)
+
+#define NEW_SIZE_SECTORS(x) ((x & ~(RESERVED_SECTORS - 1)) \
+ - RESERVED_SECTORS)
+
+#define SB_BYTES 4096
+#define SB_WORDS (SB_BYTES / 4)
+#define SB_SECTORS (SB_BYTES / 512)
+
+/*
+ * The following are counted in 32-bit words
+ */
+#define SB_GENERIC_OFFSET 0
+
+#define SB_PERSONALITY_OFFSET 64
+#define SB_DISKS_OFFSET 128
+#define SB_DESCRIPTOR_OFFSET 992
+
+#define SB_GENERIC_CONSTANT_WORDS 32
+#define SB_GENERIC_STATE_WORDS 32
+#define SB_GENERIC_WORDS (SB_GENERIC_CONSTANT_WORDS + \
+ SB_GENERIC_STATE_WORDS)
+
+#define SB_PERSONALITY_WORDS 64
+#define SB_DESCRIPTOR_WORDS 32
+#define SB_DISKS 27
+#define SB_DISKS_WORDS (SB_DISKS * SB_DESCRIPTOR_WORDS)
+
+#define SB_RESERVED_WORDS (1024 \
+ - SB_GENERIC_WORDS \
+ - SB_PERSONALITY_WORDS \
+ - SB_DISKS_WORDS \
+ - SB_DESCRIPTOR_WORDS)
+
+#define SB_EQUAL_WORDS (SB_GENERIC_WORDS \
+ + SB_PERSONALITY_WORDS \
+ + SB_DISKS_WORDS)
+
+/*
+ * Device "operational" state bits
+ */
+#define DISK_FAULTY 0
+#define DISK_ACTIVE 1
+#define DISK_SYNC 2
+#define DISK_REMOVED 3
+
+#define DISK_WRITEMOSTLY 9
+
+#define SB_MAGIC 0xa92b4efc
+
+/*
+ * Superblock state bits
+ */
+#define SB_CLEAN 0
+#define SB_ERRORS 1
+
+#define SB_BITMAP_PRESENT 8
+
+struct grub_raid_disk_09
+{
+ grub_uint32_t number; /* Device number in the entire set. */
+ grub_uint32_t major; /* Device major number. */
+ grub_uint32_t minor; /* Device minor number. */
+ grub_uint32_t raid_disk; /* The role of the device in the raid set. */
+ grub_uint32_t state; /* Operational state. */
+ grub_uint32_t reserved[SB_DESCRIPTOR_WORDS - 5];
+};
+
+struct grub_raid_super_09
+{
+ /*
+ * Constant generic information
+ */
+ grub_uint32_t md_magic; /* MD identifier. */
+ grub_uint32_t major_version; /* Major version. */
+ grub_uint32_t minor_version; /* Minor version. */
+ grub_uint32_t patch_version; /* Patchlevel version. */
+ grub_uint32_t gvalid_words; /* Number of used words in this section. */
+ grub_uint32_t set_uuid0; /* Raid set identifier. */
+ grub_uint32_t ctime; /* Creation time. */
+ grub_uint32_t level; /* Raid personality. */
+ grub_uint32_t size; /* Apparent size of each individual disk. */
+ grub_uint32_t nr_disks; /* Total disks in the raid set. */
+ grub_uint32_t raid_disks; /* Disks in a fully functional raid set. */
+ grub_uint32_t md_minor; /* Preferred MD minor device number. */
+ grub_uint32_t not_persistent; /* Does it have a persistent superblock. */
+ grub_uint32_t set_uuid1; /* Raid set identifier #2. */
+ grub_uint32_t set_uuid2; /* Raid set identifier #3. */
+ grub_uint32_t set_uuid3; /* Raid set identifier #4. */
+ grub_uint32_t gstate_creserved[SB_GENERIC_CONSTANT_WORDS - 16];
+
+ /*
+ * Generic state information
+ */
+ grub_uint32_t utime; /* Superblock update time. */
+ grub_uint32_t state; /* State bits (clean, ...). */
+ grub_uint32_t active_disks; /* Number of currently active disks. */
+ grub_uint32_t working_disks; /* Number of working disks. */
+ grub_uint32_t failed_disks; /* Number of failed disks. */
+ grub_uint32_t spare_disks; /* Number of spare disks. */
+ grub_uint32_t sb_csum; /* Checksum of the whole superblock. */
+ grub_uint64_t events; /* Superblock update count. */
+ grub_uint64_t cp_events; /* Checkpoint update count. */
+ grub_uint32_t recovery_cp; /* Recovery checkpoint sector count. */
+ grub_uint32_t gstate_sreserved[SB_GENERIC_STATE_WORDS - 12];
+
+ /*
+ * Personality information
+ */
+ grub_uint32_t layout; /* The array's physical layout. */
+ grub_uint32_t chunk_size; /* Chunk size in bytes. */
+ grub_uint32_t root_pv; /* LV root PV. */
+ grub_uint32_t root_block; /* LV root block. */
+ grub_uint32_t pstate_reserved[SB_PERSONALITY_WORDS - 4];
+
+ /*
+ * Disks information
+ */
+ struct grub_raid_disk_09 disks[SB_DISKS];
+
+ /*
+ * Reserved
+ */
+ grub_uint32_t reserved[SB_RESERVED_WORDS];
+
+ /*
+ * Active descriptor
+ */
+ struct grub_raid_disk_09 this_disk;
+} __attribute__ ((packed));
+
+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 = NEW_SIZE_SECTORS (size);
+
+ if (grub_disk_read (disk, sector, 0, SB_BYTES, (char *) &sb))
+ return grub_errno;
+
+ /* Look whether there is a RAID superblock. */
+ if (sb.md_magic != SB_MAGIC)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "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. */
+
+ /* Multipath. */
+ if ((int) sb.level == -4)
+ sb.level = 1;
+
+ if (sb.level != 0 && sb.level != 1 && sb.level != 4 &&
+ sb.level != 5 && sb.level != 6 && sb.level != 10)
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "Unsupported RAID level: %d", sb.level);
+
+ array->number = sb.md_minor;
+ array->level = sb.level;
+ array->layout = sb.layout;
+ array->total_devs = sb.raid_disk;
+ array->disk_size = (sb.size) ? sb.size * 2 : sector;
+ 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 = "mdraid",
+ .detect = grub_mdraid_detect,
+ .next = 0
+};
+
+GRUB_MOD_INIT (mdraid)
+{
+ grub_raid_register (&grub_mdraid_dev);
+}
+
+GRUB_MOD_FINI (mdraid)
+{
+ grub_raid_register (&grub_mdraid_dev);
+}
diff --git a/disk/raid.c b/disk/raid.c
index 7aa2398..50c0671 100644
--- a/disk/raid.c
+++ b/disk/raid.c
@@ -26,6 +26,8 @@
/* Linked list of RAID arrays. */
static struct grub_raid_array *array_list;
+grub_raid5_recover_func_t grub_raid5_recover_func;
+grub_raid6_recover_func_t grub_raid6_recover_func;
static char
@@ -43,10 +45,29 @@ grub_is_array_readable (struct grub_raid_array *array)
return 1;
break;
+ case 4:
case 5:
- if (array->nr_devs >= array->total_devs - 1)
- return 1;
- break;
+ case 6:
+ case 10:
+ {
+ unsigned int n;
+
+ if (array->level == 10)
+ {
+ n = array->layout & 0xFF;
+ if (n == 1)
+ n = (array->layout >> 8) & 0xFF;
+
+ n--;
+ }
+ else
+ n = array->level / 3;
+
+ if (array->nr_devs >= array->total_devs - n)
+ return 1;
+
+ break;
+ }
}
return 0;
@@ -76,12 +97,13 @@ grub_raid_memberlist (grub_disk_t disk)
unsigned int i;
for (i = 0; i < array->total_devs; i++)
- {
- tmp = grub_malloc (sizeof (*tmp));
- tmp->disk = array->device[i];
- tmp->next = list;
- list = tmp;
- }
+ if (array->device[i])
+ {
+ tmp = grub_malloc (sizeof (*tmp));
+ tmp->disk = array->device[i];
+ tmp->next = list;
+ list = tmp;
+ }
return list;
}
@@ -91,6 +113,7 @@ static grub_err_t
grub_raid_open (const char *name, grub_disk_t disk)
{
struct grub_raid_array *array;
+ unsigned n;
for (array = array_list; array != NULL; array = array->next)
{
@@ -100,7 +123,8 @@ grub_raid_open (const char *name, grub_disk_t disk)
}
if (!array)
- return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Unknown RAID device %s", name);
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Unknown RAID device %s",
+ name);
disk->has_partitions = 1;
disk->id = array->number;
@@ -111,17 +135,27 @@ grub_raid_open (const char *name, grub_disk_t disk)
switch (array->level)
{
- case 0:
- /* FIXME: RAID0 disks can have different sizes! */
- disk->total_sectors = array->total_devs * array->disk_size;
- break;
-
case 1:
disk->total_sectors = array->disk_size;
break;
+ case 10:
+ n = array->layout & 0xFF;
+ if (n == 1)
+ n = (array->layout >> 8) & 0xFF;
+
+ disk->total_sectors = grub_divmod64 (array->total_devs *
+ array->disk_size,
+ n, 0);
+ break;
+
+ case 0:
+ case 4:
case 5:
- disk->total_sectors = (array->total_devs - 1) * array->disk_size;
+ case 6:
+ n = array->level / 3;
+
+ disk->total_sectors = (array->total_devs - n) * array->disk_size;
break;
}
@@ -137,6 +171,22 @@ grub_raid_close (grub_disk_t disk __attribute ((unused)))
return;
}
+void
+grub_raid_block_xor (char *buf1, char *buf2, int size)
+{
+ grub_size_t *p1, *p2;
+
+ p1 = (grub_size_t *) buf1;
+ p2 = (grub_size_t *) buf2;
+ size /= GRUB_CPU_SIZEOF_VOID_P;
+
+ while (size)
+ {
+ *(p1++) ^= *(p2++);
+ size--;
+ }
+}
+
static grub_err_t
grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector,
grub_size_t size, char *buf)
@@ -147,190 +197,264 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector,
switch (array->level)
{
case 0:
+ case 1:
+ case 10:
{
- grub_uint64_t a;
- grub_uint32_t b;
- unsigned int disknr;
- grub_disk_addr_t read_sector;
- grub_size_t read_size;
-
- /* Find the first sector to read. */
- a = grub_divmod64 (sector, array->chunk_size, NULL);
- grub_divmod64 (a, array->total_devs, &disknr);
-
- a = grub_divmod64 (sector, array->chunk_size * array->total_devs, NULL);
- grub_divmod64 (sector, array->chunk_size, &b);
- read_sector = a * array->chunk_size + b;
-
- grub_divmod64 (read_sector, array->chunk_size, &b);
- read_size = array->chunk_size - b;
-
- if (read_size > size)
- read_size = size;
-
- while (1)
- {
- grub_uint32_t i;
-
- err = grub_disk_read (array->device[disknr], read_sector, 0,
- read_size << GRUB_DISK_SECTOR_BITS, buf);
- if (err)
- break;
-
- buf += read_size << GRUB_DISK_SECTOR_BITS;
+ grub_disk_addr_t read_sector, far_ofs;
+ grub_uint32_t disknr, b, near, far, ofs;
+
+ read_sector = grub_divmod64 (sector, array->chunk_size, &b);
+ far = ofs = near = 1;
+ far_ofs = 0;
+
+ if (array->level == 1)
+ near = array->total_devs;
+ else if (array->level == 10)
+ {
+ near = array->layout & 0xFF;
+ far = (array->layout >> 8) & 0xFF;
+ if (array->layout >> 16)
+ {
+ ofs = far;
+ far_ofs = 1;
+ }
+ else
+ far_ofs = grub_divmod64 (array->disk_size,
+ far * array->chunk_size, 0);
+
+ far_ofs *= array->chunk_size;
+ }
+
+ read_sector = grub_divmod64 (read_sector * near, array->total_devs,
+ &disknr);
+
+ ofs *= array->chunk_size;
+ read_sector *= ofs;
+
+ while (1)
+ {
+ grub_size_t read_size;
+ unsigned int i, j;
+
+ read_size = array->chunk_size - b;
+ if (read_size > size)
+ read_size = size;
+
+ for (i = 0; i < near; i++)
+ {
+ unsigned int k;
+
+ k = disknr;
+ for (j = 0; j < far; j++)
+ {
+ if (array->device[k])
+ {
+ if (grub_errno == GRUB_ERR_READ_ERROR)
+ grub_errno = GRUB_ERR_NONE;
+
+ err = grub_disk_read (array->device[k],
+ read_sector + j * far_ofs + b,
+ 0,
+ read_size << GRUB_DISK_SECTOR_BITS,
+ buf);
+ if (! err)
+ break;
+ else if (err != GRUB_ERR_READ_ERROR)
+ return err;
+ }
+ else
+ err = grub_error (GRUB_ERR_READ_ERROR,
+ "disk missing.");
+
+ k++;
+ if (k == array->total_devs)
+ k = 0;
+ }
+
+ if (! err)
+ break;
+
+ disknr++;
+ if (disknr == array->total_devs)
+ {
+ disknr = 0;
+ read_sector += ofs;
+ }
+ }
+
+ if (err)
+ return err;
+
+ buf += read_size << GRUB_DISK_SECTOR_BITS;
size -= read_size;
if (! size)
break;
- if (size > array->chunk_size)
- read_size = array->chunk_size;
- else
- read_size = size;
-
- /* Check whether the sector was aligned on a chunk size
- boundary. If this isn't the case, it's the first read
- and the next read should be set back to start of the
- boundary. */
- grub_divmod64 (read_sector, array->chunk_size, &i);
- read_sector -= i;
-
- disknr++;
- /* See whether the disk was the last disk, and start
- reading from the first disk in that case. */
- if (disknr == array->total_devs)
- {
- disknr = 0;
- read_sector += array->chunk_size;
- }
- }
- }
- break;
-
- case 1:
- /* This is easy, we can read from any disk we want. We will loop
- over all disks until we've found one that is available. In
- case of errs, we will try the to read the next disk. */
- {
- unsigned int i = 0;
-
- for (i = 0; i < array->total_devs; i++)
- {
- if (array->device[i])
- {
- err = grub_disk_read (array->device[i], sector, 0,
- size << GRUB_DISK_SECTOR_BITS, buf);
-
- if (!err)
- break;
- }
- }
+ b = 0;
+ disknr += (near - i);
+ while (disknr >= array->total_devs)
+ {
+ disknr -= array->total_devs;
+ read_sector += ofs;
+ }
+ }
+ break;
}
- break;
+ case 4:
case 5:
+ case 6:
{
- grub_uint64_t a;
- grub_uint32_t b;
- int disknr;
grub_disk_addr_t read_sector;
- grub_size_t read_size;
-
- /* Find the first sector to read. */
- a = grub_divmod64 (sector, array->chunk_size, NULL);
- grub_divmod64 (a, (array->total_devs - 1), &b);
- disknr = b;
+ grub_uint32_t b, p, n, disknr, e;
- a = grub_divmod64 (sector, array->chunk_size * (array->total_devs - 1),
- NULL);
- grub_divmod64 (sector, array->chunk_size, &b);
- read_sector = a * array->chunk_size + b;
+ /* n = 1 for level 4 and 5, 2 for level 6. */
+ n = array->level / 3;
- grub_divmod64 (read_sector, array->chunk_size * array->total_devs, &b);
- disknr -= (b / array->chunk_size);
- if (disknr < 0)
- disknr += array->total_devs;
-
- grub_divmod64 (read_sector, array->chunk_size, &b);
- read_size = array->chunk_size - b;
-
- if (read_size > size)
- read_size = size;
+ /* Find the first sector to read. */
+ read_sector = grub_divmod64 (sector, array->chunk_size, &b);
+ read_sector = grub_divmod64 (read_sector, array->total_devs - n,
+ &disknr);
+ if (array->level >= 5)
+ {
+ grub_divmod64 (read_sector, array->total_devs, &p);
+
+ if (! (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK))
+ p = array->total_devs - 1 - p;
+
+ if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
+ {
+ disknr += p + n;
+ }
+ else
+ {
+ grub_uint32_t q;
+
+ q = p + (n - 1);
+ if (q >= array->total_devs)
+ q -= array->total_devs;
+
+ if (disknr >= p)
+ disknr += n;
+ else if (disknr >= q)
+ disknr += q + 1;
+ }
+
+ if (disknr >= array->total_devs)
+ disknr -= array->total_devs;
+ }
+ else
+ p = array->total_devs - n;
+
+ read_sector *= array->chunk_size;
while (1)
{
- grub_uint32_t i;
-
- if (array->device[disknr])
- err = grub_disk_read (array->device[disknr], read_sector, 0,
- read_size << GRUB_DISK_SECTOR_BITS, buf);
+ grub_size_t read_size;
+ int next_level;
+
+ read_size = array->chunk_size - b;
+ if (read_size > size)
+ read_size = size;
+
+ e = 0;
+ if (array->device[disknr])
+ {
+ /* Reset read error. */
+ if (grub_errno == GRUB_ERR_READ_ERROR)
+ grub_errno = GRUB_ERR_NONE;
+
+ err = grub_disk_read (array->device[disknr],
+ read_sector + b, 0,
+ read_size << GRUB_DISK_SECTOR_BITS,
+ buf);
+
+ if ((err) && (err != GRUB_ERR_READ_ERROR))
+ break;
+ e++;
+ }
+ else
+ err = GRUB_ERR_READ_ERROR;
- /* If an error occurs when we already have an degraded
- array we can't recover from that. */
- if (err && ((array->total_devs - 1) == array->nr_devs))
- break;
+ if (err)
+ {
+ if (array->nr_devs < array->total_devs - n + e)
+ break;
+
+ grub_errno = GRUB_ERR_NONE;
+ if (array->level == 6)
+ {
+ err = ((grub_raid6_recover_func) ?
+ (*grub_raid6_recover_func) (array, disknr, p,
+ buf, read_sector + b,
+ read_size) :
+ grub_error (GRUB_ERR_BAD_DEVICE,
+ "raid6rec is not loaded"));
+ }
+ else
+ {
+ err = ((grub_raid5_recover_func) ?
+ (*grub_raid5_recover_func) (array, disknr,
+ buf, read_sector + b,
+ read_size) :
+ grub_error (GRUB_ERR_BAD_DEVICE,
+ "raid5rec is not loaded"));
+ }
+
+ if (err)
+ break;
+ }
- if (err || ! array->device[disknr])
- {
- /* Either an error occured or the disk is not
- available. We have to compute this block from the
- blocks on the other hard disks. */
- grub_size_t buf_size = read_size << GRUB_DISK_SECTOR_BITS;
- char buf2[buf_size];
- unsigned int j;
-
- grub_memset (buf, 0, buf_size);
-
- for (j = 0; j < array->total_devs; j++)
- {
- unsigned int k;
-
- if (j != (unsigned int) disknr)
- {
- err = grub_disk_read (array->device[j], read_sector,
- 0, buf_size, buf2);
- if (err)
- return err;
-
- for (k = 0; k < buf_size; k++)
- buf[k] = buf[k] ^ buf2[k];
- }
- }
- }
-
- buf += (read_size << GRUB_DISK_SECTOR_BITS);
+ buf += read_size << GRUB_DISK_SECTOR_BITS;
size -= read_size;
if (! size)
break;
- if (size > array->chunk_size)
- read_size = array->chunk_size;
- else
- read_size = size;
-
- /* Check whether the sector was aligned on a chunk size
- boundary. If this isn't the case, it's the first read
- and the next read should be set back to start of the
- boundary. */
- grub_divmod64 (read_sector, array->chunk_size, &i);
- read_sector -= i;
-
+ b = 0;
disknr++;
- grub_divmod64 (read_sector,
- array->chunk_size * array->total_devs, &i);
- if ((unsigned int) disknr == (array->total_devs - (i / array->chunk_size) - 1))
- disknr++;
- /* See whether the disk was the last disk, and start
- reading from the first disk in that case. */
- if ((unsigned int) disknr == array->total_devs)
- {
- disknr = 0;
- read_sector += array->chunk_size;
- grub_divmod64 (read_sector,
- array->chunk_size * array->total_devs, &i);
-
- if ((i / array->chunk_size) == (array->total_devs - 1))
- disknr++;
- }
+
+ if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
+ {
+ if (disknr == array->total_devs)
+ disknr = 0;
+
+ next_level = (disknr == p);
+ }
+ else
+ {
+ if (disknr == p)
+ disknr += n;
+
+ next_level = (disknr >= array->total_devs);
+ }
+
+ if (next_level)
+ {
+ read_sector += array->chunk_size;
+
+ if (array->level >= 5)
+ {
+ if (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK)
+ p = (p == array->total_devs - 1) ? 0 : p + 1;
+ else
+ p = (p == 0) ? array->total_devs - 1 : p - 1;
+
+ if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
+ {
+ disknr = p + n;
+ if (disknr >= array->total_devs)
+ disknr -= array->total_devs;
+ }
+ else
+ {
+ disknr -= array->total_devs;
+ if (disknr == p)
+ disknr += n;
+ }
+ }
+ else
+ disknr = 0;
+ }
}
}
break;
@@ -348,169 +472,105 @@ grub_raid_write (grub_disk_t disk __attribute ((unused)),
return GRUB_ERR_NOT_IMPLEMENTED_YET;
}
-static int
-grub_raid_scan_device (const char *name)
+static grub_err_t
+insert_array (grub_disk_t disk, struct grub_raid_array *new_array,
+ const char *scanner_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);
-
- 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;
- }
-
- /* FIXME: Check the checksum. */
-
- /* 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;
- }
-
- /* 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;
- }
+ 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);
+
+ if (new_array->disk_size < array->disk_size)
+ array->disk_size = new_array->disk_size;
+ 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++;
- }
- }
+ {
+ /* 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);
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_dprintf ("raid", "Found array: %s\n", array->name);
+ grub_dprintf ("raid", "Found array %s (%s)\n", array->name,
+ scanner_name);
/* Add our new array to the list. */
array->next = array_list;
@@ -518,47 +578,116 @@ 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;
+}
- if (array->disk_size != array->device[sb.this_disk.number]->total_sectors)
+static grub_raid_t grub_raid_list;
+
+static void
+grub_raid_scan_device (int head_only)
+{
+ auto int hook (const char *name);
+ int hook (const char *name)
{
- 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_t disk;
+ struct grub_raid_array array;
+ struct grub_raid *p;
+
+ grub_dprintf ("raid", "Scanning for RAID devices\n");
+
+ disk = grub_disk_open (name);
+ if (!disk)
+ return 0;
+
+ if (disk->total_sectors == ULONG_MAX)
+ {
+ grub_disk_close (disk);
+ return 0;
+ }
+
+ for (p = grub_raid_list; p; p = p->next)
+ {
+ if (! p->detect (disk, &array))
+ {
+ if (! insert_array (disk, &array, p->name))
+ return 0;
+
+ break;
+ }
+
+ /* This error usually means it's not raid, no need to display
+ it. */
+ if (grub_errno != GRUB_ERR_OUT_OF_RANGE)
+ grub_print_error ();
+
+ grub_errno = GRUB_ERR_NONE;
+ if (head_only)
+ break;
+ }
+
+ grub_disk_close (disk);
+
+ return 0;
}
- if (! array->device[sb.this_disk.number])
+ grub_device_iterate (&hook);
+}
+
+static void
+free_array (void)
+{
+ struct grub_raid_array *array;
+
+ array = array_list;
+ while (array)
{
- /* 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);
- }
+ struct grub_raid_array *p;
+ int i;
- return 0;
+ 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->nr_devs++;
+ array_list = 0;
+}
- return 0;
+void
+grub_raid_register (grub_raid_t raid)
+{
+ raid->next = grub_raid_list;
+ grub_raid_list = raid;
+ grub_raid_scan_device (1);
+}
+
+void
+grub_raid_unregister (grub_raid_t raid)
+{
+ grub_raid_t *p, q;
+
+ for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next)
+ if (q == raid)
+ {
+ *p = q->next;
+ break;
+ }
+}
+
+void
+grub_raid_rescan (void)
+{
+ free_array ();
+ grub_raid_scan_device (0);
}
static struct grub_disk_dev grub_raid_dev =
@@ -579,18 +708,11 @@ static struct grub_disk_dev grub_raid_dev =
GRUB_MOD_INIT(raid)
{
- grub_device_iterate (&grub_raid_scan_device);
- if (grub_errno)
- {
- grub_print_error ();
- grub_errno = GRUB_ERR_NONE;
- }
-
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/disk/raid5_recover.c b/disk/raid5_recover.c
new file mode 100644
index 0000000..31cef88
--- /dev/null
+++ b/disk/raid5_recover.c
@@ -0,0 +1,72 @@
+/* raid5_recover.c - module to recover from faulty RAID4/5 arrays. */
+/*
+ * 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
+
+static grub_err_t
+grub_raid5_recover (struct grub_raid_array *array, int disknr,
+ char *buf, grub_disk_addr_t sector, int size)
+{
+ char *buf2;
+ int i;
+
+ size <<= GRUB_DISK_SECTOR_BITS;
+ buf2 = grub_malloc (size);
+ if (!buf2)
+ return grub_errno;
+
+ grub_memset (buf, 0, size);
+
+ for (i = 0; i < (int) array->total_devs; i++)
+ {
+ grub_err_t err;
+
+ if (i == disknr)
+ continue;
+
+ err = grub_disk_read (array->device[i], sector, 0, size, buf2);
+
+ if (err)
+ {
+ grub_free (buf2);
+ return err;
+ }
+
+ grub_raid_block_xor (buf, buf2, size);
+ }
+
+ grub_free (buf2);
+
+ return GRUB_ERR_NONE;
+}
+
+GRUB_MOD_INIT(raid5rec)
+{
+ grub_raid5_recover_func = grub_raid5_recover;
+}
+
+GRUB_MOD_FINI(raid5rec)
+{
+ grub_raid5_recover_func = 0;
+}
diff --git a/disk/raid6_recover.c b/disk/raid6_recover.c
new file mode 100644
index 0000000..3cb08ab
--- /dev/null
+++ b/disk/raid6_recover.c
@@ -0,0 +1,216 @@
+/* raid6_recover.c - module to recover from faulty RAID6 arrays. */
+/*
+ * 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
+
+static grub_uint8_t raid6_table1[256][256];
+static grub_uint8_t raid6_table2[256][256];
+
+static void
+grub_raid_block_mul (grub_uint8_t mul, char *buf, int size)
+{
+ int i;
+ grub_uint8_t *p;
+
+ p = (grub_uint8_t *) buf;
+ for (i = 0; i < size; i++, p++)
+ *p = raid6_table1[mul][*p];
+}
+
+static void
+grub_raid6_init_table (void)
+{
+ int i, j;
+
+ for (i = 0; i < 256; i++)
+ raid6_table1[i][1] = raid6_table1[1][i] = i;
+
+ for (i = 2; i < 256; i++)
+ for (j = i; j < 256; j++)
+ {
+ int n;
+ grub_uint8_t c;
+
+ n = i >> 1;
+
+ c = raid6_table1[n][j];
+ c = (c << 1) ^ ((c & 0x80) ? 0x1d : 0);
+ if (i & 1)
+ c ^= j;
+
+ raid6_table1[j][i] = raid6_table1[i][j] = c;
+ }
+
+ raid6_table2[0][0] = 1;
+ for (i = 1; i < 256; i++)
+ raid6_table2[i][i] = raid6_table1[raid6_table2[i - 1][i - 1]][2];
+
+ for (i = 0; i < 254; i++)
+ for (j = 0; j < 254; j++)
+ {
+ grub_uint8_t c, n;
+ int k;
+
+ if (i == j)
+ continue;
+
+ k = i - j;
+ if (k < 0)
+ k += 255;
+
+ c = n = raid6_table2[k][k] ^ 1;
+ for (k = 0; k < 253; k++)
+ c = raid6_table1[c][n];
+
+ raid6_table2[i][j] = raid6_table1[raid6_table2[255 - j][255 - j]][c];
+ }
+}
+
+static grub_err_t
+grub_raid6_recover (struct grub_raid_array *array, int disknr, int p,
+ char *buf, grub_disk_addr_t sector, int size)
+{
+ int i, q, pos;
+ int err[2], nerr;
+ char *pbuf = 0, *qbuf = 0;
+
+ size <<= GRUB_DISK_SECTOR_BITS;
+ pbuf = grub_malloc (size);
+ if (!pbuf)
+ goto quit;
+
+ qbuf = grub_malloc (size);
+ if (!qbuf)
+ goto quit;
+
+ q = p + 1;
+ if (q == (int) array->total_devs)
+ q = 0;
+
+ grub_memset (pbuf, 0, size);
+ grub_memset (qbuf, 0, size);
+
+ pos = q + 1;
+ if (pos == (int) array->total_devs)
+ pos = 0;
+
+ nerr = 1;
+ for (i = 0; i < (int) array->total_devs - 2; i++)
+ {
+ if (pos == disknr)
+ err[0] = i;
+ else
+ {
+ if ((array->device[pos]) &&
+ (! grub_disk_read (array->device[pos], sector, 0, size, buf)))
+ {
+ grub_raid_block_xor (pbuf, buf, size);
+ grub_raid_block_mul (raid6_table2[i][i], buf, size);
+ grub_raid_block_xor (qbuf, buf, size);
+ }
+ else
+ {
+ if (nerr >= 2)
+ goto quit;
+
+ err[nerr++] = i;
+ grub_errno = GRUB_ERR_NONE;
+ }
+ }
+
+ pos++;
+ if (pos == (int) array->total_devs)
+ pos = 0;
+ }
+
+ if (nerr == 1)
+ {
+ if ((array->device[p]) &&
+ (! grub_disk_read (array->device[p], sector, 0, size, buf)))
+ {
+ grub_raid_block_xor (buf, pbuf, size);
+ goto quit;
+ }
+
+ if (! array->device[q])
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "Not enough disk to restore");
+ goto quit;
+ }
+
+ grub_errno = GRUB_ERR_NONE;
+ if (grub_disk_read (array->device[q], sector, 0, size, buf))
+ goto quit;
+
+ grub_raid_block_xor (buf, qbuf, size);
+ grub_raid_block_mul (raid6_table2[255 - err[0]][255 - err[0]], buf,
+ size);
+ }
+ else
+ {
+ grub_uint8_t c;
+
+ if ((! array->device[p]) || (! array->device[q]))
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "Not enough disk to restore");
+ goto quit;
+ }
+
+ if (grub_disk_read (array->device[p], sector, 0, size, buf))
+ goto quit;
+
+ grub_raid_block_xor (pbuf, buf, size);
+
+ if (grub_disk_read (array->device[q], sector, 0, size, buf))
+ goto quit;
+
+ grub_raid_block_xor (qbuf, buf, size);
+
+ c = raid6_table2[err[1]][err[0]];
+ grub_raid_block_mul (c, qbuf, size);
+
+ c = raid6_table1[raid6_table2[err[1]][err[1]]][c];
+ grub_raid_block_mul (c, pbuf, size);
+
+ grub_raid_block_xor (pbuf, qbuf, size);
+ grub_memcpy (buf, pbuf, size);
+ }
+
+quit:
+ grub_free (pbuf);
+ grub_free (qbuf);
+
+ return grub_errno;
+}
+
+GRUB_MOD_INIT(raid6rec)
+{
+ grub_raid6_init_table ();
+ grub_raid6_recover_func = grub_raid6_recover;
+}
+
+GRUB_MOD_FINI(raid6rec)
+{
+ grub_raid6_recover_func = 0;
+}
diff --git a/include/grub/raid.h b/include/grub/raid.h
index 4af97f1..a36be6d 100644
--- a/include/grub/raid.h
+++ b/include/grub/raid.h
@@ -22,165 +22,65 @@
#include
+#define GRUB_RAID_MAX_DEVICES 32
+
+#define GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC 0
+#define GRUB_RAID_LAYOUT_RIGHT_ASYMMETRIC 1
+#define GRUB_RAID_LAYOUT_LEFT_SYMMETRIC 2
+#define GRUB_RAID_LAYOUT_RIGHT_SYMMETRIC 3
+
+#define GRUB_RAID_LAYOUT_RIGHT_MASK 1
+#define GRUB_RAID_LAYOUT_SYMMETRIC_MASK 2
+
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. */
+ int layout; /* Layout for RAID 5/6. */
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)
+struct grub_raid
+{
+ const char *name;
-/*
- * 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)
+ grub_err_t (*detect) (grub_disk_t disk, struct grub_raid_array *array);
-/*
- * 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 */
+ struct grub_raid *next;
+};
+typedef struct grub_raid *grub_raid_t;
-#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config.
- * read requests will only be sent here in
- * dire need
- */
+void grub_raid_register (grub_raid_t raid);
+void grub_raid_unregister (grub_raid_t raid);
+void grub_raid_rescan (void);
+void grub_raid_block_xor (char *buf1, char *buf2, int size);
-#define GRUB_RAID_SB_MAGIC 0xa92b4efc
+typedef grub_err_t (*grub_raid5_recover_func_t) (struct grub_raid_array *array,
+ int disknr, char *buf,
+ grub_disk_addr_t sector,
+ int size);
-/*
- * 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];
-};
+typedef grub_err_t (*grub_raid6_recover_func_t) (struct grub_raid_array *array,
+ int disknr, int p, char *buf,
+ grub_disk_addr_t sector,
+ int size);
-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;
-};
+extern grub_raid5_recover_func_t grub_raid5_recover_func;
+extern grub_raid6_recover_func_t grub_raid6_recover_func;
#endif /* ! GRUB_RAID_H */
diff --git a/util/grub-fstest.c b/util/grub-fstest.c
index 35af6a5..a1c3c89 100644
--- a/util/grub-fstest.c
+++ b/util/grub-fstest.c
@@ -29,7 +29,9 @@
#include
#include
#include
+#include
#include
+#include
#include
@@ -136,12 +138,12 @@ grub_unregister_command (const char *name __attribute__ ((unused)))
#define CMD_CP 2
#define CMD_CMP 3
#define CMD_HEX 4
-#define CMD_BLOCKLIST 5
+#define CMD_CRC 6
+#define CMD_BLOCKLIST 7
#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))
@@ -150,16 +152,53 @@ read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len))
grub_file_t file;
grub_off_t ofs, len;
+ if ((pathname[0] == '-') && (pathname[1] == 0))
+ {
+ grub_device_t dev;
+
+ dev = grub_device_open (0);
+ if ((! dev) || (! dev->disk))
+ grub_util_error ("Can\'t open device.");
+
+ grub_util_info ("total sectors : %lld.",
+ (unsigned long long) dev->disk->total_sectors);
+
+ if (! leng)
+ leng = (dev->disk->total_sectors << GRUB_DISK_SECTOR_BITS) - skip;
+
+ 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.",
+ skip, len);
+
+ if (hook (skip, buf, len))
+ break;
+
+ skip += len;
+ leng -= len;
+ }
+
+ grub_device_close (dev);
+ return;
+ }
+
file = grub_file_open (pathname);
if (!file)
{
- grub_util_error ("cannot open file %s.\n", pathname);
+ grub_util_error ("cannot open file %s.", pathname);
return;
}
+ grub_util_info ("file size : %lld.", (unsigned long long) file->size);
+
if (skip > file->size)
{
- grub_util_error ("invalid skip value %d.\n");
+ grub_util_error ("invalid skip value %d.");
return;
}
@@ -177,7 +216,7 @@ read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len))
sz = grub_file_read (file, buf, (len > BUF_SIZE) ? BUF_SIZE : len);
if (sz < 0)
{
- grub_util_error ("read error at offset %llu.\n", ofs);
+ grub_util_error ("read error at offset %llu.", ofs);
break;
}
@@ -203,7 +242,7 @@ cmd_cp (char *src, char *dest)
if ((int) fwrite (buf, 1, len, ff) != len)
{
- grub_util_error ("write error.\n");
+ grub_util_error ("write error.");
return 1;
}
@@ -213,7 +252,7 @@ cmd_cp (char *src, char *dest)
ff = fopen (dest, "w");
if (ff == NULL)
{
- grub_util_error ("open error.\n");
+ grub_util_error ("open error.");
return;
}
read_file (src, cp_hook);
@@ -231,7 +270,7 @@ cmd_cmp (char *src, char *dest)
{
if ((int) fread (buf_1, 1, len, ff) != len)
{
- grub_util_error ("read error at offset %llu.\n", ofs);
+ grub_util_error ("read error at offset %llu.", ofs);
return 1;
}
@@ -242,7 +281,7 @@ cmd_cmp (char *src, char *dest)
for (i = 0; i < len; i++, ofs++)
if (buf_1[i] != buf[i])
{
- grub_util_error ("compare fail at offset %llu.\n", ofs);
+ grub_util_error ("compare fail at offset %llu.", ofs);
return 1;
}
}
@@ -252,12 +291,12 @@ cmd_cmp (char *src, char *dest)
ff = fopen (dest, "r");
if (ff == NULL)
{
- grub_util_error ("open error.\n");
+ grub_util_error ("open error.");
return;
}
if ((skip) && (fseeko (ff, skip, SEEK_SET)))
- grub_util_error ("seek error.\n");
+ grub_util_error ("seek error.");
read_file (src, cmp_hook);
fclose (ff);
@@ -277,28 +316,44 @@ cmd_hex (char *pathname)
}
static void
-fstest (char *image_path, int cmd, int n, char **args)
+cmd_crc (char *pathname)
{
- 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_uint32_t crc = 0;
+
+ auto int crc_hook (grub_off_t ofs, char *buf, int len);
+ int crc_hook (grub_off_t ofs, char *buf, int len)
+ {
+ (void) ofs;
+
+ crc = grub_getcrc32 (crc, buf, len);
+ return 0;
+ }
+ read_file (pathname, crc_hook);
+ printf ("%08x\n", crc);
+}
- grub_sprintf (host_file, "(host)/%s", image_path);
+static void
+fstest (char **images, int num_disks, int cmd, int n, char **args)
+{
+ 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.");
+ }
+ grub_raid_rescan ();
switch (cmd)
{
case CMD_LS:
@@ -313,29 +368,32 @@ fstest (char *image_path, int cmd, int n, char **args)
case CMD_HEX:
cmd_hex (args[0]);
break;
+ case CMD_CRC:
+ cmd_crc (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}
};
@@ -351,17 +409,17 @@ Usage: grub-fstest [OPTION]... IMAGE_PATH COMMANDS\n\
Debug tool for filesystem driver.\n\
\nCommands:\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\
+ cp FILE LOCAL copy FILE to local file LOCAL\n\
+ cmp FILE LOCAL compare FILE with local file LOCAL\n\
+ hex FILE Hex dump FILE\n\
+ crc FILE Get crc32 checksum of 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 +432,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,52 +511,53 @@ main (int argc, char *argv[])
}
/* Obtain PATH. */
- if (optind >= argc)
+ if (optind + num_disks - 1 >= argc)
{
- fprintf (stderr, "No path is specified.\n");
+ fprintf (stderr, "Not enough pathname.\n");
usage (1);
}
- image_path = argv[optind];
-
- if (*image_path != '/')
- {
- fprintf (stderr, "Must use absolute path.\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;
- nparm = 2;
+ nparm = 2;
}
else if (!grub_strcmp (argv[optind], "cmp"))
{
cmd = CMD_CMP;
- nparm = 2;
+ nparm = 2;
}
else if (!grub_strcmp (argv[optind], "hex"))
{
cmd = CMD_HEX;
+ nparm = 1;
+ }
+ else if (!grub_strcmp (argv[optind], "crc"))
+ {
+ cmd = CMD_CRC;
+ nparm = 1;
}
else if (!grub_strcmp (argv[optind], "blocklist"))
{
cmd = CMD_BLOCKLIST;
+ nparm = 1;
}
else
{
@@ -503,14 +583,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 ();
diff --git a/util/grub-probe.c b/util/grub-probe.c
index a4f51e2..ce9cbff 100644
--- a/util/grub-probe.c
+++ b/util/grub-probe.c
@@ -142,7 +142,7 @@ probe (const char *path, char *device_name)
abstraction_name = "lvm";
break;
case GRUB_DEV_ABSTRACTION_RAID:
- abstraction_name = "raid";
+ abstraction_name = "raid mdraid";
break;
default:
grub_util_info ("did not find LVM/RAID in %s, assuming raw device", device_name);