Strange timing. I ran into needing support for 1.2 metadata a few days
ago. Wrote the code yesterday and when I pulled to check for conflicts
before I submitted someone else had also just written the code.

Here are a few changes I had that are not in the tree:

- Don't use disks with outdated superblocks
- Don't use disks that are being rebuilt/transfomed
- Check superblock checksums
- Check superblock is in the correct location
- Update the recovery code to use the start_sector

Doug


=== modified file 'disk/dmraid_nvidia.c'
--- disk/dmraid_nvidia.c        2010-07-18 17:31:10 +0000
+++ disk/dmraid_nvidia.c        2010-07-23 03:23:14 +0000
@@ -137,6 +137,7 @@
   array->number = 0;
   array->total_devs = sb.array.total_volumes;
   array->chunk_size = sb.array.stripe_block_size;
+  array->freshness = 0;
   array->index = sb.unit_number;
   array->uuid_len = sizeof (sb.array.signature);
   array->uuid = grub_malloc (sizeof (sb.array.signature));

=== modified file 'disk/mdraid_linux.c'
--- disk/mdraid_linux.c 2010-07-20 10:10:49 +0000
+++ disk/mdraid_linux.c 2010-07-23 05:22:12 +0000
@@ -229,6 +229,10 @@
    are already appropriately aligned, we can omit this and avoid suboptimal
    assembly in some cases.  */
 
+#define MD_FEATURE_BITMAP_OFFSET        1
+#define MD_FEATURE_RECOVERY_OFFSET      2
+#define MD_FEATURE_RESHAPE_ACTIVE       4
+
 #define WriteMostly1    1      /* Mask for writemostly flag in above devflags. 
 */
 
 static grub_err_t
@@ -262,6 +266,7 @@
   array->total_devs = sb->raid_disks;
   array->disk_size = (sb->size) ? sb->size * 2 : sector;
   array->chunk_size = sb->chunk_size >> 9;
+  array->freshness = sb->events;
   array->index = sb->this_disk.number;
   array->uuid_len = 16;
   array->uuid = grub_malloc (16);
@@ -279,6 +284,31 @@
   return 0;
 }
 
+static grub_uint32_t
+grub_mdraid_calc_csum(struct grub_raid_super_1x * sb)
+{
+  grub_uint32_t disk_csum;
+  grub_uint32_t csum;
+  grub_uint64_t newcsum;
+  int size = 256 + sb->max_dev*2;
+  grub_uint32_t *isuper = (grub_uint32_t*)sb;
+  int i;
+
+  disk_csum = sb->sb_csum;
+  sb->sb_csum = 0;
+  newcsum = 0;
+  for (i=0; size>=4; size -= 4 )
+    newcsum += *isuper++;
+
+  if (size == 2)
+    newcsum += *(grub_uint16_t*) isuper;
+
+  csum = (newcsum & 0xffffffff) + (newcsum >> 32);
+  sb->sb_csum = disk_csum;
+
+  return csum;
+}
+
 static grub_err_t
 grub_mdraid_detect_1x (grub_disk_t disk, grub_disk_addr_t sector,
                       struct grub_raid_super_1x *sb,
@@ -293,6 +323,18 @@
                       "Unsupported RAID version: %d",
                       sb->major_version);
 
+  if (sb->super_offset != sector)
+    /* We're not where we're supposed to be. Usually caused by a 1.0
+     superblock at the end of a disk with a single large partition. */
+    return grub_error (GRUB_ERR_OUT_OF_RANGE, "wrong superblock location");
+
+  if (sb->feature_map & MD_FEATURE_RECOVERY_OFFSET)
+    /* Disk is currently being recovered. Silently ignore it. */
+    return grub_error (GRUB_ERR_OUT_OF_RANGE, "recovery in progress");
+
+  if (sb->feature_map & MD_FEATURE_RESHAPE_ACTIVE)
+    return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "reshape active");
+
   /* Multipath.  */
   if ((int) sb->level == -4)
     sb->level = 1;
@@ -315,6 +357,12 @@
       return grub_errno;
     }
 
+  if (grub_mdraid_calc_csum(real_sb) != real_sb->sb_csum)
+  {
+    grub_free (real_sb);
+    return grub_error (GRUB_ERR_OUT_OF_RANGE, "csum invalid");
+  }
+
   array->name = grub_strdup (real_sb->set_name);
   if (! array->name)
     {
@@ -328,6 +376,7 @@
   array->total_devs = grub_le_to_cpu32 (real_sb->raid_disks);
   array->disk_size = grub_le_to_cpu64 (real_sb->size);
   array->chunk_size = grub_le_to_cpu32 (real_sb->chunksize);
+  array->freshness = grub_le_to_cpu32(real_sb->events);
   if (grub_le_to_cpu32 (real_sb->dev_number) <
       grub_le_to_cpu32 (real_sb->max_dev))
     array->index = grub_le_to_cpu16

=== modified file 'disk/raid.c'
--- disk/raid.c 2010-07-22 08:38:06 +0000
+++ disk/raid.c 2010-07-23 05:22:34 +0000
@@ -130,8 +130,8 @@
   disk->id = array->number;
   disk->data = array;
 
-  grub_dprintf ("raid", "%s: total_devs=%d, disk_size=%lld\n", name,
-               array->total_devs, (unsigned long long) array->disk_size);
+  grub_dprintf ("raid", "%s: total_devs=%d, nr_devs=%d, disk_size=%lld\n", 
name,
+               array->total_devs, array->nr_devs, (unsigned long long) 
array->disk_size);
 
   switch (array->level)
     {
@@ -495,21 +495,48 @@
         /* FIXME: Check whether the update time of the superblocks are
            the same.  */
 
+        if (new_array->freshness < array->freshness)
+        {
+         /* This disk is outdated. Don't add it to the array. */
+          grub_dprintf ("raid", "member '%s' is not fresh: disk: %llu, array: 
%llu\n",
+                        disk->name, new_array->freshness, array->freshness);
+          return grub_error (GRUB_ERR_OUT_OF_RANGE, "disk not fresh");
+        }
+
         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.  */
-          grub_dprintf ("raid", "array->nr_devs > array->total_devs (%d)?!?",
+          grub_dprintf ("raid", "array->nr_devs > array->total_devs (%d)?!?\n",
                        array->total_devs);
 
         if (array->device[new_array->index] != NULL)
           /* We found multiple devices with the same number. Again,
              this shouldn't happen.  */
-          grub_dprintf ("raid", "Found two disks with the number %d?!?",
+          grub_dprintf ("raid", "Found two disks with the number %d?!?\n",
                        new_array->number);
 
         if (new_array->disk_size < array->disk_size)
           array->disk_size = new_array->disk_size;
+
+        if (new_array->freshness > array->freshness)
+        {
+          int i;
+
+          /* Kick out all already found array members that are outdated */
+          for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++)
+           if (array->device[i])
+            {
+              grub_dprintf ("raid", "member '%s' is not fresh: disk: %llu, 
array: %llu\n",
+                            array->device[i]->name, array->freshness, 
new_array->freshness);
+              grub_disk_close (array->device[i]);
+              array->device[i] = 0;
+              array->nr_devs--;
+            }
+
+          array->freshness = new_array->freshness;
+        }
+
         break;
       }
 

=== modified file 'disk/raid5_recover.c'
--- disk/raid5_recover.c        2009-07-31 04:38:20 +0000
+++ disk/raid5_recover.c        2010-07-23 05:22:46 +0000
@@ -45,7 +45,8 @@
       if (i == disknr)
         continue;
 
-      err = grub_disk_read (array->device[i], sector, 0, size, buf2);
+      err = grub_disk_read (array->device[i],
+                           array->start_sector[i] + sector, 0, size, buf2);
 
       if (err)
         {

=== modified file 'disk/raid6_recover.c'
--- disk/raid6_recover.c        2010-01-03 22:05:07 +0000
+++ disk/raid6_recover.c        2010-07-23 05:23:13 +0000
@@ -119,7 +119,9 @@
       else
         {
           if ((array->device[pos]) &&
-              (! grub_disk_read (array->device[pos], sector, 0, size, buf)))
+              (! grub_disk_read (array->device[pos],
+                                array->start_sector[pos] + sector,
+                                0, size, buf)))
             {
               grub_raid_block_xor (pbuf, buf, size);
               grub_raid_block_mul (raid6_table2[i][i], buf, size);
@@ -149,7 +151,8 @@
     {
       /* One bad device */
       if ((array->device[p]) &&
-          (! grub_disk_read (array->device[p], sector, 0, size, buf)))
+          (! grub_disk_read (array->device[p],
+                            array->start_sector[p] + sector, 0, size, buf)))
         {
           grub_raid_block_xor (buf, pbuf, size);
           goto quit;
@@ -162,7 +165,8 @@
         }
 
       grub_errno = GRUB_ERR_NONE;
-      if (grub_disk_read (array->device[q], sector, 0, size, buf))
+      if (grub_disk_read (array->device[q], array->start_sector[q] + sector,
+                         0, size, buf))
         goto quit;
 
       grub_raid_block_xor (buf, qbuf, size);
@@ -180,12 +184,14 @@
           goto quit;
         }
 
-      if (grub_disk_read (array->device[p], sector, 0, size, buf))
+      if (grub_disk_read (array->device[p], array->start_sector[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))
+      if (grub_disk_read (array->device[q], array->start_sector[q] + sector,
+                         0, size, buf))
         goto quit;
 
       grub_raid_block_xor (qbuf, buf, size);

=== modified file 'include/grub/raid.h'
--- include/grub/raid.h 2010-07-20 10:10:49 +0000
+++ include/grub/raid.h 2010-07-23 04:31:24 +0000
@@ -43,6 +43,8 @@
   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_uint64_t freshness;   /* Indicator of freshness of disk. All valid
+                             devices will have the same highest number */
   int index;               /* Index of current device.  */
   int uuid_len;            /* The length of uuid.  */
   char *uuid;              /* The UUID of the device. */


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to