]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - util.c
Add mbr pseudo metadata handler.
[thirdparty/mdadm.git] / util.c
diff --git a/util.c b/util.c
index a0e4bcf0aac94e46c9995523ebead1b06a2b7131..c93b0a7ab81b632fa743e66e54e7edd6ad2bcdbe 100644 (file)
--- a/util.c
+++ b/util.c
@@ -65,6 +65,17 @@ struct blkpg_partition {
        char volname[BLKPG_VOLNAMELTH]; /* volume label */
 };
 
+#include "part.h"
+
+/* Force a compilation error if condition is true */
+#define BUILD_BUG_ON(condition) ((void)BUILD_BUG_ON_ZERO(condition))
+
+/* Force a compilation error if condition is true, but also produce a
+   result (of value 0 and type size_t), so the expression can be used
+   e.g. in a structure initializer (or where-ever else comma expressions
+   aren't permitted). */
+#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
+
 /*
  * Parse a 128 bit uuid in 4 integers
  * format is 32 hexx nibbles with options :.<space> separator
@@ -235,6 +246,31 @@ void remove_partitions(int fd)
 #endif
 }
 
+int test_partition(int fd)
+{
+       /* Check if fd is a whole-disk or a partition.
+        * BLKPG will return EINVAL on a partition, and BLKPG_DEL_PARTITION
+        * will return ENXIO on an invalid partition number.
+        */
+       struct blkpg_ioctl_arg a;
+       struct blkpg_partition p;
+       a.op = BLKPG_DEL_PARTITION;
+       a.data = (void*)&p;
+       a.datalen = sizeof(p);
+       a.flags = 0;
+       memset(a.data, 0, a.datalen);
+       p.pno = 1<<30;
+       if (ioctl(fd, BLKPG, &a) == 0)
+               /* Very unlikely, but not a partition */
+               return 0;
+       if (errno == ENXIO)
+               /* not a partition */
+               return 0;
+
+       return 1;
+}
+
+
 int enough(int level, int raid_disks, int layout, int clean,
           char *avail, int avail_disks)
 {
@@ -358,7 +394,12 @@ 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)
 {
-       return __fname_from_uuid(info->uuid, st->ss->swapuuid, buf, sep);
+       // 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
+       return __fname_from_uuid(info->uuid, (st->ss == &super1) ? 1 : st->ss->swapuuid, buf, sep);
 }
 
 #ifndef MDASSEMBLE
@@ -862,19 +903,33 @@ int dev_open(char *dev, int flags)
        int minor;
 
        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) {
-               snprintf(devname, sizeof(devname), "/dev/.tmp.md.%d:%d:%d",
-                        (int)getpid(), major, minor);
-               if (mknod(devname, S_IFBLK|0600, makedev(major, minor))==0) {
-                       fd = open(devname, flags|O_DIRECT);
-                       unlink(devname);
+               char *path = map_dev(major, minor, 0);
+               if (path)
+                       fd = open(path, flags);
+               if (fd < 0) {
+                       snprintf(devname, sizeof(devname), "/dev/.tmp.md.%d:%d:%d",
+                                (int)getpid(), major, minor);
+                       if (mknod(devname, S_IFBLK|0600, makedev(major, minor))==0) {
+                               fd = open(devname, flags);
+                               unlink(devname);
+                       }
+               }
+               if (fd < 0) {
+                       snprintf(devname, sizeof(devname), "/tmp/.tmp.md.%d:%d:%d",
+                                (int)getpid(), major, minor);
+                       if (mknod(devname, S_IFBLK|0600, makedev(major, minor))==0) {
+                               fd = open(devname, flags);
+                               unlink(devname);
+                       }
                }
        } else
-               fd = open(dev, flags|O_DIRECT);
+               fd = open(dev, flags);
        return fd;
 }
 
@@ -938,7 +993,12 @@ void wait_for(char *dev, int fd)
                dprintf("%s: timeout waiting for %s\n", __func__, dev);
 }
 
-struct superswitch *superlist[] = { &super0, &super1, &super_ddf, &super_imsm, NULL };
+struct superswitch *superlist[] =
+{
+       &super0, &super1,
+       &super_ddf, &super_imsm,
+       &mbr,
+       NULL };
 
 #if !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO)
 
@@ -1034,7 +1094,7 @@ struct supertype *guess_super(int fd)
         */
        struct superswitch  *ss;
        struct supertype *st;
-       unsigned long besttime = 0;
+       time_t besttime = 0;
        int bestsuper = -1;
        int i;
 
@@ -1096,6 +1156,146 @@ int get_dev_size(int fd, char *dname, unsigned long long *sizep)
        return 1;
 }
 
+
+/* Sets endofpart parameter to the last block used by the last GPT partition on the device.
+ * Returns: 1 if successful
+ *         -1 for unknown partition type
+ *          0 for other errors
+ */
+static int get_gpt_last_partition_end(int fd, unsigned long long *endofpart)
+{
+       struct GPT gpt;
+       unsigned char buf[512];
+       unsigned char empty_gpt_entry[16]= {0};
+       struct GPT_part_entry *part;
+       unsigned long long curr_part_end;
+       unsigned all_partitions, entry_size;
+       unsigned part_nr;
+
+       *endofpart = 0;
+
+       BUILD_BUG_ON(sizeof(gpt) != 512);
+       /* read GPT header */
+       lseek(fd, 512, SEEK_SET);
+       if (read(fd, &gpt, 512) != 512)
+               return 0;
+
+       /* get the number of partition entries and the entry size */
+       all_partitions = __le32_to_cpu(gpt.part_cnt);
+       entry_size = __le32_to_cpu(gpt.part_size);
+
+       /* Check GPT signature*/
+       if (gpt.magic != GPT_SIGNATURE_MAGIC)
+               return -1;
+
+       /* sanity checks */
+       if (all_partitions > 1024 ||
+           entry_size > 512)
+               return -1;
+
+       /* read first GPT partition entries */
+       if (read(fd, buf, 512) != 512)
+               return 0;
+
+       part = (struct GPT_part_entry*)buf;
+
+       for (part_nr=0; part_nr < all_partitions; part_nr++) {
+               /* is this valid partition? */
+               if (memcmp(part->type_guid, empty_gpt_entry, 16) != 0) {
+                       /* check the last lba for the current partition */
+                       curr_part_end = __le64_to_cpu(part->ending_lba);
+                       if (curr_part_end > *endofpart)
+                               *endofpart = curr_part_end;
+               }
+
+               part = (struct GPT_part_entry*)((unsigned char*)part + entry_size);
+
+               if ((unsigned char *)part >= buf + 512) {
+                       if (read(fd, buf, 512) != 512)
+                               return 0;
+                       part = (struct GPT_part_entry*)buf;
+               }
+       }
+       return 1;
+}
+
+/* Sets endofpart parameter to the last block used by the last partition on the device.
+ * Returns: 1 if successful
+ *         -1 for unknown partition type
+ *          0 for other errors
+ */
+static int get_last_partition_end(int fd, unsigned long long *endofpart)
+{
+       struct MBR boot_sect;
+       struct MBR_part_record *part;
+       unsigned long long curr_part_end;
+       unsigned part_nr;
+       int retval = 0;
+
+       *endofpart = 0;
+
+       BUILD_BUG_ON(sizeof(boot_sect) != 512);
+       /* read MBR */
+       lseek(fd, 0, 0);
+       if (read(fd, &boot_sect, 512) != 512)
+               goto abort;
+
+       /* check MBP signature */
+       if (boot_sect.magic == MBR_SIGNATURE_MAGIC) {
+               retval = 1;
+               /* found the correct signature */
+               part = boot_sect.parts;
+
+               for (part_nr=0; part_nr < MBR_PARTITIONS; part_nr++) {
+                       /* check for GPT type */
+                       if (part->part_type == MBR_GPT_PARTITION_TYPE) {
+                               retval = get_gpt_last_partition_end(fd, endofpart);
+                               break;
+                       }
+                       /* check the last used lba for the current partition  */
+                       curr_part_end = __le32_to_cpu(part->first_sect_lba) +
+                               __le32_to_cpu(part->blocks_num);
+                       if (curr_part_end > *endofpart)
+                               *endofpart = curr_part_end;
+
+                       part++;
+               }
+       } else {
+               /* Unknown partition table */
+               retval = -1;
+       }
+ abort:
+       return retval;
+}
+
+int check_partitions(int fd, char *dname, unsigned long long freesize)
+{
+       /*
+        * Check where the last partition ends
+        */
+       unsigned long long endofpart;
+       int ret;
+
+       if ((ret = get_last_partition_end(fd, &endofpart)) > 0) {
+               /* There appears to be a partition table here */
+               if (freesize == 0) {
+                       /* partitions will not be visible in new device */
+                       fprintf(stderr,
+                               Name ": partition table exists on %s but will be lost or\n"
+                               "       meaningless after creating array\n",
+                               dname);
+                       return 1;
+               } else if (endofpart > freesize) {
+                       /* last partition overlaps metadata */
+                       fprintf(stderr,
+                               Name ": metadata will over-write last partition on %s.\n",
+                               dname);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
 void get_one_disk(int mdfd, mdu_array_info_t *ainf, mdu_disk_info_t *disk)
 {
        int d;
@@ -1139,7 +1339,7 @@ int open_container(int fd)
                        continue;
                n = read(dfd, buf, sizeof(buf));
                close(dfd);
-               if (n <= 0 || n >= sizeof(buf))
+               if (n <= 0 || (unsigned)n >= sizeof(buf))
                        continue;
                buf[n] = 0;
                if (sscanf(buf, "%d:%d", &major, &minor) != 2)
@@ -1155,6 +1355,148 @@ int open_container(int fd)
        return -1;
 }
 
+struct superswitch *version_to_superswitch(char *vers)
+{
+       int i;
+
+       for (i = 0; superlist[i]; i++) {
+               struct superswitch *ss = superlist[i];
+
+               if (strcmp(vers, ss->name) == 0)
+                       return ss;
+       }
+
+       return NULL;
+}
+
+int is_container_member(struct mdstat_ent *mdstat, char *container)
+{
+       if (mdstat->metadata_version == NULL ||
+           strncmp(mdstat->metadata_version, "external:", 9) != 0 ||
+           !is_subarray(mdstat->metadata_version+9) ||
+           strncmp(mdstat->metadata_version+10, container, strlen(container)) != 0 ||
+           mdstat->metadata_version[10+strlen(container)] != '/')
+               return 0;
+
+       return 1;
+}
+
+int is_subarray_active(char *subarray, char *container)
+{
+       struct mdstat_ent *mdstat = mdstat_read(0, 0);
+       struct mdstat_ent *ent;
+
+       for (ent = mdstat; ent; ent = ent->next) {
+               if (is_container_member(ent, container)) {
+                       char *inst = &ent->metadata_version[10+strlen(container)+1];
+
+                       if (!subarray || strcmp(inst, subarray) == 0)
+                               break;
+               }
+       }
+
+       free_mdstat(mdstat);
+
+       return ent != NULL;
+}
+
+int is_container_active(char *container)
+{
+       return is_subarray_active(NULL, container);
+}
+
+/* open_subarray - opens a subarray in a container
+ * @dev: container device name
+ * @st: supertype with only ->subarray set
+ * @quiet: block reporting errors flag
+ *
+ * On success returns an fd to a container and fills in *st
+ */
+int open_subarray(char *dev, struct supertype *st, int quiet)
+{
+       struct mdinfo *mdi;
+       int fd, err = 1;
+
+       fd = open(dev, O_RDWR|O_EXCL);
+       if (fd < 0) {
+               if (!quiet)
+                       fprintf(stderr, Name ": Couldn't open %s, aborting\n",
+                               dev);
+               return 2;
+       }
+
+       st->devnum = fd2devnum(fd);
+       if (st->devnum == NoMdDev) {
+               if (!quiet)
+                       fprintf(stderr,
+                               Name ": Failed to determine device number for %s\n",
+                               dev);
+               goto close_fd;
+       }
+
+       mdi = sysfs_read(fd, st->devnum, GET_VERSION|GET_LEVEL);
+       if (!mdi) {
+               if (!quiet)
+                       fprintf(stderr, Name ": Failed to read sysfs for %s\n",
+                               dev);
+               goto close_fd;
+       }
+
+       if (mdi->array.level != UnSet) {
+               if (!quiet)
+                       fprintf(stderr, Name ": %s is not a container\n", dev);
+               goto free_sysfs;
+       }
+
+       st->ss = version_to_superswitch(mdi->text_version);
+       if (!st->ss) {
+               if (!quiet)
+                       fprintf(stderr,
+                               Name ": Operation not supported for %s metadata\n",
+                               mdi->text_version);
+               goto free_sysfs;
+       }
+
+       st->devname = devnum2devname(st->devnum);
+       if (!st->devname) {
+               if (!quiet)
+                       fprintf(stderr, Name ": Failed to allocate device name\n");
+               goto free_sysfs;
+       }
+
+       if (st->ss->load_super(st, fd, NULL)) {
+               if (!quiet)
+                       fprintf(stderr, Name ": Failed to find subarray-%s in %s\n",
+                               st->subarray, dev);
+               goto free_name;
+       }
+
+       if (!st->loaded_container) {
+               if (!quiet)
+                       fprintf(stderr, Name ": %s is not a container\n", dev);
+               goto free_super;
+       }
+
+       err = 0;
+
+ free_super:
+       if (err)
+               st->ss->free_super(st);
+ free_name:
+       if (err)
+               free(st->devname);
+ free_sysfs:
+       sysfs_free(mdi);
+ close_fd:
+       if (err)
+               close(fd);
+
+       if (err)
+               return -1;
+       else
+               return fd;
+}
+
 int add_disk(int mdfd, struct supertype *st,
             struct mdinfo *sra, struct mdinfo *info)
 {
@@ -1162,8 +1504,11 @@ int add_disk(int mdfd, struct supertype *st,
        int rv;
 #ifndef MDASSEMBLE
        if (st->ss->external) {
-               rv = sysfs_add_disk(sra, info,
-                                   info->disk.state & (1<<MD_DISK_SYNC));
+               if (info->disk.state & (1<<MD_DISK_SYNC))
+                       info->recovery_start = MaxSector;
+               else
+                       info->recovery_start = 0;
+               rv = sysfs_add_disk(sra, info, 0);
                if (! rv) {
                        struct mdinfo *sd2;
                        for (sd2 = sra->devs; sd2; sd2=sd2->next)
@@ -1207,10 +1552,25 @@ int set_array_info(int mdfd, struct supertype *st, struct mdinfo *info)
        return rv;
 }
 
+unsigned long long min_recovery_start(struct mdinfo *array)
+{
+       /* find the minimum recovery_start in an array for metadata
+        * formats that only record per-array recovery progress instead
+        * of per-device
+        */
+       unsigned long long recovery_start = MaxSector;
+       struct mdinfo *d;
+
+       for (d = array->devs; d; d = d->next)
+               recovery_start = min(recovery_start, d->recovery_start);
+
+       return recovery_start;
+}
+
 char *devnum2devname(int num)
 {
        char name[100];
-       if (num > 0)
+       if (num >= 0)
                sprintf(name, "md%d", num);
        else
                sprintf(name, "md_d%d", -1-num);
@@ -1238,7 +1598,7 @@ int stat2devnum(struct stat *st)
        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) == get_mdp_major())
+               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
@@ -1269,42 +1629,34 @@ int fd2devnum(int fd)
        return NoMdDev;
 }
 
-int mdmon_running(int devnum)
+int mdmon_pid(int devnum)
 {
        char path[100];
        char pid[10];
        int fd;
        int n;
-       sprintf(path, "/var/run/mdadm/%s.pid", devnum2devname(devnum));
-       fd = open(path, O_RDONLY, 0);
+       char *devname = devnum2devname(devnum);
+
+       sprintf(path, "%s/%s.pid", MDMON_DIR, devname);
+       free(devname);
+
+       fd = open(path, O_RDONLY | O_NOATIME, 0);
 
        if (fd < 0)
-               return 0;
+               return -1;
        n = read(fd, pid, 9);
        close(fd);
        if (n <= 0)
-               return 0;
-       if (kill(atoi(pid), 0) == 0)
-               return 1;
-       return 0;
+               return -1;
+       return atoi(pid);
 }
 
-int signal_mdmon(int devnum)
+int mdmon_running(int devnum)
 {
-       char path[100];
-       char pid[10];
-       int fd;
-       int n;
-       sprintf(path, "/var/run/mdadm/%s.pid", devnum2devname(devnum));
-       fd = open(path, O_RDONLY, 0);
-
-       if (fd < 0)
-               return 0;
-       n = read(fd, pid, 9);
-       close(fd);
-       if (n <= 0)
+       int pid = mdmon_pid(devnum);
+       if (pid <= 0)
                return 0;
-       if (kill(atoi(pid), SIGUSR1) == 0)
+       if (kill(pid, 0) == 0)
                return 1;
        return 0;
 }