]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - super1.c
Stuff
[thirdparty/mdadm.git] / super1.c
index 86787653b7273d9decb3295752816853489366a9..114ef83ed1a654a4e1edc8f179323838b32c7f1b 100644 (file)
--- a/super1.c
+++ b/super1.c
@@ -83,7 +83,9 @@ struct mdp_superblock_1 {
        __u16   dev_roles[0];   /* role in array, or 0xffff for a spare, or 0xfffe for faulty */
 };
 
+#ifndef offsetof
 #define offsetof(t,f) ((int)&(((t*)0)->f))
+#endif
 static unsigned int calc_sb_1_csum(struct mdp_superblock_1 * sb)
 {
        unsigned int disk_csum, csum;
@@ -119,7 +121,7 @@ static void examine_super1(void *sbv)
        struct mdp_superblock_1 *sb = sbv;
        time_t atime;
        int d;
-       int spares, faulty;
+       int faulty;
        int i;
        char *c;
 
@@ -139,11 +141,12 @@ static void examine_super1(void *sbv)
        printf("     Raid Level : %s\n", c?c:"-unknown-");
        printf("   Raid Devices : %d\n", __le32_to_cpu(sb->raid_disks));
        printf("\n");
-       printf("    Device Size : %llu%s\n", sb->data_size, human_size(sb->data_size<<9));
+       printf("    Device Size : %llu%s\n", (unsigned long long)sb->data_size, human_size(sb->data_size<<9));
        if (sb->data_offset)
-               printf("    Data Offset : %llu sectors\n", __le64_to_cpu(sb->data_offset));
+               printf("    Data Offset : %llu sectors\n", (unsigned long long)__le64_to_cpu(sb->data_offset));
        if (sb->super_offset)
-               printf("   Super Offset : %llu sectors\n", __le64_to_cpu(sb->super_offset));
+               printf("   Super Offset : %llu sectors\n", (unsigned long long)__le64_to_cpu(sb->super_offset));
+       printf("          State : %s\n", (__le64_to_cpu(sb->resync_offset)+1)? "active":"clean");
        printf("    Device UUID : ");
        for (i=0; i<16; i++) {
                printf("%02x", sb->set_uuid[i]);
@@ -159,7 +162,7 @@ static void examine_super1(void *sbv)
        else
                printf("       Checksum : %x - expected %x\n", __le32_to_cpu(sb->sb_csum),
                       __le32_to_cpu(calc_sb_1_csum(sb)));
-       printf("         Events : %llu\n", __le64_to_cpu(sb->events));
+       printf("         Events : %llu\n", (unsigned long long)__le64_to_cpu(sb->events));
        printf("\n");
        if (__le32_to_cpu(sb->level) == 5) {
                c = map_num(r5layout, __le32_to_cpu(sb->layout));
@@ -195,15 +198,12 @@ static void examine_super1(void *sbv)
                else if (cnt == 1) printf("u");
                else printf ("_");
        }
-       spares = faulty = 0;
+       faulty = 0;
        for (i=0; i< __le32_to_cpu(sb->max_dev); i++) {
                int role = __le16_to_cpu(sb->dev_roles[i]);
-               switch (role) {
-               case 0xFFFF: spares++; break;
-               case 0xFFFE: faulty++;
-               }
+               if (role == 0xFFFE)
+                       faulty++;
        }
-       if (spares) printf(" %d spares", spares);
        if (faulty) printf(" %d failed", faulty);
        printf("\n");
 }
@@ -235,7 +235,7 @@ static void detail_super1(void *sbv)
                printf("%02x", sb->set_uuid[i]);
                if ((i&3)==0 && i != 0) printf(":");
        }
-       printf("\n         Events : %llu\n\n", __le64_to_cpu(sb->events));
+       printf("\n         Events : %llu\n\n", (unsigned long long)__le64_to_cpu(sb->events));
 }
 
 static void brief_detail_super1(void *sbv)
@@ -365,7 +365,7 @@ static __u64 event_super1(void *sbv)
        return __le64_to_cpu(sb->events);
 }
 
-static int init_super1(void **sbp, mdu_array_info_t *info)
+static int init_super1(struct supertype *st, void **sbp, mdu_array_info_t *info)
 {
        struct mdp_superblock_1 *sb = malloc(1024);
        int spares;
@@ -396,7 +396,7 @@ static int init_super1(void **sbp, mdu_array_info_t *info)
 
        sb->ctime = __cpu_to_le64((unsigned long long)time(0));
        sb->level = __cpu_to_le32(info->level);
-       sb->layout = __cpu_to_le32(info->level);
+       sb->layout = __cpu_to_le32(info->layout);
        sb->size = __cpu_to_le64(info->size*2ULL);
        sb->chunksize = __cpu_to_le32(info->chunk_size>>9);
        sb->raid_disks = __cpu_to_le32(info->raid_disks);
@@ -408,7 +408,7 @@ static int init_super1(void **sbp, mdu_array_info_t *info)
 
        sb->utime = sb->ctime;
        sb->events = __cpu_to_le64(1);
-       if (info->state & MD_SB_CLEAN)
+       if (info->state & (1<<MD_SB_CLEAN))
                sb->resync_offset = ~0ULL;
        else
                sb->resync_offset = 0;
@@ -429,22 +429,60 @@ static void add_to_super1(void *sbv, mdu_disk_info_t *dk)
        __u16 *rp = sb->dev_roles + dk->number;
        if (dk->state == 6) /* active, sync */
                *rp = __cpu_to_le16(dk->raid_disk);
-       else if (dk->state == 2) /* active -> spare */
+       else if ((dk->state & ~2) == 0) /* active or idle -> spare */
                *rp = 0xffff;
        else 
                *rp = 0xfffe;
 }
 
-static int store_super1(int fd, void *sbv)
+static int store_super1(struct supertype *st, int fd, void *sbv)
 {
        struct mdp_superblock_1 *sb = sbv;
-       long long sb_offset;
+       unsigned long long sb_offset;
        int sbsize;
+       long size;
+
+       if (ioctl(fd, BLKGETSIZE, &size))
+               return 1;
+
+
+       if (size < 24)
+               return 2;
+
+       /*
+        * Calculate the position of the superblock.
+        * It is always aligned to a 4K boundary and
+        * depending on minor_version, it can be:
+        * 0: At least 8K, but less than 12K, from end of device
+        * 1: At start of device
+        * 2: 4K from start of device.
+        */
+       switch(st->minor_version) {
+       case 0:
+               sb_offset = size;
+               sb_offset -= 8*2;
+               sb_offset &= ~(4*2-1);
+               break;
+       case 1:
+               sb_offset = 0;
+               break;
+       case 2:
+               sb_offset = 4*2;
+               break;
+       default:
+               return -EINVAL;
+       }
+
 
     
-       sb_offset = __le64_to_cpu(sb->super_offset) << 9;
+       if (sb_offset != __le64_to_cpu(sb->super_offset) &&
+           0 != __le64_to_cpu(sb->super_offset)
+               ) {
+               fprintf(stderr, Name ": internal error - sb_offset is wrong\n");
+               abort();
+       }
 
-       if (lseek64(fd, sb_offset, 0)< 0LL)
+       if (lseek64(fd, sb_offset << 9, 0)< 0LL)
                return 3;
 
        sbsize = sizeof(*sb) + 2 * __le32_to_cpu(sb->max_dev);
@@ -452,13 +490,18 @@ static int store_super1(int fd, void *sbv)
        if (write(fd, sb, sbsize) != sbsize)
                return 4;
 
+       fsync(fd);
        return 0;
 }
 
+static int load_super1(struct supertype *st, int fd, void **sbp, char *devname);
+
 static int write_init_super1(struct supertype *st, void *sbv, mdu_disk_info_t *dinfo, char *devname)
 {
        struct mdp_superblock_1 *sb = sbv;
-       int fd = open(devname, O_RDWR, O_EXCL);
+       struct mdp_superblock_1 *refsb = NULL;
+       int fd = open(devname, O_RDWR | O_EXCL);
+       int rfd;
        int rv;
 
        long size;
@@ -472,17 +515,36 @@ static int write_init_super1(struct supertype *st, void *sbv, mdu_disk_info_t *d
        }
 
        sb->dev_number = __cpu_to_le32(dinfo->number);
-       *(__u32*)(sb->device_uuid) = random();
-       *(__u32*)(sb->device_uuid+4) = random();
-       *(__u32*)(sb->device_uuid+8) = random();
-       *(__u32*)(sb->device_uuid+12) = random();
 
+       if ((rfd = open("/dev/urandom", O_RDONLY)) < 0 ||
+           read(rfd, sb->device_uuid, 16) != 16) {
+               *(__u32*)(sb->device_uuid) = random();
+               *(__u32*)(sb->device_uuid+4) = random();
+               *(__u32*)(sb->device_uuid+8) = random();
+               *(__u32*)(sb->device_uuid+12) = random();
+       }
+       if (rfd >= 0) close(rfd);
+       sb->events = 0;
+
+       if (load_super1(st, fd, (void**)&refsb, NULL)==0) {
+               memcpy(sb->device_uuid, refsb->device_uuid, 16);
+               if (memcmp(sb->set_uuid, refsb->set_uuid, 16)==0) {
+                       /* same array, so preserve events and dev_number */
+                       sb->events = refsb->events;
+                       sb->dev_number = refsb->dev_number;
+               }
+               free(refsb);
+       }
     
-       if (ioctl(fd, BLKGETSIZE, &size))
+       if (ioctl(fd, BLKGETSIZE, &size)) {
+               close(fd);
                return 1;
+       }
 
-       if (size < 24)
+       if (size < 24) {
+               close(fd);
                return 2;
+       }
 
 
        /*
@@ -519,9 +581,10 @@ static int write_init_super1(struct supertype *st, void *sbv, mdu_disk_info_t *d
 
 
        sb->sb_csum = calc_sb_1_csum(sb);
-       rv = store_super1(fd, sb);
+       rv = store_super1(st, fd, sb);
        if (rv)
                fprintf(stderr, Name ": failed to write superblock to %s\n", devname);
+       close(fd);
        return rv;
 }
 
@@ -570,15 +633,34 @@ static int load_super1(struct supertype *st, int fd, void **sbp, char *devname)
 
 
        if (st->ss == NULL) {
-               /* guess... */
+               int bestvers = -1;
+               __u64 bestctime = 0;
+               /* guess... choose latest ctime */
                st->ss = &super1;
                for (st->minor_version = 0; st->minor_version <= 2 ; st->minor_version++) {
                        switch(load_super1(st, fd, sbp, devname)) {
-                       case 0: return 0; /* good */
+                       case 0: super = *sbp;
+                               if (bestvers == -1 ||
+                                   bestctime < __le64_to_cpu(super->ctime)) {
+                                       bestvers = st->minor_version;
+                                       bestctime = __le64_to_cpu(super->ctime);
+                               }
+                               free(super);
+                               *sbp = NULL;
+                               break;
                        case 1: st->ss = NULL; return 1; /*bad device */
                        case 2: break; /* bad, try next */
                        }
                }
+               if (bestvers != -1) {
+                       int rv;
+                       st->minor_version = bestvers;
+                       st->ss = &super1;
+                       st->max_devs = 384;
+                       rv = load_super1(st, fd, sbp, devname);
+                       if (rv) st->ss = NULL;
+                       return rv;
+               }
                st->ss = NULL;
                return 2;
        }
@@ -673,6 +755,7 @@ static struct supertype *match_metadata_desc1(char *arg)
        if (!st) return st;
 
        st->ss = &super1;
+       st->max_devs = 384;
        if (strcmp(arg, "1") == 0 ||
            strcmp(arg, "1.0") == 0) {
                st->minor_version = 0;