e2image.c revision e3ef3502f7b587c3d8418aff2433f3f9ffc4c0ba
1/*
2 * e2image.c --- Program which writes an image file backing up
3 * critical metadata for the filesystem.
4 *
5 * Copyright 2000, 2001 by Theodore Ts'o.
6 *
7 * %Begin-Header%
8 * This file may be redistributed under the terms of the GNU Public
9 * License.
10 * %End-Header%
11 */
12
13#define _LARGEFILE_SOURCE
14#define _LARGEFILE64_SOURCE
15
16#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
35#include "ext2fs/ext2_fs.h"
36#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
45const char * program_name = "e2image";
46char * device_name = NULL;
47
48static void usage(void)
49{
50	fprintf(stderr, _("Usage: %s [-r] device file\n"), program_name);
51	exit (1);
52}
53
54static void write_header(int fd, struct ext2_image_hdr *hdr, int blocksize)
55{
56	char *header_buf;
57	int actual;
58
59	header_buf = malloc(blocksize);
60	if (!header_buf) {
61		fprintf(stderr, _("Couldn't allocate header buffer\n"));
62		exit(1);
63	}
64
65	if (lseek(fd, 0, SEEK_SET) < 0) {
66		perror("lseek while writing header");
67		exit(1);
68	}
69	memset(header_buf, 0, blocksize);
70
71	if (hdr)
72		memcpy(header_buf, hdr, sizeof(struct ext2_image_hdr));
73
74	actual = write(fd, header_buf, blocksize);
75	if (actual < 0) {
76		perror("write header");
77		exit(1);
78	}
79	if (actual != blocksize) {
80		fprintf(stderr, _("short write (only %d bytes) for"
81				  "writing image header"), actual);
82		exit(1);
83	}
84	free(header_buf);
85}
86
87static void write_image_file(ext2_filsys fs, int fd)
88{
89	struct ext2_image_hdr	hdr;
90	struct stat		st;
91	errcode_t		retval;
92
93	write_header(fd, NULL, fs->blocksize);
94	memset(&hdr, 0, sizeof(struct ext2_image_hdr));
95
96	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);
104	retval = ext2fs_image_inode_write(fs, fd,
105				  (fd != 1) ? IMAGER_FLAG_SPARSEWRITE : 0);
106	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	}
124
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));
128	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
132	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);
142	write_header(fd, &hdr, fs->blocksize);
143}
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 sparse_offset,
269			int blocksize, blk_t block)
270{
271	int		count;
272	errcode_t	err;
273
274	if (sparse_offset) {
275#ifdef HAVE_LSEEK64
276		if (lseek64(fd, sparse_offset, SEEK_CUR) < 0)
277			perror("lseek");
278#else
279		if (lseek(fd, sparse_offset, SEEK_CUR) < 0)
280			perror("lseek");
281#endif
282	}
283	if (blocksize) {
284		count = write(fd, buf, blocksize);
285		if (count != blocksize) {
286			if (count == -1)
287				err = errno;
288			else
289				err = 0;
290			com_err(program_name, err, "error writing block %d",
291				block);
292		}
293	}
294}
295
296static output_meta_data_blocks(ext2_filsys fs, int fd)
297{
298	errcode_t	retval;
299	blk_t		blk;
300	char		buf[8192], zero_buf[8192];
301	int		sparse = 0;
302
303	memset(zero_buf, 0, sizeof(zero_buf));
304	for (blk = 0; blk < fs->super->s_blocks_count; blk++) {
305		if ((blk >= fs->super->s_first_data_block) &&
306		    ext2fs_test_block_bitmap(meta_block_map, blk)) {
307			retval = io_channel_read_blk(fs->io, blk, 1, buf);
308			if (retval) {
309				com_err(program_name, retval,
310					"error reading block %d", blk);
311			}
312			if ((fd != 1) && check_zero_block(buf, fs->blocksize))
313				goto sparse_write;
314			write_block(fd, buf, sparse, fs->blocksize, blk);
315			sparse = 0;
316		} else {
317		sparse_write:
318			if (fd == 1) {
319				write_block(fd, zero_buf, 0,
320					    fs->blocksize, blk);
321				continue;
322			}
323			sparse += fs->blocksize;
324			if (sparse >= 1024*1024) {
325				write_block(fd, 0, sparse, 0, 0);
326				sparse = 0;
327			}
328		}
329	}
330	write_block(fd, zero_buf, sparse, 1, -1);
331}
332
333static void write_raw_image_file(ext2_filsys fs, int fd)
334{
335	struct process_block_struct	pb;
336	struct ext2_inode		inode;
337	ext2_inode_scan			scan;
338	ext2_ino_t			ino;
339	errcode_t			retval;
340	char *				block_buf;
341
342	retval = ext2fs_allocate_block_bitmap(fs, "in-use block map",
343					      &meta_block_map);
344	if (retval) {
345		com_err(program_name, retval, "while allocating block bitmap");
346		exit(1);
347	}
348
349	mark_table_blocks(fs);
350
351	retval = ext2fs_open_inode_scan(fs, 0, &scan);
352	if (retval) {
353		com_err(program_name, retval, _("while opening inode scan"));
354		exit(1);
355	}
356
357	block_buf = malloc(fs->blocksize * 3);
358	if (!block_buf) {
359		com_err(program_name, 0, "Can't allocate block buffer");
360		exit(1);
361	}
362
363	stashed_inode = &inode;
364	while (1) {
365		retval = ext2fs_get_next_inode(scan, &ino, &inode);
366		if (retval) {
367			com_err(program_name, retval,
368				_("while getting next inode"));
369			exit(1);
370		}
371		if (ino == 0)
372			break;
373		if (!inode.i_links_count ||
374		    !ext2fs_inode_has_valid_blocks(&inode))
375			continue;
376
377		stashed_ino = ino;
378		if (LINUX_S_ISDIR(inode.i_mode) ||
379		    ino == fs->super->s_journal_inum) {
380			retval = ext2fs_block_iterate2(fs, ino, 0,
381				       block_buf, process_dir_block, &pb);
382			if (retval) {
383				com_err(program_name, retval,
384					"while iterating over inode %d",
385					ino);
386				exit(1);
387			}
388		} else {
389			if (inode.i_block[EXT2_IND_BLOCK] ||
390			    inode.i_block[EXT2_DIND_BLOCK] ||
391			    inode.i_block[EXT2_TIND_BLOCK]) {
392				retval = ext2fs_block_iterate2(fs,
393				       ino, 0, block_buf,
394				       process_file_block, &pb);
395				if (retval) {
396					com_err(program_name, retval,
397					"while iterating over %d", ino);
398					exit(1);
399				}
400			}
401			if (inode.i_file_acl) {
402				ext2fs_mark_block_bitmap(meta_block_map,
403							 inode.i_file_acl);
404			}
405		}
406	}
407
408	output_meta_data_blocks(fs, fd);
409}
410
411int main (int argc, char ** argv)
412{
413	int c;
414	errcode_t retval;
415	ext2_filsys fs;
416	char *outfn;
417	int open_flag = 0;
418	int raw_flag = 0;
419	int fd = 0;
420
421#ifdef ENABLE_NLS
422	setlocale(LC_MESSAGES, "");
423	bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
424	textdomain(NLS_CAT_NAME);
425#endif
426	fprintf (stderr, "e2image %s (%s)\n", E2FSPROGS_VERSION,
427		 E2FSPROGS_DATE);
428	if (argc && *argv)
429		program_name = *argv;
430	initialize_ext2_error_table();
431	while ((c = getopt (argc, argv, "r")) != EOF)
432		switch (c) {
433		case 'r':
434			raw_flag++;
435			break;
436		default:
437			usage();
438		}
439	if (optind != argc - 2 )
440		usage();
441	device_name = argv[optind];
442	outfn = argv[optind+1];
443	retval = ext2fs_open (device_name, open_flag, 0, 0,
444			      unix_io_manager, &fs);
445        if (retval) {
446		com_err (program_name, retval, _("while trying to open %s"),
447			 device_name);
448		printf(_("Couldn't find valid filesystem superblock.\n"));
449		exit(1);
450	}
451
452	if (strcmp(outfn, "-") == 0)
453		fd = 1;
454	else {
455#ifdef HAVE_OPEN64
456		fd = open64(outfn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
457#else
458		fd = open(outfn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
459#endif
460		if (fd < 0) {
461			com_err(program_name, errno,
462				_("while trying to open %s"), argv[optind+1]);
463			exit(1);
464		}
465	}
466
467	if (raw_flag)
468		write_raw_image_file(fs, fd);
469	else
470		write_image_file(fs, fd);
471
472	ext2fs_close (fs);
473	exit (0);
474}
475