e2image.c revision e3ef3502f7b587c3d8418aff2433f3f9ffc4c0ba
1b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato/* 2b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * e2image.c --- Program which writes an image file backing up 3b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * critical metadata for the filesystem. 4b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * 52270795fbe0b277bfd49f40950ecaa78583175ccBrian Carlstrom * Copyright 2000, 2001 by Theodore Ts'o. 6b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * 7b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * %Begin-Header% 8b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * This file may be redistributed under the terms of the GNU Public 9b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * License. 10b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * %End-Header% 11b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato */ 12b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato 13b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#define _LARGEFILE_SOURCE 14b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#define _LARGEFILE64_SOURCE 15b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato 16b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include <fcntl.h> 17b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include <grp.h> 18b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#ifdef HAVE_GETOPT_H 19b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include <getopt.h> 20b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#else 21b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratoextern char *optarg; 22b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratoextern int optind; 23b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#endif 24b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include <pwd.h> 25b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include <stdio.h> 26b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include <stdlib.h> 27b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include <string.h> 28b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include <time.h> 29b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include <unistd.h> 30b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include <fcntl.h> 31b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include <errno.h> 32b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include <sys/stat.h> 33b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include <sys/types.h> 34b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato 35b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include "ext2fs/ext2_fs.h" 36b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include "ext2fs/ext2fs.h" 37b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include "et/com_err.h" 38b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include "uuid/uuid.h" 39b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include "e2p/e2p.h" 40b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include "ext2fs/e2image.h" 41b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato 42b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include "../version.h" 43b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato#include "nls-enable.h" 44b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato 45b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratoconst char * program_name = "e2image"; 46b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratochar * device_name = NULL; 47b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato 48b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratostatic void usage(void) 49b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato{ 50b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato fprintf(stderr, _("Usage: %s [-r] device file\n"), program_name); 51b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato exit (1); 52b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato} 53b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato 54b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratostatic void write_header(int fd, struct ext2_image_hdr *hdr, int blocksize) 55b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato{ 56b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato char *header_buf; 57b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato int actual; 58b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato 59b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato header_buf = malloc(blocksize); 60b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato if (!header_buf) { 61b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato fprintf(stderr, _("Couldn't allocate header buffer\n")); 62b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato exit(1); 63b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato } 64b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato 65b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato if (lseek(fd, 0, SEEK_SET) < 0) { 66b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato perror("lseek while writing header"); 67b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato exit(1); 68b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato } 69b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato memset(header_buf, 0, blocksize); 70b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato 71b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato if (hdr) 72b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang memcpy(header_buf, hdr, sizeof(struct ext2_image_hdr)); 73b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato 74b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato actual = write(fd, header_buf, blocksize); 75b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato if (actual < 0) { 76b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang perror("write header"); 77b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang exit(1); 78b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato } 79b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato if (actual != blocksize) { 80b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato 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