bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2008 Karel Zak <kzak@redhat.com> |
| 3 | * |
| 4 | * Inspired by libvolume_id by |
| 5 | * Kay Sievers <kay.sievers@vrfy.org> |
| 6 | * |
| 7 | * This file may be redistributed under the terms of the |
| 8 | * GNU Lesser General Public License. |
| 9 | */ |
| 10 | #include <stdio.h> |
| 11 | #include <stdlib.h> |
| 12 | #include <unistd.h> |
| 13 | #include <string.h> |
| 14 | #include <stdint.h> |
| 15 | |
| 16 | #include "superblocks.h" |
| 17 | |
| 18 | struct mdp0_super_block { |
| 19 | uint32_t md_magic; |
| 20 | uint32_t major_version; |
| 21 | uint32_t minor_version; |
| 22 | uint32_t patch_version; |
| 23 | uint32_t gvalid_words; |
| 24 | uint32_t set_uuid0; |
| 25 | uint32_t ctime; |
| 26 | uint32_t level; |
| 27 | uint32_t size; |
| 28 | uint32_t nr_disks; |
| 29 | uint32_t raid_disks; |
| 30 | uint32_t md_minor; |
| 31 | uint32_t not_persistent; |
| 32 | uint32_t set_uuid1; |
| 33 | uint32_t set_uuid2; |
| 34 | uint32_t set_uuid3; |
| 35 | }; |
| 36 | |
| 37 | /* |
| 38 | * Version-1, little-endian. |
| 39 | */ |
| 40 | struct mdp1_super_block { |
| 41 | /* constant array information - 128 bytes */ |
| 42 | uint32_t magic; /* MD_SB_MAGIC: 0xa92b4efc - little endian */ |
| 43 | uint32_t major_version; /* 1 */ |
| 44 | uint32_t feature_map; /* 0 for now */ |
| 45 | uint32_t pad0; /* always set to 0 when writing */ |
| 46 | |
| 47 | uint8_t set_uuid[16]; /* user-space generated. */ |
| 48 | unsigned char set_name[32]; /* set and interpreted by user-space */ |
| 49 | |
| 50 | uint64_t ctime; /* lo 40 bits are seconds, top 24 are microseconds or 0*/ |
| 51 | uint32_t level; /* -4 (multipath), -1 (linear), 0,1,4,5 */ |
| 52 | uint32_t layout; /* only for raid5 currently */ |
| 53 | uint64_t size; /* used size of component devices, in 512byte sectors */ |
| 54 | |
| 55 | uint32_t chunksize; /* in 512byte sectors */ |
| 56 | uint32_t raid_disks; |
| 57 | uint32_t bitmap_offset; /* sectors after start of superblock that bitmap starts |
| 58 | * NOTE: signed, so bitmap can be before superblock |
| 59 | * only meaningful of feature_map[0] is set. |
| 60 | */ |
| 61 | |
| 62 | /* These are only valid with feature bit '4' */ |
| 63 | uint32_t new_level; /* new level we are reshaping to */ |
| 64 | uint64_t reshape_position; /* next address in array-space for reshape */ |
| 65 | uint32_t delta_disks; /* change in number of raid_disks */ |
| 66 | uint32_t new_layout; /* new layout */ |
| 67 | uint32_t new_chunk; /* new chunk size (bytes) */ |
| 68 | uint8_t pad1[128-124]; /* set to 0 when written */ |
| 69 | |
| 70 | /* constant this-device information - 64 bytes */ |
| 71 | uint64_t data_offset; /* sector start of data, often 0 */ |
| 72 | uint64_t data_size; /* sectors in this device that can be used for data */ |
| 73 | uint64_t super_offset; /* sector start of this superblock */ |
| 74 | uint64_t recovery_offset;/* sectors before this offset (from data_offset) have been recovered */ |
| 75 | uint32_t dev_number; /* permanent identifier of this device - not role in raid */ |
| 76 | uint32_t cnt_corrected_read; /* number of read errors that were corrected by re-writing */ |
| 77 | uint8_t device_uuid[16]; /* user-space setable, ignored by kernel */ |
| 78 | uint8_t devflags; /* per-device flags. Only one defined...*/ |
| 79 | uint8_t pad2[64-57]; /* set to 0 when writing */ |
| 80 | |
| 81 | /* array state information - 64 bytes */ |
| 82 | uint64_t utime; /* 40 bits second, 24 btes microseconds */ |
| 83 | uint64_t events; /* incremented when superblock updated */ |
| 84 | uint64_t resync_offset; /* data before this offset (from data_offset) known to be in sync */ |
| 85 | uint32_t sb_csum; /* checksum up to dev_roles[max_dev] */ |
| 86 | uint32_t max_dev; /* size of dev_roles[] array to consider */ |
| 87 | uint8_t pad3[64-32]; /* set to 0 when writing */ |
| 88 | |
| 89 | /* device state information. Indexed by dev_number. |
| 90 | * 2 bytes per device |
| 91 | * Note there are no per-device state flags. State information is rolled |
| 92 | * into the 'roles' value. If a device is spare or faulty, then it doesn't |
| 93 | * have a meaningful role. |
| 94 | */ |
| 95 | uint16_t dev_roles[0]; /* role in array, or 0xffff for a spare, or 0xfffe for faulty */ |
| 96 | }; |
| 97 | |
| 98 | |
| 99 | #define MD_RESERVED_BYTES 0x10000 |
| 100 | #define MD_SB_MAGIC 0xa92b4efc |
| 101 | |
| 102 | static int probe_raid0(blkid_probe pr, blkid_loff_t off) |
| 103 | { |
| 104 | struct mdp0_super_block *mdp0; |
| 105 | union { |
| 106 | uint32_t ints[4]; |
| 107 | uint8_t bytes[16]; |
| 108 | } uuid; |
| 109 | uint32_t ma, mi, pa; |
| 110 | uint64_t size; |
| 111 | |
| 112 | if (pr->size < MD_RESERVED_BYTES) |
| 113 | return -1; |
| 114 | mdp0 = (struct mdp0_super_block *) |
| 115 | blkid_probe_get_buffer(pr, |
| 116 | off, |
| 117 | sizeof(struct mdp0_super_block)); |
| 118 | if (!mdp0) |
| 119 | return -1; |
| 120 | |
| 121 | memset(uuid.ints, 0, sizeof(uuid.ints)); |
| 122 | |
| 123 | if (le32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) { |
| 124 | uuid.ints[0] = swab32(mdp0->set_uuid0); |
| 125 | if (le32_to_cpu(mdp0->minor_version) >= 90) { |
| 126 | uuid.ints[1] = swab32(mdp0->set_uuid1); |
| 127 | uuid.ints[2] = swab32(mdp0->set_uuid2); |
| 128 | uuid.ints[3] = swab32(mdp0->set_uuid3); |
| 129 | } |
| 130 | ma = le32_to_cpu(mdp0->major_version); |
| 131 | mi = le32_to_cpu(mdp0->minor_version); |
| 132 | pa = le32_to_cpu(mdp0->patch_version); |
| 133 | size = le32_to_cpu(mdp0->size); |
| 134 | |
| 135 | } else if (be32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) { |
| 136 | uuid.ints[0] = mdp0->set_uuid0; |
| 137 | if (be32_to_cpu(mdp0->minor_version) >= 90) { |
| 138 | uuid.ints[1] = mdp0->set_uuid1; |
| 139 | uuid.ints[2] = mdp0->set_uuid2; |
| 140 | uuid.ints[3] = mdp0->set_uuid3; |
| 141 | } |
| 142 | ma = be32_to_cpu(mdp0->major_version); |
| 143 | mi = be32_to_cpu(mdp0->minor_version); |
| 144 | pa = be32_to_cpu(mdp0->patch_version); |
| 145 | size = be32_to_cpu(mdp0->size); |
| 146 | } else |
| 147 | return 1; |
| 148 | |
| 149 | size <<= 10; /* convert KiB to bytes */ |
| 150 | |
| 151 | if (pr->size < 0 || (uint64_t) pr->size < size + MD_RESERVED_BYTES) |
| 152 | /* device is too small */ |
| 153 | return 1; |
| 154 | |
| 155 | if (off < 0 || (uint64_t) off < size) |
| 156 | /* no space before superblock */ |
| 157 | return 1; |
| 158 | |
| 159 | /* |
| 160 | * Check for collisions between RAID and partition table |
| 161 | * |
| 162 | * For example the superblock is at the end of the last partition, it's |
| 163 | * the same position as at the end of the disk... |
| 164 | */ |
| 165 | if ((S_ISREG(pr->mode) || blkid_probe_is_wholedisk(pr)) && |
| 166 | blkid_probe_is_covered_by_pt(pr, |
| 167 | off - size, /* min. start */ |
| 168 | size + MD_RESERVED_BYTES)) { /* min. length */ |
| 169 | |
| 170 | /* ignore this superblock, it's within any partition and |
| 171 | * we are working with whole-disk now */ |
| 172 | return 1; |
| 173 | } |
| 174 | |
| 175 | if (blkid_probe_sprintf_version(pr, "%u.%u.%u", ma, mi, pa) != 0) |
| 176 | return -1; |
| 177 | if (blkid_probe_set_uuid(pr, (unsigned char *) uuid.bytes) != 0) |
| 178 | return -1; |
| 179 | if (blkid_probe_set_magic(pr, off, sizeof(mdp0->md_magic), |
| 180 | (unsigned char *) &mdp0->md_magic)) |
| 181 | return -1; |
| 182 | return 0; |
| 183 | } |
| 184 | |
| 185 | static int probe_raid1(blkid_probe pr, off_t off) |
| 186 | { |
| 187 | struct mdp1_super_block *mdp1; |
| 188 | |
| 189 | mdp1 = (struct mdp1_super_block *) |
| 190 | blkid_probe_get_buffer(pr, |
| 191 | off, |
| 192 | sizeof(struct mdp1_super_block)); |
| 193 | if (!mdp1) |
| 194 | return -1; |
| 195 | if (le32_to_cpu(mdp1->magic) != MD_SB_MAGIC) |
| 196 | return -1; |
| 197 | if (le32_to_cpu(mdp1->major_version) != 1U) |
| 198 | return -1; |
| 199 | if (le64_to_cpu(mdp1->super_offset) != (uint64_t) off >> 9) |
| 200 | return -1; |
| 201 | if (blkid_probe_set_uuid(pr, (unsigned char *) mdp1->set_uuid) != 0) |
| 202 | return -1; |
| 203 | if (blkid_probe_set_uuid_as(pr, |
| 204 | (unsigned char *) mdp1->device_uuid, "UUID_SUB") != 0) |
| 205 | return -1; |
| 206 | if (blkid_probe_set_label(pr, mdp1->set_name, |
| 207 | sizeof(mdp1->set_name)) != 0) |
| 208 | return -1; |
| 209 | if (blkid_probe_set_magic(pr, off, sizeof(mdp1->magic), |
| 210 | (unsigned char *) &mdp1->magic)) |
| 211 | return -1; |
| 212 | return 0; |
| 213 | } |
| 214 | |
| 215 | int probe_raid(blkid_probe pr, |
| 216 | const struct blkid_idmag *mag __attribute__((__unused__))) |
| 217 | { |
| 218 | const char *ver = NULL; |
| 219 | |
| 220 | if (pr->size > MD_RESERVED_BYTES) { |
| 221 | /* version 0 at the end of the device */ |
| 222 | uint64_t sboff = (pr->size & ~(MD_RESERVED_BYTES - 1)) |
| 223 | - MD_RESERVED_BYTES; |
| 224 | if (probe_raid0(pr, sboff) == 0) |
| 225 | return 0; |
| 226 | |
| 227 | /* version 1.0 at the end of the device */ |
| 228 | sboff = (pr->size & ~(0x1000 - 1)) - 0x2000; |
| 229 | if (probe_raid1(pr, sboff) == 0) |
| 230 | ver = "1.0"; |
| 231 | } |
| 232 | |
| 233 | if (!ver) { |
| 234 | /* version 1.1 at the start of the device */ |
| 235 | if (probe_raid1(pr, 0) == 0) |
| 236 | ver = "1.1"; |
| 237 | |
| 238 | /* version 1.2 at 4k offset from the start */ |
| 239 | else if (probe_raid1(pr, 0x1000) == 0) |
| 240 | ver = "1.2"; |
| 241 | } |
| 242 | |
| 243 | if (ver) { |
| 244 | blkid_probe_set_version(pr, ver); |
| 245 | return 0; |
| 246 | } |
| 247 | return -1; |
| 248 | } |
| 249 | |
| 250 | |
| 251 | const struct blkid_idinfo linuxraid_idinfo = { |
| 252 | .name = "linux_raid_member", |
| 253 | .usage = BLKID_USAGE_RAID, |
| 254 | .probefunc = probe_raid, |
| 255 | .magics = BLKID_NONE_MAGIC |
| 256 | }; |
| 257 | |
| 258 | |