blob: 0a2ed63ece193433c618644fd2558745c838b635 [file] [log] [blame]
Theodore Ts'o72ed1262000-11-12 19:32:20 +00001/*
2 * e2image.c --- Program which writes an image file backing up
3 * critical metadata for the filesystem.
4 *
Theodore Ts'oa3827952001-05-03 16:24:57 +00005 * Copyright 2000, 2001 by Theodore Ts'o.
Theodore Ts'o72ed1262000-11-12 19:32:20 +00006 *
7 * %Begin-Header%
8 * This file may be redistributed under the terms of the GNU Public
9 * License.
10 * %End-Header%
11 */
12
Theodore Ts'o6304baf2001-08-09 05:41:29 -040013#define _LARGEFILE_SOURCE
14#define _LARGEFILE64_SOURCE
15
Theodore Ts'o72ed1262000-11-12 19:32:20 +000016#include <fcntl.h>
17#include <grp.h>
18#ifdef HAVE_GETOPT_H
19#include <getopt.h>
20#else
21extern char *optarg;
22extern int optind;
23#endif
24#include <pwd.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <time.h>
29#include <unistd.h>
30#include <fcntl.h>
31#include <errno.h>
32#include <sys/stat.h>
33#include <sys/types.h>
34
Theodore Ts'o54c637d2001-05-14 11:45:38 +000035#include "ext2fs/ext2_fs.h"
Theodore Ts'o72ed1262000-11-12 19:32:20 +000036#include "ext2fs/ext2fs.h"
37#include "et/com_err.h"
38#include "uuid/uuid.h"
39#include "e2p/e2p.h"
40#include "ext2fs/e2image.h"
41
42#include "../version.h"
43#include "nls-enable.h"
44
Theodore Ts'o3e377db2000-12-09 02:37:33 +000045const char * program_name = "e2image";
Theodore Ts'o72ed1262000-11-12 19:32:20 +000046char * device_name = NULL;
47
48static void usage(void)
49{
Theodore Ts'o6304baf2001-08-09 05:41:29 -040050 fprintf(stderr, _("Usage: %s [-r] device file\n"), program_name);
Theodore Ts'o72ed1262000-11-12 19:32:20 +000051 exit (1);
52}
53
Theodore Ts'o095b4592001-05-03 13:33:11 +000054static void write_header(int fd, struct ext2_image_hdr *hdr, int blocksize)
Theodore Ts'o72ed1262000-11-12 19:32:20 +000055{
Theodore Ts'o095b4592001-05-03 13:33:11 +000056 char *header_buf;
Theodore Ts'o72ed1262000-11-12 19:32:20 +000057 int actual;
58
Theodore Ts'o095b4592001-05-03 13:33:11 +000059 header_buf = malloc(blocksize);
60 if (!header_buf) {
61 fprintf(stderr, _("Couldn't allocate header buffer\n"));
62 exit(1);
63 }
64
Theodore Ts'o72ed1262000-11-12 19:32:20 +000065 if (lseek(fd, 0, SEEK_SET) < 0) {
66 perror("lseek while writing header");
67 exit(1);
68 }
Theodore Ts'o095b4592001-05-03 13:33:11 +000069 memset(header_buf, 0, blocksize);
Theodore Ts'o72ed1262000-11-12 19:32:20 +000070
71 if (hdr)
72 memcpy(header_buf, hdr, sizeof(struct ext2_image_hdr));
73
Theodore Ts'o095b4592001-05-03 13:33:11 +000074 actual = write(fd, header_buf, blocksize);
Theodore Ts'o72ed1262000-11-12 19:32:20 +000075 if (actual < 0) {
76 perror("write header");
77 exit(1);
78 }
Theodore Ts'o095b4592001-05-03 13:33:11 +000079 if (actual != blocksize) {
Theodore Ts'o72ed1262000-11-12 19:32:20 +000080 fprintf(stderr, _("short write (only %d bytes) for"
81 "writing image header"), actual);
82 exit(1);
83 }
Theodore Ts'o095b4592001-05-03 13:33:11 +000084 free(header_buf);
Theodore Ts'o72ed1262000-11-12 19:32:20 +000085}
86
Theodore Ts'o6304baf2001-08-09 05:41:29 -040087static void write_image_file(ext2_filsys fs, int fd)
Theodore Ts'o72ed1262000-11-12 19:32:20 +000088{
Theodore Ts'o6304baf2001-08-09 05:41:29 -040089 struct ext2_image_hdr hdr;
90 struct stat st;
91 errcode_t retval;
Theodore Ts'o72ed1262000-11-12 19:32:20 +000092
Theodore Ts'o095b4592001-05-03 13:33:11 +000093 write_header(fd, NULL, fs->blocksize);
Theodore Ts'o72ed1262000-11-12 19:32:20 +000094 memset(&hdr, 0, sizeof(struct ext2_image_hdr));
Theodore Ts'o6304baf2001-08-09 05:41:29 -040095
Theodore Ts'o72ed1262000-11-12 19:32:20 +000096 hdr.offset_super = lseek(fd, 0, SEEK_CUR);
97 retval = ext2fs_image_super_write(fs, fd, 0);
98 if (retval) {
99 com_err(program_name, retval, _("while writing superblock"));
100 exit(1);
101 }
102
103 hdr.offset_inode = lseek(fd, 0, SEEK_CUR);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400104 retval = ext2fs_image_inode_write(fs, fd,
105 (fd != 1) ? IMAGER_FLAG_SPARSEWRITE : 0);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000106 if (retval) {
107 com_err(program_name, retval, _("while writing inode table"));
108 exit(1);
109 }
110
111 hdr.offset_blockmap = lseek(fd, 0, SEEK_CUR);
112 retval = ext2fs_image_bitmap_write(fs, fd, 0);
113 if (retval) {
114 com_err(program_name, retval, _("while writing block bitmap"));
115 exit(1);
116 }
117
118 hdr.offset_inodemap = lseek(fd, 0, SEEK_CUR);
119 retval = ext2fs_image_bitmap_write(fs, fd, IMAGER_FLAG_INODEMAP);
120 if (retval) {
121 com_err(program_name, retval, _("while writing inode bitmap"));
122 exit(1);
123 }
Theodore Ts'oc5423c52001-02-08 05:45:17 +0000124
125 hdr.magic_number = EXT2_ET_MAGIC_E2IMAGE;
126 strcpy(hdr.magic_descriptor, "Ext2 Image 1.0");
127 gethostname(hdr.fs_hostname, sizeof(hdr.fs_hostname));
Theodore Ts'o095b4592001-05-03 13:33:11 +0000128 strncat(hdr.fs_device_name, device_name, sizeof(hdr.fs_device_name));
129 hdr.fs_device_name[sizeof(hdr.fs_device_name) - 1] = 0;
130 hdr.fs_blocksize = fs->blocksize;
131
Theodore Ts'oc5423c52001-02-08 05:45:17 +0000132 if (stat(device_name, &st) == 0)
133 hdr.fs_device = st.st_rdev;
134
135 if (fstat(fd, &st) == 0) {
136 hdr.image_device = st.st_dev;
137 hdr.image_inode = st.st_ino;
138 }
139 memcpy(hdr.fs_uuid, fs->super->s_uuid, sizeof(hdr.fs_uuid));
140
141 hdr.image_time = time(0);
Theodore Ts'o095b4592001-05-03 13:33:11 +0000142 write_header(fd, &hdr, fs->blocksize);
Theodore Ts'o6304baf2001-08-09 05:41:29 -0400143}
144
145/*
146 * These set of functions are used to write a RAW image file.
147 */
148ext2fs_block_bitmap meta_block_map;
149
150struct process_block_struct {
151 ext2_ino_t ino;
152};
153
154/*
155 * These subroutines short circuits ext2fs_get_blocks and
156 * ext2fs_check_directory; we use them since we already have the inode
157 * structure, so there's no point in letting the ext2fs library read
158 * the inode again.
159 */
160static ino_t stashed_ino = 0;
161static struct ext2_inode *stashed_inode;
162
163static errcode_t meta_get_blocks(ext2_filsys fs, ext2_ino_t ino,
164 blk_t *blocks)
165{
166 int i;
167
168 if ((ino != stashed_ino) || !stashed_inode)
169 return EXT2_ET_CALLBACK_NOTHANDLED;
170
171 for (i=0; i < EXT2_N_BLOCKS; i++)
172 blocks[i] = stashed_inode->i_block[i];
173 return 0;
174}
175
176static errcode_t meta_read_inode(ext2_filsys fs, ext2_ino_t ino,
177 struct ext2_inode *inode)
178{
179 if ((ino != stashed_ino) || !stashed_inode)
180 return EXT2_ET_CALLBACK_NOTHANDLED;
181 *inode = *stashed_inode;
182 return 0;
183}
184
185static int process_dir_block(ext2_filsys fs, blk_t *block_nr,
186 e2_blkcnt_t blockcnt, blk_t ref_block,
187 int ref_offset, void *priv_data)
188{
189 ext2fs_mark_block_bitmap(meta_block_map, *block_nr);
190 return 0;
191}
192
193static int process_file_block(ext2_filsys fs, blk_t *block_nr,
194 e2_blkcnt_t blockcnt, blk_t ref_block,
195 int ref_offset, void *priv_data)
196{
197 if (blockcnt < 0) {
198 ext2fs_mark_block_bitmap(meta_block_map, *block_nr);
199 }
200 return 0;
201}
202
203static void mark_table_blocks(ext2_filsys fs)
204{
205 blk_t block, b;
206 int i,j;
207
208 block = fs->super->s_first_data_block;
209 /*
210 * Mark primary superblock
211 */
212 ext2fs_mark_block_bitmap(meta_block_map, block);
213
214 /*
215 * Mark the primary superblock descriptors
216 */
217 for (j = 0; j < fs->desc_blocks; j++) {
218 ext2fs_mark_block_bitmap(meta_block_map,
219 block + j + 1);
220 }
221
222 for (i = 0; i < fs->group_desc_count; i++) {
223 /*
224 * Mark the blocks used for the inode table
225 */
226 if (fs->group_desc[i].bg_inode_table) {
227 for (j = 0, b = fs->group_desc[i].bg_inode_table;
228 j < fs->inode_blocks_per_group;
229 j++, b++)
230 ext2fs_mark_block_bitmap(meta_block_map, b);
231 }
232
233 /*
234 * Mark block used for the block bitmap
235 */
236 if (fs->group_desc[i].bg_block_bitmap) {
237 ext2fs_mark_block_bitmap(meta_block_map,
238 fs->group_desc[i].bg_block_bitmap);
239 }
240
241 /*
242 * Mark block used for the inode bitmap
243 */
244 if (fs->group_desc[i].bg_inode_bitmap) {
245 ext2fs_mark_block_bitmap(meta_block_map,
246 fs->group_desc[i].bg_inode_bitmap);
247 }
248 block += fs->super->s_blocks_per_group;
249 }
250}
251
252/*
253 * This function returns 1 if the specified block is all zeros
254 */
255static int check_zero_block(char *buf, int blocksize)
256{
257 char *cp = buf;
258 int left = blocksize;
259
260 while (left > 0) {
261 if (*cp++)
262 return 0;
263 left--;
264 }
265 return 1;
266}
267
268static void write_block(int fd, char *buf, int blocksize, blk_t block)
269{
270 int count;
271 errcode_t err;
272
273 count = write(fd, buf, blocksize);
274 if (count != blocksize) {
275 if (count == -1)
276 err = errno;
277 else
278 err = 0;
279 com_err(program_name, err, "error writing block %d", block);
280 }
281}
282
283static output_meta_data_blocks(ext2_filsys fs, int fd)
284{
285 errcode_t retval;
286 blk_t blk;
287 char buf[8192], zero_buf[8192];
288
289 memset(zero_buf, 0, sizeof(zero_buf));
290 for (blk = 0; blk < fs->super->s_blocks_count; blk++) {
291 if ((blk >= fs->super->s_first_data_block) &&
292 ext2fs_test_block_bitmap(meta_block_map, blk)) {
293 retval = io_channel_read_blk(fs->io, blk, 1, buf);
294 if (retval) {
295 com_err(program_name, retval,
296 "error reading block %d", blk);
297 }
298 if ((fd != 1) && check_zero_block(buf, fs->blocksize))
299 goto sparse_write;
300 write_block(fd, buf, fs->blocksize, blk);
301 } else {
302 sparse_write:
303 if (fd == 1) {
304 write_block(fd, zero_buf, fs->blocksize, blk);
305 continue;
306 }
307#ifdef HAVE_LSEEK64
308 if (lseek64(fd, fs->blocksize, SEEK_CUR) < 0)
309 perror("lseek");
310#else
311 if (lseek(fd, fs->blocksize, SEEK_CUR) < 0)
312 perror("lseek");
313#endif
314 }
315 }
316 buf[0] = 0;
317 write(fd, buf, 1);
318}
319
320static void write_raw_image_file(ext2_filsys fs, int fd)
321{
322 struct process_block_struct pb;
323 struct ext2_inode inode;
324 ext2_inode_scan scan;
325 ext2_ino_t ino;
326 errcode_t retval;
327 char * block_buf;
328
329 retval = ext2fs_allocate_block_bitmap(fs, "in-use block map",
330 &meta_block_map);
331 if (retval) {
332 com_err(program_name, retval, "while allocating block bitmap");
333 exit(1);
334 }
335
336 mark_table_blocks(fs);
337
338 retval = ext2fs_open_inode_scan(fs, 0, &scan);
339 if (retval) {
340 com_err(program_name, retval, _("while opening inode scan"));
341 exit(1);
342 }
343
344 block_buf = malloc(fs->blocksize * 3);
345 if (!block_buf) {
346 com_err(program_name, 0, "Can't allocate block buffer");
347 exit(1);
348 }
349
350 stashed_inode = &inode;
351 while (1) {
352 retval = ext2fs_get_next_inode(scan, &ino, &inode);
353 if (retval) {
354 com_err(program_name, retval,
355 _("while getting next inode"));
356 exit(1);
357 }
358 if (ino == 0)
359 break;
360 if (!inode.i_links_count ||
361 !ext2fs_inode_has_valid_blocks(&inode))
362 continue;
363
364 stashed_ino = ino;
365 if (LINUX_S_ISDIR(inode.i_mode)) {
366 retval = ext2fs_block_iterate2(fs, ino, 0,
367 block_buf, process_dir_block, &pb);
368 if (retval) {
369 com_err(program_name, retval,
370 "while iterating over inode %d",
371 ino);
372 exit(1);
373 }
374 } else {
375 if (inode.i_block[EXT2_IND_BLOCK] ||
376 inode.i_block[EXT2_DIND_BLOCK] ||
377 inode.i_block[EXT2_TIND_BLOCK]) {
378 retval = ext2fs_block_iterate2(fs,
379 ino, 0, block_buf,
380 process_file_block, &pb);
381 if (retval) {
382 com_err(program_name, retval,
383 "while iterating over %d", ino);
384 exit(1);
385 }
386 }
387 if (inode.i_file_acl) {
388 ext2fs_mark_block_bitmap(meta_block_map,
389 inode.i_file_acl);
390 }
391 }
392 }
393
394 output_meta_data_blocks(fs, fd);
395}
396
397int main (int argc, char ** argv)
398{
399 int c;
400 errcode_t retval;
401 ext2_filsys fs;
402 char *outfn;
403 int open_flag = 0;
404 int raw_flag = 0;
405 int fd = 0;
406
407#ifdef ENABLE_NLS
408 setlocale(LC_MESSAGES, "");
409 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
410 textdomain(NLS_CAT_NAME);
411#endif
412 fprintf (stderr, _("e2image %s, %s for EXT2 FS %s, %s\n"),
413 E2FSPROGS_VERSION, E2FSPROGS_DATE,
414 EXT2FS_VERSION, EXT2FS_DATE);
415 if (argc && *argv)
416 program_name = *argv;
417 initialize_ext2_error_table();
418 while ((c = getopt (argc, argv, "r")) != EOF)
419 switch (c) {
420 case 'r':
421 raw_flag++;
422 break;
423 default:
424 usage();
425 }
426 if (optind != argc - 2 )
427 usage();
428 device_name = argv[optind];
429 outfn = argv[optind+1];
430 retval = ext2fs_open (device_name, open_flag, 0, 0,
431 unix_io_manager, &fs);
432 if (retval) {
433 com_err (program_name, retval, _("while trying to open %s"),
434 device_name);
435 printf(_("Couldn't find valid filesystem superblock.\n"));
436 exit(1);
437 }
438
439#ifdef HAVE_OPEN64
440 fd = open64(outfn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
441#else
442 fd = open(outfn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
443#endif
444 if (fd < 0) {
445 com_err(program_name, errno, _("while trying to open %s"),
446 argv[optind+1]);
447 exit(1);
448 }
449
450 if (raw_flag)
451 write_raw_image_file(fs, fd);
452 else
453 write_image_file(fs, fd);
Theodore Ts'oc5423c52001-02-08 05:45:17 +0000454
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000455 ext2fs_close (fs);
456 exit (0);
457}
Theodore Ts'oc5423c52001-02-08 05:45:17 +0000458
459