]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - Grow.c
Create: cleanup after failed create in duplicated array member case
[thirdparty/mdadm.git] / Grow.c
diff --git a/Grow.c b/Grow.c
index 4423b787d6147257c14bfa91c91512fbdc0109a8..053a372ac3b526f7a5b5cccce6955f02735914ea 100644 (file)
--- a/Grow.c
+++ b/Grow.c
@@ -288,6 +288,11 @@ int Grow_addbitmap(char *devname, int fd, char *file, int chunk, int delay, int
                return 1;
        } else if (strcmp(file, "internal") == 0) {
                int d;
+               if (st->ss->add_internal_bitmap == NULL) {
+                       fprintf(stderr, Name ": Internal bitmaps not supported "
+                               "with %s metadata\n", st->ss->name);
+                       return 1;
+               }
                for (d=0; d< st->max_devs; d++) {
                        mdu_disk_info_t disk;
                        char *dv;
@@ -381,7 +386,7 @@ int Grow_addbitmap(char *devname, int fd, char *file, int chunk, int delay, int
 /*
  * When reshaping an array we might need to backup some data.
  * This is written to all spares with a 'super_block' describing it.
- * The superblock goes 1K form the end of the used space on the
+ * The superblock goes 4K from the end of the used space on the
  * device.
  * It if written after the backup is complete.
  * It has the following structure.
@@ -524,6 +529,15 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
                return 1;
        }
 
+       if (size >= 0 &&
+           (chunksize || level!= UnSet || layout_str || raid_disks)) {
+               fprintf(stderr, Name ": cannot change component size at the same time "
+                       "as other changes.\n"
+                       "   Change size first, then check data is intact before "
+                       "making other changes.\n");
+               return 1;
+       }
+
        if (raid_disks && raid_disks < array.raid_disks && array.level > 1 &&
            get_linux_version() < 2006032 &&
            !check_env("MDADM_FORCE_FEWER")) {
@@ -554,16 +568,27 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
                } else
                        rv = ioctl(fd, SET_ARRAY_INFO, &array);
                if (rv != 0) {
+                       int err = errno;
                        fprintf(stderr, Name ": Cannot set device size for %s: %s\n",
-                               devname, strerror(errno));
+                               devname, strerror(err));
+                       if (err == EBUSY && 
+                           (array.state & (1<<MD_SB_BITMAP_PRESENT)))
+                               fprintf(stderr, "       Bitmap must be removed before size can be changed\n");
                        rv = 1;
                        goto release;
                }
                ioctl(fd, GET_ARRAY_INFO, &array);
+               size = get_component_size(fd)/2;
+               if (size == 0)
+                       size = array.size;
                if (!quiet)
-                       fprintf(stderr, Name ": component size of %s has been set to %dK\n",
-                               devname, array.size);
+                       fprintf(stderr, Name ": component size of %s has been set to %lluK\n",
+                               devname, size);
                changed = 1;
+       } else {
+               size = get_component_size(fd)/2;
+               if (size == 0)
+                       size = array.size;
        }
 
        /* ======= set level =========== */
@@ -644,12 +669,18 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
                                layout_str = "parity-last";
                } else {
                        c = map_num(pers, level);
-                       if (c == NULL)
-                               return 1;/* not possible */
+                       if (c == NULL) {
+                               rv = 1;/* not possible */
+                               goto release;
+                       }
                        err = sysfs_set_str(sra, NULL, "level", c);
                        if (err) {
+                               err = errno;
                                fprintf(stderr, Name ": %s: could not set level to %s\n",
                                        devname, c);
+                               if (err == EBUSY && 
+                                   (array.state & (1<<MD_SB_BITMAP_PRESENT)))
+                                       fprintf(stderr, "       Bitmap must be removed before level can be changed\n");
                                rv = 1;
                                goto release;
                        }
@@ -715,9 +746,14 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
                        c = map_num(pers, level);
                        if (c) {
                                rv = sysfs_set_str(sra, NULL, "level", c);
-                               if (rv)
+                               if (rv) {
+                                       int err = errno;
                                        fprintf(stderr, Name ": %s: could not set level to %s\n",
                                                devname, c);
+                                       if (err == EBUSY && 
+                                           (array.state & (1<<MD_SB_BITMAP_PRESENT)))
+                                               fprintf(stderr, "       Bitmap must be removed before level can be changed\n");
+                               }
                        }
                } else if (!changed && !quiet)
                        fprintf(stderr, Name ": %s: no change requested\n",
@@ -833,10 +869,10 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
 
                if (chunksize) {
                        nchunk = chunksize * 1024;
-                       if (array.size % chunksize) {
-                               fprintf(stderr, Name ": component size %dK is not"
+                       if (size % chunksize) {
+                               fprintf(stderr, Name ": component size %lluK is not"
                                        " a multiple of chunksize %dK\n",
-                                       array.size, chunksize);
+                                       size, chunksize);
                                break;
                        }
                }
@@ -849,7 +885,8 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
                                if (nlayout == UnSet) {
                                        fprintf(stderr, Name ": layout %s not understood for raid5.\n",
                                                layout_str);
-                                       return 1;
+                                       rv = 1;
+                                       goto release;
                                }
                                break;
 
@@ -858,7 +895,8 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
                                if (nlayout == UnSet) {
                                        fprintf(stderr, Name ": layout %s not understood for raid6.\n",
                                                layout_str);
-                                       return 1;
+                                       rv = 1;
+                                       goto release;
                                }
                                break;
                        }
@@ -878,13 +916,12 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
                }
 
                /* Check that we can hold all the data */
-               size = ndata * array.size;
                get_dev_size(fd, NULL, &array_size);
-               if (size < (array_size/1024)) {
+               if (ndata * size < (array_size/1024)) {
                        fprintf(stderr, Name ": this change will reduce the size of the array.\n"
                                "       use --grow --array-size first to truncate array.\n"
                                "       e.g. mdadm --grow %s --array-size %llu\n",
-                               devname, size);
+                               devname, ndata * size);
                        rv = 1;
                        break;
                }
@@ -969,7 +1006,7 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
                                char *dn = map_dev(sd->disk.major,
                                                   sd->disk.minor, 1);
                                fdlist[d] = dev_open(dn, O_RDWR);
-                               offsets[d] = (sra->component_size - blocks - 8)*512;
+                               offsets[d] = (sd->data_offset + sra->component_size - blocks - 8)*512;
                                if (fdlist[d]<0) {
                                        fprintf(stderr, Name ": %s: cannot open component %s\n",
                                                devname, dn?dn:"-unknown");
@@ -1047,12 +1084,16 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
                if (ochunk == nchunk && olayout == nlayout) {
                        array.raid_disks = ndisks;
                        if (ioctl(fd, SET_ARRAY_INFO, &array) != 0) {
+                               int err = errno;
                                rv = 1;
                                fprintf(stderr, Name ": Cannot set device shape for %s: %s\n",
                                        devname, strerror(errno));
                                if (ndisks < odisks &&
                                    get_linux_version() < 2006030)
                                        fprintf(stderr, Name ": linux 2.6.30 or later required\n");
+                               if (err == EBUSY && 
+                                   (array.state & (1<<MD_SB_BITMAP_PRESENT)))
+                                       fprintf(stderr, "       Bitmap must be removed before shape can be changed\n");
 
                                break;
                        }
@@ -1060,17 +1101,24 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
                        /* set them all just in case some old 'new_*' value
                         * persists from some earlier problem
                         */
+                       int err = err; /* only used if rv==1, and always set if
+                                       * rv==1, so initialisation not needed,
+                                       * despite gcc warning
+                                       */
                        if (sysfs_set_num(sra, NULL, "chunk_size", nchunk) < 0)
-                               rv = 1;
-                       if (sysfs_set_num(sra, NULL, "layout", nlayout) < 0)
-                               rv = 1;
-                       if (sysfs_set_num(sra, NULL, "raid_disks", ndisks) < 0)
-                               rv = 1;
+                               rv = 1, err = errno;
+                       if (!rv && sysfs_set_num(sra, NULL, "layout", nlayout) < 0)
+                               rv = 1, err = errno;
+                       if (!rv && sysfs_set_num(sra, NULL, "raid_disks", ndisks) < 0)
+                               rv = 1, err = errno;
                        if (rv) {
                                fprintf(stderr, Name ": Cannot set device shape for %s\n",
                                        devname);
                                if (get_linux_version() < 2006030)
                                        fprintf(stderr, Name ": linux 2.6.30 or later required\n");
+                               if (err == EBUSY && 
+                                   (array.state & (1<<MD_SB_BITMAP_PRESENT)))
+                                       fprintf(stderr, "       Bitmap must be removed before shape can be changed\n");
                                break;
                        }
                }
@@ -1211,6 +1259,7 @@ int Grow_reshape(char *devname, int fd, int quiet, char *backup_file,
  * 
  */
 
+/* FIXME return status is never checked */
 int grow_backup(struct mdinfo *sra,
                unsigned long long offset, /* per device */
                unsigned long stripes, /* per device */
@@ -1293,12 +1342,19 @@ int grow_backup(struct mdinfo *sra,
                        bsb.sb_csum2 = bsb_csum((char*)&bsb,
                                                ((char*)&bsb.sb_csum2)-((char*)&bsb));
 
-               lseek64(destfd[i], destoffsets[i] - 4096, 0);
-               write(destfd[i], &bsb, 512);
+               if (lseek64(destfd[i], destoffsets[i] - 4096, 0) != destoffsets[i] - 4096)
+                       rv = 1;
+               rv = rv ?: write(destfd[i], &bsb, 512);
+               if (destoffsets[i] > 4096) {
+                       if (lseek64(destfd[i], destoffsets[i]+stripes*chunk*odata, 0) !=
+                           destoffsets[i]+stripes*chunk*odata)
+                               rv = 1;
+                       rv = rv ?: write(destfd[i], &bsb, 512);
+               }
                fsync(destfd[i]);
        }
 
-       return 0;
+       return rv;
 }
 
 /* in 2.6.30, the value reported by sync_completed can be
@@ -1311,6 +1367,7 @@ int grow_backup(struct mdinfo *sra,
  * The various caller give appropriate values so that
  * every works.
  */
+/* FIXME return value is often ignored */
 int wait_backup(struct mdinfo *sra,
                unsigned long long offset, /* per device */
                unsigned long long blocks, /* per device */
@@ -1324,6 +1381,7 @@ int wait_backup(struct mdinfo *sra,
        int fd = sysfs_get_fd(sra, NULL, "sync_completed");
        unsigned long long completed;
        int i;
+       int rv;
 
        if (fd < 0)
                return -1;
@@ -1355,24 +1413,28 @@ int wait_backup(struct mdinfo *sra,
                bsb.length = __cpu_to_le64(0);
        }
        bsb.mtime = __cpu_to_le64(time(0));
+       rv = 0;
        for (i = 0; i < dests; i++) {
                bsb.devstart = __cpu_to_le64(destoffsets[i]/512);
                bsb.sb_csum = bsb_csum((char*)&bsb, ((char*)&bsb.sb_csum)-((char*)&bsb));
                if (memcmp(bsb.magic, "md_backup_data-2", 16) == 0)
                        bsb.sb_csum2 = bsb_csum((char*)&bsb,
                                                ((char*)&bsb.sb_csum2)-((char*)&bsb));
-               lseek64(destfd[i], destoffsets[i]-4096, 0);
-               write(destfd[i], &bsb, 512);
+               if (lseek64(destfd[i], destoffsets[i]-4096, 0) !=
+                   destoffsets[i]-4096)
+                       rv = 1;
+               rv = rv ?: write(destfd[i], &bsb, 512);
                fsync(destfd[i]);
        }
-       return 0;
+       return rv;
 }
 
 static void fail(char *msg)
 {
-       write(2, msg, strlen(msg));
-       write(2, "\n", 1);
-       exit(1);
+       int rv;
+       rv = write(2, msg, strlen(msg));
+       rv |= write(2, "\n", 1);
+       exit(rv ? 1 : 2);
 }
 
 static char *abuf, *bbuf;
@@ -1408,27 +1470,33 @@ static void validate(int afd, int bfd, unsigned long long offset)
                        free(abuf);
                        free(bbuf);
                        abuflen = len;
-                       posix_memalign((void**)&abuf, 4096, abuflen);
-                       posix_memalign((void**)&bbuf, 4096, abuflen);
+                       if (posix_memalign((void**)&abuf, 4096, abuflen) ||
+                           posix_memalign((void**)&bbuf, 4096, abuflen)) {
+                               abuflen = 0;
+                               /* just stop validating on mem-alloc failure */
+                               return;
+                       }
                }
 
                lseek64(bfd, offset, 0);
                if (read(bfd, bbuf, len) != len) {
-                       printf("len %llu\n", len);
+                       //printf("len %llu\n", len);
                        fail("read first backup failed");
                }
                lseek64(afd, __le64_to_cpu(bsb2.arraystart)*512, 0);
                if (read(afd, abuf, len) != len)
                        fail("read first from array failed");
                if (memcmp(bbuf, abuf, len) != 0) {
+                       #if 0
                        int i;
                        printf("offset=%llu len=%llu\n",
-                              __le64_to_cpu(bsb2.arraystart)*512, len);
+                              (unsigned long long)__le64_to_cpu(bsb2.arraystart)*512, len);
                        for (i=0; i<len; i++)
                                if (bbuf[i] != abuf[i]) {
                                        printf("first diff byte %d\n", i);
                                        break;
                                }
+                       #endif
                        fail("data1 compare failed");
                }
        }
@@ -1462,7 +1530,9 @@ static int child_grow(int afd, struct mdinfo *sra, unsigned long stripes,
        char *buf;
        int degraded = 0;
 
-       posix_memalign((void**)&buf, 4096, disks * chunk);
+       if (posix_memalign((void**)&buf, 4096, disks * chunk))
+               /* Don't start the 'reshape' */
+               return 0;
        sysfs_set_num(sra, NULL, "suspend_hi", 0);
        sysfs_set_num(sra, NULL, "suspend_lo", 0);
        grow_backup(sra, 0, stripes,
@@ -1490,7 +1560,8 @@ static int child_shrink(int afd, struct mdinfo *sra, unsigned long stripes,
        int rv;
        int degraded = 0;
 
-       posix_memalign((void**)&buf, 4096, disks * chunk);
+       if (posix_memalign((void**)&buf, 4096, disks * chunk))
+               return 0;
        start = sra->component_size - stripes * chunk/512;
        sysfs_set_num(sra, NULL, "sync_max", start);
        sysfs_set_str(sra, NULL, "sync_action", "reshape");
@@ -1529,7 +1600,8 @@ static int child_same_size(int afd, struct mdinfo *sra, unsigned long stripes,
        int degraded = 0;
 
 
-       posix_memalign((void**)&buf, 4096, disks * chunk);
+       if (posix_memalign((void**)&buf, 4096, disks * chunk))
+               return 0;
 
        sysfs_set_num(sra, NULL, "suspend_lo", 0);
        sysfs_set_num(sra, NULL, "suspend_hi", 0);
@@ -1615,7 +1687,6 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt
                old_disks = cnt;
        for (i=old_disks-(backup_file?1:0); i<cnt; i++) {
                struct mdinfo dinfo;
-               char buf[4096];
                int fd;
                int bsbsize;
                char *devname, namebuf[20];
@@ -1682,11 +1753,23 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt
                        continue; /* Wrong uuid */
                }
 
-               if (info->array.utime > __le64_to_cpu(bsb.mtime) + 10*60 ||
+               /* array utime and backup-mtime should be updated at much the same time, but it seems that
+                * sometimes they aren't... So allow considerable flexability in matching, and allow
+                * this test to be overridden by an environment variable.
+                */
+               if (info->array.utime > __le64_to_cpu(bsb.mtime) + 2*60*60 ||
                    info->array.utime < __le64_to_cpu(bsb.mtime) - 10*60) {
-                       if (verbose)
-                               fprintf(stderr, Name ": too-old timestamp on backup-metadata on %s\n", devname);
-                       continue; /* time stamp is too bad */
+                       if (check_env("MDADM_GROW_ALLOW_OLD")) {
+                               fprintf(stderr, Name ": accepting backup with timestamp %lu "
+                                       "for array with timestamp %lu\n",
+                                       (unsigned long)__le64_to_cpu(bsb.mtime),
+                                       (unsigned long)info->array.utime);
+                       } else {
+                               if (verbose)
+                                       fprintf(stderr, Name ": too-old timestamp on "
+                                               "backup-metadata on %s\n", devname);
+                               continue; /* time stamp is too bad */
+                       }
                }
 
                if (bsb.magic[15] == '1') {
@@ -1731,13 +1814,13 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt
                }
                /* There should be a duplicate backup superblock 4k before here */
                if (lseek64(fd, -4096, 1) < 0 ||
-                   read(fd, buf, 4096) != 4096)
+                   read(fd, &bsb2, 4096) != 4096)
                        goto second_fail; /* Cannot find leading superblock */
                if (bsb.magic[15] == '1')
                        bsbsize = offsetof(struct mdp_backup_super, pad1);
                else
                        bsbsize = offsetof(struct mdp_backup_super, pad);
-               if (memcmp(buf, &bsb, bsbsize) != 0)
+               if (memcmp(&bsb2, &bsb, bsbsize) != 0)
                        goto second_fail; /* Cannot find leading superblock */
 
                /* Now need the data offsets for all devices. */
@@ -1750,7 +1833,7 @@ int Grow_restart(struct supertype *st, struct mdinfo *info, int *fdlist, int cnt
                                continue;
                        st->ss->getinfo_super(st, &dinfo);
                        st->ss->free_super(st);
-                       offsets[j] = dinfo.data_offset;
+                       offsets[j] = dinfo.data_offset * 512;
                }
                printf(Name ": restoring critical section\n");