e2image.c revision ed909bbe20d3fbeeee65c48dc0df2dbffdf2a0a9
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_check_directory(ext2_filsys fs, ext2_ino_t ino)
177{
178	if ((ino != stashed_ino) || !stashed_inode)
179		return EXT2_ET_CALLBACK_NOTHANDLED;
180
181	if (!LINUX_S_ISDIR(stashed_inode->i_mode))
182		return EXT2_ET_NO_DIRECTORY;
183	return 0;
184}
185
186static errcode_t meta_read_inode(ext2_filsys fs, ext2_ino_t ino,
187				 struct ext2_inode *inode)
188{
189	if ((ino != stashed_ino) || !stashed_inode)
190		return EXT2_ET_CALLBACK_NOTHANDLED;
191	*inode = *stashed_inode;
192	return 0;
193}
194
195static void use_inode_shortcuts(ext2_filsys fs, int bool)
196{
197	if (bool) {
198		fs->get_blocks = meta_get_blocks;
199		fs->check_directory = meta_check_directory;
200		fs->read_inode = meta_read_inode;
201		stashed_ino = 0;
202	} else {
203		fs->get_blocks = 0;
204		fs->check_directory = 0;
205		fs->read_inode = 0;
206	}
207}
208
209static int process_dir_block(ext2_filsys fs, blk_t *block_nr,
210			     e2_blkcnt_t blockcnt, blk_t ref_block,
211			     int ref_offset, void *priv_data)
212{
213	ext2fs_mark_block_bitmap(meta_block_map, *block_nr);
214	return 0;
215}
216
217static int process_file_block(ext2_filsys fs, blk_t *block_nr,
218			     e2_blkcnt_t blockcnt, blk_t ref_block,
219			     int ref_offset, void *priv_data)
220{
221	if (blockcnt < 0) {
222		ext2fs_mark_block_bitmap(meta_block_map, *block_nr);
223	}
224	return 0;
225}
226
227static void mark_table_blocks(ext2_filsys fs)
228{
229	blk_t	block, b;
230	int	i,j;
231
232	block = fs->super->s_first_data_block;
233	/*
234	 * Mark primary superblock
235	 */
236	ext2fs_mark_block_bitmap(meta_block_map, block);
237
238	/*
239	 * Mark the primary superblock descriptors
240	 */
241	for (j = 0; j < fs->desc_blocks; j++) {
242		ext2fs_mark_block_bitmap(meta_block_map,
243					 block + j + 1);
244	}
245
246	for (i = 0; i < fs->group_desc_count; i++) {
247		/*
248		 * Mark the blocks used for the inode table
249		 */
250		if (fs->group_desc[i].bg_inode_table) {
251			for (j = 0, b = fs->group_desc[i].bg_inode_table;
252			     j < fs->inode_blocks_per_group;
253			     j++, b++)
254				ext2fs_mark_block_bitmap(meta_block_map, b);
255		}
256
257		/*
258		 * Mark block used for the block bitmap
259		 */
260		if (fs->group_desc[i].bg_block_bitmap) {
261			ext2fs_mark_block_bitmap(meta_block_map,
262				     fs->group_desc[i].bg_block_bitmap);
263		}
264
265		/*
266		 * Mark block used for the inode bitmap
267		 */
268		if (fs->group_desc[i].bg_inode_bitmap) {
269			ext2fs_mark_block_bitmap(meta_block_map,
270				 fs->group_desc[i].bg_inode_bitmap);
271		}
272		block += fs->super->s_blocks_per_group;
273	}
274}
275
276/*
277 * This function returns 1 if the specified block is all zeros
278 */
279static int check_zero_block(char *buf, int blocksize)
280{
281	char	*cp = buf;
282	int	left = blocksize;
283
284	while (left > 0) {
285		if (*cp++)
286			return 0;
287		left--;
288	}
289	return 1;
290}
291
292static void write_block(int fd, char *buf, int sparse_offset,
293			int blocksize, blk_t block)
294{
295	int		count;
296	errcode_t	err;
297
298	if (sparse_offset) {
299#ifdef HAVE_LSEEK64
300		if (lseek64(fd, sparse_offset, SEEK_CUR) < 0)
301			perror("lseek");
302#else
303		if (lseek(fd, sparse_offset, SEEK_CUR) < 0)
304			perror("lseek");
305#endif
306	}
307	if (blocksize) {
308		count = write(fd, buf, blocksize);
309		if (count != blocksize) {
310			if (count == -1)
311				err = errno;
312			else
313				err = 0;
314			com_err(program_name, err, "error writing block %d",
315				block);
316		}
317	}
318}
319
320static void output_meta_data_blocks(ext2_filsys fs, int fd)
321{
322	errcode_t	retval;
323	blk_t		blk;
324	char		buf[8192], zero_buf[8192];
325	int		sparse = 0;
326
327	memset(zero_buf, 0, sizeof(zero_buf));
328	for (blk = 0; blk < fs->super->s_blocks_count; blk++) {
329		if ((blk >= fs->super->s_first_data_block) &&
330		    ext2fs_test_block_bitmap(meta_block_map, blk)) {
331			retval = io_channel_read_blk(fs->io, blk, 1, buf);
332			if (retval) {
333				com_err(program_name, retval,
334					"error reading block %d", blk);
335			}
336			if ((fd != 1) && check_zero_block(buf, fs->blocksize))
337				goto sparse_write;
338			write_block(fd, buf, sparse, fs->blocksize, blk);
339			sparse = 0;
340		} else {
341		sparse_write:
342			if (fd == 1) {
343				write_block(fd, zero_buf, 0,
344					    fs->blocksize, blk);
345				continue;
346			}
347			sparse += fs->blocksize;
348			if (sparse >= 1024*1024) {
349				write_block(fd, 0, sparse, 0, 0);
350				sparse = 0;
351			}
352		}
353	}
354	write_block(fd, zero_buf, sparse, 1, -1);
355}
356
357static void write_raw_image_file(ext2_filsys fs, int fd)
358{
359	struct process_block_struct	pb;
360	struct ext2_inode		inode;
361	ext2_inode_scan			scan;
362	ext2_ino_t			ino;
363	errcode_t			retval;
364	char *				block_buf;
365
366	retval = ext2fs_allocate_block_bitmap(fs, "in-use block map",
367					      &meta_block_map);
368	if (retval) {
369		com_err(program_name, retval, "while allocating block bitmap");
370		exit(1);
371	}
372
373	mark_table_blocks(fs);
374
375	retval = ext2fs_open_inode_scan(fs, 0, &scan);
376	if (retval) {
377		com_err(program_name, retval, _("while opening inode scan"));
378		exit(1);
379	}
380
381	block_buf = malloc(fs->blocksize * 3);
382	if (!block_buf) {
383		com_err(program_name, 0, "Can't allocate block buffer");
384		exit(1);
385	}
386
387	use_inode_shortcuts(fs, 1);
388	stashed_inode = &inode;
389	while (1) {
390		retval = ext2fs_get_next_inode(scan, &ino, &inode);
391		if (retval) {
392			com_err(program_name, retval,
393				_("while getting next inode"));
394			exit(1);
395		}
396		if (ino == 0)
397			break;
398		if (!inode.i_links_count)
399			continue;
400		if (inode.i_file_acl) {
401			ext2fs_mark_block_bitmap(meta_block_map,
402						 inode.i_file_acl);
403		}
404		if (!ext2fs_inode_has_valid_blocks(&inode))
405			continue;
406
407		stashed_ino = ino;
408		if (LINUX_S_ISDIR(inode.i_mode) ||
409		    ino == fs->super->s_journal_inum) {
410			retval = ext2fs_block_iterate2(fs, ino, 0,
411				       block_buf, process_dir_block, &pb);
412			if (retval) {
413				com_err(program_name, retval,
414					"while iterating over inode %d",
415					ino);
416				exit(1);
417			}
418		} else {
419			if (inode.i_block[EXT2_IND_BLOCK] ||
420			    inode.i_block[EXT2_DIND_BLOCK] ||
421			    inode.i_block[EXT2_TIND_BLOCK]) {
422				retval = ext2fs_block_iterate2(fs,
423				       ino, 0, block_buf,
424				       process_file_block, &pb);
425				if (retval) {
426					com_err(program_name, retval,
427					"while iterating over %d", ino);
428					exit(1);
429				}
430			}
431		}
432	}
433	use_inode_shortcuts(fs, 0);
434	output_meta_data_blocks(fs, fd);
435}
436
437int main (int argc, char ** argv)
438{
439	int c;
440	errcode_t retval;
441	ext2_filsys fs;
442	char *outfn;
443	int open_flag = 0;
444	int raw_flag = 0;
445	int fd = 0;
446
447#ifdef ENABLE_NLS
448	setlocale(LC_MESSAGES, "");
449	setlocale(LC_CTYPE, "");
450	bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
451	textdomain(NLS_CAT_NAME);
452#endif
453	fprintf (stderr, "e2image %s (%s)\n", E2FSPROGS_VERSION,
454		 E2FSPROGS_DATE);
455	if (argc && *argv)
456		program_name = *argv;
457	initialize_ext2_error_table();
458	while ((c = getopt (argc, argv, "r")) != EOF)
459		switch (c) {
460		case 'r':
461			raw_flag++;
462			break;
463		default:
464			usage();
465		}
466	if (optind != argc - 2 )
467		usage();
468	device_name = argv[optind];
469	outfn = argv[optind+1];
470	retval = ext2fs_open (device_name, open_flag, 0, 0,
471			      unix_io_manager, &fs);
472        if (retval) {
473		com_err (program_name, retval, _("while trying to open %s"),
474			 device_name);
475		printf(_("Couldn't find valid filesystem superblock.\n"));
476		exit(1);
477	}
478
479	if (strcmp(outfn, "-") == 0)
480		fd = 1;
481	else {
482#ifdef HAVE_OPEN64
483		fd = open64(outfn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
484#else
485		fd = open(outfn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
486#endif
487		if (fd < 0) {
488			com_err(program_name, errno,
489				_("while trying to open %s"), argv[optind+1]);
490			exit(1);
491		}
492	}
493
494	if (raw_flag)
495		write_raw_image_file(fs, fd);
496	else
497		write_image_file(fs, fd);
498
499	ext2fs_close (fs);
500	exit (0);
501}
502