=== 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. */