]> git.ipfire.org Git - thirdparty/mdadm.git/blame - lib.c
mdadm: move data_offset to struct shape
[thirdparty/mdadm.git] / lib.c
CommitLineData
78c0a3b1
N
1/*
2 * mdadm - manage Linux "md" devices aka RAID arrays.
3 *
4 * Copyright (C) 2011 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@suse.de>
23 */
24
25#include "mdadm.h"
d0c017a6 26#include "dlink.h"
78c0a3b1 27#include <ctype.h>
60815698 28#include <limits.h>
78c0a3b1 29
a35aa68f
XN
30bool is_dev_alive(char *path)
31{
32 if (!path)
33 return false;
34
35 if (access(path, R_OK) == 0)
36 return true;
37
38 return false;
39}
40
78c0a3b1
N
41/* This fill contains various 'library' style function. They
42 * have no dependency on anything outside this file.
43 */
44
45int get_mdp_major(void)
46{
ad7ac9ac 47 static int mdp_major = -1;
78c0a3b1
N
48 FILE *fl;
49 char *w;
50 int have_block = 0;
51 int have_devices = 0;
52 int last_num = -1;
53
54 if (mdp_major != -1)
55 return mdp_major;
ad7ac9ac 56
78c0a3b1
N
57 fl = fopen("/proc/devices", "r");
58 if (!fl)
59 return -1;
ad7ac9ac 60
78c0a3b1 61 while ((w = conf_word(fl, 1))) {
ad7ac9ac 62 if (have_block && strcmp(w, "devices:") == 0)
78c0a3b1 63 have_devices = 1;
ad7ac9ac 64 have_block = (strcmp(w, "Block") == 0);
78c0a3b1
N
65 if (isdigit(w[0]))
66 last_num = atoi(w);
ad7ac9ac 67 if (have_devices && strcmp(w, "mdp") == 0)
78c0a3b1
N
68 mdp_major = last_num;
69 free(w);
70 }
71 fclose(fl);
ad7ac9ac 72
78c0a3b1
N
73 return mdp_major;
74}
75
d3c40fab 76char *devid2kname(dev_t devid)
bc17158d
N
77{
78 char path[30];
52a94085 79 char link[PATH_MAX];
bc17158d
N
80 static char devnm[32];
81 char *cp;
82 int n;
83
84 /* Look at the
85 * /sys/dev/block/%d:%d link which must look like
86 * and take the last component.
87 */
d3c40fab 88 sprintf(path, "/sys/dev/block/%d:%d", major(devid), minor(devid));
ad7ac9ac 89 n = readlink(path, link, sizeof(link) - 1);
bc17158d
N
90 if (n > 0) {
91 link[n] = 0;
92 cp = strrchr(link, '/');
93 if (cp) {
ad7ac9ac 94 strcpy(devnm, cp + 1);
bc17158d
N
95 return devnm;
96 }
97 }
98 return NULL;
99}
100
f96b1302
AP
101char *stat2kname(struct stat *st)
102{
103 if ((S_IFMT & st->st_mode) != S_IFBLK)
104 return NULL;
ad7ac9ac 105
f96b1302
AP
106 return devid2kname(st->st_rdev);
107}
108
109char *fd2kname(int fd)
110{
111 struct stat stb;
ad7ac9ac 112
f96b1302
AP
113 if (fstat(fd, &stb) == 0)
114 return stat2kname(&stb);
ad7ac9ac 115
f96b1302
AP
116 return NULL;
117}
118
13db17bd 119char *devid2devnm(dev_t devid)
78c0a3b1
N
120{
121 char path[30];
122 char link[200];
4dd2df09
N
123 static char devnm[32];
124 char *cp, *ep;
78c0a3b1
N
125 int n;
126
4dd2df09
N
127 /* Might be an extended-minor partition or a
128 * named md device. Look at the
129 * /sys/dev/block/%d:%d link which must look like
130 * ../../block/mdXXX/mdXXXpYY
131 * or
132 * ...../block/md_FOO
133 */
ad7ac9ac
JS
134 sprintf(path, "/sys/dev/block/%d:%d", major(devid), minor(devid));
135 n = readlink(path, link, sizeof(link) - 1);
4dd2df09 136 if (n > 0) {
78c0a3b1 137 link[n] = 0;
4dd2df09
N
138 cp = strstr(link, "/block/");
139 if (cp) {
140 cp += 7;
141 ep = strchr(cp, '/');
142 if (ep)
143 *ep = 0;
144 strcpy(devnm, cp);
145 return devnm;
146 }
78c0a3b1 147 }
4dd2df09
N
148 if (major(devid) == MD_MAJOR)
149 sprintf(devnm,"md%d", minor(devid));
150 else if (major(devid) == (unsigned)get_mdp_major())
151 sprintf(devnm,"md_d%d",
152 (minor(devid)>>MdpMinorShift));
153 else
154 return NULL;
ad7ac9ac 155
4dd2df09
N
156 return devnm;
157}
78c0a3b1 158
4dd2df09
N
159char *stat2devnm(struct stat *st)
160{
161 if ((S_IFMT & st->st_mode) != S_IFBLK)
162 return NULL;
ad7ac9ac 163
4dd2df09 164 return devid2devnm(st->st_rdev);
78c0a3b1
N
165}
166
4dd2df09 167char *fd2devnm(int fd)
78c0a3b1
N
168{
169 struct stat stb;
ad7ac9ac 170
78c0a3b1 171 if (fstat(fd, &stb) == 0)
4dd2df09 172 return stat2devnm(&stb);
ad7ac9ac 173
4dd2df09 174 return NULL;
78c0a3b1
N
175}
176
cd6cbb08
N
177/* When we create a new array, we don't want the content to
178 * be immediately examined by udev - it is probably meaningless.
dd180cb1 179 * So create /run/mdadm/creating-mdXXX and expect that a udev
cd6cbb08
N
180 * rule will noticed this and act accordingly.
181 */
182static char block_path[] = "/run/mdadm/creating-%s";
183static char *unblock_path = NULL;
184void udev_block(char *devnm)
185{
186 int fd;
187 char *path = NULL;
188
189 xasprintf(&path, block_path, devnm);
190 fd = open(path, O_CREAT|O_RDWR, 0600);
191 if (fd >= 0) {
192 close(fd);
193 unblock_path = path;
194 } else
195 free(path);
196}
197
198void udev_unblock(void)
199{
200 if (unblock_path)
201 unlink(unblock_path);
202 free(unblock_path);
203 unblock_path = NULL;
204}
205
78c0a3b1
N
206/*
207 * convert a major/minor pair for a block device into a name in /dev, if possible.
208 * On the first call, walk /dev collecting name.
209 * Put them in a simple linked listfor now.
210 */
211struct devmap {
5d500228
N
212 int major, minor;
213 char *name;
214 struct devmap *next;
78c0a3b1
N
215} *devlist = NULL;
216int devlist_ready = 0;
217
218int add_dev(const char *name, const struct stat *stb, int flag, struct FTW *s)
219{
220 struct stat st;
221
222 if (S_ISLNK(stb->st_mode)) {
223 if (stat(name, &st) != 0)
224 return 0;
225 stb = &st;
226 }
227
228 if ((stb->st_mode&S_IFMT)== S_IFBLK) {
503975b9
N
229 char *n = xstrdup(name);
230 struct devmap *dm = xmalloc(sizeof(*dm));
ad7ac9ac
JS
231 if (strncmp(n, "/dev/./", 7) == 0)
232 strcpy(n + 4, name + 6);
78c0a3b1
N
233 if (dm) {
234 dm->major = major(stb->st_rdev);
235 dm->minor = minor(stb->st_rdev);
236 dm->name = n;
237 dm->next = devlist;
238 devlist = dm;
239 }
240 }
ad7ac9ac 241
78c0a3b1
N
242 return 0;
243}
244
245#ifndef HAVE_NFTW
246#ifdef HAVE_FTW
247int add_dev_1(const char *name, const struct stat *stb, int flag)
248{
249 return add_dev(name, stb, flag, NULL);
250}
ad7ac9ac
JS
251int nftw(const char *path,
252 int (*han)(const char *name, const struct stat *stb,
253 int flag, struct FTW *s), int nopenfd, int flags)
78c0a3b1
N
254{
255 return ftw(path, add_dev_1, nopenfd);
256}
257#else
ad7ac9ac
JS
258int nftw(const char *path,
259 int (*han)(const char *name, const struct stat *stb,
260 int flag, struct FTW *s), int nopenfd, int flags)
78c0a3b1
N
261{
262 return 0;
263}
264#endif /* HAVE_FTW */
265#endif /* HAVE_NFTW */
266
267/*
268 * Find a block device with the right major/minor number.
269 * If we find multiple names, choose the shortest.
270 * If we find a name in /dev/md/, we prefer that.
271 * This applies only to names for MD devices.
c2ecf5f6
N
272 * If 'prefer' is set (normally to e.g. /by-path/)
273 * then we prefer a name which contains that string.
78c0a3b1 274 */
c2ecf5f6
N
275char *map_dev_preferred(int major, int minor, int create,
276 char *prefer)
78c0a3b1
N
277{
278 struct devmap *p;
279 char *regular = NULL, *preferred=NULL;
280 int did_check = 0;
281
282 if (major == 0 && minor == 0)
781f7efb 283 return NULL;
78c0a3b1
N
284
285 retry:
286 if (!devlist_ready) {
287 char *dev = "/dev";
288 struct stat stb;
289 while(devlist) {
290 struct devmap *d = devlist;
291 devlist = d->next;
292 free(d->name);
293 free(d);
294 }
ad7ac9ac 295 if (lstat(dev, &stb) == 0 && S_ISLNK(stb.st_mode))
78c0a3b1
N
296 dev = "/dev/.";
297 nftw(dev, add_dev, 10, FTW_PHYS);
298 devlist_ready=1;
299 did_check = 1;
300 }
301
ad7ac9ac
JS
302 for (p = devlist; p; p = p->next)
303 if (p->major == major && p->minor == minor) {
304 if (strncmp(p->name, "/dev/md/",8) == 0 ||
305 (prefer && strstr(p->name, prefer))) {
78c0a3b1
N
306 if (preferred == NULL ||
307 strlen(p->name) < strlen(preferred))
308 preferred = p->name;
309 } else {
310 if (regular == NULL ||
311 strlen(p->name) < strlen(regular))
312 regular = p->name;
313 }
314 }
315 if (!regular && !preferred && !did_check) {
316 devlist_ready = 0;
317 goto retry;
318 }
319 if (create && !regular && !preferred) {
320 static char buf[30];
321 snprintf(buf, sizeof(buf), "%d:%d", major, minor);
322 regular = buf;
323 }
324
325 return preferred ? preferred : regular;
326}
327
78c0a3b1
N
328/* conf_word gets one word from the conf file.
329 * if "allow_key", then accept words at the start of a line,
330 * otherwise stop when such a word is found.
331 * We assume that the file pointer is at the end of a word, so the
332 * next character is a space, or a newline. If not, it is the start of a line.
333 */
334
335char *conf_word(FILE *file, int allow_key)
336{
337 int wsize = 100;
338 int len = 0;
339 int c;
340 int quote;
341 int wordfound = 0;
503975b9 342 char *word = xmalloc(wsize);
78c0a3b1 343
ad7ac9ac 344 while (wordfound == 0) {
78c0a3b1
N
345 /* at the end of a word.. */
346 c = getc(file);
347 if (c == '#')
348 while (c != EOF && c != '\n')
349 c = getc(file);
ad7ac9ac
JS
350 if (c == EOF)
351 break;
352 if (c == '\n')
353 continue;
78c0a3b1
N
354
355 if (c != ' ' && c != '\t' && ! allow_key) {
356 ungetc(c, file);
357 break;
358 }
359 /* looks like it is safe to get a word here, if there is one */
360 quote = 0;
361 /* first, skip any spaces */
362 while (c == ' ' || c == '\t')
363 c = getc(file);
364 if (c != EOF && c != '\n' && c != '#') {
365 /* we really have a character of a word, so start saving it */
ad7ac9ac
JS
366 while (c != EOF && c != '\n' &&
367 (quote || (c != ' ' && c != '\t'))) {
78c0a3b1 368 wordfound = 1;
ad7ac9ac
JS
369 if (quote && c == quote)
370 quote = 0;
78c0a3b1
N
371 else if (quote == 0 && (c == '\'' || c == '"'))
372 quote = c;
373 else {
374 if (len == wsize-1) {
375 wsize += 100;
503975b9 376 word = xrealloc(word, wsize);
78c0a3b1
N
377 }
378 word[len++] = c;
379 }
380 c = getc(file);
381 /* Hack for broken kernels (2.6.14-.24) that put
382 * "active(auto-read-only)"
383 * in /proc/mdstat instead of
384 * "active (auto-read-only)"
385 */
ad7ac9ac
JS
386 if (c == '(' && len >= 6 &&
387 strncmp(word + len - 6, "active", 6) == 0)
78c0a3b1
N
388 c = ' ';
389 }
390 }
ad7ac9ac
JS
391 if (c != EOF)
392 ungetc(c, file);
78c0a3b1
N
393 }
394 word[len] = 0;
395
396 /* Further HACK for broken kernels.. 2.6.14-2.6.24 */
397 if (strcmp(word, "auto-read-only)") == 0)
398 strcpy(word, "(auto-read-only)");
399
400/* printf("word is <%s>\n", word); */
401 if (!wordfound) {
402 free(word);
403 word = NULL;
404 }
405 return word;
406}
7103b9b8
N
407
408void print_quoted(char *str)
409{
410 /* Printf the string with surrounding quotes
411 * iff needed.
412 * If no space, tab, or quote - leave unchanged.
413 * Else print surrounded by " or ', swapping quotes
414 * when we find one that will cause confusion.
415 */
416
417 char first_quote = 0, q;
418 char *c;
419
420 for (c = str; *c; c++) {
421 switch(*c) {
422 case '\'':
423 case '"':
424 first_quote = *c;
425 break;
426 case ' ':
427 case '\t':
428 first_quote = *c;
429 continue;
430 default:
431 continue;
432 }
433 break;
434 }
435 if (!first_quote) {
436 printf("%s", str);
437 return;
438 }
439
440 if (first_quote == '"')
441 q = '\'';
442 else
443 q = '"';
444 putchar(q);
445 for (c = str; *c; c++) {
446 if (*c == q) {
447 putchar(q);
448 q ^= '"' ^ '\'';
449 putchar(q);
450 }
451 putchar(*c);
452 }
453 putchar(q);
454}
455
456void print_escape(char *str)
457{
458 /* print str, but change space and tab to '_'
459 * as is suitable for device names
460 */
ad7ac9ac 461 for (; *str; str++) {
7103b9b8
N
462 switch (*str) {
463 case ' ':
464 case '\t':
465 putchar('_');
466 break;
467 case '/':
468 putchar('-');
469 break;
470 default:
471 putchar(*str);
472 }
473 }
474}
06d2ffc3 475
f8fcf7a1
BS
476int check_env(char *name)
477{
478 char *val = getenv(name);
479
480 if (val && atoi(val) == 1)
481 return 1;
482
483 return 0;
484}
485
06d2ffc3
N
486int use_udev(void)
487{
488 static int use = -1;
489 struct stat stb;
490
491 if (use < 0) {
ad7ac9ac
JS
492 use = ((stat("/dev/.udev", &stb) == 0 ||
493 stat("/run/udev", &stb) == 0) &&
494 check_env("MDADM_NO_UDEV") == 0);
06d2ffc3
N
495 }
496 return use;
497}
2eba8496
N
498
499unsigned long GCD(unsigned long a, unsigned long b)
500{
501 while (a != b) {
502 if (a < b)
503 b -= a;
504 if (b < a)
505 a -= b;
506 }
507 return a;
508}
d0c017a6
N
509
510/*
511 * conf_line reads one logical line from the conffile or mdstat.
512 * It skips comments and continues until it finds a line that starts
513 * with a non blank/comment. This character is pushed back for the next call
514 * A doubly linked list of words is returned.
515 * the first word will be a keyword. Other words will have had quotes removed.
516 */
517
518char *conf_line(FILE *file)
519{
520 char *w;
521 char *list;
522
523 w = conf_word(file, 1);
781f7efb
JS
524 if (w == NULL)
525 return NULL;
d0c017a6
N
526
527 list = dl_strdup(w);
528 free(w);
529 dl_init(list);
530
ad7ac9ac 531 while ((w = conf_word(file, 0))){
d0c017a6
N
532 char *w2 = dl_strdup(w);
533 free(w);
534 dl_add(list, w2);
535 }
536/* printf("got a line\n");*/
537 return list;
538}
539
540void free_line(char *line)
541{
542 char *w;
ad7ac9ac 543 for (w = dl_next(line); w != line; w = dl_next(line)) {
d0c017a6
N
544 dl_del(w);
545 dl_free(w);
546 }
547 dl_free(line);
548}
60815698
MG
549
550/**
551 * parse_num() - Parse int from string.
552 * @dest: Pointer to destination.
553 * @num: Pointer to string that is going to be parsed.
554 *
555 * If string contains anything after a number, error code is returned.
556 * The same happens when number is bigger than INT_MAX or smaller than 0.
557 * Writes to destination only if successfully read the number.
558 *
559 * Return: 0 on success, 1 otherwise.
560 */
561int parse_num(int *dest, char *num)
562{
563 char *c = NULL;
564 long temp;
565
566 if (!num)
567 return 1;
568
569 errno = 0;
570 temp = strtol(num, &c, 10);
571 if (temp < 0 || temp > INT_MAX || *c || errno != 0 || num == c)
572 return 1;
573 *dest = temp;
574 return 0;
575}