summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Dump.c311
-rw-r--r--Makefile2
-rw-r--r--ReadMe.c3
-rw-r--r--mdadm.8.in49
-rw-r--r--mdadm.c26
-rw-r--r--mdadm.h9
-rw-r--r--super-ddf.c63
-rw-r--r--super-intel.c54
-rw-r--r--super0.c47
-rw-r--r--super1.c138
10 files changed, 697 insertions, 5 deletions
diff --git a/Dump.c b/Dump.c
new file mode 100644
index 0000000..7bdbf6f
--- /dev/null
+++ b/Dump.c
@@ -0,0 +1,311 @@
+/*
+ * mdadm - manage Linux "md" devices aka RAID arrays.
+ *
+ * Copyright (C) 2013 Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * Author: Neil Brown
+ * Email: <neilb@suse.de>
+ */
+
+#include "mdadm.h"
+#include <sys/dir.h>
+
+int Dump_metadata(char *dev, char *dir, struct context *c,
+ struct supertype *st)
+{
+ /* create a new file in 'dir' named for the basename of 'dev'.
+ * Truncate to the same size as 'dev' and ask the metadata
+ * handler to copy metadata there.
+ * For every name in /dev/disk/by-id that points to this device,
+ * create a hardlink in 'dir'.
+ * Complain if any of those hardlinks cannot be created.
+ */
+ int fd, fl;
+ struct stat stb, dstb;
+ char *base;
+ char *fname = NULL;
+ unsigned long long size;
+ DIR *dirp;
+ struct dirent *de;
+
+ if (stat(dir, &stb) != 0 ||
+ (S_IFMT & stb.st_mode) != S_IFDIR) {
+ pr_err("--dump requires an existing directory, not: %s\n",
+ dir);
+ return 16;
+ }
+
+ fd = dev_open(dev, O_RDONLY);
+ if (fd < 0) {
+ pr_err("Cannot open %s to dump metadata: %s\n",
+ dev, strerror(errno));
+ return 1;
+ }
+ if (!get_dev_size(fd, dev, &size)) {
+ close(fd);
+ return 1;
+ }
+
+ if (st == NULL)
+ st = guess_super_type(fd, guess_array);
+ if (!st) {
+ pr_err("Cannot find RAID metadata on %s\n", dev);
+ close(fd);
+ return 1;
+ }
+
+ st->ignore_hw_compat = 1;
+ if (st->ss->load_super(st, fd, NULL) != 0) {
+ pr_err("No %s metadata found on %s\n",
+ st->ss->name, dev);
+ close(fd);
+ return 1;
+ }
+ if (st->ss->copy_metadata == NULL) {
+ pr_err("%s metadata on %s cannot be copied\n",
+ st->ss->name, dev);
+ close(fd);
+ return 1;
+ }
+
+ base = strrchr(dev, '/');
+ if (base)
+ base++;
+ else
+ base = dev;
+ xasprintf(&fname, "%s/%s", dir, base);
+ fl = open(fname, O_RDWR|O_CREAT|O_EXCL, 0666);
+ if (fl < 0) {
+ pr_err("Cannot create dump file %s: %s\n",
+ fname, strerror(errno));
+ close(fd);
+ free(fname);
+ return 1;
+ }
+ if (ftruncate(fl, size) < 0) {
+ pr_err("failed to set size of dump file: %s\n",
+ strerror(errno));
+ close(fd);
+ close(fl);
+ free(fname);
+ return 1;
+ }
+
+ if (st->ss->copy_metadata(st, fd, fl) != 0) {
+ pr_err("Failed to copy metadata from %s to %s\n",
+ dev, fname);
+ close(fd);
+ close(fl);
+ unlink(fname);
+ free(fname);
+ return 1;
+ }
+ if (c->verbose >= 0)
+ printf("%s saved as %s.\n", dev, fname);
+ fstat(fd, &dstb);
+ close(fd);
+ close(fl);
+ if ((dstb.st_mode & S_IFMT) != S_IFBLK) {
+ /* Not a block device, so cannot create links */
+ free(fname);
+ return 0;
+ }
+ /* mostly done: just want to find some other names */
+ dirp = opendir("/dev/disk/by-id");
+ if (!dirp) {
+ free(fname);
+ return 0;
+ }
+ while ((de = readdir(dirp)) != NULL) {
+ char *p = NULL;
+ if (de->d_name[0] == '.')
+ continue;
+ xasprintf(&p, "/dev/disk/by-id/%s", de->d_name);
+ if (stat(p, &stb) != 0 ||
+ (stb.st_mode & S_IFMT) != S_IFBLK ||
+ stb.st_rdev != dstb.st_rdev) {
+ /* Not this one */
+ free(p);
+ continue;
+ }
+ free(p);
+ xasprintf(&p, "%s/%s", dir, de->d_name);
+ if (link(fname, p) == 0) {
+ if (c->verbose >= 0)
+ printf("%s also saved as %s.\n",
+ dev, p);
+ } else {
+ pr_err("Could not save %s as %s!!\n",
+ dev, p);
+ }
+ free(p);
+ }
+ closedir(dirp);
+ free(fname);
+ return 0;
+}
+
+int Restore_metadata(char *dev, char *dir, struct context *c,
+ struct supertype *st, int only)
+{
+ /* If 'dir' really is a directory we choose a name
+ * from it that matches a suitable name in /dev/disk/by-id,
+ * and copy metadata from the file to the device.
+ * If two names from by-id match and aren't both the same
+ * inode, we fail. If none match and basename of 'dev'
+ * can be found in dir, use that.
+ * If 'dir' is really a file then it is only permitted if
+ * 'only' is set (meaning there was only one device given)
+ * and the metadata is restored irrespective of file names.
+ */
+ int fd, fl;
+ struct stat stb, dstb;
+ char *fname = NULL;
+ unsigned long long size;
+
+ if (stat(dir, &stb) != 0) {
+ pr_err("%s does not exist: cannot restore from there.\n",
+ dir);
+ return 16;
+ } else if ((S_IFMT & stb.st_mode) != S_IFDIR && !only) {
+ pr_err("--restore requires a directory when multiple devices given\n");
+ return 16;
+ }
+
+ fd = dev_open(dev, O_RDWR);
+ if (fd < 0) {
+ pr_err("Cannot open %s to restore metadata: %s\n",
+ dev, strerror(errno));
+ return 1;
+ }
+ if (!get_dev_size(fd, dev, &size)) {
+ close(fd);
+ return 1;
+ }
+
+ if ((S_IFMT & stb.st_mode) == S_IFDIR) {
+ /* choose one name from the directory. */
+ DIR *d = opendir(dir);
+ struct dirent *de;
+ char *chosen = NULL;
+ unsigned int chosen_inode = 0;
+
+ fstat(fd, &dstb);
+
+ while (d && (de = readdir(d)) != NULL) {
+ if (de->d_name[0] == '.')
+ continue;
+ xasprintf(&fname, "/dev/disk/by-id/%s", de->d_name);
+ if (stat(fname, &stb) != 0) {
+ free(fname);
+ continue;
+ }
+ free(fname);
+ if ((S_IFMT & stb.st_mode) != S_IFBLK)
+ continue;
+ if (stb.st_rdev != dstb.st_rdev)
+ continue;
+ /* This file is a good match for our device. */
+ xasprintf(&fname, "%s/%s", dir, de->d_name);
+ if (stat(fname, &stb) != 0) {
+ /* Weird! */
+ free(fname);
+ continue;
+ }
+ if (chosen == NULL) {
+ chosen = fname;
+ chosen_inode = stb.st_ino;
+ continue;
+ }
+ if (chosen_inode == stb.st_ino) {
+ /* same, no need to change */
+ free(fname);
+ continue;
+ }
+ /* Oh dear, two names both match. Must give up. */
+ pr_err("Both %s and %s seem suitable for %s. Please choose one.\n",
+ chosen, fname, dev);
+ free(fname);
+ free(chosen);
+ close(fd);
+ closedir(d);
+ return 1;
+ }
+ closedir(d);
+ if (!chosen) {
+ /* One last chance: try basename of device */
+ char *base = strrchr(dev, '/');
+ if (base)
+ base++;
+ else
+ base = dev;
+ xasprintf(&fname, "%s/%s", dir, base);
+ if (stat(fname, &stb) == 0)
+ chosen = fname;
+ else
+ free(fname);
+ }
+ fname = chosen;
+ } else
+ fname = strdup(dir);
+
+ if (!fname) {
+ pr_err("Cannot find suitable file in %s for %s\n",
+ dir, dev);
+ close(fd);
+ return 1;
+ }
+
+ fl = open(fname, O_RDONLY);
+ if (!fl) {
+ pr_err("Could not open %s for --restore.\n",
+ fname);
+ goto err;
+ }
+ if (((unsigned long long)stb.st_size) != size) {
+ pr_err("%s is not the same size as %s - cannot restore.\n",
+ fname, dev);
+ goto err;
+ }
+ if (st == NULL)
+ st = guess_super_type(fl, guess_array);
+ if (!st) {
+ pr_err("Cannot find metadata on %s\n", fname);
+ goto err;
+ }
+ st->ignore_hw_compat = 1;
+ if (st->ss->load_super(st, fl, NULL) != 0) {
+ pr_err("No %s metadata found on %s\n",
+ st->ss->name, fname);
+ goto err;
+ }
+ if (st->ss->copy_metadata == NULL) {
+ pr_err("%s metadata on %s cannot be copied\n",
+ st->ss->name, dev);
+ goto err;
+ }
+ if (st->ss->copy_metadata(st, fl, fd) != 0) {
+ pr_err("Failed to copy metadata from %s to %s\n",
+ fname, dev);
+ goto err;
+ }
+ if (c->verbose >= 0)
+ printf("%s restored from %s.\n", dev, fname);
+ return 0;
+
+err:
+ close(fd);
+ close(fl);
+ free(fname);
+ return 1;
+}
diff --git a/Makefile b/Makefile
index ad0819d..8cb8d51 100644
--- a/Makefile
+++ b/Makefile
@@ -107,7 +107,7 @@ endif
OBJS = mdadm.o config.o policy.o mdstat.o ReadMe.o util.o maps.o lib.o \
Manage.o Assemble.o Build.o \
Create.o Detail.o Examine.o Grow.o Monitor.o dlink.o Kill.o Query.o \
- Incremental.o \
+ Incremental.o Dump.o \
mdopen.o super0.o super1.o super-ddf.o super-intel.o bitmap.o \
super-mbr.o super-gpt.o \
restripe.o sysfs.o sha1.o mapfile.o crc32.o sg_io.o msg.o xmalloc.o \
diff --git a/ReadMe.c b/ReadMe.c
index c4bb730..cb0678a 100644
--- a/ReadMe.c
+++ b/ReadMe.c
@@ -97,6 +97,9 @@ struct option long_options[] = {
{"offroot", 0, 0, OffRootOpt},
{"examine-badblocks", 0, 0, ExamineBB},
+ {"dump", 1, 0, Dump},
+ {"restore", 1, 0, Restore},
+
/* synonyms */
{"monitor", 0, 0, 'F'},
diff --git a/mdadm.8.in b/mdadm.8.in
index c876659..c8559da 100644
--- a/mdadm.8.in
+++ b/mdadm.8.in
@@ -1449,6 +1449,12 @@ been configured. Currently only
metadata supports bad-blocks lists.
.TP
+.BI \-\-dump= directory
+.TP
+.BI \-\-restore= directory
+Save metadata from lists devices, or restore metadata to listed devices.
+
+.TP
.BR \-R ", " \-\-run
start a partially assembled array. If
.B \-\-assemble
@@ -2136,6 +2142,49 @@ without listing any devices will cause all devices listed in the
config file to be examined.
.TP
+.BI \-\-dump= directory
+If the device contains RAID metadata, a file will be created in the
+.I directory
+and the metadata will be written to it. The file will be the same
+size as the device and have the metadata written in the file at the
+same locate that it exists in the device. However the file will be "sparse" so
+that only those blocks containing metadata will be allocated. The
+total space used will be small.
+
+The file name used in the
+.I directory
+will be the base name of the device. Further if any links appear in
+.I /dev/disk/by-id
+which point to the device, then hard links to the file will be created
+in
+.I directory
+based on these
+.I by-id
+names.
+
+Multiple devices can be listed and their metadata will all be stored
+in the one directory.
+
+.TP
+.BI \-\-restore= directory
+This is the reverse of
+.BR \-\-dump .
+.I mdadm
+will locate a file in the directory that has a name appropriate for
+the given device and will restore metadata from it. Names that match
+.I /dev/disk/by-id
+names are preferred, however if two of those refer to different files,
+.I mdadm
+will not choose between them but will abort the operation.
+
+If a file name is given instead of a
+.I directory
+then
+.I mdadm
+will restore from that file to a single device, always provided the
+size of the file matches that of the device, and the file contains
+valid metadata.
+.TP
.B \-\-stop
The devices should be active md arrays which will be deactivated, as
long as they are not currently in use.
diff --git a/mdadm.c b/mdadm.c
index e053db5..ff3fed7 100644
--- a/mdadm.c
+++ b/mdadm.c
@@ -37,6 +37,7 @@ static int misc_scan(char devmode, struct context *c);
static int stop_scan(int verbose);
static int misc_list(struct mddev_dev *devlist,
struct mddev_ident *ident,
+ char *dump_directory,
struct supertype *ss, struct context *c);
@@ -94,6 +95,7 @@ int main(int argc, char *argv[])
int rebuild_map = 0;
char *remove_path = NULL;
char *udev_filename = NULL;
+ char *dump_directory = NULL;
int print_help = 0;
FILE *outf;
@@ -234,6 +236,8 @@ int main(int argc, char *argv[])
case 'X':
case 'Q':
case ExamineBB:
+ case Dump:
+ case Restore:
newmode = MISC;
break;
@@ -982,6 +986,8 @@ int main(int argc, char *argv[])
case O(MISC, DetailPlatform):
case O(MISC, KillSubarray):
case O(MISC, UpdateSubarray):
+ case O(MISC, Dump):
+ case O(MISC, Restore):
if (opt == KillSubarray || opt == UpdateSubarray) {
if (c.subarray) {
pr_err("subarray can only"
@@ -1006,6 +1012,14 @@ int main(int argc, char *argv[])
exit(2);
}
devmode = opt;
+ if (opt == Dump || opt == Restore) {
+ if (dump_directory != NULL) {
+ pr_err("dump/restore directory specified twice: %s and %s\n",
+ dump_directory, optarg);
+ exit(2);
+ }
+ dump_directory = optarg;
+ }
continue;
case O(MISC, UdevRules):
if (devmode && devmode != opt) {
@@ -1407,7 +1421,7 @@ int main(int argc, char *argv[])
exit(2);
}
} else
- rv = misc_list(devlist, &ident, ss, &c);
+ rv = misc_list(devlist, &ident, dump_directory, ss, &c);
break;
case MONITOR:
if (!devlist && !c.scan) {
@@ -1721,12 +1735,13 @@ static int stop_scan(int verbose)
static int misc_list(struct mddev_dev *devlist,
struct mddev_ident *ident,
+ char *dump_directory,
struct supertype *ss, struct context *c)
{
struct mddev_dev *dv;
int rv = 0;
- for (dv=devlist ; dv; dv=dv->next) {
+ for (dv=devlist ; dv; dv=(rv & 16) ? NULL : dv->next) {
int mdfd;
switch(dv->disposition) {
@@ -1768,6 +1783,13 @@ static int misc_list(struct mddev_dev *devlist,
rv |= Update_subarray(dv->devname, c->subarray,
c->update, ident, c->verbose);
continue;
+ case Dump:
+ rv |= Dump_metadata(dv->devname, dump_directory, c, ss);
+ continue;
+ case Restore:
+ rv |= Restore_metadata(dv->devname, dump_directory, c, ss,
+ (dv == devlist && dv->next == NULL));
+ continue;
}
mdfd = open_mddev(dv->devname, 1);
if (mdfd>=0) {
diff --git a/mdadm.h b/mdadm.h
index 3139259..3244c3d 100644
--- a/mdadm.h
+++ b/mdadm.h
@@ -339,6 +339,8 @@ enum special_options {
KillOpt,
DataOffset,
ExamineBB,
+ Dump,
+ Restore,
};
enum prefix_standard {
@@ -630,7 +632,7 @@ struct reshape {
/* A superswitch provides entry point the a metadata handler.
*
- * The super_switch primarily operates on some "metadata" that
+ * The superswitch primarily operates on some "metadata" that
* is accessed via the 'supertype'.
* This metadata has one of three possible sources.
* 1/ It is read from a single device. In this case it may not completely
@@ -665,6 +667,7 @@ extern struct superswitch {
void (*brief_examine_subarrays)(struct supertype *st, int verbose);
void (*export_examine_super)(struct supertype *st);
int (*examine_badblocks)(struct supertype *st, int fd, char *devname);
+ int (*copy_metadata)(struct supertype *st, int from, int to);
/* Used to report details of an active array.
* ->load_super was possibly given a 'component' string.
@@ -1177,6 +1180,10 @@ extern int ExamineBitmap(char *filename, int brief, struct supertype *st);
extern int Write_rules(char *rule_name);
extern int bitmap_update_uuid(int fd, int *uuid, int swap);
extern unsigned long bitmap_sectors(struct bitmap_super_s *bsb);
+extern int Dump_metadata(char *dev, char *dir, struct context *c,
+ struct supertype *st);
+extern int Restore_metadata(char *dev, char *dir, struct context *c,
+ struct supertype *st, int only);
extern int md_get_version(int fd);
extern int get_linux_version(void);
diff --git a/super-ddf.c b/super-ddf.c
index a78692c..9192ccd 100644
--- a/super-ddf.c
+++ b/super-ddf.c
@@ -1327,6 +1327,68 @@ static void export_examine_super_ddf(struct supertype *st)
printf("MD_UUID=%s\n", nbuf+5);
}
+static int copy_metadata_ddf(struct supertype *st, int from, int to)
+{
+ void *buf;
+ unsigned long long dsize, offset;
+ int bytes;
+ struct ddf_header *ddf;
+ int written = 0;
+
+ /* The meta consists of an anchor, a primary, and a secondary.
+ * This all lives at the end of the device.
+ * So it is easiest to find the earliest of primary and
+ * secondary, and copy everything from there.
+ *
+ * Anchor is 512 from end It contains primary_lba and secondary_lba
+ * we choose one of those
+ */
+
+ if (posix_memalign(&buf, 4096, 4096) != 0)
+ return 1;
+
+ if (!get_dev_size(from, NULL, &dsize))
+ goto err;
+
+ if (lseek64(from, dsize-512, 0) < 0)
+ goto err;
+ if (read(from, buf, 512) != 512)
+ goto err;
+ ddf = buf;
+ if (ddf->magic != DDF_HEADER_MAGIC ||
+ calc_crc(ddf, 512) != ddf->crc ||
+ (memcmp(ddf->revision, DDF_REVISION_0, 8) != 0 &&
+ memcmp(ddf->revision, DDF_REVISION_2, 8) != 0))
+ goto err;
+
+ offset = dsize - 512;
+ if ((__be64_to_cpu(ddf->primary_lba) << 9) < offset)
+ offset = __be64_to_cpu(ddf->primary_lba) << 9;
+ if ((__be64_to_cpu(ddf->secondary_lba) << 9) < offset)
+ offset = __be64_to_cpu(ddf->secondary_lba) << 9;
+
+ bytes = dsize - offset;
+
+ if (lseek64(from, offset, 0) < 0 ||
+ lseek64(to, offset, 0) < 0)
+ goto err;
+ while (written < bytes) {
+ int n = bytes - written;
+ if (n > 4096)
+ n = 4096;
+ if (read(from, buf, n) != n)
+ goto err;
+ if (write(to, buf, n) != n)
+ goto err;
+ written += n;
+ }
+ free(buf);
+ return 0;
+err:
+ free(buf);
+ return 1;
+}
+
static void detail_super_ddf(struct supertype *st, char *homehost)
{
/* FIXME later
@@ -4298,6 +4360,7 @@ struct superswitch super_ddf = {
.add_to_super = add_to_super_ddf,
.remove_from_super = remove_from_super_ddf,
.load_container = load_container_ddf,
+ .copy_metadata = copy_metadata_ddf,
#endif
.match_home = match_home_ddf,
.uuid_from_super= uuid_from_super_ddf,
diff --git a/super-intel.c b/super-intel.c
index 2cb28b0..e0b86b6 100644
--- a/super-intel.c
+++ b/super-intel.c
@@ -1510,6 +1510,59 @@ static void export_examine_super_imsm(struct supertype *st)
printf("MD_DEVICES=%u\n", mpb->num_disks);
}
+static int copy_metadata_imsm(struct supertype *st, int from, int to)
+{
+ /* The second last 512byte sector of the device contains
+ * the "struct imsm_super" metadata.
+ * This contains mpb_size which is the size in bytes of the
+ * extended metadata. This is located immediately before
+ * the imsm_super.
+ * We want to read all that, plus the last sector which
+ * may contain a migration record, and write it all
+ * to the target.
+ */
+ void *buf;
+ unsigned long long dsize, offset;
+ int sectors;
+ struct imsm_super *sb;
+ int written = 0;
+
+ if (posix_memalign(&buf, 4096, 4096) != 0)
+ return 1;
+
+ if (!get_dev_size(from, NULL, &dsize))
+ goto err;
+
+ if (lseek64(from, dsize-1024, 0) < 0)
+ goto err;
+ if (read(from, buf, 512) != 512)
+ goto err;
+ sb = buf;
+ if (strncmp((char*)sb->sig, MPB_SIGNATURE, MPB_SIG_LEN) != 0)
+ goto err;
+
+ sectors = mpb_sectors(sb) + 2;
+ offset = dsize - sectors * 512;
+ if (lseek64(from, offset, 0) < 0 ||
+ lseek64(to, offset, 0) < 0)
+ goto err;
+ while (written < sectors * 512) {
+ int n = sectors*512 - written;
+ if (n > 4096)
+ n = 4096;
+ if (read(from, buf, n) != n)
+ goto err;
+ if (write(to, buf, n) != n)
+ goto err;
+ written += n;
+ }
+ free(buf);
+ return 0;
+err:
+ free(buf);
+ return 1;
+}
+
static void detail_super_imsm(struct supertype *st, char *homehost)
{
struct mdinfo info;
@@ -10463,6 +10516,7 @@ struct superswitch super_imsm = {
.reshape_super = imsm_reshape_super,
.manage_reshape = imsm_manage_reshape,
.recover_backup = recover_backup_imsm,
+ .copy_metadata = copy_metadata_imsm,
#endif
.match_home = match_home_imsm,
.uuid_from_super= uuid_from_super_imsm,
diff --git a/super0.c b/super0.c
index 1f4dc75..58785e2 100644
--- a/super0.c
+++ b/super0.c
@@ -47,7 +47,6 @@ static unsigned long calc_sb0_csum(mdp_super_t *super)
return newcsum;
}
-
static void super0_swap_endian(struct mdp_superblock_s *sb)
{
/* as super0 superblocks are host-endian, it is sometimes
@@ -281,6 +280,51 @@ static void export_examine_super0(struct supertype *st)
+ sb->events_lo);
}
+static int copy_metadata0(struct supertype *st, int from, int to)
+{
+ /* Read 64K from the appropriate offset of 'from'
+ * and if it looks a little like a 0.90 superblock,
+ * write it to the same offset of 'to'
+ */
+ void *buf;
+ unsigned long long dsize, offset;
+ const int bufsize = 64*1024;
+ mdp_super_t *super;
+
+ if (posix_memalign(&buf, 4096, bufsize) != 0)
+ return 1;
+
+ if (!get_dev_size(from, NULL, &dsize))
+ goto err;
+
+ if (dsize < MD_RESERVED_SECTORS*512)
+ goto err;
+
+ offset = MD_NEW_SIZE_SECTORS(dsize>>9);
+
+ offset *= 512;
+
+ if (lseek64(from, offset, 0) < 0LL)
+ goto err;
+ if (read(from, buf, bufsize) != bufsize)
+ goto err;
+
+ if (lseek64(to, offset, 0) < 0LL)
+ goto err;
+ super = buf;
+ if (super->md_magic != MD_SB_MAGIC ||
+ super->major_version != 0 ||
+ calc_sb0_csum(super) != super->sb_csum)
+ goto err;
+ if (write(to, buf, bufsize) != bufsize)
+ goto err;
+ free(buf);
+ return 0;
+err:
+ free(buf);
+ return 1;
+}
+
static void detail_super0(struct supertype *st, char *homehost)
{
mdp_super_t *sb = st->sb;
@@ -1201,6 +1245,7 @@ struct superswitch super0 = {
.write_init_super = write_init_super0,
.validate_geometry = validate_geometry0,
.add_to_super = add_to_super0,
+ .copy_metadata = copy_metadata0,
#endif
.match_home = match_home0,
.uuid_from_super = uuid_from_super0,
diff --git a/super1.c b/super1.c
index 92e51f7..89f441f 100644
--- a/super1.c
+++ b/super1.c
@@ -597,6 +597,143 @@ static void export_examine_super1(struct supertype *st)
(unsigned long long)__le64_to_cpu(sb->events));
}
+static int copy_metadata1(struct supertype *st, int from, int to)
+{
+ /* Read superblock. If it looks good, write it out.
+ * Then if a bitmap is present, copy that.
+ * And if a bad-block-list is present, copy that too.
+ */
+ void *buf;
+ unsigned long long dsize, sb_offset;
+ const int bufsize = 4*1024;
+ struct mdp_superblock_1 super, *sb;
+
+ if (posix_memalign(&buf, 4096, bufsize) != 0)
+ return 1;
+
+ if (!get_dev_size(from, NULL, &dsize))
+ goto err;
+
+ dsize >>= 9;
+ if (dsize < 24)
+ goto err;
+ switch(st->minor_version) {
+ case 0:
+ sb_offset = dsize;
+ sb_offset -= 8*2;
+ sb_offset &= ~(4*2-1);
+ break;
+ case 1:
+ sb_offset = 0;
+ break;
+ case 2:
+ sb_offset = 4*2;
+ break;
+ default:
+ goto err;
+ }
+
+ if (lseek64(from, sb_offset << 9, 0) < 0LL)
+ goto err;
+ if (read(from, buf, bufsize) != bufsize)
+ goto err;
+
+ sb = buf;
+ super = *sb; // save most of sb for when we reuse buf
+
+ if (__le32_to_cpu(super.magic) != MD_SB_MAGIC ||
+ __le32_to_cpu(super.major_version) != 1 ||
+ __le64_to_cpu(super.super_offset) != sb_offset ||
+ calc_sb_1_csum(sb) != super.sb_csum)
+ goto err;
+
+ if (lseek64(to, sb_offset << 9, 0) < 0LL)
+ goto err;
+ if (write(to, buf, bufsize) != bufsize)
+ goto err;
+
+ if (super.feature_map & __le32_to_cpu(MD_FEATURE_BITMAP_OFFSET)) {
+ unsigned long long bitmap_offset = sb_offset;
+ int bytes = 4096; // just an estimate.
+ int written = 0;
+ struct align_fd afrom, ato;
+
+ init_afd(&afrom, from);
+ init_afd(&ato, to);
+
+ bitmap_offset += (int32_t)__le32_to_cpu(super.bitmap_offset);
+
+ if (lseek64(from, bitmap_offset<<9, 0) < 0)
+ goto err;
+ if (lseek64(to, bitmap_offset<<9, 0) < 0)
+ goto err;
+
+ for (written = 0; written < bytes ; ) {
+ int n = bytes - written;
+ if (n > 4096)
+ n = 4096;
+ if (aread(&afrom, buf, n) != n)
+ goto err;
+ if (written == 0) {
+ /* have the header, can calculate
+ * correct bitmap bytes */
+ bitmap_super_t *bms;
+ int bits;
+ bms = (void*)buf;
+ bits = __le64_to_cpu(bms->sync_size) / (__le32_to_cpu(bms->chunksize)>>9);
+ bytes = (bits+7) >> 3;
+ bytes += sizeof(bitmap_super_t);
+ bytes = ROUND_UP(bytes, 512);
+ if (n > bytes)
+ n = bytes;
+ }
+ if (awrite(&ato, buf, n) != n)
+ goto err;
+ written += n;
+ }
+ }
+
+ if (super.bblog_size != 0 &&
+ __le32_to_cpu(super.bblog_size) <= 100 &&
+ super.bblog_offset != 0 &&
+ (super.feature_map & __le32_to_cpu(MD_FEATURE_BAD_BLOCKS))) {
+ /* There is a bad block log */
+ unsigned long long bb_offset = sb_offset;
+ int bytes = __le32_to_cpu(super.bblog_size) * 512;
+ int written = 0;
+ struct align_fd afrom, ato;
+
+ init_afd(&afrom, from);
+ init_afd(&ato, to);
+
+ bb_offset += (int32_t)__le32_to_cpu(super.bblog_offset);
+
+ if (lseek64(from, bb_offset<<9, 0) < 0)
+ goto err;
+ if (lseek64(to, bb_offset<<9, 0) < 0)
+ goto err;
+
+ for (written = 0; written < bytes ; ) {
+ int n = bytes - written;
+ if (n > 4096)
+ n = 4096;
+ if (aread(&afrom, buf, n) != n)
+ goto err;
+
+ if (awrite(&ato, buf, n) != n)
+ goto err;
+ written += n;
+ }
+ }
+
+ free(buf);
+ return 0;
+
+err:
+ free(buf);
+ return 1;
+}
+
static void detail_super1(struct supertype *st, char *homehost)
{
struct mdp_superblock_1 *sb = st->sb;
@@ -2109,6 +2246,7 @@ struct superswitch super1 = {
.validate_geometry = validate_geometry1,
.add_to_super = add_to_super1,
.examine_badblocks = examine_badblocks_super1,
+ .copy_metadata = copy_metadata1,
#endif
.match_home = match_home1,
.uuid_from_super = uuid_from_super1,