]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - util.c
Fix RAID metadata check
[thirdparty/mdadm.git] / util.c
diff --git a/util.c b/util.c
index 7937eb6efde2aae25bd1092f6d5d95422229c868..a238a2107b968443ad530506340cfed7e21f8ef3 100644 (file)
--- a/util.c
+++ b/util.c
 #include       <sys/resource.h>
 #include       <sys/vfs.h>
 #include       <linux/magic.h>
+#include       <poll.h>
 #include       <ctype.h>
 #include       <dirent.h>
 #include       <signal.h>
+#include       <dlfcn.h>
+
 
 /*
  * following taken from linux/blkpg.h because they aren't
@@ -79,6 +82,135 @@ struct blkpg_partition {
    aren't permitted). */
 #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
 
+static int is_dlm_hooks_ready = 0;
+
+int dlm_funs_ready(void)
+{
+       return is_dlm_hooks_ready ? 1 : 0;
+}
+
+#ifndef MDASSEMBLE
+static struct dlm_hooks *dlm_hooks = NULL;
+struct dlm_lock_resource *dlm_lock_res = NULL;
+static int ast_called = 0;
+
+struct dlm_lock_resource {
+       dlm_lshandle_t *ls;
+       struct dlm_lksb lksb;
+};
+
+/* Using poll(2) to wait for and dispatch ASTs */
+static int poll_for_ast(dlm_lshandle_t ls)
+{
+       struct pollfd pfd;
+
+       pfd.fd = dlm_hooks->ls_get_fd(ls);
+       pfd.events = POLLIN;
+
+       while (!ast_called)
+       {
+               if (poll(&pfd, 1, 0) < 0)
+               {
+                       perror("poll");
+                       return -1;
+               }
+               dlm_hooks->dispatch(dlm_hooks->ls_get_fd(ls));
+       }
+       ast_called = 0;
+
+       return 0;
+}
+
+static void dlm_ast(void *arg)
+{
+       ast_called = 1;
+}
+
+static char *cluster_name = NULL;
+/* Create the lockspace, take bitmapXXX locks on all the bitmaps. */
+int cluster_get_dlmlock(int *lockid)
+{
+       int ret = -1;
+       char str[64];
+       int flags = LKF_NOQUEUE;
+
+       ret = get_cluster_name(&cluster_name);
+       if (ret) {
+               pr_err("The md can't get cluster name\n");
+               return -1;
+       }
+
+       dlm_lock_res = xmalloc(sizeof(struct dlm_lock_resource));
+       dlm_lock_res->ls = dlm_hooks->create_lockspace(cluster_name, O_RDWR);
+       if (!dlm_lock_res->ls) {
+               pr_err("%s failed to create lockspace\n", cluster_name);
+               return -ENOMEM;
+       }
+
+       snprintf(str, 64, "bitmap%s", cluster_name);
+       ret = dlm_hooks->ls_lock(dlm_lock_res->ls, LKM_PWMODE, &dlm_lock_res->lksb,
+                         flags, str, strlen(str), 0, dlm_ast,
+                         dlm_lock_res, NULL, NULL);
+       if (ret) {
+               pr_err("error %d when get PW mode on lock %s\n", errno, str);
+               dlm_hooks->release_lockspace(cluster_name, dlm_lock_res->ls, 1);
+               return ret;
+       }
+
+       /* Wait for it to complete */
+       poll_for_ast(dlm_lock_res->ls);
+       *lockid = dlm_lock_res->lksb.sb_lkid;
+
+       return dlm_lock_res->lksb.sb_status;
+}
+
+int cluster_release_dlmlock(int lockid)
+{
+       int ret = -1;
+
+       if (!cluster_name)
+               return -1;
+
+       ret = dlm_hooks->ls_unlock(dlm_lock_res->ls, lockid, 0,
+                                    &dlm_lock_res->lksb, dlm_lock_res);
+       if (ret) {
+               pr_err("error %d happened when unlock\n", errno);
+               /* XXX make sure the lock is unlocked eventually */
+                goto out;
+       }
+
+       /* Wait for it to complete */
+       poll_for_ast(dlm_lock_res->ls);
+
+       errno = dlm_lock_res->lksb.sb_status;
+       if (errno != EUNLOCK) {
+               pr_err("error %d happened in ast when unlock lockspace\n", errno);
+               /* XXX make sure the lockspace is unlocked eventually */
+                goto out;
+       }
+
+       ret = dlm_hooks->release_lockspace(cluster_name, dlm_lock_res->ls, 1);
+       if (ret) {
+               pr_err("error %d happened when release lockspace\n", errno);
+               /* XXX make sure the lockspace is released eventually */
+                goto out;
+       }
+       free(dlm_lock_res);
+
+out:
+       return ret;
+}
+#else
+int cluster_get_dlmlock(int *lockid)
+{
+       return -1;
+}
+int cluster_release_dlmlock(int lockid)
+{
+       return -1;
+}
+#endif
+
 /*
  * Parse a 128 bit uuid in 4 integers
  * format is 32 hexx nibbles with options :.<space> separator
@@ -271,6 +403,16 @@ long parse_num(char *num)
 }
 #endif
 
+int parse_cluster_confirm_arg(char *input, char **devname, int *slot)
+{
+       char *dev;
+       *slot = strtoul(input, &dev, 10);
+       if (dev == input || dev[0] != ':')
+               return -1;
+       *devname = dev+1;
+       return 0;
+}
+
 void remove_partitions(int fd)
 {
        /* remove partitions from this block devices.
@@ -368,6 +510,13 @@ int enough(int level, int raid_disks, int layout, int clean, char *avail)
        case 1:
                return avail_disks >= 1;
        case 4:
+               if (avail_disks == raid_disks - 1 &&
+                   !avail[raid_disks - 1])
+                       /* If just the parity device is missing, then we
+                        * have enough, even if not clean
+                        */
+                       return 1;
+               /* FALL THROUGH */
        case 5:
                if (clean)
                        return avail_disks >= raid_disks-1;
@@ -561,17 +710,22 @@ int check_raid(int fd, char *name)
 
        if (!st)
                return 0;
-       st->ss->load_super(st, fd, name);
-       /* Looks like a raid array .. */
-       pr_err("%s appears to be part of a raid array:\n",
-               name);
-       st->ss->getinfo_super(st, &info, NULL);
-       st->ss->free_super(st);
-       crtime = info.array.ctime;
-       level = map_num(pers, info.array.level);
-       if (!level) level = "-unknown-";
-       cont_err("level=%s devices=%d ctime=%s",
-                level, info.array.raid_disks, ctime(&crtime));
+       if (st->ss->add_to_super != NULL) {
+               st->ss->load_super(st, fd, name);
+               /* Looks like a raid array .. */
+               pr_err("%s appears to be part of a raid array:\n", name);
+               st->ss->getinfo_super(st, &info, NULL);
+               st->ss->free_super(st);
+               crtime = info.array.ctime;
+               level = map_num(pers, info.array.level);
+               if (!level)
+                       level = "-unknown-";
+               cont_err("level=%s devices=%d ctime=%s",
+                       level, info.array.raid_disks, ctime(&crtime));
+       } else {
+               /* Looks like GPT or MBR */
+               pr_err("partition table exists on %s\n", name);
+       }
        return 1;
 }
 
@@ -671,13 +825,13 @@ char *human_size(long long bytes)
        if (bytes < 5000*1024)
                buf[0] = 0;
        else if (bytes < 2*1024LL*1024LL*1024LL) {
-               long cMiB = (bytes / ( (1LL<<20) / 200LL ) +1) /2;
+               long cMiB = (bytes * 200LL / (1LL<<20) + 1) / 2;
                long cMB  = (bytes / ( 1000000LL / 200LL ) +1) /2;
                snprintf(buf, sizeof(buf), " (%ld.%02ld MiB %ld.%02ld MB)",
                        cMiB/100 , cMiB % 100,
                        cMB/100, cMB % 100);
        } else {
-               long cGiB = (bytes / ( (1LL<<30) / 200LL ) +1) /2;
+               long cGiB = (bytes * 200LL / (1LL<<30) +1) / 2;
                long cGB  = (bytes / (1000000000LL/200LL ) +1) /2;
                snprintf(buf, sizeof(buf), " (%ld.%02ld GiB %ld.%02ld GB)",
                        cGiB/100 , cGiB % 100,
@@ -706,11 +860,11 @@ char *human_size_brief(long long bytes, int prefix)
                buf[0] = 0;
        else if (prefix == IEC) {
                if (bytes < 2*1024LL*1024LL*1024LL) {
-                       long cMiB = (bytes / ( (1LL<<20) / 200LL ) +1) /2;
+                       long cMiB = (bytes * 200LL / (1LL<<20) +1) /2;
                        snprintf(buf, sizeof(buf), "%ld.%02ldMiB",
                                cMiB/100 , cMiB % 100);
                } else {
-                       long cGiB = (bytes / ( (1LL<<30) / 200LL ) +1) /2;
+                       long cGiB = (bytes * 200LL / (1LL<<30) +1) /2;
                        snprintf(buf, sizeof(buf), "%ld.%02ldGiB",
                                        cGiB/100 , cGiB % 100);
                }
@@ -779,7 +933,7 @@ int get_data_disks(int level, int layout, int raid_disks)
        return data_disks;
 }
 
-int devnm2devid(char *devnm)
+dev_t devnm2devid(char *devnm)
 {
        /* First look in /sys/block/$DEVNM/dev for %d:%d
         * If that fails, try parsing out a number
@@ -871,24 +1025,30 @@ void put_md_name(char *name)
 }
 #endif /* !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO) */
 
+int get_maj_min(char *dev, int *major, int *minor)
+{
+       char *e;
+       *major = strtoul(dev, &e, 0);
+       return (e > dev && *e == ':' && e[1] &&
+               (*minor = strtoul(e+1, &e, 0)) >= 0 &&
+               *e == 0);
+}
+
 int dev_open(char *dev, int flags)
 {
        /* like 'open', but if 'dev' matches %d:%d, create a temp
         * block device and open that
         */
-       char *e;
        int fd = -1;
        char devname[32];
        int major;
        int minor;
 
-       if (!dev) return -1;
+       if (!dev)
+               return -1;
        flags |= O_DIRECT;
 
-       major = strtoul(dev, &e, 0);
-       if (e > dev && *e == ':' && e[1] &&
-           (minor = strtoul(e+1, &e, 0)) >= 0 &&
-           *e == 0) {
+       if (get_maj_min(dev, &major, &minor)) {
                snprintf(devname, sizeof(devname), "/dev/.tmp.md.%d:%d:%d",
                         (int)getpid(), major, minor);
                if (mknod(devname, S_IFBLK|0600, makedev(major, minor)) == 0) {
@@ -911,7 +1071,7 @@ int dev_open(char *dev, int flags)
 
 int open_dev_flags(char *devnm, int flags)
 {
-       int devid;
+       dev_t devid;
        char buf[20];
 
        devid = devnm2devid(devnm);
@@ -929,7 +1089,7 @@ int open_dev_excl(char *devnm)
        char buf[20];
        int i;
        int flags = O_RDWR;
-       int devid = devnm2devid(devnm);
+       dev_t devid = devnm2devid(devnm);
        long delay = 1000;
 
        sprintf(buf, "%d:%d", major(devid), minor(devid));
@@ -985,7 +1145,7 @@ void wait_for(char *dev, int fd)
                        delay *= 2;
        }
        if (i == 25)
-               dprintf("%s: timeout waiting for %s\n", __func__, dev);
+               dprintf("timeout waiting for %s\n", dev);
 }
 
 struct superswitch *superlist[] =
@@ -1037,8 +1197,7 @@ struct supertype *super_by_fd(int fd, char **subarrayp)
                        subarray = xstrdup(subarray);
                }
                strcpy(container, dev);
-               if (sra)
-                       sysfs_free(sra);
+               sysfs_free(sra);
                sra = sysfs_read(-1, container, GET_VERSION);
                if (sra && sra->text_version[0])
                        verstr = sra->text_version;
@@ -1049,8 +1208,7 @@ struct supertype *super_by_fd(int fd, char **subarrayp)
        for (i = 0; st == NULL && superlist[i] ; i++)
                st = superlist[i]->match_metadata_desc(verstr);
 
-       if (sra)
-               sysfs_free(sra);
+       sysfs_free(sra);
        if (st) {
                st->sb = NULL;
                if (subarrayp)
@@ -1105,7 +1263,7 @@ struct supertype *guess_super_type(int fd, enum guess_types guess_type)
         */
        struct superswitch  *ss;
        struct supertype *st;
-       time_t besttime = 0;
+       unsigned int besttime = 0;
        int bestsuper = -1;
        int i;
 
@@ -1735,8 +1893,7 @@ int start_mdmon(char *devnm)
                        status = execl("/bin/systemctl", "systemctl", "start",
                                       pathbuf, NULL);
                        exit(1);
-               case -1: pr_err("cannot run mdmon. "
-                               "Array remains readonly\n");
+               case -1: pr_err("cannot run mdmon. Array remains readonly\n");
                        return -1;
                default: /* parent - good */
                        pid = wait(&status);
@@ -1761,14 +1918,12 @@ int start_mdmon(char *devnm)
                                      devnm, NULL);
                        }
                exit(1);
-       case -1: pr_err("cannot run mdmon. "
-                        "Array remains readonly\n");
+       case -1: pr_err("cannot run mdmon. Array remains readonly\n");
                return -1;
        default: /* parent - good */
                pid = wait(&status);
                if (pid < 0 || status != 0) {
-                       pr_err("failed to launch mdmon. "
-                              "Array remains readonly\n");
+                       pr_err("failed to launch mdmon. Array remains readonly\n");
                        return -1;
                }
        }
@@ -1786,6 +1941,27 @@ __u32 random32(void)
        return rv;
 }
 
+void random_uuid(__u8 *buf)
+{
+       int fd, i, len;
+       __u32 r[4];
+
+       fd = open("/dev/urandom", O_RDONLY);
+       if (fd < 0)
+               goto use_random;
+       len = read(fd, buf, 16);
+       close(fd);
+       if (len != 16)
+               goto use_random;
+
+       return;
+
+use_random:
+       for (i = 0; i < 4; i++)
+               r[i] = random();
+       memcpy(buf, r, 16);
+}
+
 #ifndef MDASSEMBLE
 int flush_metadata_updates(struct supertype *st)
 {
@@ -1840,8 +2016,7 @@ int experimental(void)
        if (check_env("MDADM_EXPERIMENTAL"))
                return 1;
        else {
-               pr_err("To use this feature MDADM_EXPERIMENTAL"
-                               " environment variable has to be defined.\n");
+               pr_err("To use this feature MDADM_EXPERIMENTAL environment variable has to be defined.\n");
                return 0;
        }
 }
@@ -1968,3 +2143,80 @@ void reopen_mddev(int mdfd)
        if (fd >= 0 && fd != mdfd)
                dup2(fd, mdfd);
 }
+
+#ifndef MDASSEMBLE
+static struct cmap_hooks *cmap_hooks = NULL;
+static int is_cmap_hooks_ready = 0;
+
+void set_cmap_hooks(void)
+{
+       cmap_hooks = xmalloc(sizeof(struct cmap_hooks));
+       cmap_hooks->cmap_handle = dlopen("libcmap.so.4", RTLD_NOW | RTLD_LOCAL);
+       if (!cmap_hooks->cmap_handle)
+               return;
+
+       cmap_hooks->initialize = dlsym(cmap_hooks->cmap_handle, "cmap_initialize");
+       cmap_hooks->get_string = dlsym(cmap_hooks->cmap_handle, "cmap_get_string");
+       cmap_hooks->finalize = dlsym(cmap_hooks->cmap_handle, "cmap_finalize");
+
+       if (!cmap_hooks->initialize || !cmap_hooks->get_string ||
+           !cmap_hooks->finalize)
+               dlclose(cmap_hooks->cmap_handle);
+       else
+               is_cmap_hooks_ready = 1;
+}
+
+int get_cluster_name(char **cluster_name)
+{
+        int rv = -1;
+       cmap_handle_t handle;
+
+       if (!is_cmap_hooks_ready)
+               return rv;
+
+        rv = cmap_hooks->initialize(&handle);
+        if (rv != CS_OK)
+                goto out;
+
+        rv = cmap_hooks->get_string(handle, "totem.cluster_name", cluster_name);
+        if (rv != CS_OK) {
+                free(*cluster_name);
+                rv = -1;
+                goto name_err;
+        }
+
+        rv = 0;
+name_err:
+        cmap_hooks->finalize(handle);
+out:
+        return rv;
+}
+
+void set_dlm_hooks(void)
+{
+       dlm_hooks = xmalloc(sizeof(struct dlm_hooks));
+       dlm_hooks->dlm_handle = dlopen("libdlm_lt.so.3", RTLD_NOW | RTLD_LOCAL);
+       if (!dlm_hooks->dlm_handle)
+               return;
+
+       dlm_hooks->create_lockspace = dlsym(dlm_hooks->dlm_handle, "dlm_create_lockspace");
+       dlm_hooks->release_lockspace = dlsym(dlm_hooks->dlm_handle, "dlm_release_lockspace");
+       dlm_hooks->ls_lock = dlsym(dlm_hooks->dlm_handle, "dlm_ls_lock");
+       dlm_hooks->ls_unlock = dlsym(dlm_hooks->dlm_handle, "dlm_ls_unlock");
+       dlm_hooks->ls_get_fd = dlsym(dlm_hooks->dlm_handle, "dlm_ls_get_fd");
+       dlm_hooks->dispatch = dlsym(dlm_hooks->dlm_handle, "dlm_dispatch");
+
+       if (!dlm_hooks->create_lockspace || !dlm_hooks->ls_lock ||
+           !dlm_hooks->ls_unlock || !dlm_hooks->release_lockspace ||
+           !dlm_hooks->ls_get_fd || !dlm_hooks->dispatch)
+               dlclose(dlm_hooks->dlm_handle);
+       else
+               is_dlm_hooks_ready = 1;
+}
+
+void set_hooks(void)
+{
+       set_dlm_hooks();
+       set_cmap_hooks();
+}
+#endif