2N/A/* raid.c - module to read RAID arrays. */
2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (C) 2006,2007,2008,2009,2010 Free Software Foundation, Inc.
2N/A *
2N/A * GRUB is free software: you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation, either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A#include <grub/dl.h>
2N/A#include <grub/disk.h>
2N/A#include <grub/mm.h>
2N/A#include <grub/err.h>
2N/A#include <grub/misc.h>
2N/A#include <grub/raid.h>
2N/A#include <grub/partition.h>
2N/A#ifdef GRUB_UTIL
2N/A#include <grub/util/misc.h>
2N/A#endif
2N/A
2N/AGRUB_MOD_LICENSE ("GPLv3+");
2N/A
2N/A/* Linked list of RAID arrays. */
2N/Astatic struct grub_raid_array *array_list;
2N/Agrub_raid5_recover_func_t grub_raid5_recover_func;
2N/Agrub_raid6_recover_func_t grub_raid6_recover_func;
2N/Astatic grub_raid_t grub_raid_list;
2N/Astatic int inscnt = 0;
2N/A
2N/Astatic struct grub_raid_array *
2N/Afind_array (const char *name);
2N/A
2N/A
2N/Astatic char
2N/Agrub_is_array_readable (struct grub_raid_array *array)
2N/A{
2N/A switch (array->level)
2N/A {
2N/A case 0:
2N/A if (array->nr_devs == array->total_devs)
2N/A return 1;
2N/A break;
2N/A
2N/A case 1:
2N/A if (array->nr_devs >= 1)
2N/A return 1;
2N/A break;
2N/A
2N/A case 4:
2N/A case 5:
2N/A case 6:
2N/A case 10:
2N/A {
2N/A unsigned int n;
2N/A
2N/A if (array->level == 10)
2N/A {
2N/A n = array->layout & 0xFF;
2N/A if (n == 1)
2N/A n = (array->layout >> 8) & 0xFF;
2N/A
2N/A n--;
2N/A }
2N/A else
2N/A n = array->level / 3;
2N/A
2N/A if (array->nr_devs >= array->total_devs - n)
2N/A return 1;
2N/A
2N/A break;
2N/A }
2N/A }
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Ainsert_array (grub_disk_t disk, struct grub_raid_array *new_array,
2N/A grub_disk_addr_t start_sector, const char *scanner_name,
2N/A grub_raid_t raid __attribute__ ((unused)));
2N/A
2N/Astatic int scan_depth = 0;
2N/A
2N/Astatic void
2N/Ascan_devices (const char *arname)
2N/A{
2N/A grub_raid_t raid;
2N/A
2N/A auto int hook (const char *name);
2N/A int hook (const char *name)
2N/A {
2N/A grub_disk_t disk;
2N/A struct grub_raid_array array;
2N/A struct grub_raid_array *arr;
2N/A grub_disk_addr_t start_sector;
2N/A
2N/A grub_dprintf ("raid", "Scanning for %s RAID devices on disk %s\n",
2N/A raid->name, name);
2N/A#ifdef GRUB_UTIL
2N/A grub_util_info ("Scanning for %s RAID devices on disk %s",
2N/A raid->name, name);
2N/A#endif
2N/A
2N/A disk = grub_disk_open (name);
2N/A if (!disk)
2N/A return 0;
2N/A
2N/A for (arr = array_list; arr != NULL; arr = arr->next)
2N/A {
2N/A struct grub_raid_member *m;
2N/A for (m = arr->members; m < arr->members + arr->nr_devs; m++)
2N/A if (m->device && m->device->id == disk->id
2N/A && m->device->dev->id == disk->dev->id
2N/A && grub_partition_get_start (m->device->partition)
2N/A == grub_partition_get_start (disk->partition)
2N/A && grub_disk_get_size (m->device)
2N/A == grub_disk_get_size (disk))
2N/A {
2N/A grub_disk_close (disk);
2N/A return 0;
2N/A }
2N/A }
2N/A
2N/A if ((! raid->detect (disk, &array, &start_sector)) &&
2N/A (! insert_array (disk, &array, start_sector, raid->name,
2N/A raid)))
2N/A return 0;
2N/A
2N/A /* This error usually means it's not raid, no need to display
2N/A it. */
2N/A if (grub_errno != GRUB_ERR_OUT_OF_RANGE)
2N/A grub_print_error ();
2N/A
2N/A grub_errno = GRUB_ERR_NONE;
2N/A
2N/A grub_disk_close (disk);
2N/A
2N/A if (arname && find_array (arname))
2N/A return 1;
2N/A
2N/A return 0;
2N/A }
2N/A
2N/A if (scan_depth)
2N/A return;
2N/A
2N/A scan_depth++;
2N/A for (raid = grub_raid_list; raid; raid = raid->next)
2N/A grub_device_iterate (&hook);
2N/A scan_depth--;
2N/A}
2N/A
2N/Astatic int
2N/Agrub_raid_iterate (int (*hook) (const char *name),
2N/A grub_disk_pull_t pull)
2N/A{
2N/A struct grub_raid_array *array;
2N/A int islcnt = 0;
2N/A
2N/A if (pull == GRUB_DISK_PULL_RESCAN)
2N/A {
2N/A islcnt = inscnt;
2N/A scan_devices (NULL);
2N/A }
2N/A
2N/A if (pull != GRUB_DISK_PULL_NONE && pull != GRUB_DISK_PULL_RESCAN)
2N/A return 0;
2N/A
2N/A for (array = array_list; array != NULL; array = array->next)
2N/A {
2N/A if (grub_is_array_readable (array) && array->became_readable_at >= islcnt)
2N/A if (hook (array->name))
2N/A return 1;
2N/A }
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A#ifdef GRUB_UTIL
2N/Astatic grub_disk_memberlist_t
2N/Agrub_raid_memberlist (grub_disk_t disk)
2N/A{
2N/A struct grub_raid_array *array = disk->data;
2N/A grub_disk_memberlist_t list = NULL, tmp;
2N/A unsigned int i;
2N/A
2N/A for (i = 0; i < array->total_devs; i++)
2N/A if (array->members[i].device)
2N/A {
2N/A tmp = grub_malloc (sizeof (*tmp));
2N/A tmp->disk = array->members[i].device;
2N/A tmp->next = list;
2N/A list = tmp;
2N/A }
2N/A
2N/A return list;
2N/A}
2N/A
2N/Astatic const char *
2N/Agrub_raid_getname (struct grub_disk *disk)
2N/A{
2N/A struct grub_raid_array *array = disk->data;
2N/A
2N/A return array->driver->name;
2N/A}
2N/A#endif
2N/A
2N/Astatic inline int
2N/Aascii2hex (char c)
2N/A{
2N/A if (c >= '0' && c <= '9')
2N/A return c - '0';
2N/A if (c >= 'a' && c <= 'f')
2N/A return c - 'a' + 10;
2N/A if (c >= 'A' && c <= 'F')
2N/A return c - 'A' + 10;
2N/A return 0;
2N/A}
2N/A
2N/Astatic struct grub_raid_array *
2N/Afind_array (const char *name)
2N/A{
2N/A struct grub_raid_array *array;
2N/A
2N/A if (grub_memcmp (name, "mduuid/", sizeof ("mduuid/") - 1) == 0)
2N/A {
2N/A const char *uuidstr = name + sizeof ("mduuid/") - 1;
2N/A grub_size_t uuid_len = grub_strlen (uuidstr) / 2;
2N/A grub_uint8_t uuidbin[uuid_len];
2N/A unsigned i;
2N/A for (i = 0; i < uuid_len; i++)
2N/A uuidbin[i] = ascii2hex (uuidstr[2 * i + 1])
2N/A | (ascii2hex (uuidstr[2 * i]) << 4);
2N/A
2N/A for (array = array_list; array != NULL; array = array->next)
2N/A {
2N/A if (uuid_len == (unsigned) array->uuid_len
2N/A && grub_memcmp (uuidbin, array->uuid, uuid_len) == 0)
2N/A if (grub_is_array_readable (array))
2N/A return array;
2N/A }
2N/A }
2N/A else
2N/A for (array = array_list; array != NULL; array = array->next)
2N/A {
2N/A if (!grub_strcmp (array->name, name))
2N/A if (grub_is_array_readable (array))
2N/A return array;
2N/A }
2N/A return NULL;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Agrub_raid_open (const char *name, grub_disk_t disk)
2N/A{
2N/A struct grub_raid_array *array;
2N/A unsigned n;
2N/A
2N/A if (grub_memcmp (name, "md", sizeof ("md") - 1) != 0)
2N/A return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown RAID device %s",
2N/A name);
2N/A
2N/A array = find_array (name);
2N/A
2N/A if (! array)
2N/A {
2N/A scan_devices (name);
2N/A if (grub_errno)
2N/A {
2N/A grub_print_error ();
2N/A grub_errno = GRUB_ERR_NONE;
2N/A }
2N/A array = find_array (name);
2N/A }
2N/A
2N/A if (!array)
2N/A return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown RAID device %s",
2N/A name);
2N/A
2N/A disk->id = array->number;
2N/A disk->data = array;
2N/A
2N/A grub_dprintf ("raid", "%s: total_devs=%d, disk_size=%lld\n", name,
2N/A array->total_devs, (unsigned long long) array->disk_size);
2N/A
2N/A switch (array->level)
2N/A {
2N/A case 1:
2N/A disk->total_sectors = array->disk_size;
2N/A break;
2N/A
2N/A case 10:
2N/A n = array->layout & 0xFF;
2N/A if (n == 1)
2N/A n = (array->layout >> 8) & 0xFF;
2N/A
2N/A disk->total_sectors = grub_divmod64 (array->total_devs *
2N/A array->disk_size,
2N/A n, 0);
2N/A break;
2N/A
2N/A case 0:
2N/A case 4:
2N/A case 5:
2N/A case 6:
2N/A n = array->level / 3;
2N/A
2N/A disk->total_sectors = (array->total_devs - n) * array->disk_size;
2N/A break;
2N/A }
2N/A
2N/A grub_dprintf ("raid", "%s: level=%d, total_sectors=%lld\n", name,
2N/A array->level, (unsigned long long) disk->total_sectors);
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic void
2N/Agrub_raid_close (grub_disk_t disk __attribute ((unused)))
2N/A{
2N/A return;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Agrub_raid_read (grub_disk_t disk, grub_disk_addr_t sector,
2N/A grub_size_t size, char *buf)
2N/A{
2N/A struct grub_raid_array *array = disk->data;
2N/A grub_err_t err = 0;
2N/A
2N/A switch (array->level)
2N/A {
2N/A case 0:
2N/A case 1:
2N/A case 10:
2N/A {
2N/A grub_disk_addr_t read_sector, far_ofs;
2N/A grub_uint64_t disknr, b, near, far, ofs;
2N/A
2N/A read_sector = grub_divmod64 (sector, array->chunk_size, &b);
2N/A far = ofs = near = 1;
2N/A far_ofs = 0;
2N/A
2N/A if (array->level == 1)
2N/A near = array->total_devs;
2N/A else if (array->level == 10)
2N/A {
2N/A near = array->layout & 0xFF;
2N/A far = (array->layout >> 8) & 0xFF;
2N/A if (array->layout >> 16)
2N/A {
2N/A ofs = far;
2N/A far_ofs = 1;
2N/A }
2N/A else
2N/A far_ofs = grub_divmod64 (array->disk_size,
2N/A far * array->chunk_size, 0);
2N/A
2N/A far_ofs *= array->chunk_size;
2N/A }
2N/A
2N/A read_sector = grub_divmod64 (read_sector * near, array->total_devs,
2N/A &disknr);
2N/A
2N/A ofs *= array->chunk_size;
2N/A read_sector *= ofs;
2N/A
2N/A while (1)
2N/A {
2N/A grub_size_t read_size;
2N/A unsigned int i, j;
2N/A
2N/A read_size = array->chunk_size - b;
2N/A if (read_size > size)
2N/A read_size = size;
2N/A
2N/A for (i = 0; i < near; i++)
2N/A {
2N/A unsigned int k;
2N/A
2N/A k = disknr;
2N/A for (j = 0; j < far; j++)
2N/A {
2N/A if (array->members[k].device)
2N/A {
2N/A if (grub_errno == GRUB_ERR_READ_ERROR)
2N/A grub_errno = GRUB_ERR_NONE;
2N/A
2N/A err = grub_disk_read (array->members[k].device,
2N/A array->members[k].start_sector +
2N/A read_sector + j * far_ofs + b,
2N/A 0,
2N/A read_size << GRUB_DISK_SECTOR_BITS,
2N/A buf);
2N/A if (! err)
2N/A break;
2N/A else if (err != GRUB_ERR_READ_ERROR)
2N/A return err;
2N/A }
2N/A else
2N/A err = grub_error (GRUB_ERR_READ_ERROR,
2N/A "disk missing");
2N/A
2N/A k++;
2N/A if (k == array->total_devs)
2N/A k = 0;
2N/A }
2N/A
2N/A if (! err)
2N/A break;
2N/A
2N/A disknr++;
2N/A if (disknr == array->total_devs)
2N/A {
2N/A disknr = 0;
2N/A read_sector += ofs;
2N/A }
2N/A }
2N/A
2N/A if (err)
2N/A return err;
2N/A
2N/A buf += read_size << GRUB_DISK_SECTOR_BITS;
2N/A size -= read_size;
2N/A if (! size)
2N/A break;
2N/A
2N/A b = 0;
2N/A disknr += (near - i);
2N/A while (disknr >= array->total_devs)
2N/A {
2N/A disknr -= array->total_devs;
2N/A read_sector += ofs;
2N/A }
2N/A }
2N/A break;
2N/A }
2N/A
2N/A case 4:
2N/A case 5:
2N/A case 6:
2N/A {
2N/A grub_disk_addr_t read_sector;
2N/A grub_uint64_t b, p, n, disknr, e;
2N/A
2N/A /* n = 1 for level 4 and 5, 2 for level 6. */
2N/A n = array->level / 3;
2N/A
2N/A /* Find the first sector to read. */
2N/A read_sector = grub_divmod64 (sector, array->chunk_size, &b);
2N/A read_sector = grub_divmod64 (read_sector, array->total_devs - n,
2N/A &disknr);
2N/A if (array->level >= 5)
2N/A {
2N/A grub_divmod64 (read_sector, array->total_devs, &p);
2N/A
2N/A if (! (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK))
2N/A p = array->total_devs - 1 - p;
2N/A
2N/A if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
2N/A {
2N/A disknr += p + n;
2N/A }
2N/A else
2N/A {
2N/A grub_uint32_t q;
2N/A
2N/A q = p + (n - 1);
2N/A if (q >= array->total_devs)
2N/A q -= array->total_devs;
2N/A
2N/A if (disknr >= p)
2N/A disknr += n;
2N/A else if (disknr >= q)
2N/A disknr += q + 1;
2N/A }
2N/A
2N/A if (disknr >= array->total_devs)
2N/A disknr -= array->total_devs;
2N/A }
2N/A else
2N/A p = array->total_devs - n;
2N/A
2N/A read_sector *= array->chunk_size;
2N/A
2N/A while (1)
2N/A {
2N/A grub_size_t read_size;
2N/A int next_level;
2N/A
2N/A read_size = array->chunk_size - b;
2N/A if (read_size > size)
2N/A read_size = size;
2N/A
2N/A e = 0;
2N/A if (array->members[disknr].device)
2N/A {
2N/A /* Reset read error. */
2N/A if (grub_errno == GRUB_ERR_READ_ERROR)
2N/A grub_errno = GRUB_ERR_NONE;
2N/A
2N/A err = grub_disk_read (array->members[disknr].device,
2N/A array->members[disknr].start_sector +
2N/A read_sector + b, 0,
2N/A read_size << GRUB_DISK_SECTOR_BITS,
2N/A buf);
2N/A
2N/A if ((err) && (err != GRUB_ERR_READ_ERROR))
2N/A break;
2N/A e++;
2N/A }
2N/A else
2N/A err = GRUB_ERR_READ_ERROR;
2N/A
2N/A if (err)
2N/A {
2N/A if (array->nr_devs < array->total_devs - n + e)
2N/A break;
2N/A
2N/A grub_errno = GRUB_ERR_NONE;
2N/A if (array->level == 6)
2N/A {
2N/A err = ((grub_raid6_recover_func) ?
2N/A (*grub_raid6_recover_func) (array, disknr, p,
2N/A buf, read_sector + b,
2N/A read_size) :
2N/A grub_error (GRUB_ERR_BAD_DEVICE,
2N/A "raid6rec is not loaded"));
2N/A }
2N/A else
2N/A {
2N/A err = ((grub_raid5_recover_func) ?
2N/A (*grub_raid5_recover_func) (array, disknr,
2N/A buf, read_sector + b,
2N/A read_size) :
2N/A grub_error (GRUB_ERR_BAD_DEVICE,
2N/A "raid5rec is not loaded"));
2N/A }
2N/A
2N/A if (err)
2N/A break;
2N/A }
2N/A
2N/A buf += read_size << GRUB_DISK_SECTOR_BITS;
2N/A size -= read_size;
2N/A if (! size)
2N/A break;
2N/A
2N/A b = 0;
2N/A disknr++;
2N/A
2N/A if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
2N/A {
2N/A if (disknr == array->total_devs)
2N/A disknr = 0;
2N/A
2N/A next_level = (disknr == p);
2N/A }
2N/A else
2N/A {
2N/A if (disknr == p)
2N/A disknr += n;
2N/A
2N/A next_level = (disknr >= array->total_devs);
2N/A }
2N/A
2N/A if (next_level)
2N/A {
2N/A read_sector += array->chunk_size;
2N/A
2N/A if (array->level >= 5)
2N/A {
2N/A if (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK)
2N/A p = (p == array->total_devs - 1) ? 0 : p + 1;
2N/A else
2N/A p = (p == 0) ? array->total_devs - 1 : p - 1;
2N/A
2N/A if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
2N/A {
2N/A disknr = p + n;
2N/A if (disknr >= array->total_devs)
2N/A disknr -= array->total_devs;
2N/A }
2N/A else
2N/A {
2N/A disknr -= array->total_devs;
2N/A if (disknr == p)
2N/A disknr += n;
2N/A }
2N/A }
2N/A else
2N/A disknr = 0;
2N/A }
2N/A }
2N/A }
2N/A break;
2N/A }
2N/A
2N/A return err;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Agrub_raid_write (grub_disk_t disk __attribute ((unused)),
2N/A grub_disk_addr_t sector __attribute ((unused)),
2N/A grub_size_t size __attribute ((unused)),
2N/A const char *buf __attribute ((unused)))
2N/A{
2N/A return GRUB_ERR_NOT_IMPLEMENTED_YET;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Ainsert_array (grub_disk_t disk, struct grub_raid_array *new_array,
2N/A grub_disk_addr_t start_sector, const char *scanner_name,
2N/A grub_raid_t raid __attribute__ ((unused)))
2N/A{
2N/A struct grub_raid_array *array = 0, *p;
2N/A int was_readable = 0;
2N/A
2N/A /* See whether the device is part of an array we have already seen a
2N/A device from. */
2N/A for (p = array_list; p != NULL; p = p->next)
2N/A if ((p->uuid_len == new_array->uuid_len) &&
2N/A (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len)))
2N/A {
2N/A grub_free (new_array->uuid);
2N/A array = p;
2N/A
2N/A was_readable = grub_is_array_readable (array);
2N/A
2N/A /* Do some checks before adding the device to the array. */
2N/A
2N/A if (new_array->index >= array->allocated_devs)
2N/A {
2N/A void *tmp;
2N/A unsigned int newnum = 2 * (new_array->index + 1);
2N/A tmp = grub_realloc (array->members, newnum
2N/A * sizeof (array->members[0]));
2N/A if (!tmp)
2N/A return grub_errno;
2N/A array->members = tmp;
2N/A grub_memset (array->members + array->allocated_devs,
2N/A 0, (newnum - array->allocated_devs)
2N/A * sizeof (array->members[0]));
2N/A array->allocated_devs = newnum;
2N/A }
2N/A
2N/A /* FIXME: Check whether the update time of the superblocks are
2N/A the same. */
2N/A
2N/A if (array->total_devs == array->nr_devs)
2N/A /* We found more members of the array than the array
2N/A actually has according to its superblock. This shouldn't
2N/A happen normally. */
2N/A return grub_error (GRUB_ERR_BAD_DEVICE,
2N/A "superfluous RAID member (%d found)",
2N/A array->total_devs);
2N/A
2N/A if (array->members[new_array->index].device != NULL)
2N/A /* We found multiple devices with the same number. Again,
2N/A this shouldn't happen. */
2N/A return grub_error (GRUB_ERR_BAD_DEVICE,
2N/A "found two disks with the index %d for RAID %s",
2N/A new_array->index, array->name);
2N/A
2N/A if (new_array->disk_size < array->disk_size)
2N/A array->disk_size = new_array->disk_size;
2N/A break;
2N/A }
2N/A
2N/A /* Add an array to the list if we didn't find any. */
2N/A if (!array)
2N/A {
2N/A array = grub_malloc (sizeof (*array));
2N/A if (!array)
2N/A {
2N/A grub_free (new_array->uuid);
2N/A return grub_errno;
2N/A }
2N/A
2N/A *array = *new_array;
2N/A array->nr_devs = 0;
2N/A#ifdef GRUB_UTIL
2N/A array->driver = raid;
2N/A#endif
2N/A array->allocated_devs = 32;
2N/A if (new_array->index >= array->allocated_devs)
2N/A array->allocated_devs = 2 * (new_array->index + 1);
2N/A
2N/A array->members = grub_zalloc (array->allocated_devs
2N/A * sizeof (array->members[0]));
2N/A
2N/A if (!array->members)
2N/A {
2N/A grub_free (new_array->uuid);
2N/A return grub_errno;
2N/A }
2N/A
2N/A if (! array->name)
2N/A {
2N/A for (p = array_list; p != NULL; p = p->next)
2N/A {
2N/A if (p->number == array->number)
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (array->name || p)
2N/A {
2N/A /* The number is already in use, so we need to find a new one.
2N/A (Or, in the case of named arrays, the array doesn't have its
2N/A own number, but we need one that doesn't clash for use as a key
2N/A in the disk cache. */
2N/A int i = array->name ? 0x40000000 : 0;
2N/A
2N/A while (1)
2N/A {
2N/A for (p = array_list; p != NULL; p = p->next)
2N/A {
2N/A if (p->number == i)
2N/A break;
2N/A }
2N/A
2N/A if (! p)
2N/A {
2N/A /* We found an unused number. */
2N/A array->number = i;
2N/A break;
2N/A }
2N/A
2N/A i++;
2N/A }
2N/A }
2N/A
2N/A /* mdraid 1.x superblocks have only a name stored not a number.
2N/A Use it directly as GRUB device. */
2N/A if (! array->name)
2N/A {
2N/A array->name = grub_xasprintf ("md%d", array->number);
2N/A if (! array->name)
2N/A {
2N/A grub_free (array->members);
2N/A grub_free (array->uuid);
2N/A grub_free (array);
2N/A
2N/A return grub_errno;
2N/A }
2N/A }
2N/A else
2N/A {
2N/A /* Strip off the homehost if present. */
2N/A char *colon = grub_strchr (array->name, ':');
2N/A char *new_name = grub_xasprintf ("md/%s",
2N/A colon ? colon + 1 : array->name);
2N/A
2N/A if (! new_name)
2N/A {
2N/A grub_free (array->members);
2N/A grub_free (array->uuid);
2N/A grub_free (array);
2N/A
2N/A return grub_errno;
2N/A }
2N/A
2N/A grub_free (array->name);
2N/A array->name = new_name;
2N/A }
2N/A
2N/A grub_dprintf ("raid", "Found array %s (%s)\n", array->name,
2N/A scanner_name);
2N/A#ifdef GRUB_UTIL
2N/A grub_util_info ("Found array %s (%s)", array->name,
2N/A scanner_name);
2N/A#endif
2N/A
2N/A {
2N/A int max_used_number = 0, len, need_new_name = 0;
2N/A int add_us = 0;
2N/A len = grub_strlen (array->name);
2N/A if (len && grub_isdigit (array->name[len-1]))
2N/A add_us = 1;
2N/A for (p = array_list; p != NULL; p = p->next)
2N/A {
2N/A int cur_num;
2N/A char *num, *end;
2N/A if (grub_strncmp (p->name, array->name, len) != 0)
2N/A continue;
2N/A if (p->name[len] == 0)
2N/A {
2N/A need_new_name = 1;
2N/A continue;
2N/A }
2N/A if (add_us && p->name[len] != '_')
2N/A continue;
2N/A if (add_us)
2N/A num = p->name + len + 1;
2N/A else
2N/A num = p->name + len;
2N/A if (!grub_isdigit (num[0]))
2N/A continue;
2N/A cur_num = grub_strtoull (num, &end, 10);
2N/A if (end[0])
2N/A continue;
2N/A if (cur_num > max_used_number)
2N/A max_used_number = cur_num;
2N/A }
2N/A if (need_new_name)
2N/A {
2N/A char *tmp;
2N/A tmp = grub_xasprintf ("%s%s%d", array->name, add_us ? "_" : "",
2N/A max_used_number + 1);
2N/A if (!tmp)
2N/A return grub_errno;
2N/A grub_free (array->name);
2N/A array->name = tmp;
2N/A }
2N/A }
2N/A
2N/A /* Add our new array to the list. */
2N/A array->next = array_list;
2N/A array_list = array;
2N/A
2N/A /* RAID 1 doesn't use a chunksize but code assumes one so set
2N/A one. */
2N/A if (array->level == 1)
2N/A array->chunk_size = 64;
2N/A }
2N/A
2N/A /* Add the device to the array. */
2N/A array->members[new_array->index].device = disk;
2N/A array->members[new_array->index].start_sector = start_sector;
2N/A array->nr_devs++;
2N/A if (!was_readable && grub_is_array_readable (array))
2N/A array->became_readable_at = inscnt++;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic void
2N/Afree_array (void)
2N/A{
2N/A struct grub_raid_array *array;
2N/A
2N/A array = array_list;
2N/A while (array)
2N/A {
2N/A struct grub_raid_array *p;
2N/A unsigned int i;
2N/A
2N/A p = array;
2N/A array = array->next;
2N/A
2N/A for (i = 0; i < p->allocated_devs; i++)
2N/A if (p->members[i].device)
2N/A grub_disk_close (p->members[i].device);
2N/A grub_free (p->members);
2N/A
2N/A grub_free (p->uuid);
2N/A grub_free (p->name);
2N/A grub_free (p);
2N/A }
2N/A
2N/A array_list = 0;
2N/A}
2N/A
2N/Avoid
2N/Agrub_raid_register (grub_raid_t raid)
2N/A{
2N/A raid->next = grub_raid_list;
2N/A grub_raid_list = raid;
2N/A}
2N/A
2N/Avoid
2N/Agrub_raid_unregister (grub_raid_t raid)
2N/A{
2N/A grub_raid_t *p, q;
2N/A
2N/A for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next)
2N/A if (q == raid)
2N/A {
2N/A *p = q->next;
2N/A break;
2N/A }
2N/A}
2N/A
2N/Astatic struct grub_disk_dev grub_raid_dev =
2N/A {
2N/A .name = "raid",
2N/A .id = GRUB_DISK_DEVICE_RAID_ID,
2N/A .iterate = grub_raid_iterate,
2N/A .open = grub_raid_open,
2N/A .close = grub_raid_close,
2N/A .read = grub_raid_read,
2N/A .write = grub_raid_write,
2N/A#ifdef GRUB_UTIL
2N/A .memberlist = grub_raid_memberlist,
2N/A .raidname = grub_raid_getname,
2N/A#endif
2N/A .next = 0
2N/A };
2N/A
2N/A
2N/AGRUB_MOD_INIT(raid)
2N/A{
2N/A grub_disk_dev_register (&grub_raid_dev);
2N/A}
2N/A
2N/AGRUB_MOD_FINI(raid)
2N/A{
2N/A grub_disk_dev_unregister (&grub_raid_dev);
2N/A free_array ();
2N/A}