X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=util.c;h=0af433130bb723dcf4943a865cebda0f7f8e5553;hb=e86c9dd6d847ec57ec400b118efaf2c1808f10bc;hp=23968714d94be319688735283465190b5f90670a;hpb=8d80900b492998a112eda80614a793012bee2770;p=thirdparty%2Fmdadm.git diff --git a/util.c b/util.c index 23968714..0af43313 100644 --- a/util.c +++ b/util.c @@ -94,7 +94,7 @@ int md_get_version(int fd) return (vers.major*10000) + (vers.minor*100) + vers.patchlevel; if (errno == EACCES) return -1; - if (MAJOR(stb.st_rdev) == MD_MAJOR) + if (major(stb.st_rdev) == MD_MAJOR) return (3600); return -1; } @@ -118,10 +118,31 @@ int get_linux_version() return (a*1000000)+(b*1000)+c; } -int enough(int level, int raid_disks, int avail_disks) +int enough(int level, int raid_disks, int layout, + char *avail, int avail_disks) { + int copies, first; switch (level) { - case 10: return 1; /* a lie, but it is hard to tell */ + case 10: + /* This is the tricky one - we need to check + * which actual disks are present. + */ + copies = (layout&255)* (layout>>8); + first=0; + do { + /* there must be one of the 'copies' form 'first' */ + int n = copies; + int cnt=0; + while (n--) { + if (avail[first]) + cnt++; + first = (first+1) % raid_disks; + } + if (cnt == 0) + return 0; + + } while (first != 0); + return 1; case -4: return avail_disks>= 1; @@ -140,148 +161,33 @@ int enough(int level, int raid_disks, int avail_disks) } } -int same_uuid(int a[4], int b[4]) +int same_uuid(int a[4], int b[4], int swapuuid) { - if (a[0]==b[0] && - a[1]==b[1] && - a[2]==b[2] && - a[3]==b[3]) - return 1; - return 0; -} - -void uuid_from_super(int uuid[4], mdp_super_t *super) -{ - uuid[0] = super->set_uuid0; - if (super->minor_version >= 90) { - uuid[1] = super->set_uuid1; - uuid[2] = super->set_uuid2; - uuid[3] = super->set_uuid3; - } else { - uuid[1] = 0; - uuid[2] = 0; - uuid[3] = 0; - } -} - -int compare_super(mdp_super_t *first, mdp_super_t *second) -{ - /* - * return: - * 0 same, or first was empty, and second was copied - * 1 second had wrong number - * 2 wrong uuid - * 3 wrong other info - */ - int uuid1[4], uuid2[4]; - if (second->md_magic != MD_SB_MAGIC) - return 1; - if (first-> md_magic != MD_SB_MAGIC) { - memcpy(first, second, sizeof(*first)); - return 0; - } - - uuid_from_super(uuid1, first); - uuid_from_super(uuid2, second); - if (!same_uuid(uuid1, uuid2)) - return 2; - if (first->major_version != second->major_version || - first->minor_version != second->minor_version || - first->patch_version != second->patch_version || - first->gvalid_words != second->gvalid_words || - first->ctime != second->ctime || - first->level != second->level || - first->size != second->size || - first->raid_disks != second->raid_disks ) - return 3; - - return 0; -} - -int load_super(int fd, mdp_super_t *super) -{ - /* try to read in the superblock - * - * return - * 0 - success - * 1 - no block size - * 2 - too small - * 3 - no seek - * 4 - no read - * 5 - no magic - * 6 - wrong major version - */ - unsigned long size; - unsigned long long dsize; - unsigned long long offset; - -#ifdef BLKGETSIZE64 - if (ioctl(fd, BLKGETSIZE64, &dsize) != 0) -#endif - { - if (ioctl(fd, BLKGETSIZE, &size)) - return 1; - else - dsize = size << 9; - } - - if (dsize < MD_RESERVED_SECTORS*2) - return 2; - - offset = MD_NEW_SIZE_SECTORS(dsize>>9); - - offset *= 512; - - ioctl(fd, BLKFLSBUF, 0); /* make sure we read current data */ - - if (lseek64(fd, offset, 0)< 0LL) - return 3; - - if (read(fd, super, sizeof(*super)) != sizeof(*super)) - return 4; - - if (super->md_magic != MD_SB_MAGIC) - return 5; - - if (super->major_version != 0) - return 6; - return 0; -} - -int store_super(int fd, mdp_super_t *super) -{ - unsigned long size; - unsigned long long dsize; - - long long offset; - -#ifdef BLKGETSIZE64 - if (ioctl(fd, BLKGETSIZE64, &dsize) != 0) -#endif - { - if (ioctl(fd, BLKGETSIZE, &size)) + if (swapuuid) { + /* parse uuids are hostendian. + * uuid's from some superblocks are big-ending + * if there is a difference, we need to swap.. + */ + unsigned char *ac = (unsigned char *)a; + unsigned char *bc = (unsigned char *)b; + int i; + for (i=0; i<16; i+= 4) { + if (ac[i+0] != bc[i+3] || + ac[i+1] != bc[i+2] || + ac[i+2] != bc[i+1] || + ac[i+3] != bc[i+0]) + return 0; + } + return 1; + } else { + if (a[0]==b[0] && + a[1]==b[1] && + a[2]==b[2] && + a[3]==b[3]) return 1; - else - dsize = ((unsigned long long)size) << 9; + return 0; } - - if (dsize < MD_RESERVED_SECTORS*2) - return 2; - - offset = MD_NEW_SIZE_SECTORS(dsize>>9); - - offset *= 512; - - if (lseek64(fd, offset, 0)< 0LL) - return 3; - - if (write(fd, super, sizeof(*super)) != sizeof(*super)) - return 4; - - return 0; } - - int check_ext2(int fd, char *name) { @@ -323,37 +229,45 @@ int check_reiser(int fd, char *name) * */ unsigned char sb[1024]; - int size; + unsigned long size; if (lseek(fd, 64*1024, 0) != 64*1024) return 0; if (read(fd, sb, 1024) != 1024) return 0; - if (strncmp(sb+52, "ReIsErFs",8)!=0 && - strncmp(sb+52, "ReIsEr2Fs",9)!=0) + if (strncmp((char*)sb+52, "ReIsErFs",8)!=0 && + strncmp((char*)sb+52, "ReIsEr2Fs",9)!=0) return 0; fprintf(stderr, Name ": %s appears to contain a reiserfs file system\n",name); size = sb[0]|(sb[1]|(sb[2]|sb[3]<<8)<<8)<<8; - fprintf(stderr, " size = %dK\n", size*4); + fprintf(stderr, " size = %luK\n", size*4); return 1; } int check_raid(int fd, char *name) { - mdp_super_t super; + void *super; + struct mdinfo info; + struct mddev_ident_s ident; time_t crtime; - if (load_super(fd, &super)) - return 0; + char *level; + struct supertype *st = guess_super(fd); + + if (!st) return 0; + st->ss->load_super(st, fd, &super, name); /* Looks like a raid array .. */ fprintf(stderr, Name ": %s appears to be part of a raid array:\n", name); - crtime = super.ctime; - fprintf(stderr, " level=%d devices=%d ctime=%s", - super.level, super.raid_disks, ctime(&crtime)); + st->ss->getinfo_super(&info, &ident, super); + free(super); + crtime = info.array.ctime; + level = map_num(pers, info.array.level); + if (!level) level = "-unknown-"; + fprintf(stderr, " level=%s devices=%d ctime=%s", + level, info.array.raid_disks, ctime(&crtime)); return 1; } - int ask(char *mesg) { char *add = ""; @@ -413,7 +327,7 @@ int is_standard(char *dev, int *nump) else if (strncmp(d, "/md", 3)==0) d += 3, type=-1; /* /dev/mdN */ else if (d-dev > 3 && strncmp(d-2, "md/", 3)==0) - type = -1; /* /dev/md/N */ + d += 1, type=-1; /* /dev/md/N */ else return 0; if (!*d) @@ -442,6 +356,9 @@ struct devmap { int devlist_ready = 0; #ifdef UCLIBC +int add_dev(const char *name, const struct stat *stb, int flag, struct FTW *s) +{ +} char *map_dev(int major, int minor) { #if 0 @@ -451,22 +368,26 @@ char *map_dev(int major, int minor) return NULL; } #else -#define __USE_XOPEN_EXTENDED -#include +#ifdef __dietlibc__ +int add_dev_1(const char *name, const struct stat *stb, int flag) +{ + return add_dev(name, stb, flag, NULL); +} +int nftw(const char *path, int (*han)(const char *name, const struct stat *stb, int flag, struct FTW *s), int nopenfd, int flags) +{ + ftw(path, add_dev_1, nopenfd); +} +#endif -#ifndef __dietlibc__ int add_dev(const char *name, const struct stat *stb, int flag, struct FTW *s) -#else -int add_dev(const char *name, const struct stat *stb, int flag) -#endif { if ((stb->st_mode&S_IFMT)== S_IFBLK) { char *n = strdup(name); struct devmap *dm = malloc(sizeof(*dm)); if (dm) { - dm->major = MAJOR(stb->st_rdev); - dm->minor = MINOR(stb->st_rdev); + dm->major = major(stb->st_rdev); + dm->minor = minor(stb->st_rdev); dm->name = n; dm->next = devlist; devlist = dm; @@ -477,71 +398,88 @@ int add_dev(const char *name, const struct stat *stb, int flag) /* * Find a block device with the right major/minor number. - * Avoid /dev/mdNN and /dev/md/dNN if possible + * 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. + * This applies only to names for MD devices. */ char *map_dev(int major, int minor) { struct devmap *p; - char *std = NULL; + char *std = NULL, *nonstd=NULL; if (!devlist_ready) { -#ifndef __dietlibc__ nftw("/dev", add_dev, 10, FTW_PHYS); -#else - ftw("/dev", add_dev, 10); -#endif devlist_ready=1; } for (p=devlist; p; p=p->next) if (p->major == major && p->minor == minor) { - if (is_standard(p->name, NULL)) - std = p->name; - else - return p->name; + if (is_standard(p->name, NULL)) { + if (std == NULL || + strlen(p->name) < strlen(std)) + std = p->name; + } else { + if (nonstd == NULL || + strlen(p->name) < strlen(nonstd)) + nonstd = p->name; + } } - return std; + return nonstd ? nonstd : std; } #endif -unsigned long calc_sb_csum(mdp_super_t *super) +unsigned long calc_csum(void *super, int bytes) { - unsigned int oldcsum = super->sb_csum; unsigned long long newcsum = 0; - unsigned long csum; int i; - unsigned int *superc = (int*) super; - super->sb_csum = 0; + unsigned int csum; + unsigned int *superc = (unsigned int*) super; - for(i=0; i>32); - super->sb_csum = oldcsum; +#ifdef __alpha__ +/* The in-kernel checksum calculation is always 16bit on + * the alpha, though it is 32 bit on i386... + * I wonder what it is elsewhere... (it uses and API in + * a way that it shouldn't). + */ + csum = (csum & 0xffff) + (csum >> 16); + csum = (csum & 0xffff) + (csum >> 16); +#endif return csum; } char *human_size(long long bytes) { static char buf[30]; - + + /* We convert bytes to either centi-M{ega,ibi}bytes or + * centi-G{igi,ibi}bytes, with appropriate rounding, + * and then print 1/100th of those as a decimal. + * We allow upto 2048Megabytes before converting to + * gigabytes, as that shows more precision and isn't + * too large a number. + * Terrabytes are not yet handled. + */ if (bytes < 5000*1024) buf[0]=0; - else if (bytes < 2*1024LL*1024LL*1024LL) - sprintf(buf, " (%ld.%02ld MiB %ld.%02ld MB)", - (long)(bytes>>20), - (long)((bytes&0xfffff)+0x100000/200)/(0x100000/100), - (long)(bytes/1000/1000), - (long)(((bytes%1000000)+5000)/10000) - ); - else - sprintf(buf, " (%ld.%02ld GiB %ld.%02ld GB)", - (long)(bytes>>30), - (long)(((bytes>>10)&0xfffff)+0x100000/200)/(0x100000/100), - (long)(bytes/1000LL/1000LL/1000LL), - (long)((((bytes/1000)%1000000)+5000)/10000) - ); + else if (bytes < 2*1024LL*1024LL*1024LL) { + long cMiB = (bytes / ( (1LL<<20) / 200LL ) +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 cGB = (bytes / (1000000000LL/200LL ) +1) /2; + snprintf(buf, sizeof(buf), " (%ld.%02ld GiB %ld.%02ld GB)", + cGiB/100 , cGiB % 100, + cGB/100, cGB % 100); + } return buf; } @@ -551,16 +489,16 @@ char *human_size_brief(long long bytes) if (bytes < 5000*1024) - sprintf(buf, "%ld.%02ldKiB", + snprintf(buf, sizeof(buf), "%ld.%02ldKiB", (long)(bytes>>10), (long)(((bytes&1023)*100+512)/1024) ); else if (bytes < 2*1024LL*1024LL*1024LL) - sprintf(buf, "%ld.%02ldMiB", + snprintf(buf, sizeof(buf), "%ld.%02ldMiB", (long)(bytes>>20), (long)((bytes&0xfffff)+0x100000/200)/(0x100000/100) ); else - sprintf(buf, "%ld.%02ldGiB", + snprintf(buf, sizeof(buf), "%ld.%02ldGiB", (long)(bytes>>30), (long)(((bytes>>10)&0xfffff)+0x100000/200)/(0x100000/100) ); @@ -609,21 +547,21 @@ char *get_md_name(int dev) if (dev < 0) { int mdp = get_mdp_major(); if (mdp < 0) return NULL; - rdev = MKDEV(mdp, (-1-dev)<<6); - sprintf(devname, "/dev/md/d%d", -1-dev); + rdev = makedev(mdp, (-1-dev)<<6); + snprintf(devname, sizeof(devname), "/dev/md/d%d", -1-dev); if (stat(devname, &stb) == 0 && (S_IFMT&stb.st_mode) == S_IFBLK && (stb.st_rdev == rdev)) return devname; } else { - rdev = MKDEV(MD_MAJOR, dev); - sprintf(devname, "/dev/md%d", dev); + rdev = makedev(MD_MAJOR, dev); + snprintf(devname, sizeof(devname), "/dev/md%d", dev); if (stat(devname, &stb) == 0 && (S_IFMT&stb.st_mode) == S_IFBLK && (stb.st_rdev == rdev)) return devname; - sprintf(devname, "/dev/md/%d", dev); + snprintf(devname, sizeof(devname), "/dev/md/%d", dev); if (stat(devname, &stb) == 0 && (S_IFMT&stb.st_mode) == S_IFBLK && (stb.st_rdev == rdev)) @@ -632,7 +570,7 @@ char *get_md_name(int dev) dn = map_dev(major(rdev), minor(rdev)); if (dn) return dn; - sprintf(devname, "/dev/.tmp.md%d", dev); + snprintf(devname, sizeof(devname), "/dev/.tmp.md%d", dev); if (mknod(devname, S_IFBLK | 0600, rdev) == -1) if (errno != EEXIST) return NULL; @@ -650,3 +588,97 @@ void put_md_name(char *name) if (strncmp(name, "/dev/.tmp.md", 12)==0) unlink(name); } + +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 = strtoul(dev, &e, 0); + int minor; + if (e > dev && *e == ':' && e[1] && + (minor = strtoul(e+1, &e, 0)) >= 0 && + *e == 0) { + snprintf(devname, sizeof(devname), "/dev/.tmp.md.%d:%d", major, minor); + if (mknod(devname, S_IFBLK|0600, makedev(major, minor))==0) { + fd = open(devname, flags); + unlink(devname); + } + } else + fd = open(dev, flags); + return fd; +} + +struct superswitch *superlist[] = { &super0, &super1, NULL }; + +struct supertype *super_by_version(int vers, int minor) +{ + struct supertype *st = malloc(sizeof(*st)); + if (!st) return st; + if (vers == 0) { + st->ss = &super0; + st->max_devs = MD_SB_DISKS; + } + + if (vers == 1) { + st->ss = &super1; + st->max_devs = 384; + } + st->minor_version = minor; + return st; +} + +struct supertype *guess_super(int fd) +{ + /* try each load_super to find the best match, + * and return the best superswitch + */ + struct superswitch *ss; + struct supertype *st; + unsigned long besttime = 0; + int bestsuper = -1; + + void *sbp = NULL; + int i; + + st = malloc(sizeof(*st)); + memset(st, 0, sizeof(*st)); + for (i=0 ; superlist[i]; i++) { + int rv; + ss = superlist[i]; + st->ss = NULL; + rv = ss->load_super(st, fd, &sbp, NULL); + if (rv == 0) { + struct mdinfo info; + struct mddev_ident_s ident; + ss->getinfo_super(&info, &ident, sbp); + if (bestsuper == -1 || + besttime < info.array.ctime) { + bestsuper = i; + besttime = info.array.ctime; + } + free(sbp); + } + } + if (bestsuper != -1) { + int rv; + st->ss = NULL; + rv = superlist[bestsuper]->load_super(st, fd, &sbp, NULL); + if (rv == 0) { + free(sbp); + return st; + } + } + free(st); + return NULL; +} + + +#ifdef __TINYC__ +/* tinyc doesn't optimize this check in ioctl.h out ... */ +unsigned int __invalid_size_argument_for_IOC = 0; +#endif +