]>
Commit | Line | Data |
---|---|---|
61983f67 BN |
1 | /* |
2 | * Copyright (c) 2007 Silicon Graphics, Inc. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or | |
6 | * modify it under the terms of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it would be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write the Free Software Foundation, | |
16 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
17 | */ | |
18 | ||
6b803e5a | 19 | #include "libxfs.h" |
61983f67 BN |
20 | #include "xfs_metadump.h" |
21 | ||
22 | char *progname; | |
23 | int show_progress = 0; | |
2291c68b | 24 | int show_info = 0; |
61983f67 BN |
25 | int progress_since_warning = 0; |
26 | ||
27 | static void | |
28 | fatal(const char *msg, ...) | |
29 | { | |
30 | va_list args; | |
31 | ||
32 | va_start(args, msg); | |
33 | fprintf(stderr, "%s: ", progname); | |
34 | vfprintf(stderr, msg, args); | |
35 | exit(1); | |
36 | } | |
37 | ||
38 | static void | |
39 | print_progress(const char *fmt, ...) | |
40 | { | |
41 | char buf[60]; | |
42 | va_list ap; | |
43 | ||
44 | va_start(ap, fmt); | |
45 | vsnprintf(buf, sizeof(buf), fmt, ap); | |
46 | va_end(ap); | |
47 | buf[sizeof(buf)-1] = '\0'; | |
48 | ||
49 | printf("\r%-59s", buf); | |
50 | fflush(stdout); | |
51 | progress_since_warning = 1; | |
52 | } | |
53 | ||
54 | static void | |
55 | perform_restore( | |
56 | FILE *src_f, | |
57 | int dst_fd, | |
58 | int is_target_file) | |
59 | { | |
60 | xfs_metablock_t *metablock; /* header + index + blocks */ | |
61 | __be64 *block_index; | |
62 | char *block_buffer; | |
63 | int block_size; | |
d7006beb | 64 | int max_indices; |
61983f67 | 65 | int cur_index; |
5e656dbb | 66 | int mb_count; |
61983f67 BN |
67 | xfs_metablock_t tmb; |
68 | xfs_sb_t sb; | |
69 | __int64_t bytes_read; | |
70 | ||
71 | /* | |
72 | * read in first blocks (superblock 0), set "inprogress" flag for it, | |
73 | * read in the rest of the file, and if complete, clear SB 0's | |
74 | * "inprogress flag" | |
75 | */ | |
76 | ||
77 | if (fread(&tmb, sizeof(tmb), 1, src_f) != 1) | |
78 | fatal("error reading from file: %s\n", strerror(errno)); | |
79 | ||
80 | if (be32_to_cpu(tmb.mb_magic) != XFS_MD_MAGIC) | |
81 | fatal("specified file is not a metadata dump\n"); | |
82 | ||
83 | block_size = 1 << tmb.mb_blocklog; | |
d7006beb | 84 | max_indices = (block_size - sizeof(xfs_metablock_t)) / sizeof(__be64); |
61983f67 | 85 | |
d7006beb | 86 | metablock = (xfs_metablock_t *)calloc(max_indices + 1, block_size); |
61983f67 BN |
87 | if (metablock == NULL) |
88 | fatal("memory allocation failure\n"); | |
89 | ||
5e656dbb | 90 | mb_count = be16_to_cpu(tmb.mb_count); |
d7006beb | 91 | if (mb_count == 0 || mb_count > max_indices) |
5e656dbb | 92 | fatal("bad block count: %u\n", mb_count); |
61983f67 BN |
93 | |
94 | block_index = (__be64 *)((char *)metablock + sizeof(xfs_metablock_t)); | |
95 | block_buffer = (char *)metablock + block_size; | |
96 | ||
97 | if (fread(block_index, block_size - sizeof(tmb), 1, src_f) != 1) | |
98 | fatal("error reading from file: %s\n", strerror(errno)); | |
99 | ||
100 | if (block_index[0] != 0) | |
101 | fatal("first block is not the primary superblock\n"); | |
102 | ||
103 | ||
5e656dbb | 104 | if (fread(block_buffer, mb_count << tmb.mb_blocklog, |
61983f67 BN |
105 | 1, src_f) != 1) |
106 | fatal("error reading from file: %s\n", strerror(errno)); | |
107 | ||
5e656dbb | 108 | libxfs_sb_from_disk(&sb, (xfs_dsb_t *)block_buffer); |
61983f67 BN |
109 | |
110 | if (sb.sb_magicnum != XFS_SB_MAGIC) | |
111 | fatal("bad magic number for primary superblock\n"); | |
112 | ||
d7006beb ES |
113 | /* |
114 | * Normally the upper bound would be simply XFS_MAX_SECTORSIZE | |
115 | * but the metadump format has a maximum number of BBSIZE blocks | |
116 | * it can store in a single metablock. | |
117 | */ | |
118 | if (sb.sb_sectsize < XFS_MIN_SECTORSIZE || | |
119 | sb.sb_sectsize > XFS_MAX_SECTORSIZE || | |
120 | sb.sb_sectsize > max_indices * block_size) | |
121 | fatal("bad sector size %u in metadump image\n", sb.sb_sectsize); | |
122 | ||
5e656dbb | 123 | ((xfs_dsb_t*)block_buffer)->sb_inprogress = 1; |
61983f67 BN |
124 | |
125 | if (is_target_file) { | |
126 | /* ensure regular files are correctly sized */ | |
127 | ||
dde67673 | 128 | if (ftruncate(dst_fd, sb.sb_dblocks * sb.sb_blocksize)) |
61983f67 BN |
129 | fatal("cannot set filesystem image size: %s\n", |
130 | strerror(errno)); | |
131 | } else { | |
132 | /* ensure device is sufficiently large enough */ | |
133 | ||
5e656dbb | 134 | char *lb[XFS_MAX_SECTORSIZE] = { NULL }; |
61983f67 BN |
135 | off64_t off; |
136 | ||
137 | off = sb.sb_dblocks * sb.sb_blocksize - sizeof(lb); | |
2f9a125c | 138 | if (pwrite(dst_fd, lb, sizeof(lb), off) < 0) |
61983f67 BN |
139 | fatal("failed to write last block, is target too " |
140 | "small? (error: %s)\n", strerror(errno)); | |
141 | } | |
142 | ||
143 | bytes_read = 0; | |
144 | ||
145 | for (;;) { | |
146 | if (show_progress && (bytes_read & ((1 << 20) - 1)) == 0) | |
c1c3d921 | 147 | print_progress("%lld MB read", bytes_read >> 20); |
61983f67 | 148 | |
5e656dbb | 149 | for (cur_index = 0; cur_index < mb_count; cur_index++) { |
2f9a125c | 150 | if (pwrite(dst_fd, &block_buffer[cur_index << |
5e656dbb | 151 | tmb.mb_blocklog], block_size, |
61983f67 BN |
152 | be64_to_cpu(block_index[cur_index]) << |
153 | BBSHIFT) < 0) | |
154 | fatal("error writing block %llu: %s\n", | |
155 | be64_to_cpu(block_index[cur_index]) << BBSHIFT, | |
156 | strerror(errno)); | |
157 | } | |
d7006beb | 158 | if (mb_count < max_indices) |
61983f67 BN |
159 | break; |
160 | ||
161 | if (fread(metablock, block_size, 1, src_f) != 1) | |
162 | fatal("error reading from file: %s\n", strerror(errno)); | |
163 | ||
5e656dbb BN |
164 | mb_count = be16_to_cpu(metablock->mb_count); |
165 | if (mb_count == 0) | |
61983f67 | 166 | break; |
d7006beb | 167 | if (mb_count > max_indices) |
5e656dbb | 168 | fatal("bad block count: %u\n", mb_count); |
61983f67 | 169 | |
f8149110 | 170 | if (fread(block_buffer, mb_count << tmb.mb_blocklog, |
5e656dbb | 171 | 1, src_f) != 1) |
61983f67 BN |
172 | fatal("error reading from file: %s\n", strerror(errno)); |
173 | ||
c1c3d921 | 174 | bytes_read += block_size + (mb_count << tmb.mb_blocklog); |
61983f67 BN |
175 | } |
176 | ||
177 | if (progress_since_warning) | |
178 | putchar('\n'); | |
179 | ||
180 | memset(block_buffer, 0, sb.sb_sectsize); | |
181 | sb.sb_inprogress = 0; | |
19ebedcf | 182 | libxfs_sb_to_disk((xfs_dsb_t *)block_buffer, &sb); |
f3ece211 DC |
183 | if (xfs_sb_version_hascrc(&sb)) { |
184 | xfs_update_cksum(block_buffer, sb.sb_sectsize, | |
185 | offsetof(struct xfs_sb, sb_crc)); | |
186 | } | |
187 | ||
61983f67 BN |
188 | if (pwrite(dst_fd, block_buffer, sb.sb_sectsize, 0) < 0) |
189 | fatal("error writing primary superblock: %s\n", strerror(errno)); | |
190 | ||
191 | free(metablock); | |
192 | } | |
193 | ||
194 | static void | |
195 | usage(void) | |
196 | { | |
30626ef6 | 197 | fprintf(stderr, "Usage: %s [-V] [-g] source target\n", progname); |
61983f67 BN |
198 | exit(1); |
199 | } | |
200 | ||
f594a0d1 | 201 | extern int platform_check_ismounted(char *, char *, struct stat *, int); |
61983f67 BN |
202 | |
203 | int | |
204 | main( | |
205 | int argc, | |
206 | char **argv) | |
207 | { | |
208 | FILE *src_f; | |
209 | int dst_fd; | |
210 | int c; | |
211 | int open_flags; | |
f594a0d1 | 212 | struct stat statbuf; |
61983f67 BN |
213 | int is_target_file; |
214 | ||
215 | progname = basename(argv[0]); | |
216 | ||
2291c68b | 217 | while ((c = getopt(argc, argv, "giV")) != EOF) { |
61983f67 BN |
218 | switch (c) { |
219 | case 'g': | |
220 | show_progress = 1; | |
221 | break; | |
2291c68b ES |
222 | case 'i': |
223 | show_info = 1; | |
224 | break; | |
61983f67 BN |
225 | case 'V': |
226 | printf("%s version %s\n", progname, VERSION); | |
227 | exit(0); | |
228 | default: | |
229 | usage(); | |
230 | } | |
231 | } | |
232 | ||
2291c68b ES |
233 | if (argc - optind < 1 || argc - optind > 2) |
234 | usage(); | |
235 | ||
236 | /* show_info without a target is ok */ | |
237 | if (!show_info && argc - optind != 2) | |
61983f67 BN |
238 | usage(); |
239 | ||
240 | /* open source */ | |
241 | if (strcmp(argv[optind], "-") == 0) { | |
242 | src_f = stdin; | |
243 | if (isatty(fileno(stdin))) | |
244 | fatal("cannot read from a terminal\n"); | |
245 | } else { | |
246 | src_f = fopen(argv[optind], "rb"); | |
247 | if (src_f == NULL) | |
248 | fatal("cannot open source dump file\n"); | |
249 | } | |
2291c68b ES |
250 | |
251 | if (show_info) { | |
252 | xfs_metablock_t mb; | |
253 | ||
254 | if (fread(&mb, sizeof(mb), 1, src_f) != 1) | |
255 | fatal("error reading from file: %s\n", strerror(errno)); | |
256 | ||
257 | if (be32_to_cpu(mb.mb_magic) != XFS_MD_MAGIC) | |
258 | fatal("specified file is not a metadata dump\n"); | |
259 | ||
260 | if (mb.mb_info & XFS_METADUMP_INFO_FLAGS) { | |
261 | printf("%s: %sobfuscated, %s log, %s metadata blocks\n", | |
262 | argv[optind], | |
263 | mb.mb_info & XFS_METADUMP_OBFUSCATED ? "":"not ", | |
264 | mb.mb_info & XFS_METADUMP_DIRTYLOG ? "dirty":"clean", | |
265 | mb.mb_info & XFS_METADUMP_FULLBLOCKS ? "full":"zeroed"); | |
266 | } else { | |
267 | printf("%s: no informational flags present\n", | |
268 | argv[optind]); | |
269 | } | |
270 | ||
271 | if (argc - optind == 1) | |
272 | exit(0); | |
273 | ||
274 | /* Go back to the beginning for the restore function */ | |
275 | fseek(src_f, 0L, SEEK_SET); | |
276 | } | |
277 | ||
61983f67 BN |
278 | optind++; |
279 | ||
280 | /* check and open target */ | |
281 | open_flags = O_RDWR; | |
282 | is_target_file = 0; | |
f594a0d1 | 283 | if (stat(argv[optind], &statbuf) < 0) { |
61983f67 BN |
284 | /* ok, assume it's a file and create it */ |
285 | open_flags |= O_CREAT; | |
286 | is_target_file = 1; | |
287 | } else if (S_ISREG(statbuf.st_mode)) { | |
288 | open_flags |= O_TRUNC; | |
289 | is_target_file = 1; | |
290 | } else { | |
291 | /* | |
292 | * check to make sure a filesystem isn't mounted on the device | |
293 | */ | |
294 | if (platform_check_ismounted(argv[optind], NULL, &statbuf, 0)) | |
295 | fatal("a filesystem is mounted on target device \"%s\"," | |
296 | " cannot restore to a mounted filesystem.\n", | |
297 | argv[optind]); | |
298 | } | |
299 | ||
300 | dst_fd = open(argv[optind], open_flags, 0644); | |
301 | if (dst_fd < 0) | |
302 | fatal("couldn't open target \"%s\"\n", argv[optind]); | |
303 | ||
304 | perform_restore(src_f, dst_fd, is_target_file); | |
305 | ||
306 | close(dst_fd); | |
307 | if (src_f != stdin) | |
308 | fclose(src_f); | |
309 | ||
310 | return 0; | |
311 | } |