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