]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - util.c
imsm: support RAID 10 with more than 4 drives
[thirdparty/mdadm.git] / util.c
diff --git a/util.c b/util.c
index 8c7f3fd5b7ac52aa68cc46bc8e93ea9e703a6da2..4fbf11c4e2bd71fad9df89b9b259ad50ac4b28d7 100644 (file)
--- a/util.c
+++ b/util.c
@@ -36,7 +36,7 @@
 #include       <ctype.h>
 #include       <dirent.h>
 #include       <dlfcn.h>
-
+#include       <limits.h>
 
 /*
  * following taken from linux/blkpg.h because they aren't
@@ -421,14 +421,19 @@ int parse_layout_10(char *layout)
 
 int parse_layout_faulty(char *layout)
 {
+       int ln, mode;
+       char *m;
+
        if (!layout)
                return -1;
+
        /* Parse the layout string for 'faulty' */
-       int ln = strcspn(layout, "0123456789");
-       char *m = xstrdup(layout);
-       int mode;
+       ln = strcspn(layout, "0123456789");
+       m = xstrdup(layout);
        m[ln] = 0;
        mode = map_name(faultylayout, m);
+       free(m);
+
        if (mode == UnSet)
                return -1;
 
@@ -584,19 +589,21 @@ char *__fname_from_uuid(int id[4], int swap, char *buf, char sep)
 
 }
 
-char *fname_from_uuid(struct supertype *st, struct mdinfo *info,
-                     char *buf, char sep)
+/**
+ * fname_from_uuid() - generate uuid string. Should not be used with super1.
+ * @info: info with uuid
+ * @buf: buf to fill.
+ *
+ * This routine should not be used with super1. See detail_fname_from_uuid() for details. It does
+ * not use superswitch swapuuid as it should be 0 but it has to do UUID conversion if host is big
+ * endian- left for backward compatibility.
+ */
+char *fname_from_uuid(struct mdinfo *info, char *buf)
 {
-       // dirty hack to work around an issue with super1 superblocks...
-       // super1 superblocks need swapuuid set in order for assembly to
-       // work, but can't have it set if we want this printout to match
-       // all the other uuid printouts in super1.c, so we force swapuuid
-       // to 1 to make our printout match the rest of super1
 #if __BYTE_ORDER == BIG_ENDIAN
-       return __fname_from_uuid(info->uuid, 1, buf, sep);
+       return __fname_from_uuid(info->uuid, true, buf, ':');
 #else
-       return __fname_from_uuid(info->uuid, (st->ss == &super1) ? 1 :
-                                st->ss->swapuuid, buf, sep);
+       return __fname_from_uuid(info->uuid, false, buf, ':');
 #endif
 }
 
@@ -718,23 +725,33 @@ int stat_is_blkdev(char *devname, dev_t *rdev)
        return 1;
 }
 
+/**
+ * ask() - prompt user for "yes/no" dialog.
+ * @mesg: message to be printed, without '?' sign.
+ * Returns: 1 if 'Y/y', 0 otherwise.
+ *
+ * The default value is 'N/n', thus the caps on "N" on prompt.
+ */
 int ask(char *mesg)
 {
-       char *add = "";
-       int i;
-       for (i = 0; i < 5; i++) {
-               char buf[100];
-               fprintf(stderr, "%s%s", mesg, add);
-               fflush(stderr);
-               if (fgets(buf, 100, stdin)==NULL)
-                       return 0;
-               if (buf[0]=='y' || buf[0]=='Y')
-                       return 1;
-               if (buf[0]=='n' || buf[0]=='N')
-                       return 0;
-               add = "(y/n) ";
+       char buf[3] = {0};
+
+       fprintf(stderr, "%s [y/N]? ", mesg);
+       fflush(stderr);
+       if (fgets(buf, 3, stdin) == NULL)
+               return 0;
+       if (strlen(buf) == 1) {
+               pr_err("assuming no.\n");
+               return 0;
        }
-       pr_err("assuming 'no'\n");
+       if (buf[1] != '\n')
+               goto bad_option;
+       if (toupper(buf[0]) == 'Y')
+               return 1;
+       if (toupper(buf[0]) == 'N')
+               return 0;
+bad_option:
+       pr_err("bad option.\n");
        return 0;
 }
 
@@ -968,6 +985,50 @@ dev_t devnm2devid(char *devnm)
        return 0;
 }
 
+/**
+ * is_devname_numbered() - helper for numbered devname verification.
+ * @devname: path or name to check.
+ * @pref: expected devname prefix.
+ * @pref_len: prefix len.
+ */
+static bool is_devname_numbered(const char *devname, const char *pref, const int pref_len)
+{
+       int val;
+
+       assert(devname && pref);
+
+       if (strncmp(devname, pref, pref_len) != 0)
+               return false;
+
+       if (parse_num(&val, devname + pref_len) != 0)
+               return false;
+
+       if (val > 127)
+               return false;
+
+       return true;
+}
+
+/**
+ * is_devname_md_numbered() - check if &devname is numbered MD device (md).
+ * @devname: path or name to check.
+ */
+bool is_devname_md_numbered(const char *devname)
+{
+       return is_devname_numbered(devname, DEV_NUM_PREF, DEV_NUM_PREF_LEN);
+}
+
+/**
+ * is_devname_md_d_numbered() - check if &devname is secondary numbered MD device (md_d).
+ * @devname: path or name to check.
+ */
+bool is_devname_md_d_numbered(const char *devname)
+{
+       static const char d_dev[] = DEV_NUM_PREF "_d";
+
+       return is_devname_numbered(devname, d_dev, sizeof(d_dev) - 1);
+}
+
 /**
  * get_md_name() - Get main dev node of the md device.
  * @devnm: Md device name or path.
@@ -1217,40 +1278,6 @@ struct supertype *super_by_fd(int fd, char **subarrayp)
        return st;
 }
 
-int dev_size_from_id(dev_t id, unsigned long long *size)
-{
-       char buf[20];
-       int fd;
-
-       sprintf(buf, "%d:%d", major(id), minor(id));
-       fd = dev_open(buf, O_RDONLY);
-       if (fd < 0)
-               return 0;
-       if (get_dev_size(fd, NULL, size)) {
-               close(fd);
-               return 1;
-       }
-       close(fd);
-       return 0;
-}
-
-int dev_sector_size_from_id(dev_t id, unsigned int *size)
-{
-       char buf[20];
-       int fd;
-
-       sprintf(buf, "%d:%d", major(id), minor(id));
-       fd = dev_open(buf, O_RDONLY);
-       if (fd < 0)
-               return 0;
-       if (get_dev_sector_size(fd, NULL, size)) {
-               close(fd);
-               return 1;
-       }
-       close(fd);
-       return 0;
-}
-
 struct supertype *dup_super(struct supertype *orig)
 {
        struct supertype *st;
@@ -1803,7 +1830,7 @@ int remove_disk(int mdfd, struct supertype *st,
 
        /* Remove the disk given by 'info' from the array */
        if (st->ss->external)
-               rv = sysfs_set_str(sra, info, "slot", "none");
+               rv = sysfs_set_str(sra, info, "slot", STR_COMMON_NONE);
        else
                rv = ioctl(mdfd, HOT_REMOVE_DISK, makedev(info->disk.major,
                                                          info->disk.minor));
@@ -1850,8 +1877,8 @@ int set_array_info(int mdfd, struct supertype *st, struct mdinfo *info)
        int rv;
 
        if (st->ss->external)
-               return sysfs_set_array(info, 9003);
-               
+               return sysfs_set_array(info);
+
        memset(&inf, 0, sizeof(inf));
        inf.major_version = info->array.major_version;
        inf.minor_version = info->array.minor_version;
@@ -1911,6 +1938,7 @@ int start_mdmon(char *devnm)
        int len;
        pid_t pid;
        int status;
+       char *prefix = in_initrd() ? "initrd-" : "";
        char pathbuf[1024];
        char *paths[4] = {
                pathbuf,
@@ -1921,7 +1949,7 @@ int start_mdmon(char *devnm)
 
        if (check_env("MDADM_NO_MDMON"))
                return 0;
-       if (continue_via_systemd(devnm, MDMON_SERVICE))
+       if (continue_via_systemd(devnm, MDMON_SERVICE, prefix))
                return 0;
 
        /* That failed, try running mdmon directly */
@@ -2038,6 +2066,65 @@ void append_metadata_update(struct supertype *st, void *buf, int len)
 unsigned int __invalid_size_argument_for_IOC = 0;
 #endif
 
+/**
+ * disk_fd_matches_criteria() - check if device matches spare criteria.
+ * @st: supertype, not NULL.
+ * @disk_fd: file descriptor of the disk.
+ * @sc: criteria to test.
+ *
+ * Return: true if disk matches criteria, false otherwise.
+ */
+bool disk_fd_matches_criteria(struct supertype *st, int disk_fd, struct spare_criteria *sc)
+{
+       unsigned int dev_sector_size = 0;
+       unsigned long long dev_size = 0;
+
+       if (!sc->criteria_set)
+               return true;
+
+       if (!get_dev_size(disk_fd, NULL, &dev_size) || dev_size < sc->min_size)
+               return false;
+
+       if (!get_dev_sector_size(disk_fd, NULL, &dev_sector_size) ||
+           sc->sector_size != dev_sector_size)
+               return false;
+
+       if (drive_test_and_add_policies(st, &sc->pols, disk_fd, 0))
+               return false;
+
+       return true;
+}
+
+/**
+ * devid_matches_criteria() - check if device referenced by devid matches spare criteria.
+ * @st: supertype, not NULL.
+ * @devid: devid of the device to check.
+ * @sc: criteria to test.
+ *
+ * Return: true if disk matches criteria, false otherwise.
+ */
+bool devid_matches_criteria(struct supertype *st, dev_t devid, struct spare_criteria *sc)
+{
+       char buf[NAME_MAX];
+       bool ret;
+       int fd;
+
+       if (!sc->criteria_set)
+               return true;
+
+       snprintf(buf, NAME_MAX, "%d:%d", major(devid), minor(devid));
+
+       fd = dev_open(buf, O_RDONLY);
+       if (!is_fd_valid(fd))
+               return false;
+
+       /* Error code inherited */
+       ret = disk_fd_matches_criteria(st, fd, sc);
+
+       close(fd);
+       return ret;
+}
+
 /* Pick all spares matching given criteria from a container
  * if min_size == 0 do not check size
  * if domlist == NULL do not check domains
@@ -2061,28 +2148,13 @@ struct mdinfo *container_choose_spares(struct supertype *st,
        dp = &disks->devs;
        disks->array.spare_disks = 0;
        while (*dp) {
-               int found = 0;
+               bool found = false;
+
                d = *dp;
                if (d->disk.state == 0) {
-                       /* check if size is acceptable */
-                       unsigned long long dev_size;
-                       unsigned int dev_sector_size;
-                       int size_valid = 0;
-                       int sector_size_valid = 0;
-
                        dev_t dev = makedev(d->disk.major,d->disk.minor);
 
-                       if (!criteria->min_size ||
-                          (dev_size_from_id(dev,  &dev_size) &&
-                           dev_size >= criteria->min_size))
-                               size_valid = 1;
-
-                       if (!criteria->sector_size ||
-                           (dev_sector_size_from_id(dev, &dev_sector_size) &&
-                            criteria->sector_size == dev_sector_size))
-                               sector_size_valid = 1;
-
-                       found = size_valid && sector_size_valid;
+                       found = devid_matches_criteria(st, dev, criteria);
 
                        /* check if domain matches */
                        if (found && domlist) {
@@ -2091,7 +2163,8 @@ struct mdinfo *container_choose_spares(struct supertype *st,
                                        pol_add(&pol, pol_domain,
                                                spare_group, NULL);
                                if (domain_test(domlist, pol, metadata) != 1)
-                                       found = 0;
+                                       found = false;
+
                                dev_policy_free(pol);
                        }
                }
@@ -2192,7 +2265,7 @@ void manage_fork_fds(int close_all)
  *     1- if systemd service has been started
  *     0- otherwise
  */
-int continue_via_systemd(char *devnm, char *service_name)
+int continue_via_systemd(char *devnm, char *service_name, char *prefix)
 {
        int pid, status;
        char pathbuf[1024];
@@ -2204,7 +2277,7 @@ int continue_via_systemd(char *devnm, char *service_name)
        case  0:
                manage_fork_fds(1);
                snprintf(pathbuf, sizeof(pathbuf),
-                        "%s@%s.service", service_name, devnm);
+                        "%s@%s%s.service", service_name, prefix ?: "", devnm);
                status = execl("/usr/bin/systemctl", "systemctl", "restart",
                               pathbuf, NULL);
                status = execl("/bin/systemctl", "systemctl", "restart",
@@ -2222,15 +2295,7 @@ int continue_via_systemd(char *devnm, char *service_name)
 
 int in_initrd(void)
 {
-       /* This is based on similar function in systemd. */
-       struct statfs s;
-       /* statfs.f_type is signed long on s390x and MIPS, causing all
-          sorts of sign extension problems with RAMFS_MAGIC being
-          defined as 0x858458f6 */
-       return  statfs("/", &s) >= 0 &&
-               ((unsigned long)s.f_type == TMPFS_MAGIC ||
-                ((unsigned long)s.f_type & 0xFFFFFFFFUL) ==
-                ((unsigned long)RAMFS_MAGIC & 0xFFFFFFFFUL));
+       return access("/etc/initrd-release", F_OK) >= 0;
 }
 
 void reopen_mddev(int mdfd)
@@ -2401,3 +2466,48 @@ void sleep_for(unsigned int sec, long nsec, bool wake_after_interrupt)
                }
        } while (!wake_after_interrupt && errno == EINTR);
 }
+
+/* is_directory() - Checks if directory provided by path is indeed a regular directory.
+ * @path: directory path to be checked
+ *
+ * Doesn't accept symlinks.
+ *
+ * Return: true if is a directory, false if not
+ */
+bool is_directory(const char *path)
+{
+       struct stat st;
+
+       if (lstat(path, &st) != 0) {
+               pr_err("%s: %s\n", strerror(errno), path);
+               return false;
+       }
+
+       if (!S_ISDIR(st.st_mode))
+               return false;
+
+       return true;
+}
+
+/*
+ * is_file() - Checks if file provided by path is indeed a regular file.
+ * @path: file path to be checked
+ *
+ * Doesn't accept symlinks.
+ *
+ * Return: true if is  a file, false if not
+ */
+bool is_file(const char *path)
+{
+       struct stat st;
+
+       if (lstat(path, &st) != 0) {
+               pr_err("%s: %s\n", strerror(errno), path);
+               return false;
+       }
+
+       if (!S_ISREG(st.st_mode))
+               return false;
+
+       return true;
+}