]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - lib.c
mdadm: check value returned by snprintf against errors
[thirdparty/mdadm.git] / lib.c
diff --git a/lib.c b/lib.c
index 1c856541c9cc538ea2f142af75ef8f7fa81e9c2a..60890b95baf888762e0a1cf9c919e1314abf1285 100644 (file)
--- a/lib.c
+++ b/lib.c
@@ -23,6 +23,7 @@
  */
 
 #include       "mdadm.h"
+#include       "dlink.h"
 #include       <ctype.h>
 
 /* This fill contains various 'library' style function.  They
@@ -31,7 +32,7 @@
 
 int get_mdp_major(void)
 {
-static int mdp_major = -1;
+       static int mdp_major = -1;
        FILE *fl;
        char *w;
        int have_block = 0;
@@ -40,92 +41,155 @@ static int mdp_major = -1;
 
        if (mdp_major != -1)
                return mdp_major;
+
        fl = fopen("/proc/devices", "r");
        if (!fl)
                return -1;
+
        while ((w = conf_word(fl, 1))) {
-               if (have_block && strcmp(w, "devices:")==0)
+               if (have_block && strcmp(w, "devices:") == 0)
                        have_devices = 1;
-               have_block =  (strcmp(w, "Block")==0);
+               have_block =  (strcmp(w, "Block") == 0);
                if (isdigit(w[0]))
                        last_num = atoi(w);
-               if (have_devices && strcmp(w, "mdp")==0)
+               if (have_devices && strcmp(w, "mdp") == 0)
                        mdp_major = last_num;
                free(w);
        }
        fclose(fl);
+
        return mdp_major;
 }
 
-
-void fmt_devname(char *name, int num)
+char *devid2kname(dev_t devid)
 {
-       if (num >= 0)
-               sprintf(name, "md%d", num);
-       else
-               sprintf(name, "md_d%d", -1-num);
+       char path[30];
+       char link[PATH_MAX];
+       static char devnm[32];
+       char *cp;
+       int n;
+
+       /* Look at the
+        * /sys/dev/block/%d:%d link which must look like
+        * and take the last component.
+        */
+       sprintf(path, "/sys/dev/block/%d:%d", major(devid), minor(devid));
+       n = readlink(path, link, sizeof(link) - 1);
+       if (n > 0) {
+               link[n] = 0;
+               cp = strrchr(link, '/');
+               if (cp) {
+                       strcpy(devnm, cp + 1);
+                       return devnm;
+               }
+       }
+       return NULL;
 }
 
-char *devnum2devname(int num)
+char *stat2kname(struct stat *st)
 {
-       char name[100];
-       fmt_devname(name,num);
-       return xstrdup(name);
+       if ((S_IFMT & st->st_mode) != S_IFBLK)
+               return NULL;
+
+       return devid2kname(st->st_rdev);
 }
 
-int devname2devnum(char *name)
+char *fd2kname(int fd)
 {
-       char *ep;
-       int num;
-       if (strncmp(name, "md_d", 4)==0)
-               num = -1-strtoul(name+4, &ep, 10);
-       else
-               num = strtoul(name+2, &ep, 10);
-       return num;
+       struct stat stb;
+
+       if (fstat(fd, &stb) == 0)
+               return stat2kname(&stb);
+
+       return NULL;
 }
 
-int stat2devnum(struct stat *st)
+char *devid2devnm(dev_t devid)
 {
        char path[30];
        char link[200];
-       char *cp;
+       static char devnm[32];
+       char *cp, *ep;
        int n;
 
-       if ((S_IFMT & st->st_mode) == S_IFBLK) {
-               if (major(st->st_rdev) == MD_MAJOR)
-                       return minor(st->st_rdev);
-               else if (major(st->st_rdev) == (unsigned)get_mdp_major())
-                       return -1- (minor(st->st_rdev)>>MdpMinorShift);
-
-               /* must be an extended-minor partition. Look at the
-                * /sys/dev/block/%d:%d link which must look like
-                * ../../block/mdXXX/mdXXXpYY
-                */
-               sprintf(path, "/sys/dev/block/%d:%d", major(st->st_rdev),
-                       minor(st->st_rdev));
-               n = readlink(path, link, sizeof(link)-1);
-               if (n <= 0)
-                       return NoMdDev;
+       /* Might be an extended-minor partition or a
+        * named md device. Look at the
+        * /sys/dev/block/%d:%d link which must look like
+        *    ../../block/mdXXX/mdXXXpYY
+        * or
+        *    ...../block/md_FOO
+        */
+       sprintf(path, "/sys/dev/block/%d:%d", major(devid), minor(devid));
+       n = readlink(path, link, sizeof(link) - 1);
+       if (n > 0) {
                link[n] = 0;
-               cp = strrchr(link, '/');
-               if (cp) *cp = 0;
-               cp = strrchr(link, '/');
-               if (cp && strncmp(cp, "/md", 3) == 0)
-                       return devname2devnum(cp+1);
+               cp = strstr(link, "/block/");
+               if (cp) {
+                       cp += 7;
+                       ep = strchr(cp, '/');
+                       if (ep)
+                               *ep = 0;
+                       strcpy(devnm, cp);
+                       return devnm;
+               }
        }
-       return NoMdDev;
+       if (major(devid) == MD_MAJOR)
+               sprintf(devnm,"md%d", minor(devid));
+       else if (major(devid) == (unsigned)get_mdp_major())
+               sprintf(devnm,"md_d%d",
+                       (minor(devid)>>MdpMinorShift));
+       else
+               return NULL;
 
+       return devnm;
 }
 
-int fd2devnum(int fd)
+char *stat2devnm(struct stat *st)
+{
+       if ((S_IFMT & st->st_mode) != S_IFBLK)
+               return NULL;
+
+       return devid2devnm(st->st_rdev);
+}
+
+char *fd2devnm(int fd)
 {
        struct stat stb;
+
        if (fstat(fd, &stb) == 0)
-               return stat2devnum(&stb);
-       return NoMdDev;
+               return stat2devnm(&stb);
+
+       return NULL;
 }
 
+/* When we create a new array, we don't want the content to
+ * be immediately examined by udev - it is probably meaningless.
+ * So create /run/mdadm/creating-mdXXX and expect that a udev
+ * rule will noticed this and act accordingly.
+ */
+static char block_path[] = "/run/mdadm/creating-%s";
+static char *unblock_path = NULL;
+void udev_block(char *devnm)
+{
+       int fd;
+       char *path = NULL;
+
+       xasprintf(&path, block_path, devnm);
+       fd = open(path, O_CREAT|O_RDWR, 0600);
+       if (fd >= 0) {
+               close(fd);
+               unblock_path = path;
+       } else
+               free(path);
+}
 
+void udev_unblock(void)
+{
+       if (unblock_path)
+               unlink(unblock_path);
+       free(unblock_path);
+       unblock_path = NULL;
+}
 
 /*
  * convert a major/minor pair for a block device into a name in /dev, if possible.
@@ -152,8 +216,8 @@ int add_dev(const char *name, const struct stat *stb, int flag, struct FTW *s)
        if ((stb->st_mode&S_IFMT)== S_IFBLK) {
                char *n = xstrdup(name);
                struct devmap *dm = xmalloc(sizeof(*dm));
-               if (strncmp(n, "/dev/./", 7)==0)
-                       strcpy(n+4, name+6);
+               if (strncmp(n, "/dev/./", 7) == 0)
+                       strcpy(n + 4, name + 6);
                if (dm) {
                        dm->major = major(stb->st_rdev);
                        dm->minor = minor(stb->st_rdev);
@@ -162,6 +226,7 @@ int add_dev(const char *name, const struct stat *stb, int flag, struct FTW *s)
                        devlist = dm;
                }
        }
+
        return 0;
 }
 
@@ -171,12 +236,16 @@ int add_dev_1(const char *name, const struct stat *stb, int flag)
 {
        return add_dev(name, stb, flag, NULL);
 }
-int nftw(const char *path, int (*han)(const char *name, const struct stat *stb, int flag, struct FTW *s), int nopenfd, int flags)
+int nftw(const char *path,
+        int (*han)(const char *name, const struct stat *stb,
+                   int flag, struct FTW *s), int nopenfd, int flags)
 {
        return ftw(path, add_dev_1, nopenfd);
 }
 #else
-int nftw(const char *path, int (*han)(const char *name, const struct stat *stb, int flag, struct FTW *s), int nopenfd, int flags)
+int nftw(const char *path,
+        int (*han)(const char *name, const struct stat *stb,
+                   int flag, struct FTW *s), int nopenfd, int flags)
 {
        return 0;
 }
@@ -199,7 +268,7 @@ char *map_dev_preferred(int major, int minor, int create,
        int did_check = 0;
 
        if (major == 0 && minor == 0)
-                       return NULL;
+               return NULL;
 
  retry:
        if (!devlist_ready) {
@@ -211,19 +280,17 @@ char *map_dev_preferred(int major, int minor, int create,
                        free(d->name);
                        free(d);
                }
-               if (lstat(dev, &stb)==0 &&
-                   S_ISLNK(stb.st_mode))
+               if (lstat(dev, &stb) == 0 && S_ISLNK(stb.st_mode))
                        dev = "/dev/.";
                nftw(dev, add_dev, 10, FTW_PHYS);
                devlist_ready=1;
                did_check = 1;
        }
 
-       for (p=devlist; p; p=p->next)
-               if (p->major == major &&
-                   p->minor == minor) {
-                       if (strncmp(p->name, "/dev/md/",8) == 0
-                           || (prefer && strstr(p->name, prefer))) {
+       for (p = devlist; p; p = p->next)
+               if (p->major == major && p->minor == minor) {
+                       if (strncmp(p->name, "/dev/md/",8) == 0 ||
+                           (prefer && strstr(p->name, prefer))) {
                                if (preferred == NULL ||
                                    strlen(p->name) < strlen(preferred))
                                        preferred = p->name;
@@ -246,8 +313,6 @@ char *map_dev_preferred(int major, int minor, int create,
        return preferred ? preferred : regular;
 }
 
-
-
 /* conf_word gets one word from the conf file.
  * if "allow_key", then accept words at the start of a line,
  * otherwise stop when such a word is found.
@@ -264,14 +329,16 @@ char *conf_word(FILE *file, int allow_key)
        int wordfound = 0;
        char *word = xmalloc(wsize);
 
-       while (wordfound==0) {
+       while (wordfound == 0) {
                /* at the end of a word.. */
                c = getc(file);
                if (c == '#')
                        while (c != EOF && c != '\n')
                                c = getc(file);
-               if (c == EOF) break;
-               if (c == '\n') continue;
+               if (c == EOF)
+                       break;
+               if (c == '\n')
+                       continue;
 
                if (c != ' ' && c != '\t' && ! allow_key) {
                        ungetc(c, file);
@@ -284,9 +351,11 @@ char *conf_word(FILE *file, int allow_key)
                        c = getc(file);
                if (c != EOF && c != '\n' && c != '#') {
                        /* we really have a character of a word, so start saving it */
-                       while (c != EOF && c != '\n' && (quote || (c!=' ' && c != '\t'))) {
+                       while (c != EOF && c != '\n' &&
+                              (quote || (c != ' ' && c != '\t'))) {
                                wordfound = 1;
-                               if (quote && c == quote) quote = 0;
+                               if (quote && c == quote)
+                                       quote = 0;
                                else if (quote == 0 && (c == '\'' || c == '"'))
                                        quote = c;
                                else {
@@ -302,12 +371,13 @@ char *conf_word(FILE *file, int allow_key)
                                 * in /proc/mdstat instead of
                                 *        "active (auto-read-only)"
                                 */
-                               if (c == '(' && len >= 6
-                                   && strncmp(word+len-6, "active", 6) == 0)
+                               if (c == '(' && len >= 6 &&
+                                   strncmp(word + len - 6, "active", 6) == 0)
                                        c = ' ';
                        }
                }
-               if (c != EOF) ungetc(c, file);
+               if (c != EOF)
+                       ungetc(c, file);
        }
        word[len] = 0;
 
@@ -376,7 +446,7 @@ void print_escape(char *str)
        /* print str, but change space and tab to '_'
         * as is suitable for device names
         */
-       for (; *str ; str++) {
+       for (; *str; str++) {
                switch (*str) {
                case ' ':
                case '\t':
@@ -390,3 +460,77 @@ void print_escape(char *str)
                }
        }
 }
+
+int check_env(char *name)
+{
+       char *val = getenv(name);
+
+       if (val && atoi(val) == 1)
+               return 1;
+
+       return 0;
+}
+
+int use_udev(void)
+{
+       static int use = -1;
+       struct stat stb;
+
+       if (use < 0) {
+               use = ((stat("/dev/.udev", &stb) == 0 ||
+                       stat("/run/udev", &stb) == 0) &&
+                      check_env("MDADM_NO_UDEV") == 0);
+       }
+       return use;
+}
+
+unsigned long GCD(unsigned long a, unsigned long b)
+{
+       while (a != b) {
+               if (a < b)
+                       b -= a;
+               if (b < a)
+                       a -= b;
+       }
+       return a;
+}
+
+/*
+ * conf_line reads one logical line from the conffile or mdstat.
+ * It skips comments and continues until it finds a line that starts
+ * with a non blank/comment.  This character is pushed back for the next call
+ * A doubly linked list of words is returned.
+ * the first word will be a keyword.  Other words will have had quotes removed.
+ */
+
+char *conf_line(FILE *file)
+{
+       char *w;
+       char *list;
+
+       w = conf_word(file, 1);
+       if (w == NULL)
+               return NULL;
+
+       list = dl_strdup(w);
+       free(w);
+       dl_init(list);
+
+       while ((w = conf_word(file, 0))){
+               char *w2 = dl_strdup(w);
+               free(w);
+               dl_add(list, w2);
+       }
+/*    printf("got a line\n");*/
+       return list;
+}
+
+void free_line(char *line)
+{
+       char *w;
+       for (w = dl_next(line); w != line; w = dl_next(line)) {
+               dl_del(w);
+               dl_free(w);
+       }
+       dl_free(line);
+}