X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fmdadm.git;a=blobdiff_plain;f=util.c;h=c0c3f0c97c4147c541ffbce3c0398cb7cba69262;hp=3a5daa265284012702cc16c2fae4ea9cdd745846;hb=4a06e2c270948bdcfaa447c8a0e80abb0978b5c4;hpb=f7e7067b47d2ca9994f9222dfa5833ac84ce3b22 diff --git a/util.c b/util.c index 3a5daa26..c0c3f0c9 100644 --- a/util.c +++ b/util.c @@ -29,7 +29,10 @@ #include "mdadm.h" #include "md_p.h" +#include #include +#include +#include #include #include #include @@ -151,6 +154,71 @@ int get_linux_version() return (a*1000000)+(b*1000)+c; } +long long parse_size(char *size) +{ + /* parse 'size' which should be a number optionally + * followed by 'K', 'M', or 'G'. + * Without a suffix, K is assumed. + * Number returned is in sectors (half-K) + */ + char *c; + long long s = strtoll(size, &c, 10); + if (s > 0) { + switch (*c) { + case 'K': + c++; + default: + s *= 2; + break; + case 'M': + c++; + s *= 1024 * 2; + break; + case 'G': + c++; + s *= 1024 * 1024 * 2; + break; + } + } + if (*c) + s = 0; + return s; +} + +int parse_layout_10(char *layout) +{ + int copies, rv; + char *cp; + /* Parse the layout string for raid10 */ + /* 'f', 'o' or 'n' followed by a number <= raid_disks */ + if ((layout[0] != 'n' && layout[0] != 'f' && layout[0] != 'o') || + (copies = strtoul(layout+1, &cp, 10)) < 1 || + copies > 200 || + *cp) + return -1; + if (layout[0] == 'n') + rv = 256 + copies; + else if (layout[0] == 'o') + rv = 0x10000 + (copies<<8) + 1; + else + rv = 1 + (copies<<8); + return rv; +} + +int parse_layout_faulty(char *layout) +{ + /* Parse the layout string for 'faulty' */ + int ln = strcspn(layout, "0123456789"); + char *m = strdup(layout); + int mode; + m[ln] = 0; + mode = map_name(faultylayout, m); + if (mode == UnSet) + return -1; + + return mode | (atoi(layout+ln)<< ModeShift); +} + void remove_partitions(int fd) { /* remove partitions from this block devices. @@ -219,8 +287,13 @@ int enough(int level, int raid_disks, int layout, int clean, } } +const int uuid_match_any[4] = { ~0, ~0, ~0, ~0 }; int same_uuid(int a[4], int b[4], int swapuuid) { + if (memcmp(a, uuid_match_any, sizeof(int[4])) == 0 || + memcmp(b, uuid_match_any, sizeof(int[4])) == 0) + return 1; + if (swapuuid) { /* parse uuids are hostendian. * uuid's from some superblocks are big-ending @@ -266,6 +339,27 @@ void copy_uuid(void *a, int b[4], int swapuuid) memcpy(a, b, 16); } +char *fname_from_uuid(struct supertype *st, struct mdinfo *info, char *buf, char sep) +{ + int i, j; + int id; + char uuid[16]; + char *c = buf; + strcpy(c, "UUID-"); + c += strlen(c); + copy_uuid(uuid, info->uuid, st->ss->swapuuid); + for (i = 0; i < 4; i++) { + id = uuid[i]; + if (i) + *c++ = sep; + for (j = 3; j >= 0; j--) { + sprintf(c,"%02x", (unsigned char) uuid[j+4*i]); + c+= 2; + } + } + return buf; +} + #ifndef MDASSEMBLE int check_ext2(int fd, char *name) { @@ -403,7 +497,7 @@ int is_standard(char *dev, int *nump) if (strncmp(d, "/d",2)==0) d += 2, type=1; /* /dev/md/dN{pM} */ else if (strncmp(d, "/md_d", 5)==0) - d += 5, type=1; /* /dev/md_dNpM */ + d += 5, type=1; /* /dev/md_dN{pM} */ else if (strncmp(d, "/md", 3)==0) d += 3, type=-1; /* /dev/mdN */ else if (d-dev > 3 && strncmp(d-2, "md/", 3)==0) @@ -438,8 +532,10 @@ int devlist_ready = 0; int add_dev(const char *name, const struct stat *stb, int flag, struct FTW *s) { struct stat st; + if (S_ISLNK(stb->st_mode)) { - stat(name, &st); + if (stat(name, &st) != 0) + return 0; stb = &st; } @@ -480,14 +576,13 @@ int nftw(const char *path, int (*han)(const char *name, const struct stat *stb, /* * Find a block device with the right major/minor number. * If we find multiple names, choose the shortest. - * If we find a non-standard name, it is probably there - * deliberately so prefer it over a standard name. + * If we find a name in /dev/md/, we prefer that. * This applies only to names for MD devices. */ char *map_dev(int major, int minor, int create) { struct devmap *p; - char *std = NULL, *nonstd=NULL; + char *regular = NULL, *preferred=NULL; int did_check = 0; if (major == 0 && minor == 0) @@ -514,27 +609,27 @@ char *map_dev(int major, int minor, int create) for (p=devlist; p; p=p->next) if (p->major == major && p->minor == minor) { - if (is_standard(p->name, NULL)) { - if (std == NULL || - strlen(p->name) < strlen(std)) - std = p->name; + if (strncmp(p->name, "/dev/md/",8) == 0) { + if (preferred == NULL || + strlen(p->name) < strlen(preferred)) + preferred = p->name; } else { - if (nonstd == NULL || - strlen(p->name) < strlen(nonstd)) - nonstd = p->name; + if (regular == NULL || + strlen(p->name) < strlen(regular)) + regular = p->name; } } - if (!std && !nonstd && !did_check) { + if (!regular && !preferred && !did_check) { devlist_ready = 0; goto retry; } - if (create && !std && !nonstd) { + if (create && !regular && !preferred) { static char buf[30]; snprintf(buf, sizeof(buf), "%d:%d", major, minor); - nonstd = buf; + regular = buf; } - return nonstd ? nonstd : std; + return preferred ? preferred : regular; } unsigned long calc_csum(void *super, int bytes) @@ -611,6 +706,23 @@ char *human_size_brief(long long bytes) ); return buf; } + +void print_r10_layout(int layout) +{ + int near = layout & 255; + int far = (layout >> 8) & 255; + int offset = (layout&0x10000); + char *sep = ""; + + if (near != 1) { + printf("%s near=%d", sep, near); + sep = ","; + } + if (far != 1) + printf("%s %s=%d", sep, offset?"offset":"far", far); + if (near*far == 1) + printf("NO REDUNDANCY"); +} #endif unsigned long long calc_array_size(int level, int raid_disks, int layout, @@ -630,7 +742,6 @@ unsigned long long calc_array_size(int level, int raid_disks, int layout, return data_disks * devsize; } -#if !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO) int get_mdp_major(void) { static int mdp_major = -1; @@ -659,8 +770,7 @@ static int mdp_major = -1; return mdp_major; } - - +#if !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO) char *get_md_name(int dev) { /* find /dev/md%d or /dev/md/%d or make a device /dev/.tmp.md%d */ @@ -719,7 +829,7 @@ int find_free_devnum(int use_partitions) { int devnum; for (devnum = 127; devnum != 128; - devnum = devnum ? devnum-1 : (1<<22)-1) { + devnum = devnum ? devnum-1 : (1<<20)-1) { char *dn; int _devnum; @@ -759,14 +869,22 @@ int dev_open(char *dev, int flags) 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); + fd = open(devname, flags|O_DIRECT); unlink(devname); } } else - fd = open(dev, flags); + fd = open(dev, flags|O_DIRECT); return fd; } +int open_dev(int devnum) +{ + char buf[20]; + + sprintf(buf, "%d:%d", dev2major(devnum), dev2minor(devnum)); + return dev_open(buf, O_RDWR); +} + int open_dev_excl(int devnum) { char buf[20]; @@ -784,6 +902,39 @@ int open_dev_excl(int devnum) return -1; } +int same_dev(char *one, char *two) +{ + struct stat st1, st2; + if (stat(one, &st1) != 0) + return 0; + if (stat(two, &st2) != 0) + return 0; + if ((st1.st_mode & S_IFMT) != S_IFBLK) + return 0; + if ((st2.st_mode & S_IFMT) != S_IFBLK) + return 0; + return st1.st_rdev == st2.st_rdev; +} + +void wait_for(char *dev, int fd) +{ + int i; + struct stat stb_want; + + if (fstat(fd, &stb_want) != 0 || + (stb_want.st_mode & S_IFMT) != S_IFBLK) + return; + + for (i=0 ; i<25 ; i++) { + struct stat stb; + if (stat(dev, &stb) == 0 && + (stb.st_mode & S_IFMT) == S_IFBLK && + (stb.st_rdev == stb_want.st_rdev)) + return; + usleep(200000); + } +} + struct superswitch *superlist[] = { &super0, &super1, &super_ddf, &super_imsm, NULL }; #if !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO) @@ -818,21 +969,21 @@ struct supertype *super_by_fd(int fd) sprintf(version, "%d.%d", vers, minor); verstr = version; } - if (minor == -2 && verstr[0] == '/') { + if (minor == -2 && is_subarray(verstr)) { char *dev = verstr+1; subarray = strchr(dev, '/'); int devnum; if (subarray) *subarray++ = '\0'; - if (strncmp(dev, "md_d", 4) == 0) - devnum = -1-atoi(dev+4); - else - devnum = atoi(dev+2); + devnum = devname2devnum(dev); subarray = strdup(subarray); if (sra) sysfs_free(sra); sra = sysfs_read(-1, devnum, GET_VERSION); - verstr = sra->text_version ? : "-no-metadata-"; + if (sra && sra->text_version[0]) + verstr = sra->text_version; + else + verstr = "-no-metadata-"; } for (i = 0; st == NULL && superlist[i] ; i++) @@ -858,6 +1009,8 @@ struct supertype *dup_super(struct supertype *orig) { struct supertype *st; + if (!orig) + return orig; st = malloc(sizeof(*st)); if (!st) return st; @@ -999,6 +1152,58 @@ int open_container(int fd) return -1; } +int add_disk(int mdfd, struct supertype *st, + struct mdinfo *sra, struct mdinfo *info) +{ + /* Add a device to an array, in one of 2 ways. */ + int rv; +#ifndef MDASSEMBLE + if (st->ss->external) { + rv = sysfs_add_disk(sra, info, + info->disk.state & (1<devs; sd2; sd2=sd2->next) + if (sd2 == info) + break; + if (sd2 == NULL) { + sd2 = malloc(sizeof(*sd2)); + *sd2 = *info; + sd2->next = sra->devs; + sra->devs = sd2; + } + } + } else +#endif + rv = ioctl(mdfd, ADD_NEW_DISK, &info->disk); + return rv; +} + +int set_array_info(int mdfd, struct supertype *st, struct mdinfo *info) +{ + /* Initialise kernel's knowledge of array. + * This varies between externally managed arrays + * and older kernels + */ + int vers = md_get_version(mdfd); + int rv; + +#ifndef MDASSEMBLE + if (st->ss->external) + rv = sysfs_set_array(info, vers); + else +#endif + if ((vers % 100) >= 1) { /* can use different versions */ + mdu_array_info_t inf; + memset(&inf, 0, sizeof(inf)); + inf.major_version = info->array.major_version; + inf.minor_version = info->array.minor_version; + rv = ioctl(mdfd, SET_ARRAY_INFO, &inf); + } else + rv = ioctl(mdfd, SET_ARRAY_INFO, NULL); + return rv; +} + char *devnum2devname(int num) { char name[100]; @@ -1009,17 +1214,56 @@ char *devnum2devname(int num) return strdup(name); } +int devname2devnum(char *name) +{ + char *ep; + int num; + if (strncmp(name, "md_d", 4)==0) + num = -1-strtoul(name+4, &ep, 10); + else + num = strtoul(name+2, &ep, 10); + return num; +} + +int stat2devnum(struct stat *st) +{ + char path[30]; + char link[200]; + char *cp; + int n; + + 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()) + return -1- (minor(st->st_rdev)>>MdpMinorShift); + + /* must be an extended-minor partition. Look at the + * /sys/dev/block/%d:%d link which must look like + * ../../block/mdXXX/mdXXXpYY + */ + sprintf(path, "/sys/dev/block/%d:%d", major(st->st_rdev), + minor(st->st_rdev)); + n = readlink(path, link, sizeof(link)-1); + if (n <= 0) + return NoMdDev; + link[n] = 0; + cp = strrchr(link, '/'); + if (cp) *cp = 0; + cp = strchr(link, '/'); + if (cp && strncmp(cp, "/md", 3) == 0) + return devname2devnum(cp+1); + } + return NoMdDev; + +} + int fd2devnum(int fd) { struct stat stb; - if (fstat(fd, &stb) == 0 && - (S_IFMT&stb.st_mode)==S_IFBLK) { - if (major(stb.st_rdev) == MD_MAJOR) - return minor(stb.st_rdev); - else - return -1- (minor(stb.st_rdev)>>6); - } - return -1; + if (fstat(fd, &stb) == 0) + return stat2devnum(&stb); + return NoMdDev; } int mdmon_running(int devnum) @@ -1062,7 +1306,110 @@ int signal_mdmon(int devnum) return 0; } +int start_mdmon(int devnum) +{ + int i; + int len; + pid_t pid; + int status; + char pathbuf[1024]; + char *paths[4] = { + pathbuf, + "/sbin/mdmon", + "mdmon", + NULL + }; + + if (check_env("MDADM_NO_MDMON")) + return 0; + + len = readlink("/proc/self/exe", pathbuf, sizeof(pathbuf)); + if (len > 0) { + char *sl; + pathbuf[len] = 0; + sl = strrchr(pathbuf, '/'); + if (sl) + sl++; + else + sl = pathbuf; + strcpy(sl, "mdmon"); + } else + pathbuf[0] = '\0'; + + switch(fork()) { + case 0: + /* FIXME yuk. CLOSE_EXEC?? */ + for (i=3; i < 100; i++) + close(i); + for (i=0; paths[i]; i++) + if (paths[i][0]) + execl(paths[i], "mdmon", + devnum2devname(devnum), + NULL); + exit(1); + case -1: fprintf(stderr, Name ": cannot run mdmon. " + "Array remains readonly\n"); + return -1; + default: /* parent - good */ + pid = wait(&status); + if (pid < 0 || status != 0) + return -1; + } + return 0; +} + +int check_env(char *name) +{ + char *val = getenv(name); + + if (val && atoi(val) == 1) + return 1; + + return 0; +} + +#ifndef MDASSEMBLE +int flush_metadata_updates(struct supertype *st) +{ + int sfd; + if (!st->updates) { + st->update_tail = NULL; + return -1; + } + + sfd = connect_monitor(devnum2devname(st->container_dev)); + if (sfd < 0) + return -1; + + while (st->updates) { + struct metadata_update *mu = st->updates; + st->updates = mu->next; + + send_message(sfd, mu, 0); + wait_reply(sfd, 0); + free(mu->buf); + free(mu); + } + ack(sfd, 0); + wait_reply(sfd, 0); + close(sfd); + st->update_tail = NULL; + return 0; +} + +void append_metadata_update(struct supertype *st, void *buf, int len) +{ + + struct metadata_update *mu = malloc(sizeof(*mu)); + mu->buf = buf; + mu->len = len; + mu->space = NULL; + mu->next = NULL; + *st->update_tail = mu; + st->update_tail = &mu->next; +} +#endif /* MDASSEMBLE */ #ifdef __TINYC__ /* tinyc doesn't optimize this check in ioctl.h out ... */