]> git.ipfire.org Git - thirdparty/mdadm.git/blob - Dump.c
Add failfast support.
[thirdparty/mdadm.git] / Dump.c
1 /*
2 * mdadm - manage Linux "md" devices aka RAID arrays.
3 *
4 * Copyright (C) 2013 Neil Brown <neilb@suse.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15
16 * Author: Neil Brown
17 * Email: <neilb@suse.de>
18 */
19
20 #include "mdadm.h"
21 #include <sys/dir.h>
22
23 int Dump_metadata(char *dev, char *dir, struct context *c,
24 struct supertype *st)
25 {
26 /* create a new file in 'dir' named for the basename of 'dev'.
27 * Truncate to the same size as 'dev' and ask the metadata
28 * handler to copy metadata there.
29 * For every name in /dev/disk/by-id that points to this device,
30 * create a hardlink in 'dir'.
31 * Complain if any of those hardlinks cannot be created.
32 */
33 int fd, fl;
34 struct stat stb, dstb;
35 char *base;
36 char *fname = NULL;
37 unsigned long long size;
38 DIR *dirp;
39 struct dirent *de;
40
41 if (stat(dir, &stb) != 0 ||
42 (S_IFMT & stb.st_mode) != S_IFDIR) {
43 pr_err("--dump requires an existing directory, not: %s\n",
44 dir);
45 return 16;
46 }
47
48 fd = dev_open(dev, O_RDONLY);
49 if (fd < 0) {
50 pr_err("Cannot open %s to dump metadata: %s\n",
51 dev, strerror(errno));
52 return 1;
53 }
54 if (!get_dev_size(fd, dev, &size)) {
55 close(fd);
56 return 1;
57 }
58
59 if (st == NULL)
60 st = guess_super_type(fd, guess_array);
61 if (!st) {
62 pr_err("Cannot find RAID metadata on %s\n", dev);
63 close(fd);
64 return 1;
65 }
66
67 st->ignore_hw_compat = 1;
68 if (st->ss->load_super(st, fd, NULL) != 0) {
69 pr_err("No %s metadata found on %s\n",
70 st->ss->name, dev);
71 close(fd);
72 return 1;
73 }
74 if (st->ss->copy_metadata == NULL) {
75 pr_err("%s metadata on %s cannot be copied\n",
76 st->ss->name, dev);
77 close(fd);
78 return 1;
79 }
80
81 base = strrchr(dev, '/');
82 if (base)
83 base++;
84 else
85 base = dev;
86 xasprintf(&fname, "%s/%s", dir, base);
87 fl = open(fname, O_RDWR|O_CREAT|O_EXCL, 0666);
88 if (fl < 0) {
89 pr_err("Cannot create dump file %s: %s\n",
90 fname, strerror(errno));
91 close(fd);
92 free(fname);
93 return 1;
94 }
95 if (ftruncate(fl, size) < 0) {
96 pr_err("failed to set size of dump file: %s\n",
97 strerror(errno));
98 close(fd);
99 close(fl);
100 free(fname);
101 return 1;
102 }
103
104 if (st->ss->copy_metadata(st, fd, fl) != 0) {
105 pr_err("Failed to copy metadata from %s to %s\n",
106 dev, fname);
107 close(fd);
108 close(fl);
109 unlink(fname);
110 free(fname);
111 return 1;
112 }
113 if (c->verbose >= 0)
114 printf("%s saved as %s.\n", dev, fname);
115 fstat(fd, &dstb);
116 close(fd);
117 close(fl);
118 if ((dstb.st_mode & S_IFMT) != S_IFBLK) {
119 /* Not a block device, so cannot create links */
120 free(fname);
121 return 0;
122 }
123 /* mostly done: just want to find some other names */
124 dirp = opendir("/dev/disk/by-id");
125 if (!dirp) {
126 free(fname);
127 return 0;
128 }
129 while ((de = readdir(dirp)) != NULL) {
130 char *p = NULL;
131 if (de->d_name[0] == '.')
132 continue;
133 xasprintf(&p, "/dev/disk/by-id/%s", de->d_name);
134 if (stat(p, &stb) != 0 ||
135 (stb.st_mode & S_IFMT) != S_IFBLK ||
136 stb.st_rdev != dstb.st_rdev) {
137 /* Not this one */
138 free(p);
139 continue;
140 }
141 free(p);
142 xasprintf(&p, "%s/%s", dir, de->d_name);
143 if (link(fname, p) == 0) {
144 if (c->verbose >= 0)
145 printf("%s also saved as %s.\n",
146 dev, p);
147 } else {
148 pr_err("Could not save %s as %s!!\n",
149 dev, p);
150 }
151 free(p);
152 }
153 closedir(dirp);
154 free(fname);
155 return 0;
156 }
157
158 int Restore_metadata(char *dev, char *dir, struct context *c,
159 struct supertype *st, int only)
160 {
161 /* If 'dir' really is a directory we choose a name
162 * from it that matches a suitable name in /dev/disk/by-id,
163 * and copy metadata from the file to the device.
164 * If two names from by-id match and aren't both the same
165 * inode, we fail. If none match and basename of 'dev'
166 * can be found in dir, use that.
167 * If 'dir' is really a file then it is only permitted if
168 * 'only' is set (meaning there was only one device given)
169 * and the metadata is restored irrespective of file names.
170 */
171 int fd, fl;
172 struct stat stb, dstb;
173 char *fname = NULL;
174 unsigned long long size;
175
176 if (stat(dir, &stb) != 0) {
177 pr_err("%s does not exist: cannot restore from there.\n",
178 dir);
179 return 16;
180 } else if ((S_IFMT & stb.st_mode) != S_IFDIR && !only) {
181 pr_err("--restore requires a directory when multiple devices given\n");
182 return 16;
183 }
184
185 fd = dev_open(dev, O_RDWR);
186 if (fd < 0) {
187 pr_err("Cannot open %s to restore metadata: %s\n",
188 dev, strerror(errno));
189 return 1;
190 }
191 if (!get_dev_size(fd, dev, &size)) {
192 close(fd);
193 return 1;
194 }
195
196 if ((S_IFMT & stb.st_mode) == S_IFDIR) {
197 /* choose one name from the directory. */
198 DIR *d = opendir(dir);
199 struct dirent *de;
200 char *chosen = NULL;
201 unsigned int chosen_inode = 0;
202
203 fstat(fd, &dstb);
204
205 while (d && (de = readdir(d)) != NULL) {
206 if (de->d_name[0] == '.')
207 continue;
208 xasprintf(&fname, "/dev/disk/by-id/%s", de->d_name);
209 if (stat(fname, &stb) != 0) {
210 free(fname);
211 continue;
212 }
213 free(fname);
214 if ((S_IFMT & stb.st_mode) != S_IFBLK)
215 continue;
216 if (stb.st_rdev != dstb.st_rdev)
217 continue;
218 /* This file is a good match for our device. */
219 xasprintf(&fname, "%s/%s", dir, de->d_name);
220 if (stat(fname, &stb) != 0) {
221 /* Weird! */
222 free(fname);
223 continue;
224 }
225 if (chosen == NULL) {
226 chosen = fname;
227 chosen_inode = stb.st_ino;
228 continue;
229 }
230 if (chosen_inode == stb.st_ino) {
231 /* same, no need to change */
232 free(fname);
233 continue;
234 }
235 /* Oh dear, two names both match. Must give up. */
236 pr_err("Both %s and %s seem suitable for %s. Please choose one.\n",
237 chosen, fname, dev);
238 free(fname);
239 free(chosen);
240 close(fd);
241 closedir(d);
242 return 1;
243 }
244 closedir(d);
245 if (!chosen) {
246 /* One last chance: try basename of device */
247 char *base = strrchr(dev, '/');
248 if (base)
249 base++;
250 else
251 base = dev;
252 xasprintf(&fname, "%s/%s", dir, base);
253 if (stat(fname, &stb) == 0)
254 chosen = fname;
255 else
256 free(fname);
257 }
258 fname = chosen;
259 } else
260 fname = strdup(dir);
261
262 if (!fname) {
263 pr_err("Cannot find suitable file in %s for %s\n",
264 dir, dev);
265 close(fd);
266 return 1;
267 }
268
269 fl = open(fname, O_RDONLY);
270 if (!fl) {
271 pr_err("Could not open %s for --restore.\n",
272 fname);
273 goto err;
274 }
275 if (((unsigned long long)stb.st_size) != size) {
276 pr_err("%s is not the same size as %s - cannot restore.\n",
277 fname, dev);
278 goto err;
279 }
280 if (st == NULL)
281 st = guess_super_type(fl, guess_array);
282 if (!st) {
283 pr_err("Cannot find metadata on %s\n", fname);
284 goto err;
285 }
286 st->ignore_hw_compat = 1;
287 if (st->ss->load_super(st, fl, NULL) != 0) {
288 pr_err("No %s metadata found on %s\n",
289 st->ss->name, fname);
290 goto err;
291 }
292 if (st->ss->copy_metadata == NULL) {
293 pr_err("%s metadata on %s cannot be copied\n",
294 st->ss->name, dev);
295 goto err;
296 }
297 if (st->ss->copy_metadata(st, fl, fd) != 0) {
298 pr_err("Failed to copy metadata from %s to %s\n",
299 fname, dev);
300 goto err;
301 }
302 if (c->verbose >= 0)
303 printf("%s restored from %s.\n", dev, fname);
304 return 0;
305
306 err:
307 close(fd);
308 close(fl);
309 free(fname);
310 return 1;
311 }