#include <grub/err.h>
#include <grub/misc.h>
#include <grub/raid.h>
+#ifdef GRUB_UTIL
+#include <grub/util/misc.h>
+#endif
+
+GRUB_MOD_LICENSE ("GPLv3+");
/* Linked list of RAID arrays. */
static struct grub_raid_array *array_list;
unsigned int i;
for (i = 0; i < array->total_devs; i++)
- if (array->device[i])
+ if (array->members[i].device)
{
tmp = grub_malloc (sizeof (*tmp));
- tmp->disk = array->device[i];
+ tmp->disk = array->members[i].device;
tmp->next = list;
list = tmp;
}
return list;
}
+
+static const char *
+grub_raid_getname (struct grub_disk *disk)
+{
+ struct grub_raid_array *array = disk->data;
+
+ return array->driver->name;
+}
#endif
+static inline int
+ascii2hex (char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return 0;
+}
+
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)
+ if (grub_memcmp (name, "mduuid/", sizeof ("mduuid/") - 1) == 0)
{
- if (!grub_strcmp (array->name, name))
- if (grub_is_array_readable (array))
- break;
+ const char *uuidstr = name + sizeof ("mduuid/") - 1;
+ grub_size_t uuid_len = grub_strlen (uuidstr) / 2;
+ grub_uint8_t uuidbin[uuid_len];
+ unsigned i;
+ for (i = 0; i < uuid_len; i++)
+ uuidbin[i] = ascii2hex (uuidstr[2 * i + 1])
+ | (ascii2hex (uuidstr[2 * i]) << 4);
+
+ for (array = array_list; array != NULL; array = array->next)
+ {
+ if (uuid_len == (unsigned) array->uuid_len
+ && grub_memcmp (uuidbin, array->uuid, uuid_len) == 0)
+ if (grub_is_array_readable (array))
+ break;
+ }
}
+ else
+ for (array = array_list; array != NULL; array = array->next)
+ {
+ if (!grub_strcmp (array->name, name))
+ if (grub_is_array_readable (array))
+ break;
+ }
if (!array)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown RAID device %s",
case 10:
{
grub_disk_addr_t read_sector, far_ofs;
- grub_uint32_t disknr, b, near, far, ofs;
+ grub_uint64_t disknr, b, near, far, ofs;
read_sector = grub_divmod64 (sector, array->chunk_size, &b);
far = ofs = near = 1;
k = disknr;
for (j = 0; j < far; j++)
{
- if (array->device[k])
+ if (array->members[k].device)
{
if (grub_errno == GRUB_ERR_READ_ERROR)
grub_errno = GRUB_ERR_NONE;
- err = grub_disk_read (array->device[k],
- array->start_sector[k] +
+ err = grub_disk_read (array->members[k].device,
+ array->members[k].start_sector +
read_sector + j * far_ofs + b,
0,
read_size << GRUB_DISK_SECTOR_BITS,
case 6:
{
grub_disk_addr_t read_sector;
- grub_uint32_t b, p, n, disknr, e;
+ grub_uint64_t b, p, n, disknr, e;
/* n = 1 for level 4 and 5, 2 for level 6. */
n = array->level / 3;
read_size = size;
e = 0;
- if (array->device[disknr])
+ if (array->members[disknr].device)
{
/* Reset read error. */
if (grub_errno == GRUB_ERR_READ_ERROR)
grub_errno = GRUB_ERR_NONE;
- err = grub_disk_read (array->device[disknr],
- array->start_sector[disknr] +
+ err = grub_disk_read (array->members[disknr].device,
+ array->members[disknr].start_sector +
read_sector + b, 0,
read_size << GRUB_DISK_SECTOR_BITS,
buf);
static grub_err_t
insert_array (grub_disk_t disk, struct grub_raid_array *new_array,
- grub_disk_addr_t start_sector, const char *scanner_name)
+ grub_disk_addr_t start_sector, const char *scanner_name,
+ grub_raid_t raid __attribute__ ((unused)))
{
struct grub_raid_array *array = 0, *p;
/* Do some checks before adding the device to the array. */
+ if (new_array->index >= array->allocated_devs)
+ {
+ void *tmp;
+ unsigned int newnum = 2 * (new_array->index + 1);
+ tmp = grub_realloc (array->members, newnum
+ * sizeof (array->members[0]));
+ if (!tmp)
+ return grub_errno;
+ array->members = tmp;
+ grub_memset (array->members + array->allocated_devs,
+ 0, (newnum - array->allocated_devs)
+ * sizeof (array->members[0]));
+ array->allocated_devs = newnum;
+ }
+
/* FIXME: Check whether the update time of the superblocks are
the same. */
/* We found more members of the array than the array
actually has according to its superblock. This shouldn't
happen normally. */
- grub_dprintf ("raid", "array->nr_devs > array->total_devs (%d)?!?",
- array->total_devs);
+ return grub_error (GRUB_ERR_BAD_DEVICE,
+ "superfluous RAID member (%d found)",
+ array->total_devs);
- if (array->device[new_array->index] != NULL)
+ if (array->members[new_array->index].device != NULL)
/* We found multiple devices with the same number. Again,
this shouldn't happen. */
- grub_dprintf ("raid", "Found two disks with the number %d?!?",
- new_array->number);
+ return grub_error (GRUB_ERR_BAD_DEVICE,
+ "found two disks with the index %d for RAID %s",
+ new_array->index, array->name);
if (new_array->disk_size < array->disk_size)
array->disk_size = new_array->disk_size;
*array = *new_array;
array->nr_devs = 0;
- grub_memset (&array->device, 0, sizeof (array->device));
- grub_memset (&array->start_sector, 0, sizeof (array->start_sector));
+#ifdef GRUB_UTIL
+ array->driver = raid;
+#endif
+ array->allocated_devs = 32;
+ if (new_array->index >= array->allocated_devs)
+ array->allocated_devs = 2 * (new_array->index + 1);
+
+ array->members = grub_zalloc (array->allocated_devs
+ * sizeof (array->members[0]));
+
+ if (!array->members)
+ {
+ grub_free (new_array->uuid);
+ return grub_errno;
+ }
if (! array->name)
{
for (p = array_list; p != NULL; p = p->next)
{
- if (! p->name && p->number == array->number)
+ if (p->number == array->number)
break;
}
}
array->name = grub_xasprintf ("md%d", array->number);
if (! array->name)
{
+ grub_free (array->members);
grub_free (array->uuid);
grub_free (array);
if (! new_name)
{
+ grub_free (array->members);
grub_free (array->uuid);
grub_free (array);
grub_dprintf ("raid", "Found array %s (%s)\n", array->name,
scanner_name);
+#ifdef GRUB_UTIL
+ grub_util_info ("Found array %s (%s)", array->name,
+ scanner_name);
+#endif
+
+ {
+ int max_used_number = 0, len, need_new_name = 0;
+ int add_us = 0;
+ len = grub_strlen (array->name);
+ if (len && grub_isdigit (array->name[len-1]))
+ add_us = 1;
+ for (p = array_list; p != NULL; p = p->next)
+ {
+ int cur_num;
+ char *num, *end;
+ if (grub_strncmp (p->name, array->name, len) != 0)
+ continue;
+ if (p->name[len] == 0)
+ {
+ need_new_name = 1;
+ continue;
+ }
+ if (add_us && p->name[len] != '_')
+ continue;
+ if (add_us)
+ num = p->name + len + 1;
+ else
+ num = p->name + len;
+ if (!grub_isdigit (num[0]))
+ continue;
+ cur_num = grub_strtoull (num, &end, 10);
+ if (end[0])
+ continue;
+ if (cur_num > max_used_number)
+ max_used_number = cur_num;
+ }
+ if (need_new_name)
+ {
+ char *tmp;
+ tmp = grub_xasprintf ("%s%s%d", array->name, add_us ? "_" : "",
+ max_used_number + 1);
+ if (!tmp)
+ return grub_errno;
+ grub_free (array->name);
+ array->name = tmp;
+ }
+ }
/* Add our new array to the list. */
array->next = array_list;
}
/* Add the device to the array. */
- array->device[new_array->index] = disk;
- array->start_sector[new_array->index] = start_sector;
+ array->members[new_array->index].device = disk;
+ array->members[new_array->index].start_sector = start_sector;
array->nr_devs++;
return 0;
while (array)
{
struct grub_raid_array *p;
- int i;
+ unsigned 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]);
+ for (i = 0; i < p->allocated_devs; i++)
+ if (p->members[i].device)
+ grub_disk_close (p->members[i].device);
+ grub_free (p->members);
grub_free (p->uuid);
grub_free (p->name);
struct grub_raid_array array;
grub_disk_addr_t start_sector;
- grub_dprintf ("raid", "Scanning for RAID devices on disk %s\n", name);
+ grub_dprintf ("raid", "Scanning for %s RAID devices on disk %s\n",
+ grub_raid_list->name, name);
+#ifdef GRUB_UTIL
+ grub_util_info ("Scanning for %s RAID devices on disk %s",
+ grub_raid_list->name, name);
+#endif
disk = grub_disk_open (name);
if (!disk)
if ((disk->total_sectors != GRUB_ULONG_MAX) &&
(! grub_raid_list->detect (disk, &array, &start_sector)) &&
- (! insert_array (disk, &array, start_sector, grub_raid_list->name)))
+ (! insert_array (disk, &array, start_sector, grub_raid_list->name,
+ grub_raid_list)))
return 0;
/* This error usually means it's not raid, no need to display
.write = grub_raid_write,
#ifdef GRUB_UTIL
.memberlist = grub_raid_memberlist,
+ .raidname = grub_raid_getname,
#endif
.next = 0
};