Add --dump / --restore functionality.
This allows the metadata on a device to be saved and later restored. This can be useful before experimenting on an array that is misbehaving. Suggested-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: NeilBrown <neilb@suse.de>
This commit is contained in:
parent
b31df43682
commit
74db60b00a
|
@ -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;
|
||||
}
|
2
Makefile
2
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 \
|
||||
|
|
3
ReadMe.c
3
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'},
|
||||
|
||||
|
|
49
mdadm.8.in
49
mdadm.8.in
|
@ -1448,6 +1448,12 @@ been configured. Currently only
|
|||
.B 1.x
|
||||
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
|
||||
|
@ -2135,6 +2141,49 @@ Having
|
|||
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
|
||||
|
|
26
mdadm.c
26
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) {
|
||||
|
|
9
mdadm.h
9
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);
|
||||
|
|
63
super-ddf.c
63
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,
|
||||
|
|
|
@ -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,
|
||||
|
|
47
super0.c
47
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,
|
||||
|
|
138
super1.c
138
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,
|
||||
|
|
Loading…
Reference in New Issue