X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fmdadm.git;a=blobdiff_plain;f=util.c;h=05be64c6add692959d89e94ab00cd83963d2aa02;hp=fe46f3b02d9c20a58079e0f70f8b48d41308d1e5;hb=d23534e4646313a67296b295666d165a87bb2c92;hpb=44d2e36556921e82a9fde02b554f799741c92531 diff --git a/util.c b/util.c index fe46f3b0..05be64c6 100644 --- a/util.c +++ b/util.c @@ -1,7 +1,7 @@ /* * mdadm - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001-2006 Neil Brown + * Copyright (C) 2001-2009 Neil Brown * * * This program is free software; you can redistribute it and/or modify @@ -19,18 +19,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Neil Brown - * Email: - * Paper: Neil Brown - * School of Computer Science and Engineering - * The University of New South Wales - * Sydney, 2052 - * Australia + * Email: */ #include "mdadm.h" #include "md_p.h" #include #include +#include #include #include #include @@ -153,6 +149,73 @@ int get_linux_version() return (a*1000000)+(b*1000)+c; } +#ifndef MDASSEMBLE +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); +} +#endif + void remove_partitions(int fd) { /* remove partitions from this block devices. @@ -198,9 +261,9 @@ int enough(int level, int raid_disks, int layout, int clean, } while (first != 0); return 1; - case -4: + case LEVEL_MULTIPATH: return avail_disks>= 1; - case -1: + case LEVEL_LINEAR: case 0: return avail_disks == raid_disks; case 1: @@ -221,8 +284,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 @@ -268,6 +336,31 @@ void copy_uuid(void *a, int b[4], int swapuuid) memcpy(a, b, 16); } +char *__fname_from_uuid(int id[4], int swap, char *buf, char sep) +{ + int i, j; + char uuid[16]; + char *c = buf; + strcpy(c, "UUID-"); + c += strlen(c); + copy_uuid(uuid, id, swap); + for (i = 0; i < 4; i++) { + if (i) + *c++ = sep; + for (j = 3; j >= 0; j--) { + sprintf(c,"%02x", (unsigned char) uuid[j+4*i]); + c+= 2; + } + } + return buf; + +} + +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); +} + #ifndef MDASSEMBLE int check_ext2(int fd, char *name) { @@ -405,7 +498,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) @@ -440,8 +533,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; } @@ -482,14 +577,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) @@ -516,27 +610,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) @@ -613,6 +707,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, @@ -632,7 +743,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; @@ -661,8 +771,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 */ @@ -721,7 +830,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; @@ -769,6 +878,14 @@ int dev_open(char *dev, int flags) 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]; @@ -786,6 +903,41 @@ 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); + } + if (i == 25) + dprintf("%s: timeout waiting for %s\n", __func__, dev); +} + struct superswitch *superlist[] = { &super0, &super1, &super_ddf, &super_imsm, NULL }; #if !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO) @@ -820,7 +972,7 @@ 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; @@ -831,7 +983,10 @@ struct supertype *super_by_fd(int fd) 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++) @@ -1000,10 +1155,65 @@ 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) { + if (info->disk.state & (1<recovery_start = MaxSector; + else + info->recovery_start = 0; + rv = sysfs_add_disk(sra, info); + if (! rv) { + struct mdinfo *sd2; + for (sd2 = sra->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]; - if (num > 0) + if (num >= 0) sprintf(name, "md%d", num); else sprintf(name, "md_d%d", -1-num); @@ -1021,17 +1231,45 @@ int devname2devnum(char *name) 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) @@ -1078,6 +1316,8 @@ int start_mdmon(int devnum) { int i; int len; + pid_t pid; + int status; char pathbuf[1024]; char *paths[4] = { pathbuf, @@ -1086,7 +1326,7 @@ int start_mdmon(int devnum) NULL }; - if (env_no_mdmon()) + if (check_env("MDADM_NO_MDMON")) return 0; len = readlink("/proc/self/exe", pathbuf, sizeof(pathbuf)); @@ -1110,21 +1350,23 @@ int start_mdmon(int devnum) for (i=0; paths[i]; i++) if (paths[i][0]) execl(paths[i], "mdmon", - map_dev(dev2major(devnum), - dev2minor(devnum), - 1), NULL); + devnum2devname(devnum), + NULL); exit(1); case -1: fprintf(stderr, Name ": cannot run mdmon. " "Array remains readonly\n"); return -1; - default: ; /* parent - good */ + default: /* parent - good */ + pid = wait(&status); + if (pid < 0 || status != 0) + return -1; } return 0; } -int env_no_mdmon(void) +int check_env(char *name) { - char *val = getenv("MDADM_NO_MDMON"); + char *val = getenv(name); if (val && atoi(val) == 1) return 1; @@ -1132,7 +1374,18 @@ int env_no_mdmon(void) return 0; } +__u32 random32(void) +{ + __u32 rv; + int rfd = open("/dev/urandom", O_RDONLY); + if (rfd < 0 || read(rfd, &rv, 4) != 4) + rv = random(); + if (rfd >= 0) + close(rfd); + return rv; +} +#ifndef MDASSEMBLE int flush_metadata_updates(struct supertype *st) { int sfd; @@ -1173,7 +1426,7 @@ void append_metadata_update(struct supertype *st, void *buf, int len) *st->update_tail = mu; st->update_tail = &mu->next; } - +#endif /* MDASSEMBLE */ #ifdef __TINYC__ /* tinyc doesn't optimize this check in ioctl.h out ... */