]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - mdopen.c
Create.c: fix uclibc build
[thirdparty/mdadm.git] / mdopen.c
index 462743c44bd1fc3bdb183b67ecdc8b0cc2d28944..eaa59b5925af40f0d4b73c02bc9380eee5473785 100644 (file)
--- a/mdopen.c
+++ b/mdopen.c
@@ -1,7 +1,7 @@
 /*
  * mdadm - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001-2012 Neil Brown <neilb@suse.de>
+ * Copyright (C) 2001-2013 Neil Brown <neilb@suse.de>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
@@ -23,6 +23,7 @@
  */
 
 #include "mdadm.h"
+#include "udev.h"
 #include "md_p.h"
 #include <ctype.h>
 
@@ -44,7 +45,7 @@ void make_parts(char *dev, int cnt)
        int nlen = strlen(dev) + 20;
        char *name;
        int dig = isdigit(dev[strlen(dev)-1]);
-       char orig[1024];
+       char orig[1001];
        char sym[1024];
        int err;
 
@@ -58,8 +59,10 @@ void make_parts(char *dev, int cnt)
                minor_num = minor(stb.st_rdev);
                odig = -1;
        } else if (S_ISLNK(stb.st_mode)) {
-               int len = readlink(dev, orig, sizeof(orig));
-               if (len < 0 || len > 1000)
+               int len;
+
+               len = readlink(dev, orig, sizeof(orig));
+               if (len < 0 || len >= (int)sizeof(orig))
                        return;
                orig[len] = 0;
                odig = isdigit(orig[len-1]);
@@ -100,6 +103,32 @@ void make_parts(char *dev, int cnt)
        free(name);
 }
 
+int create_named_array(char *devnm)
+{
+       int fd;
+       int n = -1;
+       static const char new_array_file[] = {
+               "/sys/module/md_mod/parameters/new_array"
+       };
+
+       fd = open(new_array_file, O_WRONLY);
+       if (fd < 0 && errno == ENOENT) {
+               if (system("modprobe md_mod") == 0)
+                       fd = open(new_array_file, O_WRONLY);
+       }
+       if (fd >= 0) {
+               n = write(fd, devnm, strlen(devnm));
+               close(fd);
+       }
+       if (fd < 0 || n != (int)strlen(devnm)) {
+               pr_err("Fail to create %s when using %s, fallback to creation via node\n",
+                       devnm, new_array_file);
+               return 0;
+       }
+
+       return 1;
+}
+
 /*
  * We need a new md device to assemble/build/create an array.
  * 'dev' is a name given us by the user (command line or mdadm.conf)
@@ -126,7 +155,7 @@ void make_parts(char *dev, int cnt)
  *
  * If udev is configured, we create a temporary device, open it, and
  * unlink it.
- * If not, we create the /dev/mdXX device, and is name is usable,
+ * If not, we create the /dev/mdXX device, and if name is usable,
  * /dev/md/name
  * In any case we return /dev/md/name or (if that isn't available)
  * /dev/mdXX in 'chosen'.
@@ -135,7 +164,7 @@ void make_parts(char *dev, int cnt)
  */
 
 int create_mddev(char *dev, char *name, int autof, int trustworthy,
-                char *chosen)
+                char *chosen, int block_udev)
 {
        int mdfd;
        struct stat stb;
@@ -144,8 +173,13 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy,
        struct createinfo *ci = conf_get_create_info();
        int parts;
        char *cname;
-       char devname[20];
+       char devname[37];
+       char devnm[32];
        char cbuf[400];
+
+       if (!udev_is_available())
+               block_udev = 0;
+
        if (chosen == NULL)
                chosen = cbuf;
 
@@ -155,25 +189,24 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy,
        parts = autof >> 3;
        autof &= 7;
 
-       strcpy(chosen, "/dev/md/");
+       strcpy(chosen, DEV_MD_DIR);
        cname = chosen + strlen(chosen);
 
        if (dev) {
-               if (strncmp(dev, "/dev/md/", 8) == 0) {
-                       strcpy(cname, dev+8);
+               if (strncmp(dev, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0) {
+                       snprintf(cname, MD_NAME_MAX, "%s", dev + DEV_MD_DIR_LEN);
                } else if (strncmp(dev, "/dev/", 5) == 0) {
                        char *e = dev + strlen(dev);
                        while (e > dev && isdigit(e[-1]))
                                e--;
                        if (e[0])
                                num = strtoul(e, NULL, 10);
-                       strcpy(cname, dev+5);
+                       snprintf(cname, MD_NAME_MAX, "%s", dev + 5);
                        cname[e-(dev+5)] = 0;
                        /* name *must* be mdXX or md_dXX in this context */
                        if (num < 0 ||
                            (strcmp(cname, "md") != 0 && strcmp(cname, "md_d") != 0)) {
-                               pr_err("%s is an invalid name "
-                                       "for an md device.  Try /dev/md/%s\n",
+                               pr_err("%s is an invalid name for an md device.  Try /dev/md/%s\n",
                                        dev, dev+5);
                                return -1;
                        }
@@ -190,13 +223,11 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy,
                 * empty.
                 */
                if (strchr(cname, '/') != NULL) {
-                       pr_err("%s is an invalid name "
-                               "for an md device.\n", dev);
+                       pr_err("%s is an invalid name for an md device.\n", dev);
                        return -1;
                }
                if (cname[0] == 0) {
-                       pr_err("%s is an invalid name "
-                               "for an md device (empty!).", dev);
+                       pr_err("%s is an invalid name for an md device (empty!).\n", dev);
                        return -1;
                }
                if (num < 0) {
@@ -225,8 +256,7 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy,
        if (name && name[0] == 0)
                name = NULL;
        if (name && trustworthy == METADATA && use_mdp == 1) {
-               pr_err("%s is not allowed for a %s container. "
-                       "Consider /dev/md%d.\n", dev, name, num);
+               pr_err("%s is not allowed for a %s container. Consider /dev/md%d.\n", dev, name, num);
                return -1;
        }
        if (name && trustworthy == METADATA)
@@ -252,31 +282,13 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy,
                num = strtoul(n2, &ep, 10);
                if (ep == n2 || *ep)
                        num = -1;
-               else if (mddev_busy(use_mdp ? (-1-num) : num))
-                       num = -1;
-       }
-
-       if (num < 0) {
-               /* need to choose a free number. */
-               num = find_free_devnum(use_mdp);
-               if (num == NoMdDev) {
-                       pr_err("No avail md devices - aborting\n");
-                       return -1;
-               }
-       } else {
-               num = use_mdp ? (-1-num) : num;
-               if (mddev_busy(num)) {
-                       pr_err("%s is already in use.\n",
-                               dev);
-                       return -1;
+               else {
+                       sprintf(devnm, "md%s%d", use_mdp ? "_d":"", num);
+                       if (mddev_busy(devnm))
+                               num = -1;
                }
        }
 
-       if (num < 0)
-               sprintf(devname, "/dev/md_d%d", -1-num);
-       else
-               sprintf(devname, "/dev/md%d", num);
-
        if (cname[0] == 0 && name) {
                /* Need to find a name if we can
                 * We don't completely trust 'name'.  Truncate to
@@ -321,6 +333,49 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy,
                }
        }
 
+       devnm[0] = 0;
+       if (num < 0 && cname && ci->names) {
+               sprintf(devnm, "md_%s", cname);
+               if (block_udev && udev_block(devnm) != UDEV_STATUS_SUCCESS)
+                       return -1;
+               if (!create_named_array(devnm)) {
+                       devnm[0] = 0;
+                       udev_unblock();
+               }
+       }
+       if (num >= 0) {
+               sprintf(devnm, "md%d", num);
+               if (block_udev && udev_block(devnm) != UDEV_STATUS_SUCCESS)
+                       return -1;
+               if (!create_named_array(devnm)) {
+                       devnm[0] = 0;
+                       udev_unblock();
+               }
+       }
+       if (devnm[0] == 0) {
+               if (num < 0) {
+                       /* need to choose a free number. */
+                       char *_devnm = find_free_devnm(use_mdp);
+                       if (_devnm == NULL) {
+                               pr_err("No avail md devices - aborting\n");
+                               return -1;
+                       }
+                       strcpy(devnm, _devnm);
+               } else {
+                       sprintf(devnm, "%s%d", use_mdp?"md_d":"md", num);
+                       if (mddev_busy(devnm)) {
+                               pr_err("%s is already in use.\n",
+                                      dev);
+                               return -1;
+                       }
+               }
+               if (block_udev && udev_block(devnm) != UDEV_STATUS_SUCCESS)
+                       return -1;
+               create_named_array(devnm);
+       }
+
+       sprintf(devname, "/dev/%s", devnm);
+
        if (dev && dev[0] == '/')
                strcpy(chosen, dev);
        else if (cname[0] == 0)
@@ -330,19 +385,19 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy,
         * If we cannot detect udev, we need to make
         * devices and links ourselves.
         */
-       if (!use_udev()) {
+       if (!udev_is_available()) {
                /* Make sure 'devname' exists and 'chosen' is a symlink to it */
                if (lstat(devname, &stb) == 0) {
                        /* Must be the correct device, else error */
                        if ((stb.st_mode&S_IFMT) != S_IFBLK ||
-                           stb.st_rdev != makedev(dev2major(num),dev2minor(num))) {
+                           stb.st_rdev != devnm2devid(devnm)) {
                                pr_err("%s exists but looks wrong, please fix\n",
                                        devname);
                                return -1;
                        }
                } else {
                        if (mknod(devname, S_IFBLK|0600,
-                                 makedev(dev2major(num),dev2minor(num))) != 0) {
+                                 devnm2devid(devnm)) != 0) {
                                pr_err("failed to create %s\n",
                                        devname);
                                return -1;
@@ -358,11 +413,11 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy,
                        make_parts(devname, parts);
 
                if (strcmp(chosen, devname) != 0) {
-                       if (mkdir("/dev/md",0700) == 0) {
-                               if (chown("/dev/md", ci->uid, ci->gid))
-                                       perror("chown /dev/md");
-                               if (chmod("/dev/md", ci->mode| ((ci->mode>>2) & 0111)))
-                                       perror("chmod /dev/md");
+                       if (mkdir(DEV_NUM_PREF, 0700) == 0) {
+                               if (chown(DEV_NUM_PREF, ci->uid, ci->gid))
+                                       perror("chown " DEV_NUM_PREF);
+                               if (chmod(DEV_NUM_PREF, ci->mode | ((ci->mode >> 2) & 0111)))
+                                       perror("chmod " DEV_NUM_PREF);
                        }
 
                        if (dev && strcmp(chosen, dev) == 0)
@@ -389,7 +444,7 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy,
                                make_parts(chosen, parts);
                }
        }
-       mdfd = open_dev_excl(num);
+       mdfd = open_dev_excl(devnm);
        if (mdfd < 0)
                pr_err("unexpected failure opening %s\n",
                        devname);
@@ -403,21 +458,71 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy,
  */
 int open_mddev(char *dev, int report_errors)
 {
-       int mdfd = open(dev, O_RDWR);
-       if (mdfd < 0 && errno == EACCES)
-               mdfd = open(dev, O_RDONLY);
+       int mdfd = open(dev, O_RDONLY);
+
        if (mdfd < 0) {
                if (report_errors)
                        pr_err("error opening %s: %s\n",
                                dev, strerror(errno));
                return -1;
        }
-       if (md_get_version(mdfd) <= 0) {
+
+       if (md_array_valid(mdfd) == 0) {
                close(mdfd);
                if (report_errors)
-                       pr_err("%s does not appear to be "
-                               "an md device\n", dev);
+                       pr_err("%s does not appear to be an md device\n", dev);
                return -2;
        }
+
        return mdfd;
 }
+
+/**
+ * is_mddev() - check that file name passed is an md device.
+ * @dev: file name that has to be checked.
+ * Return: 1 if file passed is an md device, 0 if not.
+ */
+int is_mddev(char *dev)
+{
+       int fd = open_mddev(dev, 1);
+
+       if (fd >= 0) {
+               close(fd);
+               return 1;
+       }
+
+       return 0;
+}
+
+char *find_free_devnm(int use_partitions)
+{
+       static char devnm[32];
+       int devnum;
+       for (devnum = 127; devnum != 128;
+            devnum = devnum ? devnum-1 : (1<<9)-1) {
+
+               if (use_partitions)
+                       sprintf(devnm, "md_d%d", devnum);
+               else
+                       sprintf(devnm, "md%d", devnum);
+               if (mddev_busy(devnm))
+                       continue;
+               if (!conf_name_is_free(devnm))
+                       continue;
+               if (!udev_is_available()) {
+                       /* make sure it is new to /dev too, at least as a
+                        * non-standard */
+                       dev_t devid = devnm2devid(devnm);
+                       if (devid) {
+                               char *dn = map_dev(major(devid),
+                                                  minor(devid), 0);
+                               if (dn && ! is_standard(dn, NULL))
+                                       continue;
+                       }
+               }
+               break;
+       }
+       if (devnum == 128)
+               return NULL;
+       return devnm;
+}