Line 0
Link Here
|
|
|
1 |
/* |
2 |
* mdadm - manage Linux "md" devices aka RAID arrays. |
3 |
* |
4 |
* Copyright (C) 2001-2006 Neil Brown <neilb@suse.de> |
5 |
* |
6 |
* |
7 |
* This program is free software; you can redistribute it and/or modify |
8 |
* it under the terms of the GNU General Public License as published by |
9 |
* the Free Software Foundation; either version 2 of the License, or |
10 |
* (at your option) any later version. |
11 |
* |
12 |
* This program is distributed in the hope that it will be useful, |
13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
* GNU General Public License for more details. |
16 |
* |
17 |
* You should have received a copy of the GNU General Public License |
18 |
* along with this program; if not, write to the Free Software |
19 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 |
* |
21 |
* Author: Neil Brown |
22 |
* Email: <neilb@cse.unsw.edu.au> |
23 |
* Paper: Neil Brown |
24 |
* School of Computer Science and Engineering |
25 |
* The University of New South Wales |
26 |
* Sydney, 2052 |
27 |
* Australia |
28 |
*/ |
29 |
|
30 |
#include "mdadm.h" |
31 |
#include <ctype.h> |
32 |
|
33 |
static int name_matches(char *found, char *required, char *homehost) |
34 |
{ |
35 |
/* See if the name found matches the required name, possibly |
36 |
* prefixed with 'homehost' |
37 |
*/ |
38 |
char fnd[33]; |
39 |
|
40 |
strncpy(fnd, found, 32); |
41 |
fnd[32] = 0; |
42 |
if (strcmp(found, required)==0) |
43 |
return 1; |
44 |
if (homehost) { |
45 |
int l = strlen(homehost); |
46 |
if (l < 32 && fnd[l] == ':' && |
47 |
strcmp(fnd+l+1, required)==0) |
48 |
return 1; |
49 |
} |
50 |
return 0; |
51 |
} |
52 |
|
53 |
int open_mddev(char *dev, int autof/*unused */) |
54 |
{ |
55 |
int mdfd = open(dev, O_RDWR, 0); |
56 |
if (mdfd < 0) |
57 |
fprintf(stderr, Name ": error opening %s: %s\n", |
58 |
dev, strerror(errno)); |
59 |
else if (md_get_version(mdfd) <= 0) { |
60 |
fprintf(stderr, Name ": %s does not appear to be an md device\n", |
61 |
dev); |
62 |
close(mdfd); |
63 |
mdfd = -1; |
64 |
} |
65 |
return mdfd; |
66 |
} |
67 |
|
68 |
int Assemble(struct supertype *st, char *mddev, int mdfd, |
69 |
mddev_ident_t ident, |
70 |
mddev_dev_t devlist, char *backup_file, |
71 |
int readonly, int runstop, |
72 |
char *update, char *homehost, |
73 |
int verbose, int force) |
74 |
{ |
75 |
/* |
76 |
* The task of Assemble is to find a collection of |
77 |
* devices that should (according to their superblocks) |
78 |
* form an array, and to give this collection to the MD driver. |
79 |
* In Linux-2.4 and later, this involves submitting a |
80 |
* SET_ARRAY_INFO ioctl with no arg - to prepare |
81 |
* the array - and then submit a number of |
82 |
* ADD_NEW_DISK ioctls to add disks into |
83 |
* the array. Finally RUN_ARRAY might |
84 |
* be submitted to start the array. |
85 |
* |
86 |
* Much of the work of Assemble is in finding and/or |
87 |
* checking the disks to make sure they look right. |
88 |
* |
89 |
* If mddev is not set, then scan must be set and we |
90 |
* read through the config file for dev+uuid mapping |
91 |
* We recurse, setting mddev, for each device that |
92 |
* - isn't running |
93 |
* - has a valid uuid (or any uuid if !uuidset) |
94 |
* |
95 |
* If mddev is set, we try to determine state of md. |
96 |
* check version - must be at least 0.90.0 |
97 |
* check kernel version. must be at least 2.4. |
98 |
* If not, we can possibly fall back on START_ARRAY |
99 |
* Try to GET_ARRAY_INFO. |
100 |
* If possible, give up |
101 |
* If not, try to STOP_ARRAY just to make sure |
102 |
* |
103 |
* If !uuidset and scan, look in conf-file for uuid |
104 |
* If not found, give up |
105 |
* If !devlist and scan and uuidset, get list of devs from conf-file |
106 |
* |
107 |
* For each device: |
108 |
* Check superblock - discard if bad |
109 |
* Check uuid (set if we don't have one) - discard if no match |
110 |
* Check superblock similarity if we have a superblock - discard if different |
111 |
* Record events, devicenum |
112 |
* This should give us a list of devices for the array |
113 |
* We should collect the most recent event number |
114 |
* |
115 |
* Count disks with recent enough event count |
116 |
* While force && !enough disks |
117 |
* Choose newest rejected disks, update event count |
118 |
* mark clean and rewrite superblock |
119 |
* If recent kernel: |
120 |
* SET_ARRAY_INFO |
121 |
* foreach device with recent events : ADD_NEW_DISK |
122 |
* if runstop == 1 || "enough" disks and runstop==0 -> RUN_ARRAY |
123 |
* If old kernel: |
124 |
* Check the device numbers in superblock are right |
125 |
* update superblock if any changes |
126 |
* START_ARRAY |
127 |
* |
128 |
*/ |
129 |
int clean = 0; |
130 |
int old_linux = 0; |
131 |
int vers = 0; /* Keep gcc quite - it really is initialised */ |
132 |
void *first_super = NULL, *super = NULL; |
133 |
struct { |
134 |
char *devname; |
135 |
unsigned int major, minor; |
136 |
unsigned int oldmajor, oldminor; |
137 |
long long events; |
138 |
int uptodate; |
139 |
int state; |
140 |
int raid_disk; |
141 |
int disk_nr; |
142 |
} *devices; |
143 |
int *best = NULL; /* indexed by raid_disk */ |
144 |
unsigned int bestcnt = 0; |
145 |
int devcnt = 0; |
146 |
unsigned int okcnt, sparecnt; |
147 |
unsigned int req_cnt; |
148 |
unsigned int i; |
149 |
int most_recent = 0; |
150 |
int chosen_drive; |
151 |
int change = 0; |
152 |
int inargv = 0; |
153 |
int bitmap_done; |
154 |
int start_partial_ok = (runstop >= 0) && (force || devlist==NULL || mdfd < 0); |
155 |
unsigned int num_devs; |
156 |
mddev_dev_t tmpdev; |
157 |
struct mdinfo info; |
158 |
char *avail; |
159 |
int nextspare = 0; |
160 |
|
161 |
if (mdfd < 0) |
162 |
return 2; |
163 |
|
164 |
if (get_linux_version() < 2004000) |
165 |
old_linux = 1; |
166 |
|
167 |
if (mdfd >= 0) { |
168 |
vers = md_get_version(mdfd); |
169 |
if (vers <= 0) { |
170 |
fprintf(stderr, Name ": %s appears not to be an md device.\n", mddev); |
171 |
return 1; |
172 |
} |
173 |
if (vers < 9000) { |
174 |
fprintf(stderr, Name ": Assemble requires driver version 0.90.0 or later.\n" |
175 |
" Upgrade your kernel or try --build\n"); |
176 |
return 1; |
177 |
} |
178 |
|
179 |
if (ioctl(mdfd, GET_ARRAY_INFO, &info.array)>=0) { |
180 |
fprintf(stderr, Name ": device %s already active - cannot assemble it\n", |
181 |
mddev); |
182 |
return 1; |
183 |
} |
184 |
ioctl(mdfd, STOP_ARRAY, NULL); /* just incase it was started but has no content */ |
185 |
} |
186 |
/* |
187 |
* If any subdevs are listed, then any that don't |
188 |
* match ident are discarded. Remainder must all match and |
189 |
* become the array. |
190 |
* If no subdevs, then we scan all devices in the config file, but |
191 |
* there must be something in the identity |
192 |
*/ |
193 |
|
194 |
if (!devlist && |
195 |
ident->uuid_set == 0 && |
196 |
ident->super_minor < 0 && |
197 |
ident->devices == NULL) { |
198 |
fprintf(stderr, Name ": No identity information available for %s - cannot assemble.\n", |
199 |
mddev ? mddev : "further assembly"); |
200 |
return 1; |
201 |
} |
202 |
if (devlist == NULL) |
203 |
devlist = conf_get_devs(); |
204 |
else if (mdfd >= 0) |
205 |
inargv = 1; |
206 |
|
207 |
tmpdev = devlist; num_devs = 0; |
208 |
while (tmpdev) { |
209 |
if (tmpdev->used) |
210 |
tmpdev->used = 2; |
211 |
else |
212 |
num_devs++; |
213 |
tmpdev = tmpdev->next; |
214 |
} |
215 |
devices = malloc(num_devs * sizeof(*devices)); |
216 |
|
217 |
if (!st && ident->st) st = ident->st; |
218 |
|
219 |
if (verbose>0) |
220 |
fprintf(stderr, Name ": looking for devices for %s\n", |
221 |
mddev ? mddev : "further assembly"); |
222 |
|
223 |
/* first walk the list of devices to find a consistent set |
224 |
* that match the criterea, if that is possible. |
225 |
* We flag the one we like with 'used'. |
226 |
*/ |
227 |
for (tmpdev = devlist; |
228 |
tmpdev; |
229 |
tmpdev = tmpdev->next) { |
230 |
char *devname = tmpdev->devname; |
231 |
int dfd; |
232 |
struct stat stb; |
233 |
struct supertype *tst = st; |
234 |
|
235 |
if (tmpdev->used > 1) continue; |
236 |
|
237 |
if (ident->devices && |
238 |
!match_oneof(ident->devices, devname)) { |
239 |
if ((inargv && verbose>=0) || verbose > 0) |
240 |
fprintf(stderr, Name ": %s is not one of %s\n", devname, ident->devices); |
241 |
continue; |
242 |
} |
243 |
|
244 |
if (super) { |
245 |
free(super); |
246 |
super = NULL; |
247 |
} |
248 |
|
249 |
dfd = dev_open(devname, O_RDONLY|O_EXCL); |
250 |
if (dfd < 0) { |
251 |
if ((inargv && verbose >= 0) || verbose > 0) |
252 |
fprintf(stderr, Name ": cannot open device %s: %s\n", |
253 |
devname, strerror(errno)); |
254 |
tmpdev->used = 2; |
255 |
} else if (fstat(dfd, &stb)< 0) { |
256 |
/* Impossible! */ |
257 |
fprintf(stderr, Name ": fstat failed for %s: %s\n", |
258 |
devname, strerror(errno)); |
259 |
tmpdev->used = 2; |
260 |
} else if ((stb.st_mode & S_IFMT) != S_IFBLK) { |
261 |
fprintf(stderr, Name ": %s is not a block device.\n", |
262 |
devname); |
263 |
tmpdev->used = 2; |
264 |
} else if (!tst && (tst = guess_super(dfd)) == NULL) { |
265 |
if ((inargv && verbose >= 0) || verbose > 0) |
266 |
fprintf(stderr, Name ": no recogniseable superblock on %s\n", |
267 |
devname); |
268 |
tmpdev->used = 2; |
269 |
} else if (tst->ss->load_super(tst,dfd, &super, NULL)) { |
270 |
if ((inargv && verbose >= 0) || verbose > 0) |
271 |
fprintf( stderr, Name ": no RAID superblock on %s\n", |
272 |
devname); |
273 |
} else { |
274 |
tst->ss->getinfo_super(&info, super); |
275 |
} |
276 |
if (dfd >= 0) close(dfd); |
277 |
|
278 |
if (ident->uuid_set && (!update || strcmp(update, "uuid")!= 0) && |
279 |
(!super || same_uuid(info.uuid, ident->uuid, tst->ss->swapuuid)==0)) { |
280 |
if ((inargv && verbose >= 0) || verbose > 0) |
281 |
fprintf(stderr, Name ": %s has wrong uuid.\n", |
282 |
devname); |
283 |
continue; |
284 |
} |
285 |
if (ident->name[0] && (!update || strcmp(update, "name")!= 0) && |
286 |
(!super || name_matches(info.name, ident->name, homehost)==0)) { |
287 |
if ((inargv && verbose >= 0) || verbose > 0) |
288 |
fprintf(stderr, Name ": %s has wrong name.\n", |
289 |
devname); |
290 |
continue; |
291 |
} |
292 |
if (ident->super_minor != UnSet && |
293 |
(!super || ident->super_minor != info.array.md_minor)) { |
294 |
if ((inargv && verbose >= 0) || verbose > 0) |
295 |
fprintf(stderr, Name ": %s has wrong super-minor.\n", |
296 |
devname); |
297 |
continue; |
298 |
} |
299 |
if (ident->level != UnSet && |
300 |
(!super|| ident->level != info.array.level)) { |
301 |
if ((inargv && verbose >= 0) || verbose > 0) |
302 |
fprintf(stderr, Name ": %s has wrong raid level.\n", |
303 |
devname); |
304 |
continue; |
305 |
} |
306 |
if (ident->raid_disks != UnSet && |
307 |
(!super || ident->raid_disks!= info.array.raid_disks)) { |
308 |
if ((inargv && verbose >= 0) || verbose > 0) |
309 |
fprintf(stderr, Name ": %s requires wrong number of drives.\n", |
310 |
devname); |
311 |
continue; |
312 |
} |
313 |
if (mdfd < 0) { |
314 |
if (tst == NULL || super == NULL) |
315 |
continue; |
316 |
if (update == NULL && |
317 |
tst->ss->match_home(super, homehost)==0) { |
318 |
if ((inargv && verbose >= 0) || verbose > 0) |
319 |
fprintf(stderr, Name ": %s is not built for host %s.\n", |
320 |
devname, homehost); |
321 |
/* Auto-assemble, and this is not a usable host */ |
322 |
/* if update != NULL, we are updating the host |
323 |
* name... */ |
324 |
continue; |
325 |
} |
326 |
} |
327 |
/* If we are this far, then we are nearly commited to this device. |
328 |
* If the super_block doesn't exist, or doesn't match others, |
329 |
* then we probably cannot continue |
330 |
* However if one of the arrays is for the homehost, and |
331 |
* the other isn't that can disambiguate. |
332 |
*/ |
333 |
|
334 |
if (!super) { |
335 |
fprintf(stderr, Name ": %s has no superblock - assembly aborted\n", |
336 |
devname); |
337 |
free(first_super); |
338 |
return 1; |
339 |
} |
340 |
|
341 |
if (st == NULL) |
342 |
st = tst; |
343 |
if (st->ss != tst->ss || |
344 |
st->minor_version != tst->minor_version || |
345 |
st->ss->compare_super(&first_super, super) != 0) { |
346 |
/* Some mismatch. If exactly one array matches this host, |
347 |
* we can resolve on that one. |
348 |
* Or, if we are auto assembling, we just ignore the second |
349 |
* for now. |
350 |
*/ |
351 |
if (mdfd < 0) |
352 |
continue; |
353 |
if (homehost) { |
354 |
int first = st->ss->match_home(first_super, homehost); |
355 |
int last = tst->ss->match_home(super, homehost); |
356 |
if (first+last == 1) { |
357 |
/* We can do something */ |
358 |
if (first) {/* just ignore this one */ |
359 |
if ((inargv && verbose >= 0) || verbose > 0) |
360 |
fprintf(stderr, Name ": %s misses out due to wrong homehost\n", |
361 |
devname); |
362 |
continue; |
363 |
} else { /* reject all those sofar */ |
364 |
mddev_dev_t td; |
365 |
if ((inargv && verbose >= 0) || verbose > 0) |
366 |
fprintf(stderr, Name ": %s overrides previous devices due to good homehost\n", |
367 |
devname); |
368 |
for (td=devlist; td != tmpdev; td=td->next) |
369 |
if (td->used == 1) |
370 |
td->used = 0; |
371 |
tmpdev->used = 1; |
372 |
continue; |
373 |
} |
374 |
} |
375 |
} |
376 |
fprintf(stderr, Name ": superblock on %s doesn't match others - assembly aborted\n", |
377 |
devname); |
378 |
free(super); |
379 |
free(first_super); |
380 |
return 1; |
381 |
} |
382 |
|
383 |
tmpdev->used = 1; |
384 |
} |
385 |
|
386 |
/* Ok, no bad inconsistancy, we can try updating etc */ |
387 |
bitmap_done = 0; |
388 |
for (tmpdev = devlist; tmpdev; tmpdev=tmpdev->next) if (tmpdev->used == 1) { |
389 |
char *devname = tmpdev->devname; |
390 |
struct stat stb; |
391 |
/* looks like a good enough match to update the super block if needed */ |
392 |
{ |
393 |
int dfd; |
394 |
dfd = dev_open(devname, O_RDWR|O_EXCL); |
395 |
|
396 |
remove_partitions(dfd); |
397 |
|
398 |
if (super) { |
399 |
free(super); |
400 |
super = NULL; |
401 |
} |
402 |
|
403 |
st->ss->load_super(st, dfd, &super, NULL); |
404 |
st->ss->getinfo_super(&info, super); |
405 |
close(dfd); |
406 |
} |
407 |
|
408 |
stat(devname, &stb); |
409 |
|
410 |
if (verbose > 0) |
411 |
fprintf(stderr, Name ": %s is identified as a member of %s, slot %d.\n", |
412 |
devname, mddev, info.disk.raid_disk); |
413 |
devices[devcnt].devname = devname; |
414 |
devices[devcnt].major = major(stb.st_rdev); |
415 |
devices[devcnt].minor = minor(stb.st_rdev); |
416 |
devices[devcnt].oldmajor = info.disk.major; |
417 |
devices[devcnt].oldminor = info.disk.minor; |
418 |
devices[devcnt].events = info.events; |
419 |
devices[devcnt].raid_disk = info.disk.raid_disk; |
420 |
devices[devcnt].disk_nr = info.disk.number; |
421 |
devices[devcnt].uptodate = 0; |
422 |
devices[devcnt].state = info.disk.state; |
423 |
if (most_recent < devcnt) { |
424 |
if (devices[devcnt].events |
425 |
> devices[most_recent].events) |
426 |
most_recent = devcnt; |
427 |
} |
428 |
if (info.array.level == -4) |
429 |
/* with multipath, the raid_disk from the superblock is meaningless */ |
430 |
i = devcnt; |
431 |
else |
432 |
i = devices[devcnt].raid_disk; |
433 |
if (i+1 == 0) { |
434 |
if (nextspare < info.array.raid_disks) |
435 |
nextspare = info.array.raid_disks; |
436 |
i = nextspare++; |
437 |
} else { |
438 |
if (i >= info.array.raid_disks && |
439 |
i >= nextspare) |
440 |
nextspare = i+1; |
441 |
} |
442 |
if (i < 10000) { |
443 |
if (i >= bestcnt) { |
444 |
unsigned int newbestcnt = i+10; |
445 |
int *newbest = malloc(sizeof(int)*newbestcnt); |
446 |
unsigned int c; |
447 |
for (c=0; c < newbestcnt; c++) |
448 |
if (c < bestcnt) |
449 |
newbest[c] = best[c]; |
450 |
else |
451 |
newbest[c] = -1; |
452 |
if (best)free(best); |
453 |
best = newbest; |
454 |
bestcnt = newbestcnt; |
455 |
} |
456 |
if (best[i] >=0 && |
457 |
devices[best[i]].events == devices[devcnt].events && |
458 |
devices[best[i]].minor != devices[devcnt].minor && |
459 |
st->ss->major == 0 && |
460 |
info.array.level != -4) { |
461 |
/* two different devices with identical superblock. |
462 |
* Could be a mis-detection caused by overlapping |
463 |
* partitions. fail-safe. |
464 |
*/ |
465 |
fprintf(stderr, Name ": WARNING %s and %s appear" |
466 |
" to have very similar superblocks.\n" |
467 |
" If they are really different, " |
468 |
"please --zero the superblock on one\n" |
469 |
" If they are the same or overlap," |
470 |
" please remove one from %s.\n", |
471 |
devices[best[i]].devname, devname, |
472 |
inargv ? "the list" : |
473 |
"the\n DEVICE list in mdadm.conf" |
474 |
); |
475 |
return 1; |
476 |
} |
477 |
if (best[i] == -1 |
478 |
|| devices[best[i]].events < devices[devcnt].events) |
479 |
best[i] = devcnt; |
480 |
} |
481 |
devcnt++; |
482 |
} |
483 |
|
484 |
if (super) |
485 |
free(super); |
486 |
super = NULL; |
487 |
|
488 |
if (update && strcmp(update, "byteorder")==0) |
489 |
st->minor_version = 90; |
490 |
|
491 |
if (devcnt == 0) { |
492 |
fprintf(stderr, Name ": no devices found for %s\n", |
493 |
mddev); |
494 |
free(first_super); |
495 |
return 1; |
496 |
} |
497 |
|
498 |
st->ss->getinfo_super(&info, first_super); |
499 |
clean = info.array.state & 1; |
500 |
|
501 |
/* now we have some devices that might be suitable. |
502 |
* I wonder how many |
503 |
*/ |
504 |
avail = malloc(info.array.raid_disks); |
505 |
memset(avail, 0, info.array.raid_disks); |
506 |
okcnt = 0; |
507 |
sparecnt=0; |
508 |
for (i=0; i< bestcnt ;i++) { |
509 |
int j = best[i]; |
510 |
int event_margin = 1; /* always allow a difference of '1' |
511 |
* like the kernel does |
512 |
*/ |
513 |
if (j < 0) continue; |
514 |
/* note: we ignore error flags in multipath arrays |
515 |
* as they don't make sense |
516 |
*/ |
517 |
if (info.array.level != -4) |
518 |
if (!(devices[j].state & (1<<MD_DISK_SYNC))) { |
519 |
if (!(devices[j].state & (1<<MD_DISK_FAULTY))) |
520 |
sparecnt++; |
521 |
continue; |
522 |
} |
523 |
if (devices[j].events+event_margin >= |
524 |
devices[most_recent].events) { |
525 |
devices[j].uptodate = 1; |
526 |
if (i < info.array.raid_disks) { |
527 |
okcnt++; |
528 |
avail[i]=1; |
529 |
} else |
530 |
sparecnt++; |
531 |
} |
532 |
} |
533 |
while (force && !enough(info.array.level, info.array.raid_disks, |
534 |
info.array.layout, 1, |
535 |
avail, okcnt)) { |
536 |
/* Choose the newest best drive which is |
537 |
* not up-to-date, update the superblock |
538 |
* and add it. |
539 |
*/ |
540 |
int fd; |
541 |
long long current_events; |
542 |
chosen_drive = -1; |
543 |
for (i=0; i<info.array.raid_disks && i < bestcnt; i++) { |
544 |
int j = best[i]; |
545 |
if (j>=0 && |
546 |
!devices[j].uptodate && |
547 |
devices[j].events > 0 && |
548 |
(chosen_drive < 0 || |
549 |
devices[j].events > devices[chosen_drive].events)) |
550 |
chosen_drive = j; |
551 |
} |
552 |
if (chosen_drive < 0) |
553 |
break; |
554 |
current_events = devices[chosen_drive].events; |
555 |
add_another: |
556 |
if (verbose >= 0) |
557 |
fprintf(stderr, Name ": forcing event count in %s(%d) from %d upto %d\n", |
558 |
devices[chosen_drive].devname, devices[chosen_drive].raid_disk, |
559 |
(int)(devices[chosen_drive].events), |
560 |
(int)(devices[most_recent].events)); |
561 |
fd = dev_open(devices[chosen_drive].devname, O_RDWR|O_EXCL); |
562 |
if (fd < 0) { |
563 |
fprintf(stderr, Name ": Couldn't open %s for write - not updating\n", |
564 |
devices[chosen_drive].devname); |
565 |
devices[chosen_drive].events = 0; |
566 |
continue; |
567 |
} |
568 |
if (st->ss->load_super(st,fd, &super, NULL)) { |
569 |
close(fd); |
570 |
fprintf(stderr, Name ": RAID superblock disappeared from %s - not updating.\n", |
571 |
devices[chosen_drive].devname); |
572 |
devices[chosen_drive].events = 0; |
573 |
continue; |
574 |
} |
575 |
info.events = devices[most_recent].events; |
576 |
st->ss->update_super(&info, super, "force-one", |
577 |
devices[chosen_drive].devname, verbose, |
578 |
0, NULL); |
579 |
|
580 |
if (st->ss->store_super(st, fd, super)) { |
581 |
close(fd); |
582 |
fprintf(stderr, Name ": Could not re-write superblock on %s\n", |
583 |
devices[chosen_drive].devname); |
584 |
devices[chosen_drive].events = 0; |
585 |
free(super); |
586 |
continue; |
587 |
} |
588 |
close(fd); |
589 |
devices[chosen_drive].events = devices[most_recent].events; |
590 |
devices[chosen_drive].uptodate = 1; |
591 |
avail[chosen_drive] = 1; |
592 |
okcnt++; |
593 |
free(super); |
594 |
|
595 |
/* If there are any other drives of the same vintage, |
596 |
* add them in as well. We can't lose and we might gain |
597 |
*/ |
598 |
for (i=0; i<info.array.raid_disks && i < bestcnt ; i++) { |
599 |
int j = best[i]; |
600 |
if (j >= 0 && |
601 |
!devices[j].uptodate && |
602 |
devices[j].events > 0 && |
603 |
devices[j].events == current_events) { |
604 |
chosen_drive = j; |
605 |
goto add_another; |
606 |
} |
607 |
} |
608 |
} |
609 |
|
610 |
/* Now we want to look at the superblock which the kernel will base things on |
611 |
* and compare the devices that we think are working with the devices that the |
612 |
* superblock thinks are working. |
613 |
* If there are differences and --force is given, then update this chosen |
614 |
* superblock. |
615 |
*/ |
616 |
chosen_drive = -1; |
617 |
super = NULL; |
618 |
for (i=0; chosen_drive < 0 && i<bestcnt; i++) { |
619 |
int j = best[i]; |
620 |
int fd; |
621 |
|
622 |
if (j<0) |
623 |
continue; |
624 |
if (!devices[j].uptodate) |
625 |
continue; |
626 |
chosen_drive = j; |
627 |
if ((fd=dev_open(devices[j].devname, O_RDONLY|O_EXCL))< 0) { |
628 |
fprintf(stderr, Name ": Cannot open %s: %s\n", |
629 |
devices[j].devname, strerror(errno)); |
630 |
return 1; |
631 |
} |
632 |
if (st->ss->load_super(st,fd, &super, NULL)) { |
633 |
close(fd); |
634 |
fprintf(stderr, Name ": RAID superblock has disappeared from %s\n", |
635 |
devices[j].devname); |
636 |
return 1; |
637 |
} |
638 |
close(fd); |
639 |
} |
640 |
if (super == NULL) { |
641 |
fprintf(stderr, Name ": No suitable drives found for %s\n", mddev); |
642 |
return 1; |
643 |
} |
644 |
st->ss->getinfo_super(&info, super); |
645 |
for (i=0; i<bestcnt; i++) { |
646 |
int j = best[i]; |
647 |
unsigned int desired_state; |
648 |
|
649 |
if (i < info.array.raid_disks) |
650 |
desired_state = (1<<MD_DISK_ACTIVE) | (1<<MD_DISK_SYNC); |
651 |
else |
652 |
desired_state = 0; |
653 |
|
654 |
if (j<0) |
655 |
continue; |
656 |
if (!devices[j].uptodate) |
657 |
continue; |
658 |
info.disk.number = devices[j].disk_nr; |
659 |
info.disk.raid_disk = i; |
660 |
info.disk.state = desired_state; |
661 |
|
662 |
if (devices[j].uptodate && |
663 |
st->ss->update_super(&info, super, "assemble", NULL, verbose, 0, NULL)) { |
664 |
if (force) { |
665 |
if (verbose >= 0) |
666 |
fprintf(stderr, Name ": " |
667 |
"clearing FAULTY flag for device %d in %s for %s\n", |
668 |
j, mddev, devices[j].devname); |
669 |
change = 1; |
670 |
} else { |
671 |
if (verbose >= -1) |
672 |
fprintf(stderr, Name ": " |
673 |
"device %d in %s has wrong state in superblock, but %s seems ok\n", |
674 |
i, mddev, devices[j].devname); |
675 |
} |
676 |
} |
677 |
#if 0 |
678 |
if (!devices[j].uptodate && |
679 |
!(super.disks[i].state & (1 << MD_DISK_FAULTY))) { |
680 |
fprintf(stderr, Name ": devices %d of %s is not marked FAULTY in superblock, but cannot be found\n", |
681 |
i, mddev); |
682 |
} |
683 |
#endif |
684 |
} |
685 |
if (force && !clean && |
686 |
!enough(info.array.level, info.array.raid_disks, |
687 |
info.array.layout, clean, |
688 |
avail, okcnt)) { |
689 |
change += st->ss->update_super(&info, super, "force-array", |
690 |
devices[chosen_drive].devname, verbose, |
691 |
0, NULL); |
692 |
clean = 1; |
693 |
} |
694 |
|
695 |
if (change) { |
696 |
int fd; |
697 |
fd = dev_open(devices[chosen_drive].devname, O_RDWR|O_EXCL); |
698 |
if (fd < 0) { |
699 |
fprintf(stderr, Name ": Could not open %s for write - cannot Assemble array.\n", |
700 |
devices[chosen_drive].devname); |
701 |
return 1; |
702 |
} |
703 |
if (st->ss->store_super(st, fd, super)) { |
704 |
close(fd); |
705 |
fprintf(stderr, Name ": Could not re-write superblock on %s\n", |
706 |
devices[chosen_drive].devname); |
707 |
return 1; |
708 |
} |
709 |
close(fd); |
710 |
} |
711 |
|
712 |
/* count number of in-sync devices according to the superblock. |
713 |
* We must have this number to start the array without -s or -R |
714 |
*/ |
715 |
req_cnt = info.array.working_disks; |
716 |
|
717 |
/* Almost ready to actually *do* something */ |
718 |
if (!old_linux) { |
719 |
int rv; |
720 |
if ((vers % 100) >= 1) { /* can use different versions */ |
721 |
mdu_array_info_t inf; |
722 |
memset(&inf, 0, sizeof(inf)); |
723 |
inf.major_version = st->ss->major; |
724 |
inf.minor_version = st->minor_version; |
725 |
rv = ioctl(mdfd, SET_ARRAY_INFO, &inf); |
726 |
} else |
727 |
rv = ioctl(mdfd, SET_ARRAY_INFO, NULL); |
728 |
|
729 |
if (rv) { |
730 |
fprintf(stderr, Name ": SET_ARRAY_INFO failed for %s: %s\n", |
731 |
mddev, strerror(errno)); |
732 |
return 1; |
733 |
} |
734 |
if (ident->bitmap_fd >= 0) { |
735 |
if (ioctl(mdfd, SET_BITMAP_FILE, ident->bitmap_fd) != 0) { |
736 |
fprintf(stderr, Name ": SET_BITMAP_FILE failed.\n"); |
737 |
return 1; |
738 |
} |
739 |
} else if (ident->bitmap_file) { |
740 |
/* From config file */ |
741 |
int bmfd = open(ident->bitmap_file, O_RDWR); |
742 |
if (bmfd < 0) { |
743 |
fprintf(stderr, Name ": Could not open bitmap file %s\n", |
744 |
ident->bitmap_file); |
745 |
return 1; |
746 |
} |
747 |
if (ioctl(mdfd, SET_BITMAP_FILE, bmfd) != 0) { |
748 |
fprintf(stderr, Name ": Failed to set bitmapfile for %s\n", mddev); |
749 |
close(bmfd); |
750 |
return 1; |
751 |
} |
752 |
close(bmfd); |
753 |
} |
754 |
|
755 |
/* First, add the raid disks, but add the chosen one last */ |
756 |
for (i=0; i<= bestcnt; i++) { |
757 |
int j; |
758 |
if (i < bestcnt) { |
759 |
j = best[i]; |
760 |
if (j == chosen_drive) |
761 |
continue; |
762 |
} else |
763 |
j = chosen_drive; |
764 |
|
765 |
if (j >= 0 /* && devices[j].uptodate */) { |
766 |
mdu_disk_info_t disk; |
767 |
memset(&disk, 0, sizeof(disk)); |
768 |
disk.major = devices[j].major; |
769 |
disk.minor = devices[j].minor; |
770 |
if (ioctl(mdfd, ADD_NEW_DISK, &disk)!=0) { |
771 |
fprintf(stderr, Name ": failed to add %s to %s: %s\n", |
772 |
devices[j].devname, |
773 |
mddev, |
774 |
strerror(errno)); |
775 |
if (i < info.array.raid_disks || i == bestcnt) |
776 |
okcnt--; |
777 |
else |
778 |
sparecnt--; |
779 |
} else if (verbose > 0) |
780 |
fprintf(stderr, Name ": added %s to %s as %d\n", |
781 |
devices[j].devname, mddev, devices[j].raid_disk); |
782 |
} else if (verbose > 0 && i < info.array.raid_disks) |
783 |
fprintf(stderr, Name ": no uptodate device for slot %d of %s\n", |
784 |
i, mddev); |
785 |
} |
786 |
|
787 |
if (runstop == 1 || |
788 |
(runstop <= 0 && |
789 |
( enough(info.array.level, info.array.raid_disks, |
790 |
info.array.layout, clean, avail, okcnt) && |
791 |
(okcnt >= req_cnt || start_partial_ok) |
792 |
))) { |
793 |
if (ioctl(mdfd, RUN_ARRAY, NULL)==0) { |
794 |
if (verbose >= 0) { |
795 |
fprintf(stderr, Name ": %s has been started with %d drive%s", |
796 |
mddev, okcnt, okcnt==1?"":"s"); |
797 |
if (okcnt < info.array.raid_disks) |
798 |
fprintf(stderr, " (out of %d)", info.array.raid_disks); |
799 |
if (sparecnt) |
800 |
fprintf(stderr, " and %d spare%s", sparecnt, sparecnt==1?"":"s"); |
801 |
fprintf(stderr, ".\n"); |
802 |
} |
803 |
return 0; |
804 |
} |
805 |
fprintf(stderr, Name ": failed to RUN_ARRAY %s: %s\n", |
806 |
mddev, strerror(errno)); |
807 |
|
808 |
if (!enough(info.array.level, info.array.raid_disks, |
809 |
info.array.layout, 1, avail, okcnt)) |
810 |
fprintf(stderr, Name ": Not enough devices to " |
811 |
"start the array.\n"); |
812 |
else if (!enough(info.array.level, |
813 |
info.array.raid_disks, |
814 |
info.array.layout, clean, |
815 |
avail, okcnt)) |
816 |
fprintf(stderr, Name ": Not enough devices to " |
817 |
"start the array while not clean " |
818 |
"- consider --force.\n"); |
819 |
|
820 |
return 1; |
821 |
} |
822 |
if (runstop == -1) { |
823 |
fprintf(stderr, Name ": %s assembled from %d drive%s", |
824 |
mddev, okcnt, okcnt==1?"":"s"); |
825 |
if (okcnt != info.array.raid_disks) |
826 |
fprintf(stderr, " (out of %d)", info.array.raid_disks); |
827 |
fprintf(stderr, ", but not started.\n"); |
828 |
return 0; |
829 |
} |
830 |
if (verbose >= -1) { |
831 |
fprintf(stderr, Name ": %s assembled from %d drive%s", mddev, okcnt, okcnt==1?"":"s"); |
832 |
if (sparecnt) |
833 |
fprintf(stderr, " and %d spare%s", sparecnt, sparecnt==1?"":"s"); |
834 |
if (!enough(info.array.level, info.array.raid_disks, |
835 |
info.array.layout, 1, avail, okcnt)) |
836 |
fprintf(stderr, " - not enough to start the array.\n"); |
837 |
else if (!enough(info.array.level, |
838 |
info.array.raid_disks, |
839 |
info.array.layout, clean, |
840 |
avail, okcnt)) |
841 |
fprintf(stderr, " - not enough to start the " |
842 |
"array while not clean - consider " |
843 |
"--force.\n"); |
844 |
else { |
845 |
if (req_cnt == info.array.raid_disks) |
846 |
fprintf(stderr, " - need all %d to start it", req_cnt); |
847 |
else |
848 |
fprintf(stderr, " - need %d of %d to start", req_cnt, info.array.raid_disks); |
849 |
fprintf(stderr, " (use --run to insist).\n"); |
850 |
} |
851 |
} |
852 |
return 1; |
853 |
} else { |
854 |
/* The "chosen_drive" is a good choice, and if necessary, the superblock has |
855 |
* been updated to point to the current locations of devices. |
856 |
* so we can just start the array |
857 |
*/ |
858 |
unsigned long dev; |
859 |
dev = makedev(devices[chosen_drive].major, |
860 |
devices[chosen_drive].minor); |
861 |
if (ioctl(mdfd, START_ARRAY, dev)) { |
862 |
fprintf(stderr, Name ": Cannot start array: %s\n", |
863 |
strerror(errno)); |
864 |
} |
865 |
|
866 |
} |
867 |
return 0; |
868 |
} |
869 |
|
870 |
int mdfd = -1; |
871 |
int runstop = 0; |
872 |
int readonly = 0; |
873 |
int verbose = 0; |
874 |
int force = 0; |
875 |
|
876 |
int mdassemble_main(int argc, char **argv) { |
877 |
mddev_ident_t array_list = conf_get_ident(NULL); |
878 |
int minor; |
879 |
if (!array_list) { |
880 |
fprintf(stderr, Name ": No arrays found in config file\n"); |
881 |
return 1; |
882 |
} else { |
883 |
for (; array_list; array_list = array_list->next) { |
884 |
mdu_array_info_t array; |
885 |
if (!strncmp("/dev/md", array_list->devname, 7)) { |
886 |
errno = 0; |
887 |
minor = strtoul(array_list->devname + 7, NULL, 0); |
888 |
if (!errno) { |
889 |
mknod(array_list->devname, S_IFBLK|0600, makedev(MD_MAJOR, minor)); |
890 |
} |
891 |
} |
892 |
mdfd = open_mddev(array_list->devname, array_list->autof); |
893 |
if (mdfd < 0) { |
894 |
|
895 |
fprintf(stderr, Name ": failed to open array\n"); |
896 |
continue; |
897 |
} |
898 |
if (ioctl(mdfd, GET_ARRAY_INFO, &array) < 0) { |
899 |
Assemble(array_list->st, array_list->devname, mdfd, |
900 |
array_list, NULL, NULL, |
901 |
readonly, runstop, NULL, NULL, verbose, force); |
902 |
} |
903 |
close(mdfd); |
904 |
} |
905 |
} |
906 |
return 0; |
907 |
} |
908 |
|