2008-08-23 Bean <bean123ch@gmail.com>

* conf/common.rmk (grub_probe_SOURCES): Add disk/mdraid_linux.c.
	(grub_fstest_SOURCES): Add disk/raid5_recover.c, disk/raid6_recover.c,
	disk/mdraid_linux.c and disk/dmraid_nvidia.c and lib/crc.c.
	(pkglib_MODULES): Add raid5rec.mod, raid6rec.mod, mdraid.mod and
	dm_nv.mod.
	(raid5rec_mod_SOURCES): New macro.
	(raid5rec_mod_CFLAGS): Likewise.
	(raid5rec_mod_LDFLAGS): Likewise.
	(raid6rec_mod_SOURCES): Likewise.
	(raid6rec_mod_CFLAGS): Likewise.
	(raid6rec_mod_LDFLAGS): Likewise.
	(mdraid_mod_SOURCES): Likewise.
	(mdraid_mod_CFLAGS): Likewise.
	(mdraid_mod_LDFLAGS): Likewise.
	(dm_nv_mod_SOURCES): Likewise.
	(dm_nv_mod_CFLAGS): Likewise.
	(dm_nv_mod_LDFLAGS): Likewise.

	* conf/i386-pc.rmk (grub_setup_SOURCES): Add disk/mdraid_linux.c.
	(grub_emu_SOURCES):  Add disk/raid5_recover.c, disk/raid6_recover.c,
	disk/mdraid_linux.c and disk/dmraid_nvidia.c.

	* conf/i386-coreboot.rmk (grub_emu_SOURCES): Add disk/raid5_recover.c,
	disk/raid6_recover.c, disk/mdraid_linux.c and disk/dmraid_nvidia.c.

	* conf/i386-efi.rmk (grub_emu_SOURCES): Likewise.

	* conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise.

	* conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise.

	* conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise.

	* disk/raid5_recover.c: New file.

	* disk/raid6_recover.c: Likewise.

	* disk/mdraid_linux.c: Likewise.

	* disk/dmraid_nvidia.c: Likewise.

	* disk/i386/pc/biosdisk.c: Set total_sectors of cdrom device to
	ULONG_MAX.

	* disk/raid.c (grub_raid_open): Use the size of the smallest disk to
	calculate the size of raid device.
	(grub_raid_read): Simplify raid0 code. Support raid4, raid6 and four
	different layout of raid5.
	(grub_raid_scan_device): Remove code specific to mdraid.
	(grub_raid_list): New variable.
	(free_array): New function.
	(grub_raid_register): Likewise.
	(grub_raid_unregister): Likewise.
	(grub_raid_rescan): Likewise.
	(GRUB_MOD_INIT): Don't iterate device here.
	(GRUB_MOD_FINI): Use free_array to release resource.

	* include/grub/raid.h: Remove macro and structure specific to mdraid.
	(grub_raid5_recover_func_t): New function variable type.
	(grub_raid6_recover_func_t): Likewise.
	(grub_raid5_recover_func): New variable.
	(grub_raid6_recover_func): Likewise.
	(grub_raid_register): New function.
	(grub_raid_unregister): Likewise.
	(grub_raid_rescan): Likewise.
	(grub_raid_block_xor): Likewise.

	* util/grub-fstest.c: Add #include <grub/raid.h> and <grub/lib/crc.h>.
	(CMD_CRC): New macro.
	(part): Removed.
	(read_file): Handle device as well as file.
	(cmd_crc): New function.
	(fstest): Handle multiple disks.
	(options): Remove part, raw and long, add root and diskcount.
	(usage): Add crc, remove -p, -r, -l, add -r and -c.
	(main): Find the first non option entry and ignore subsequence options,
	add handling for the new options, support multiple disks.

	* util/grub-probe.c (probe): Add mdraid to abstraction_name.
This commit is contained in:
bean 2008-08-23 14:51:19 +00:00
parent 29c1891563
commit 5ed20adcb9
24 changed files with 1877 additions and 635 deletions

View file

@ -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_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;
b = 0;
disknr += (near - i);
while (disknr >= array->total_devs)
{
disknr -= array->total_devs;
read_sector += ofs;
}
}
break;
}
case 4:
case 5:
case 6:
{
grub_uint64_t a;
grub_uint32_t b;
unsigned int disknr;
grub_disk_addr_t read_sector;
grub_size_t read_size;
grub_uint32_t b, p, n, disknr, e;
/* n = 1 for level 4 and 5, 2 for level 6. */
n = array->level / 3;
/* Find the first sector to read. */
a = grub_divmod64 (sector, array->chunk_size, NULL);
grub_divmod64 (a, array->total_devs, &disknr);
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);
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;
if (! (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK))
p = array->total_devs - 1 - p;
grub_divmod64 (read_sector, array->chunk_size, &b);
read_size = array->chunk_size - b;
if (read_size > size)
read_size = size;
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;
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;
err = grub_disk_read (array->device[disknr], read_sector, 0,
read_size << GRUB_DISK_SECTOR_BITS, buf);
if (err)
break;
{
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;
}
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++;
/* 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 (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
{
if (disknr == array->total_devs)
disknr = 0;
if (!err)
break;
}
}
}
break;
next_level = (disknr == p);
}
else
{
if (disknr == p)
disknr += n;
case 5:
{
grub_uint64_t a;
grub_uint32_t b;
int disknr;
grub_disk_addr_t read_sector;
grub_size_t read_size;
next_level = (disknr >= array->total_devs);
}
/* Find the first sector to read. */
a = grub_divmod64 (sector, array->chunk_size, NULL);
grub_divmod64 (a, (array->total_devs - 1), &b);
disknr = b;
if (next_level)
{
read_sector += array->chunk_size;
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;
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;
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;
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);
/* 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 || ! 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);
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++;
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)
{
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;
}
}
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. */
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);
/* Do some checks before adding the device to the array. */
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);
}
/* 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;
{
/* 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;
}
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;
}
if (!p)
{
/* We found an unused number. */
array->number = i;
break;
}
i++;
}
}
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++;
if (array->disk_size != array->device[sb.this_disk.number]->total_sectors)
return 0;
}
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;
}
}
if (! array->device[sb.this_disk.number])
{
/* 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);
}
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;
}
array->nr_devs++;
return 0;
grub_device_iterate (&hook);
}
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_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 ();
}