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