X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fmdadm.git;a=blobdiff_plain;f=util.c;h=2a206448e4e2b07586d4baa7968941ef108afb86;hp=68f048d06f3a24584b17a48516f8e0b96e2c655a;hb=ff044d6ba707c22ab33c508640dd14ab537c1c16;hpb=5d4d1b26d3da8b489ed7e2f5a38f7d90b7f4008f diff --git a/util.c b/util.c index 68f048d0..2a206448 100644 --- a/util.c +++ b/util.c @@ -65,42 +65,16 @@ struct blkpg_partition { char volname[BLKPG_VOLNAMELTH]; /* volume label */ }; -/* partition table structures so we can check metadata position - * against the end of the last partition. - * Only handle MBR ant GPT partition tables. - */ -struct MBR_part_record { - __u8 bootable; - __u8 first_head; - __u8 first_sector; - __u8 first_cyl; - __u8 part_type; - __u8 last_head; - __u8 last_sector; - __u8 last_cyl; - __u32 first_sect_lba; - __u32 blocks_num; -}; +#include "part.h" -struct GPT_part_entry { - unsigned char type_guid[16]; - unsigned char partition_guid[16]; - unsigned char starting_lba[8]; - unsigned char ending_lba[8]; - unsigned char attr_bits[8]; - unsigned char name[72]; -}; +/* Force a compilation error if condition is true */ +#define BUILD_BUG_ON(condition) ((void)BUILD_BUG_ON_ZERO(condition)) -/* MBR/GPT magic numbers */ -#define MBR_SIGNATURE_MAGIC __cpu_to_le16(0xAA55) -#define GPT_SIGNATURE_MAGIC __cpu_to_le64(0x5452415020494645ULL) - -#define MBR_SIGNATURE_OFFSET 510 -#define MBR_PARTITION_TABLE_OFFSET 446 -#define MBR_PARTITIONS 4 -#define MBR_GPT_PARTITION_TYPE 0xEE -#define GPT_ALL_PARTITIONS_OFFSET 80 -#define GPT_ENTRY_SIZE_OFFSET 84 +/* 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 @@ -186,6 +160,31 @@ int get_linux_version() return (a*1000000)+(b*1000)+c; } +int mdadm_version(char *version) +{ + int a, b, c; + char *cp; + + if (!version) + version = Version; + + cp = strchr(version, '-'); + if (!cp || *(cp+1) != ' ' || *(cp+2) != 'v') + return -1; + cp += 3; + a = strtoul(cp, &cp, 10); + if (*cp != '.') + return -1; + b = strtoul(cp+1, &cp, 10); + if (*cp == '.') + c = strtoul(cp+1, &cp, 10); + else + c = 0; + if (*cp != ' ' && *cp != '-') + return -1; + return (a*1000000)+(b*1000)+c; +} + #ifndef MDASSEMBLE long long parse_size(char *size) { @@ -272,6 +271,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) { @@ -321,6 +345,36 @@ int enough(int level, int raid_disks, int layout, int clean, } } +int enough_fd(int fd) +{ + struct mdu_array_info_s array; + struct mdu_disk_info_s disk; + int avail_disks = 0; + int i; + char *avail; + + if (ioctl(fd, GET_ARRAY_INFO, &array) != 0 || + array.raid_disks <= 0) + return 0; + avail = calloc(array.raid_disks, 1); + for (i=0; i= array.raid_disks) + continue; + avail_disks++; + avail[disk.raid_disk] = 1; + } + /* This is used on an active array, so assume it is clean */ + return enough(array.level, array.raid_disks, array.layout, + 1, + avail, avail_disks); +} + + const int uuid_match_any[4] = { ~0, ~0, ~0, ~0 }; int same_uuid(int a[4], int b[4], int swapuuid) { @@ -395,7 +449,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 @@ -466,7 +525,7 @@ int check_raid(int fd, char *name) /* Looks like a raid array .. */ fprintf(stderr, Name ": %s appears to be part of a raid array:\n", name); - st->ss->getinfo_super(st, &info); + st->ss->getinfo_super(st, &info, NULL); st->ss->free_super(st); crtime = info.array.ctime; level = map_num(pers, info.array.level); @@ -899,19 +958,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; } @@ -975,11 +1048,16 @@ 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, &gpt, + NULL }; #if !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO) -struct supertype *super_by_fd(int fd) +struct supertype *super_by_fd(int fd, char **subarrayp) { mdu_array_info_t array; int vers; @@ -990,6 +1068,7 @@ struct supertype *super_by_fd(int fd) char version[20]; int i; char *subarray = NULL; + int container = NoMdDev; sra = sysfs_read(fd, 0, GET_VERSION); @@ -1011,15 +1090,15 @@ struct supertype *super_by_fd(int fd) } if (minor == -2 && is_subarray(verstr)) { char *dev = verstr+1; + subarray = strchr(dev, '/'); - int devnum; if (subarray) *subarray++ = '\0'; - devnum = devname2devnum(dev); subarray = strdup(subarray); + container = devname2devnum(dev); if (sra) sysfs_free(sra); - sra = sysfs_read(-1, devnum, GET_VERSION); + sra = sysfs_read(-1, container, GET_VERSION); if (sra && sra->text_version[0]) verstr = sra->text_version; else @@ -1033,17 +1112,33 @@ struct supertype *super_by_fd(int fd) sysfs_free(sra); if (st) { st->sb = NULL; - if (subarray) { - strncpy(st->subarray, subarray, 32); - st->subarray[31] = 0; - free(subarray); - } else - st->subarray[0] = 0; - } + if (subarrayp) + *subarrayp = subarray; + st->container_dev = container; + st->devnum = fd2devnum(fd); + } else + free(subarray); + return st; } #endif /* !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO) */ +int dev_size_from_id(unsigned int 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; +} struct supertype *dup_super(struct supertype *orig) { @@ -1058,32 +1153,38 @@ struct supertype *dup_super(struct supertype *orig) st->ss = orig->ss; st->max_devs = orig->max_devs; st->minor_version = orig->minor_version; - strcpy(st->subarray, orig->subarray); st->sb = NULL; st->info = NULL; return st; } -struct supertype *guess_super(int fd) +struct supertype *guess_super_type(int fd, enum guess_types guess_type) { /* try each load_super to find the best match, * and return the best superswitch */ struct superswitch *ss; struct supertype *st; - unsigned long besttime = 0; + time_t besttime = 0; int bestsuper = -1; int i; st = malloc(sizeof(*st)); + memset(st, 0, sizeof(*st)); + st->container_dev = NoMdDev; + for (i=0 ; superlist[i]; i++) { int rv; ss = superlist[i]; + if (guess_type == guess_array && ss->add_to_super == NULL) + continue; + if (guess_type == guess_partitions && ss->add_to_super != NULL) + continue; memset(st, 0, sizeof(*st)); rv = ss->load_super(st, fd, NULL); if (rv == 0) { struct mdinfo info; - st->ss->getinfo_super(st, &info); + st->ss->getinfo_super(st, &info, NULL); if (bestsuper == -1 || besttime < info.array.ctime) { bestsuper = i; @@ -1133,6 +1234,20 @@ int get_dev_size(int fd, char *dname, unsigned long long *sizep) return 1; } +/* Return true if this can only be a container, not a member device. + * i.e. is and md device and size is zero + */ +int must_be_container(int fd) +{ + unsigned long long size; + if (md_get_version(fd) < 0) + return 0; + if (get_dev_size(fd, NULL, &size) == 0) + return 1; + if (size == 0) + return 1; + return 0; +} /* Sets endofpart parameter to the last block used by the last GPT partition on the device. * Returns: 1 if successful @@ -1141,26 +1256,28 @@ int get_dev_size(int fd, char *dname, unsigned long long *sizep) */ 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; - int part_nr; + unsigned part_nr; *endofpart = 0; + BUILD_BUG_ON(sizeof(gpt) != 512); /* read GPT header */ lseek(fd, 512, SEEK_SET); - if (read(fd, buf, 512) != 512) + if (read(fd, &gpt, 512) != 512) return 0; /* get the number of partition entries and the entry size */ - all_partitions = __le32_to_cpu(buf[GPT_ALL_PARTITIONS_OFFSET]); - entry_size = __le32_to_cpu(buf[GPT_ENTRY_SIZE_OFFSET]); + all_partitions = __le32_to_cpu(gpt.part_cnt); + entry_size = __le32_to_cpu(gpt.part_size); /* Check GPT signature*/ - if (*((__u64*)buf) != GPT_SIGNATURE_MAGIC) + if (gpt.magic != GPT_SIGNATURE_MAGIC) return -1; /* sanity checks */ @@ -1178,7 +1295,7 @@ static int get_gpt_last_partition_end(int fd, unsigned long long *endofpart) /* 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(*(__u64*)part->ending_lba); + curr_part_end = __le64_to_cpu(part->ending_lba); if (curr_part_end > *endofpart) *endofpart = curr_part_end; } @@ -1201,26 +1318,25 @@ static int get_gpt_last_partition_end(int fd, unsigned long long *endofpart) */ static int get_last_partition_end(int fd, unsigned long long *endofpart) { - unsigned char boot_sect[512]; + struct MBR boot_sect; struct MBR_part_record *part; unsigned long long curr_part_end; - int part_nr; + 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) + if (read(fd, &boot_sect, 512) != 512) goto abort; /* check MBP signature */ - if (*((__u16*)(boot_sect + MBR_SIGNATURE_OFFSET)) - == MBR_SIGNATURE_MAGIC) { + if (boot_sect.magic == MBR_SIGNATURE_MAGIC) { retval = 1; /* found the correct signature */ - part = (struct MBR_part_record*) - (boot_sect + MBR_PARTITION_TABLE_OFFSET); + part = boot_sect.parts; for (part_nr=0; part_nr < MBR_PARTITIONS; part_nr++) { /* check for GPT type */ @@ -1315,7 +1431,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) @@ -1331,6 +1447,155 @@ 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)) + if (!subarray || + strcmp(to_subarray(ent, container), 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: empty supertype + * @quiet: block reporting errors flag + * + * On success returns an fd to a container and fills in *st + */ +int open_subarray(char *dev, char *subarray, struct supertype *st, int quiet) +{ + struct mdinfo *mdi; + struct mdinfo *info; + 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_container) { + if (!quiet) + fprintf(stderr, Name ": %s is not a container\n", dev); + goto free_name; + } + + if (st->ss->load_container(st, fd, NULL)) { + if (!quiet) + fprintf(stderr, Name ": Failed to load metadata for %s\n", + dev); + goto free_name; + } + + info = st->ss->container_content(st, subarray); + if (!info) { + if (!quiet) + fprintf(stderr, Name ": Failed to find subarray-%s in %s\n", + subarray, dev); + goto free_super; + } + free(info); + + 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) { @@ -1361,6 +1626,21 @@ int add_disk(int mdfd, struct supertype *st, return rv; } +int remove_disk(int mdfd, struct supertype *st, + struct mdinfo *sra, struct mdinfo *info) +{ + int rv; + /* Remove the disk given by 'info' from the array */ +#ifndef MDASSEMBLE + if (st->ss->external) + rv = sysfs_set_str(sra, info, "slot", "none"); + else +#endif + rv = ioctl(mdfd, HOT_REMOVE_DISK, makedev(info->disk.major, + info->disk.minor)); + return rv; +} + int set_array_info(int mdfd, struct supertype *st, struct mdinfo *info) { /* Initialise kernel's knowledge of array. @@ -1432,7 +1712,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 @@ -1463,23 +1743,25 @@ int fd2devnum(int fd) return NoMdDev; } -char *pid_dir = VAR_RUN; - int mdmon_pid(int devnum) { char path[100]; char pid[10]; int fd; int n; - sprintf(path, "%s/%s.pid", pid_dir, devnum2devname(devnum)); + 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; + return -1; return atoi(pid); }