]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - util.c
Fix bug on new --detail code
[thirdparty/mdadm.git] / util.c
diff --git a/util.c b/util.c
index f243d884cdd6f6e7f571a89ef384a9e340a25ef6..b4c1d5ee1372501f5d7967249587e5079218d15c 100644 (file)
--- a/util.c
+++ b/util.c
@@ -140,14 +140,32 @@ 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;
+       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;
+               return 0;
+       }
 }
 
 int check_ext2(int fd, char *name)
@@ -195,8 +213,8 @@ int check_reiser(int fd, char *name)
                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;
@@ -209,26 +227,23 @@ int check_raid(int fd, char *name)
 {
        void *super;
        struct mdinfo info;
+       struct mddev_ident_s ident;
        time_t crtime;
+       struct supertype *st = guess_super(fd);
 
-       int i;
-       for (i=0; superlist[i]; i++) {
-               if (superlist[i]->load_super(fd, &super, name))
-                       continue;
-               /* Looks like a raid array .. */
-               fprintf(stderr, Name ": %s appears to be part of a raid array:\n",
-                       name);
-               superlist[i]->getinfo_super(&info, super);
-               free(super);
-               crtime = info.array.ctime;
-               fprintf(stderr, "    level=%d devices=%d ctime=%s",
-                       info.array.level, info.array.raid_disks, ctime(&crtime));
-               return 1;
-       }
-       return 0;
+       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);
+       st->ss->getinfo_super(&info, &ident, super);
+       free(super);
+       crtime = info.array.ctime;
+       fprintf(stderr, "    level=%d devices=%d ctime=%s",
+               info.array.level, info.array.raid_disks, ctime(&crtime));
+       return 1;
 }
 
-
 int ask(char *mesg)
 {
        char *add = "";
@@ -288,7 +303,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)
@@ -317,6 +332,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
@@ -326,15 +344,19 @@ char *map_dev(int major, int minor)
        return NULL;
 }
 #else
-#define  __USE_XOPEN_EXTENDED
-#include <ftw.h>
 
+#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);
@@ -352,30 +374,34 @@ 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
@@ -390,30 +416,46 @@ unsigned long calc_csum(void *super, int bytes)
        for(i=0; i<bytes/4; i++)
                newcsum+= superc[i];
        csum = (newcsum& 0xffffffff) + (newcsum>>32);
+#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;
 }
 
@@ -423,16 +465,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)
                        );
@@ -482,20 +524,20 @@ char *get_md_name(int dev)
                int mdp =  get_mdp_major();
                if (mdp < 0) return NULL;
                rdev = makedev(mdp, (-1-dev)<<6);
-               sprintf(devname, "/dev/md/d%d", -1-dev);
+               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 = makedev(MD_MAJOR, dev);
-               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))
                        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))
@@ -504,7 +546,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;
@@ -525,39 +567,72 @@ void put_md_name(char *name)
 
 
 
+struct superswitch *superlist[] = { &super0, &super1, NULL };
 
-struct superswitch *superlist[] = { &super0, NULL };
-
-struct superswitch *super_by_version(int vers)
+struct supertype *super_by_version(int vers, int minor)
 {
-       if (vers == 0) return &super0;
-       return NULL;
+       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 superswitch *guess_super(int fd, char *dev)
+struct supertype *guess_super(int fd)
 {
        /* try each load_super to find the best match,
         * and return the best superswitch
         */
-       struct superswitch *best = NULL, *ss;
-       int bestrv = 0;
+       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];
-               rv = ss->load_super(fd, &sbp, NULL);
+               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);
-                       return ss;
                }
-               if (rv > bestrv) {
-                       bestrv  = rv;
-                       best = ss;
+       }
+       if (bestsuper != -1) {
+               int rv;
+               st->ss = NULL;
+               rv = superlist[bestsuper]->load_super(st, fd, &sbp, NULL);
+               if (rv == 0) {
+                       free(sbp);
+                       return st;
                }
        }
-       if (bestrv > 2) /* FIXME */
-               return best;
+       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
+