]>
git.ipfire.org Git - thirdparty/mdadm.git/blob - lib.c
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>
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.
35 * @str length must be bigger than 0 and be lower or equal @max_len, including termination byte.
37 bool is_string_lq(const char * const str
, size_t max_len
)
41 size_t _len
= strnlen(str
, max_len
);
43 if (_len
> 0 && _len
< max_len
)
48 bool is_dev_alive(char *path
)
53 if (access(path
, R_OK
) == 0)
59 /* This fill contains various 'library' style function. They
60 * have no dependency on anything outside this file.
63 int get_mdp_major(void)
65 static int mdp_major
= -1;
75 fl
= fopen("/proc/devices", "r");
79 while ((w
= conf_word(fl
, 1))) {
80 if (have_block
&& strcmp(w
, "devices:") == 0)
82 have_block
= (strcmp(w
, "Block") == 0);
85 if (have_devices
&& strcmp(w
, "mdp") == 0)
94 char *devid2kname(dev_t devid
)
98 static char devnm
[32];
103 * /sys/dev/block/%d:%d link which must look like
104 * and take the last component.
106 sprintf(path
, "/sys/dev/block/%d:%d", major(devid
), minor(devid
));
107 n
= readlink(path
, link
, sizeof(link
) - 1);
110 cp
= strrchr(link
, '/');
112 strcpy(devnm
, cp
+ 1);
119 char *stat2kname(struct stat
*st
)
121 if ((S_IFMT
& st
->st_mode
) != S_IFBLK
)
124 return devid2kname(st
->st_rdev
);
127 char *fd2kname(int fd
)
131 if (fstat(fd
, &stb
) == 0)
132 return stat2kname(&stb
);
137 char *devid2devnm(dev_t devid
)
141 static char devnm
[32];
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
152 sprintf(path
, "/sys/dev/block/%d:%d", major(devid
), minor(devid
));
153 n
= readlink(path
, link
, sizeof(link
) - 1);
156 cp
= strstr(link
, "/block/");
159 ep
= strchr(cp
, '/');
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
));
177 char *stat2devnm(struct stat
*st
)
179 if ((S_IFMT
& st
->st_mode
) != S_IFBLK
)
182 return devid2devnm(st
->st_rdev
);
185 bool stat_is_md_dev(struct stat
*st
)
187 if ((S_IFMT
& st
->st_mode
) != S_IFBLK
)
189 if (major(st
->st_rdev
) == MD_MAJOR
)
191 if (major(st
->st_rdev
) == (unsigned)get_mdp_major())
197 char *fd2devnm(int fd
)
201 if (fstat(fd
, &stb
) == 0)
202 return stat2devnm(&stb
);
208 * convert a major/minor pair for a block device into a name in /dev, if possible.
209 * On the first call, walk /dev collecting name.
210 * Put them in a simple linked listfor now.
217 int devlist_ready
= 0;
219 int add_dev(const char *name
, const struct stat
*stb
, int flag
, struct FTW
*s
)
223 if (S_ISLNK(stb
->st_mode
)) {
224 if (stat(name
, &st
) != 0)
229 if ((stb
->st_mode
&S_IFMT
)== S_IFBLK
) {
230 char *n
= xstrdup(name
);
231 struct devmap
*dm
= xmalloc(sizeof(*dm
));
232 if (strncmp(n
, "/dev/./", 7) == 0)
233 strcpy(n
+ 4, name
+ 6);
235 dm
->major
= major(stb
->st_rdev
);
236 dm
->minor
= minor(stb
->st_rdev
);
248 int add_dev_1(const char *name
, const struct stat
*stb
, int flag
)
250 return add_dev(name
, stb
, flag
, NULL
);
252 int nftw(const char *path
,
253 int (*han
)(const char *name
, const struct stat
*stb
,
254 int flag
, struct FTW
*s
), int nopenfd
, int flags
)
256 return ftw(path
, add_dev_1
, nopenfd
);
259 int nftw(const char *path
,
260 int (*han
)(const char *name
, const struct stat
*stb
,
261 int flag
, struct FTW
*s
), int nopenfd
, int flags
)
265 #endif /* HAVE_FTW */
266 #endif /* HAVE_NFTW */
269 * Find a block device with the right major/minor number.
270 * If we find multiple names, choose the shortest.
271 * If we find a name in /dev/md/, we prefer that.
272 * This applies only to names for MD devices.
273 * If 'prefer' is set (normally to e.g. /by-path/)
274 * then we prefer a name which contains that string.
276 char *map_dev_preferred(int major
, int minor
, int create
,
280 char *regular
= NULL
, *preferred
=NULL
;
283 if (major
== 0 && minor
== 0)
287 if (!devlist_ready
) {
291 struct devmap
*d
= devlist
;
296 if (lstat(dev
, &stb
) == 0 && S_ISLNK(stb
.st_mode
))
298 nftw(dev
, add_dev
, 10, FTW_PHYS
);
303 for (p
= devlist
; p
; p
= p
->next
)
304 if (p
->major
== major
&& p
->minor
== minor
) {
305 if (strncmp(p
->name
, DEV_MD_DIR
, DEV_MD_DIR_LEN
) == 0 ||
306 (prefer
&& strstr(p
->name
, prefer
))) {
307 if (preferred
== NULL
||
308 strlen(p
->name
) < strlen(preferred
))
311 if (regular
== NULL
||
312 strlen(p
->name
) < strlen(regular
))
316 if (!regular
&& !preferred
&& !did_check
) {
320 if (create
&& !regular
&& !preferred
) {
322 snprintf(buf
, sizeof(buf
), "%d:%d", major
, minor
);
326 return preferred
? preferred
: regular
;
329 /* conf_word gets one word from the conf file.
330 * if "allow_key", then accept words at the start of a line,
331 * otherwise stop when such a word is found.
332 * We assume that the file pointer is at the end of a word, so the
333 * next character is a space, or a newline. If not, it is the start of a line.
336 char *conf_word(FILE *file
, int allow_key
)
343 char *word
= xmalloc(wsize
);
345 while (wordfound
== 0) {
346 /* at the end of a word.. */
349 while (c
!= EOF
&& c
!= '\n')
356 if (c
!= ' ' && c
!= '\t' && ! allow_key
) {
360 /* looks like it is safe to get a word here, if there is one */
362 /* first, skip any spaces */
363 while (c
== ' ' || c
== '\t')
365 if (c
!= EOF
&& c
!= '\n' && c
!= '#') {
366 /* we really have a character of a word, so start saving it */
367 while (c
!= EOF
&& c
!= '\n' &&
368 (quote
|| (c
!= ' ' && c
!= '\t'))) {
370 if (quote
&& c
== quote
)
372 else if (quote
== 0 && (c
== '\'' || c
== '"'))
375 if (len
== wsize
-1) {
377 word
= xrealloc(word
, wsize
);
382 /* Hack for broken kernels (2.6.14-.24) that put
383 * "active(auto-read-only)"
384 * in /proc/mdstat instead of
385 * "active (auto-read-only)"
387 if (c
== '(' && len
>= 6 &&
388 strncmp(word
+ len
- 6, "active", 6) == 0)
397 /* Further HACK for broken kernels.. 2.6.14-2.6.24 */
398 if (strcmp(word
, "auto-read-only)") == 0)
399 strcpy(word
, "(auto-read-only)");
401 /* printf("word is <%s>\n", word); */
409 void print_quoted(char *str
)
411 /* Printf the string with surrounding quotes
413 * If no space, tab, or quote - leave unchanged.
414 * Else print surrounded by " or ', swapping quotes
415 * when we find one that will cause confusion.
418 char first_quote
= 0, q
;
421 for (c
= str
; *c
; c
++) {
441 if (first_quote
== '"')
446 for (c
= str
; *c
; c
++) {
458 * is_alphanum() - Check if sign is letter or digit.
459 * @c: char to analyze.
461 * Similar to isalnum() but additional locales are excluded.
463 * Return: %true on success, %false otherwise.
465 bool is_alphanum(const char c
)
467 if (isupper(c
) || islower(c
) || isdigit(c
) != 0)
473 * is_name_posix_compatible() - Check if name is POSIX compatible.
474 * @name: name to check.
476 * POSIX portable file name character set contains ASCII letters,
477 * digits, '_', '.', and '-'. Also forbid leading '-'.
478 * The length of the name cannot exceed NAME_MAX - 1 (ensure NULL ending).
480 * Return: %true on success, %false otherwise.
482 bool is_name_posix_compatible(const char * const name
)
486 char allowed_symbols
[] = "-_.";
487 const char *n
= name
;
489 if (!is_string_lq(name
, NAME_MAX
))
496 if (!is_alphanum(*n
) && !strchr(allowed_symbols
, *n
))
503 int check_env(char *name
)
505 char *val
= getenv(name
);
507 if (val
&& atoi(val
) == 1)
513 unsigned long GCD(unsigned long a
, unsigned long b
)
525 * conf_line reads one logical line from the conffile or mdstat.
526 * It skips comments and continues until it finds a line that starts
527 * with a non blank/comment. This character is pushed back for the next call
528 * A doubly linked list of words is returned.
529 * the first word will be a keyword. Other words will have had quotes removed.
532 char *conf_line(FILE *file
)
537 w
= conf_word(file
, 1);
545 while ((w
= conf_word(file
, 0))){
546 char *w2
= dl_strdup(w
);
550 /* printf("got a line\n");*/
554 void free_line(char *line
)
557 for (w
= dl_next(line
); w
!= line
; w
= dl_next(line
)) {
565 * parse_num() - Parse int from string.
566 * @dest: Pointer to destination.
567 * @num: Pointer to string that is going to be parsed.
569 * If string contains anything after a number, error code is returned.
570 * The same happens when number is bigger than INT_MAX or smaller than 0.
571 * Writes to destination only if successfully read the number.
573 * Return: 0 on success, 1 otherwise.
575 int parse_num(int *dest
, const char *num
)
584 temp
= strtol(num
, &c
, 10);
585 if (temp
< 0 || temp
> INT_MAX
|| *c
|| errno
!= 0 || num
== c
)
592 * s_gethostname() - secure get hostname. Assure null-terminated string.
594 * @buf: buffer for hostname.
595 * @buf_len: buffer length.
597 * Return: gethostname() result.
599 int s_gethostname(char *buf
, int buf_len
)
603 int ret
= gethostname(buf
, buf_len
);
605 buf
[buf_len
- 1] = 0;