]> git.neil.brown.name Git - mdadm.git/blob - Dump.c
Release mdadm-4.0
[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 }