]>
git.ipfire.org Git - thirdparty/mdadm.git/blob - lib.c
be093e8c30cbf0f490d0e3b860f3b22fd66b13ab
2 * mdadm - manage Linux "md" devices aka RAID arrays.
4 * Copyright (C) 2011 Neil Brown <neilb@suse.de>
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.
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.
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
22 * Email: <neilb@suse.de>
29 /* This fill contains various 'library' style function. They
30 * have no dependency on anything outside this file.
33 int get_mdp_major(void)
35 static int mdp_major
= -1;
45 fl
= fopen("/proc/devices", "r");
49 while ((w
= conf_word(fl
, 1))) {
50 if (have_block
&& strcmp(w
, "devices:") == 0)
52 have_block
= (strcmp(w
, "Block") == 0);
55 if (have_devices
&& strcmp(w
, "mdp") == 0)
64 char *devid2kname(int devid
)
68 static char devnm
[32];
73 * /sys/dev/block/%d:%d link which must look like
74 * and take the last component.
76 sprintf(path
, "/sys/dev/block/%d:%d", major(devid
),
78 n
= readlink(path
, link
, sizeof(link
) - 1);
81 cp
= strrchr(link
, '/');
83 strcpy(devnm
, cp
+ 1);
90 char *stat2kname(struct stat
*st
)
92 if ((S_IFMT
& st
->st_mode
) != S_IFBLK
)
95 return devid2kname(st
->st_rdev
);
98 char *fd2kname(int fd
)
102 if (fstat(fd
, &stb
) == 0)
103 return stat2kname(&stb
);
108 char *devid2devnm(dev_t devid
)
112 static char devnm
[32];
116 /* Might be an extended-minor partition or a
117 * named md device. Look at the
118 * /sys/dev/block/%d:%d link which must look like
119 * ../../block/mdXXX/mdXXXpYY
123 sprintf(path
, "/sys/dev/block/%d:%d", major(devid
), minor(devid
));
124 n
= readlink(path
, link
, sizeof(link
) - 1);
127 cp
= strstr(link
, "/block/");
130 ep
= strchr(cp
, '/');
137 if (major(devid
) == MD_MAJOR
)
138 sprintf(devnm
,"md%d", minor(devid
));
139 else if (major(devid
) == (unsigned)get_mdp_major())
140 sprintf(devnm
,"md_d%d",
141 (minor(devid
)>>MdpMinorShift
));
148 char *stat2devnm(struct stat
*st
)
150 if ((S_IFMT
& st
->st_mode
) != S_IFBLK
)
153 return devid2devnm(st
->st_rdev
);
156 char *fd2devnm(int fd
)
160 if (fstat(fd
, &stb
) == 0)
161 return stat2devnm(&stb
);
166 /* When we create a new array, we don't want the content to
167 * be immediately examined by udev - it is probably meaningless.
168 * So create /run/mdadm/creating-mdXXX and expect that a udev
169 * rule will noticed this and act accordingly.
171 static char block_path
[] = "/run/mdadm/creating-%s";
172 static char *unblock_path
= NULL
;
173 void udev_block(char *devnm
)
178 xasprintf(&path
, block_path
, devnm
);
179 fd
= open(path
, O_CREAT
|O_RDWR
, 0600);
187 void udev_unblock(void)
190 unlink(unblock_path
);
196 * convert a major/minor pair for a block device into a name in /dev, if possible.
197 * On the first call, walk /dev collecting name.
198 * Put them in a simple linked listfor now.
205 int devlist_ready
= 0;
207 int add_dev(const char *name
, const struct stat
*stb
, int flag
, struct FTW
*s
)
211 if (S_ISLNK(stb
->st_mode
)) {
212 if (stat(name
, &st
) != 0)
217 if ((stb
->st_mode
&S_IFMT
)== S_IFBLK
) {
218 char *n
= xstrdup(name
);
219 struct devmap
*dm
= xmalloc(sizeof(*dm
));
220 if (strncmp(n
, "/dev/./", 7) == 0)
221 strcpy(n
+ 4, name
+ 6);
223 dm
->major
= major(stb
->st_rdev
);
224 dm
->minor
= minor(stb
->st_rdev
);
236 int add_dev_1(const char *name
, const struct stat
*stb
, int flag
)
238 return add_dev(name
, stb
, flag
, NULL
);
240 int nftw(const char *path
,
241 int (*han
)(const char *name
, const struct stat
*stb
,
242 int flag
, struct FTW
*s
), int nopenfd
, int flags
)
244 return ftw(path
, add_dev_1
, nopenfd
);
247 int nftw(const char *path
,
248 int (*han
)(const char *name
, const struct stat
*stb
,
249 int flag
, struct FTW
*s
), int nopenfd
, int flags
)
253 #endif /* HAVE_FTW */
254 #endif /* HAVE_NFTW */
257 * Find a block device with the right major/minor number.
258 * If we find multiple names, choose the shortest.
259 * If we find a name in /dev/md/, we prefer that.
260 * This applies only to names for MD devices.
261 * If 'prefer' is set (normally to e.g. /by-path/)
262 * then we prefer a name which contains that string.
264 char *map_dev_preferred(int major
, int minor
, int create
,
268 char *regular
= NULL
, *preferred
=NULL
;
271 if (major
== 0 && minor
== 0)
275 if (!devlist_ready
) {
279 struct devmap
*d
= devlist
;
284 if (lstat(dev
, &stb
) == 0 && S_ISLNK(stb
.st_mode
))
286 nftw(dev
, add_dev
, 10, FTW_PHYS
);
291 for (p
= devlist
; p
; p
= p
->next
)
292 if (p
->major
== major
&& p
->minor
== minor
) {
293 if (strncmp(p
->name
, "/dev/md/",8) == 0 ||
294 (prefer
&& strstr(p
->name
, prefer
))) {
295 if (preferred
== NULL
||
296 strlen(p
->name
) < strlen(preferred
))
299 if (regular
== NULL
||
300 strlen(p
->name
) < strlen(regular
))
304 if (!regular
&& !preferred
&& !did_check
) {
308 if (create
&& !regular
&& !preferred
) {
310 snprintf(buf
, sizeof(buf
), "%d:%d", major
, minor
);
314 return preferred
? preferred
: regular
;
317 /* conf_word gets one word from the conf file.
318 * if "allow_key", then accept words at the start of a line,
319 * otherwise stop when such a word is found.
320 * We assume that the file pointer is at the end of a word, so the
321 * next character is a space, or a newline. If not, it is the start of a line.
324 char *conf_word(FILE *file
, int allow_key
)
331 char *word
= xmalloc(wsize
);
333 while (wordfound
== 0) {
334 /* at the end of a word.. */
337 while (c
!= EOF
&& c
!= '\n')
344 if (c
!= ' ' && c
!= '\t' && ! allow_key
) {
348 /* looks like it is safe to get a word here, if there is one */
350 /* first, skip any spaces */
351 while (c
== ' ' || c
== '\t')
353 if (c
!= EOF
&& c
!= '\n' && c
!= '#') {
354 /* we really have a character of a word, so start saving it */
355 while (c
!= EOF
&& c
!= '\n' &&
356 (quote
|| (c
!= ' ' && c
!= '\t'))) {
358 if (quote
&& c
== quote
)
360 else if (quote
== 0 && (c
== '\'' || c
== '"'))
363 if (len
== wsize
-1) {
365 word
= xrealloc(word
, wsize
);
370 /* Hack for broken kernels (2.6.14-.24) that put
371 * "active(auto-read-only)"
372 * in /proc/mdstat instead of
373 * "active (auto-read-only)"
375 if (c
== '(' && len
>= 6 &&
376 strncmp(word
+ len
- 6, "active", 6) == 0)
385 /* Further HACK for broken kernels.. 2.6.14-2.6.24 */
386 if (strcmp(word
, "auto-read-only)") == 0)
387 strcpy(word
, "(auto-read-only)");
389 /* printf("word is <%s>\n", word); */
397 void print_quoted(char *str
)
399 /* Printf the string with surrounding quotes
401 * If no space, tab, or quote - leave unchanged.
402 * Else print surrounded by " or ', swapping quotes
403 * when we find one that will cause confusion.
406 char first_quote
= 0, q
;
409 for (c
= str
; *c
; c
++) {
429 if (first_quote
== '"')
434 for (c
= str
; *c
; c
++) {
445 void print_escape(char *str
)
447 /* print str, but change space and tab to '_'
448 * as is suitable for device names
450 for (; *str
; str
++) {
465 int check_env(char *name
)
467 char *val
= getenv(name
);
469 if (val
&& atoi(val
) == 1)
481 use
= ((stat("/dev/.udev", &stb
) == 0 ||
482 stat("/run/udev", &stb
) == 0) &&
483 check_env("MDADM_NO_UDEV") == 0);
488 unsigned long GCD(unsigned long a
, unsigned long b
)
500 * conf_line reads one logical line from the conffile or mdstat.
501 * It skips comments and continues until it finds a line that starts
502 * with a non blank/comment. This character is pushed back for the next call
503 * A doubly linked list of words is returned.
504 * the first word will be a keyword. Other words will have had quotes removed.
507 char *conf_line(FILE *file
)
512 w
= conf_word(file
, 1);
520 while ((w
= conf_word(file
, 0))){
521 char *w2
= dl_strdup(w
);
525 /* printf("got a line\n");*/
529 void free_line(char *line
)
532 for (w
= dl_next(line
); w
!= line
; w
= dl_next(line
)) {