/src/util-linux/libblkid/src/superblocks/linux_raid.c
Line | Count | Source |
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 bits 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 | 20.5k | #define MD_RESERVED_BYTES 0x10000 |
100 | 25.6k | #define MD_SB_MAGIC 0xa92b4efc |
101 | | |
102 | | static int probe_raid0(blkid_probe pr, uint64_t off) |
103 | 5.13k | { |
104 | 5.13k | struct mdp0_super_block *mdp0; |
105 | 5.13k | union { |
106 | 5.13k | uint32_t ints[4]; |
107 | 5.13k | uint8_t bytes[16]; |
108 | 5.13k | } uuid; |
109 | 5.13k | uint32_t ma, mi, pa; |
110 | 5.13k | uint64_t size; |
111 | | |
112 | 5.13k | if (pr->size < MD_RESERVED_BYTES) |
113 | 0 | return 1; |
114 | 5.13k | mdp0 = (struct mdp0_super_block *) |
115 | 5.13k | blkid_probe_get_buffer(pr, |
116 | 5.13k | off, |
117 | 5.13k | sizeof(struct mdp0_super_block)); |
118 | 5.13k | if (!mdp0) |
119 | 0 | return errno ? -errno : 1; |
120 | | |
121 | 5.13k | memset(uuid.ints, 0, sizeof(uuid.ints)); |
122 | | |
123 | 5.13k | if (le32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) { |
124 | 0 | uuid.ints[0] = swab32(mdp0->set_uuid0); |
125 | 0 | if (le32_to_cpu(mdp0->minor_version) >= 90) { |
126 | 0 | uuid.ints[1] = swab32(mdp0->set_uuid1); |
127 | 0 | uuid.ints[2] = swab32(mdp0->set_uuid2); |
128 | 0 | uuid.ints[3] = swab32(mdp0->set_uuid3); |
129 | 0 | } |
130 | 0 | ma = le32_to_cpu(mdp0->major_version); |
131 | 0 | mi = le32_to_cpu(mdp0->minor_version); |
132 | 0 | pa = le32_to_cpu(mdp0->patch_version); |
133 | 0 | size = le32_to_cpu(mdp0->size); |
134 | |
|
135 | 5.13k | } else if (be32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) { |
136 | 0 | uuid.ints[0] = mdp0->set_uuid0; |
137 | 0 | if (be32_to_cpu(mdp0->minor_version) >= 90) { |
138 | 0 | uuid.ints[1] = mdp0->set_uuid1; |
139 | 0 | uuid.ints[2] = mdp0->set_uuid2; |
140 | 0 | uuid.ints[3] = mdp0->set_uuid3; |
141 | 0 | } |
142 | 0 | ma = be32_to_cpu(mdp0->major_version); |
143 | 0 | mi = be32_to_cpu(mdp0->minor_version); |
144 | 0 | pa = be32_to_cpu(mdp0->patch_version); |
145 | 0 | size = be32_to_cpu(mdp0->size); |
146 | 0 | } else |
147 | 5.13k | return 1; |
148 | | |
149 | 0 | size <<= 10; /* convert KiB to bytes */ |
150 | |
|
151 | 0 | if (pr->size < size + MD_RESERVED_BYTES) |
152 | | /* device is too small */ |
153 | 0 | return 1; |
154 | | |
155 | 0 | if (off < size) |
156 | | /* no space before superblock */ |
157 | 0 | 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 | 0 | if ((S_ISREG(pr->mode) || blkid_probe_is_wholedisk(pr)) && |
166 | 0 | blkid_probe_is_covered_by_pt(pr, |
167 | 0 | off - size, /* min. start */ |
168 | 0 | 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 | 0 | return 1; |
173 | 0 | } |
174 | | |
175 | 0 | if (blkid_probe_sprintf_version(pr, "%u.%u.%u", ma, mi, pa) != 0) |
176 | 0 | return 1; |
177 | 0 | if (blkid_probe_set_uuid(pr, (unsigned char *) uuid.bytes) != 0) |
178 | 0 | return 1; |
179 | 0 | if (blkid_probe_set_magic(pr, off, sizeof(mdp0->md_magic), |
180 | 0 | (unsigned char *) &mdp0->md_magic)) |
181 | 0 | return 1; |
182 | 0 | return 0; |
183 | 0 | } |
184 | | |
185 | | static int raid1_verify_csum(blkid_probe pr, off_t off, |
186 | | const struct mdp1_super_block *mdp1) |
187 | 12 | { |
188 | 12 | uint64_t csummed_size = sizeof(struct mdp1_super_block) |
189 | 12 | + (uint64_t) le32_to_cpu(mdp1->max_dev) * sizeof(mdp1->dev_roles[0]); |
190 | 12 | const unsigned char *csummed = blkid_probe_get_buffer(pr, off, csummed_size); |
191 | 12 | if (!csummed) |
192 | 2 | return 1; |
193 | | |
194 | 10 | uint64_t csum = 0; |
195 | | |
196 | 10 | csum -= le32_to_cpu(*(uint32_t *) (csummed + offsetof(struct mdp1_super_block, sb_csum))); |
197 | | |
198 | 12.5M | while (csummed_size >= 4) { |
199 | 12.5M | csum += le32_to_cpu(*(uint32_t *) csummed); |
200 | 12.5M | csummed_size -= 4; |
201 | 12.5M | csummed += 4; |
202 | 12.5M | } |
203 | 10 | if (csummed_size == 2) |
204 | 3 | csum += le16_to_cpu(*(uint16_t *) csummed); |
205 | | |
206 | 10 | csum = (csum >> 32) + (uint32_t) csum; |
207 | 10 | return blkid_probe_verify_csum(pr, csum, le32_to_cpu(mdp1->sb_csum)); |
208 | 12 | } |
209 | | |
210 | | static int probe_raid1(blkid_probe pr, off_t off) |
211 | 15.3k | { |
212 | 15.3k | struct mdp1_super_block *mdp1; |
213 | | |
214 | 15.3k | mdp1 = (struct mdp1_super_block *) |
215 | 15.3k | blkid_probe_get_buffer(pr, |
216 | 15.3k | off, |
217 | 15.3k | sizeof(struct mdp1_super_block)); |
218 | 15.3k | if (!mdp1) |
219 | 0 | return errno ? -errno : 1; |
220 | 15.3k | if (le32_to_cpu(mdp1->magic) != MD_SB_MAGIC) |
221 | 15.2k | return 1; |
222 | 117 | if (le32_to_cpu(mdp1->major_version) != 1U) |
223 | 26 | return 1; |
224 | 91 | if (le64_to_cpu(mdp1->super_offset) != (uint64_t) off >> 9) |
225 | 79 | return 1; |
226 | 12 | if (!raid1_verify_csum(pr, off, mdp1)) |
227 | 10 | return 1; |
228 | 2 | if (blkid_probe_set_uuid(pr, (unsigned char *) mdp1->set_uuid) != 0) |
229 | 0 | return 1; |
230 | 2 | if (blkid_probe_set_uuid_as(pr, |
231 | 2 | (unsigned char *) mdp1->device_uuid, "UUID_SUB") != 0) |
232 | 0 | return 1; |
233 | 2 | if (blkid_probe_set_label(pr, mdp1->set_name, |
234 | 2 | sizeof(mdp1->set_name)) != 0) |
235 | 0 | return 1; |
236 | 2 | if (blkid_probe_set_magic(pr, off, sizeof(mdp1->magic), |
237 | 2 | (unsigned char *) &mdp1->magic)) |
238 | 0 | return 1; |
239 | 2 | return 0; |
240 | 2 | } |
241 | | |
242 | | static int probe_raid(blkid_probe pr, |
243 | | const struct blkid_idmag *mag __attribute__((__unused__))) |
244 | 5.13k | { |
245 | 5.13k | const char *ver = NULL; |
246 | 5.13k | int ret = BLKID_PROBE_NONE; |
247 | | |
248 | 5.13k | if (pr->size > MD_RESERVED_BYTES) { |
249 | | /* version 0 at the end of the device */ |
250 | 5.13k | uint64_t sboff = (pr->size & ~(MD_RESERVED_BYTES - 1)) |
251 | 5.13k | - MD_RESERVED_BYTES; |
252 | 5.13k | ret = probe_raid0(pr, sboff); |
253 | 5.13k | if (ret < 1) |
254 | 0 | return ret; /* error */ |
255 | | |
256 | | /* version 1.0 at the end of the device */ |
257 | 5.13k | sboff = (pr->size & ~(0x1000 - 1)) - 0x2000; |
258 | 5.13k | ret = probe_raid1(pr, sboff); |
259 | 5.13k | if (ret < 0) |
260 | 0 | return ret; /* error */ |
261 | 5.13k | if (ret == 0) |
262 | 0 | ver = "1.0"; |
263 | 5.13k | } |
264 | | |
265 | 5.13k | if (!ver) { |
266 | | /* version 1.1 at the start of the device */ |
267 | 5.13k | ret = probe_raid1(pr, 0); |
268 | 5.13k | if (ret == 0) |
269 | 2 | ver = "1.1"; |
270 | | |
271 | | /* version 1.2 at 4k offset from the start */ |
272 | 5.13k | else if (ret == BLKID_PROBE_NONE) { |
273 | 5.13k | ret = probe_raid1(pr, 0x1000); |
274 | 5.13k | if (ret == 0) |
275 | 0 | ver = "1.2"; |
276 | 5.13k | } |
277 | 5.13k | } |
278 | | |
279 | 5.13k | if (ver) { |
280 | 2 | blkid_probe_set_version(pr, ver); |
281 | 2 | return BLKID_PROBE_OK; |
282 | 2 | } |
283 | 5.13k | return ret; |
284 | 5.13k | } |
285 | | |
286 | | |
287 | | const struct blkid_idinfo linuxraid_idinfo = { |
288 | | .name = "linux_raid_member", |
289 | | .usage = BLKID_USAGE_RAID, |
290 | | .probefunc = probe_raid, |
291 | | .magics = BLKID_NONE_MAGIC |
292 | | }; |
293 | | |
294 | | |