e2image.c revision c046ac7f2e4c53e20cf1e909bbe511f91074b396
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 ext2fs_descriptor_block_loc(fs, block, j)); 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 == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) 392 continue; 393 if (retval) { 394 com_err(program_name, retval, 395 _("while getting next inode")); 396 exit(1); 397 } 398 if (ino == 0) 399 break; 400 if (!inode.i_links_count) 401 continue; 402 if (inode.i_file_acl) { 403 ext2fs_mark_block_bitmap(meta_block_map, 404 inode.i_file_acl); 405 } 406 if (!ext2fs_inode_has_valid_blocks(&inode)) 407 continue; 408 409 stashed_ino = ino; 410 if (LINUX_S_ISDIR(inode.i_mode) || 411 ino == fs->super->s_journal_inum) { 412 retval = ext2fs_block_iterate2(fs, ino, 0, 413 block_buf, process_dir_block, &pb); 414 if (retval) { 415 com_err(program_name, retval, 416 "while iterating over inode %d", 417 ino); 418 exit(1); 419 } 420 } else { 421 if (inode.i_block[EXT2_IND_BLOCK] || 422 inode.i_block[EXT2_DIND_BLOCK] || 423 inode.i_block[EXT2_TIND_BLOCK]) { 424 retval = ext2fs_block_iterate2(fs, 425 ino, 0, block_buf, 426 process_file_block, &pb); 427 if (retval) { 428 com_err(program_name, retval, 429 "while iterating over %d", ino); 430 exit(1); 431 } 432 } 433 } 434 } 435 use_inode_shortcuts(fs, 0); 436 output_meta_data_blocks(fs, fd); 437} 438 439int main (int argc, char ** argv) 440{ 441 int c; 442 errcode_t retval; 443 ext2_filsys fs; 444 char *outfn; 445 int open_flag = 0; 446 int raw_flag = 0; 447 int fd = 0; 448 449#ifdef ENABLE_NLS 450 setlocale(LC_MESSAGES, ""); 451 setlocale(LC_CTYPE, ""); 452 bindtextdomain(NLS_CAT_NAME, LOCALEDIR); 453 textdomain(NLS_CAT_NAME); 454#endif 455 fprintf (stderr, "e2image %s (%s)\n", E2FSPROGS_VERSION, 456 E2FSPROGS_DATE); 457 if (argc && *argv) 458 program_name = *argv; 459 initialize_ext2_error_table(); 460 while ((c = getopt (argc, argv, "r")) != EOF) 461 switch (c) { 462 case 'r': 463 raw_flag++; 464 break; 465 default: 466 usage(); 467 } 468 if (optind != argc - 2 ) 469 usage(); 470 device_name = argv[optind]; 471 outfn = argv[optind+1]; 472 retval = ext2fs_open (device_name, open_flag, 0, 0, 473 unix_io_manager, &fs); 474 if (retval) { 475 com_err (program_name, retval, _("while trying to open %s"), 476 device_name); 477 printf(_("Couldn't find valid filesystem superblock.\n")); 478 exit(1); 479 } 480 481 if (strcmp(outfn, "-") == 0) 482 fd = 1; 483 else { 484#ifdef HAVE_OPEN64 485 fd = open64(outfn, O_CREAT|O_TRUNC|O_WRONLY, 0600); 486#else 487 fd = open(outfn, O_CREAT|O_TRUNC|O_WRONLY, 0600); 488#endif 489 if (fd < 0) { 490 com_err(program_name, errno, 491 _("while trying to open %s"), argv[optind+1]); 492 exit(1); 493 } 494 } 495 496 if (raw_flag) 497 write_raw_image_file(fs, fd); 498 else 499 write_image_file(fs, fd); 500 501 ext2fs_close (fs); 502 exit (0); 503} 504