Whamcloud - gitweb
tune2fs: Fix up to be 64-bit block number safe
[tools/e2fsprogs.git] / misc / e2image.c
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
21 extern char *optarg;
22 extern int optind;
23 #endif
24 #include <pwd.h>
25 #include <stdio.h>
26 #ifdef HAVE_STDLIB_H
27 #include <stdlib.h>
28 #endif
29 #include <string.h>
30 #include <time.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36
37 #include "ext2fs/ext2_fs.h"
38 #include "ext2fs/ext2fs.h"
39 #include "et/com_err.h"
40 #include "uuid/uuid.h"
41 #include "e2p/e2p.h"
42 #include "ext2fs/e2image.h"
43
44 #include "../version.h"
45 #include "nls-enable.h"
46
47 const char * program_name = "e2image";
48 char * device_name = NULL;
49
50 static void usage(void)
51 {
52         fprintf(stderr, _("Usage: %s [-rsI] device image_file\n"),
53                 program_name);
54         exit (1);
55 }
56
57 static void write_header(int fd, struct ext2_image_hdr *hdr, int blocksize)
58 {
59         char *header_buf;
60         int actual;
61
62         header_buf = malloc(blocksize);
63         if (!header_buf) {
64                 fputs(_("Couldn't allocate header buffer\n"), stderr);
65                 exit(1);
66         }
67
68         if (lseek(fd, 0, SEEK_SET) < 0) {
69                 perror("lseek while writing header");
70                 exit(1);
71         }
72         memset(header_buf, 0, blocksize);
73
74         if (hdr)
75                 memcpy(header_buf, hdr, sizeof(struct ext2_image_hdr));
76
77         actual = write(fd, header_buf, blocksize);
78         if (actual < 0) {
79                 perror("write header");
80                 exit(1);
81         }
82         if (actual != blocksize) {
83                 fprintf(stderr, _("short write (only %d bytes) for "
84                                   "writing image header"), actual);
85                 exit(1);
86         }
87         free(header_buf);
88 }
89
90 static void write_image_file(ext2_filsys fs, int fd)
91 {
92         struct ext2_image_hdr   hdr;
93         struct stat             st;
94         errcode_t               retval;
95
96         write_header(fd, NULL, fs->blocksize);
97         memset(&hdr, 0, sizeof(struct ext2_image_hdr));
98
99         hdr.offset_super = lseek(fd, 0, SEEK_CUR);
100         retval = ext2fs_image_super_write(fs, fd, 0);
101         if (retval) {
102                 com_err(program_name, retval, _("while writing superblock"));
103                 exit(1);
104         }
105
106         hdr.offset_inode = lseek(fd, 0, SEEK_CUR);
107         retval = ext2fs_image_inode_write(fs, fd,
108                                   (fd != 1) ? IMAGER_FLAG_SPARSEWRITE : 0);
109         if (retval) {
110                 com_err(program_name, retval, _("while writing inode table"));
111                 exit(1);
112         }
113
114         hdr.offset_blockmap = lseek(fd, 0, SEEK_CUR);
115         retval = ext2fs_image_bitmap_write(fs, fd, 0);
116         if (retval) {
117                 com_err(program_name, retval, _("while writing block bitmap"));
118                 exit(1);
119         }
120
121         hdr.offset_inodemap = lseek(fd, 0, SEEK_CUR);
122         retval = ext2fs_image_bitmap_write(fs, fd, IMAGER_FLAG_INODEMAP);
123         if (retval) {
124                 com_err(program_name, retval, _("while writing inode bitmap"));
125                 exit(1);
126         }
127
128         hdr.magic_number = EXT2_ET_MAGIC_E2IMAGE;
129         strcpy(hdr.magic_descriptor, "Ext2 Image 1.0");
130         gethostname(hdr.fs_hostname, sizeof(hdr.fs_hostname));
131         strncpy(hdr.fs_device_name, device_name, sizeof(hdr.fs_device_name)-1);
132         hdr.fs_device_name[sizeof(hdr.fs_device_name) - 1] = 0;
133         hdr.fs_blocksize = fs->blocksize;
134
135         if (stat(device_name, &st) == 0)
136                 hdr.fs_device = st.st_rdev;
137
138         if (fstat(fd, &st) == 0) {
139                 hdr.image_device = st.st_dev;
140                 hdr.image_inode = st.st_ino;
141         }
142         memcpy(hdr.fs_uuid, fs->super->s_uuid, sizeof(hdr.fs_uuid));
143
144         hdr.image_time = time(0);
145         write_header(fd, &hdr, fs->blocksize);
146 }
147
148 /*
149  * These set of functions are used to write a RAW image file.
150  */
151 ext2fs_block_bitmap meta_block_map;
152 ext2fs_block_bitmap scramble_block_map; /* Directory blocks to be scrambled */
153
154 struct process_block_struct {
155         ext2_ino_t      ino;
156         int             is_dir;
157 };
158
159 /*
160  * These subroutines short circuits ext2fs_get_blocks and
161  * ext2fs_check_directory; we use them since we already have the inode
162  * structure, so there's no point in letting the ext2fs library read
163  * the inode again.
164  */
165 static ino_t stashed_ino = 0;
166 static struct ext2_inode *stashed_inode;
167
168 static errcode_t meta_get_blocks(ext2_filsys fs EXT2FS_ATTR((unused)),
169                                  ext2_ino_t ino,
170                                  blk_t *blocks)
171 {
172         int     i;
173
174         if ((ino != stashed_ino) || !stashed_inode)
175                 return EXT2_ET_CALLBACK_NOTHANDLED;
176
177         for (i=0; i < EXT2_N_BLOCKS; i++)
178                 blocks[i] = stashed_inode->i_block[i];
179         return 0;
180 }
181
182 static errcode_t meta_check_directory(ext2_filsys fs EXT2FS_ATTR((unused)),
183                                       ext2_ino_t ino)
184 {
185         if ((ino != stashed_ino) || !stashed_inode)
186                 return EXT2_ET_CALLBACK_NOTHANDLED;
187
188         if (!LINUX_S_ISDIR(stashed_inode->i_mode))
189                 return EXT2_ET_NO_DIRECTORY;
190         return 0;
191 }
192
193 static errcode_t meta_read_inode(ext2_filsys fs EXT2FS_ATTR((unused)),
194                                  ext2_ino_t ino,
195                                  struct ext2_inode *inode)
196 {
197         if ((ino != stashed_ino) || !stashed_inode)
198                 return EXT2_ET_CALLBACK_NOTHANDLED;
199         *inode = *stashed_inode;
200         return 0;
201 }
202
203 static void use_inode_shortcuts(ext2_filsys fs, int bool)
204 {
205         if (bool) {
206                 fs->get_blocks = meta_get_blocks;
207                 fs->check_directory = meta_check_directory;
208                 fs->read_inode = meta_read_inode;
209                 stashed_ino = 0;
210         } else {
211                 fs->get_blocks = 0;
212                 fs->check_directory = 0;
213                 fs->read_inode = 0;
214         }
215 }
216
217 static int process_dir_block(ext2_filsys fs EXT2FS_ATTR((unused)),
218                              blk_t *block_nr,
219                              e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
220                              blk_t ref_block EXT2FS_ATTR((unused)),
221                              int ref_offset EXT2FS_ATTR((unused)),
222                              void *priv_data EXT2FS_ATTR((unused)))
223 {
224         struct process_block_struct *p;
225
226         p = (struct process_block_struct *) priv_data;
227
228         ext2fs_mark_block_bitmap2(meta_block_map, *block_nr);
229         if (scramble_block_map && p->is_dir && blockcnt >= 0)
230                 ext2fs_mark_block_bitmap2(scramble_block_map, *block_nr);
231         return 0;
232 }
233
234 static int process_file_block(ext2_filsys fs EXT2FS_ATTR((unused)),
235                               blk_t *block_nr,
236                               e2_blkcnt_t blockcnt,
237                               blk_t ref_block EXT2FS_ATTR((unused)),
238                               int ref_offset EXT2FS_ATTR((unused)),
239                               void *priv_data EXT2FS_ATTR((unused)))
240 {
241         if (blockcnt < 0) {
242                 ext2fs_mark_block_bitmap2(meta_block_map, *block_nr);
243         }
244         return 0;
245 }
246
247 static void mark_table_blocks(ext2_filsys fs)
248 {
249         blk_t   first_block, b;
250         unsigned int    i,j;
251
252         first_block = fs->super->s_first_data_block;
253         /*
254          * Mark primary superblock
255          */
256         ext2fs_mark_block_bitmap2(meta_block_map, first_block);
257
258         /*
259          * Mark the primary superblock descriptors
260          */
261         for (j = 0; j < fs->desc_blocks; j++) {
262                 ext2fs_mark_block_bitmap2(meta_block_map,
263                          ext2fs_descriptor_block_loc(fs, first_block, j));
264         }
265
266         for (i = 0; i < fs->group_desc_count; i++) {
267                 /*
268                  * Mark the blocks used for the inode table
269                  */
270                 if (ext2fs_inode_table_loc(fs, i)) {
271                         for (j = 0, b = ext2fs_inode_table_loc(fs, i);
272                              j < (unsigned) fs->inode_blocks_per_group;
273                              j++, b++)
274                                 ext2fs_mark_block_bitmap2(meta_block_map, b);
275                 }
276
277                 /*
278                  * Mark block used for the block bitmap
279                  */
280                 if (ext2fs_block_bitmap_loc(fs, i)) {
281                         ext2fs_mark_block_bitmap2(meta_block_map,
282                                      ext2fs_block_bitmap_loc(fs, i));
283                 }
284
285                 /*
286                  * Mark block used for the inode bitmap
287                  */
288                 if (ext2fs_inode_bitmap_loc(fs, i)) {
289                         ext2fs_mark_block_bitmap2(meta_block_map,
290                                  ext2fs_inode_bitmap_loc(fs, i));
291                 }
292         }
293 }
294
295 /*
296  * This function returns 1 if the specified block is all zeros
297  */
298 static int check_zero_block(char *buf, int blocksize)
299 {
300         char    *cp = buf;
301         int     left = blocksize;
302
303         while (left > 0) {
304                 if (*cp++)
305                         return 0;
306                 left--;
307         }
308         return 1;
309 }
310
311 static void write_block(int fd, char *buf, int sparse_offset,
312                         int blocksize, blk_t block)
313 {
314         int             count;
315         errcode_t       err;
316
317         if (sparse_offset) {
318 #ifdef HAVE_LSEEK64
319                 if (lseek64(fd, sparse_offset, SEEK_CUR) < 0)
320                         perror("lseek");
321 #else
322                 if (lseek(fd, sparse_offset, SEEK_CUR) < 0)
323                         perror("lseek");
324 #endif
325         }
326         if (blocksize) {
327                 count = write(fd, buf, blocksize);
328                 if (count != blocksize) {
329                         if (count == -1)
330                                 err = errno;
331                         else
332                                 err = 0;
333                         com_err(program_name, err, "error writing block %u",
334                                 block);
335                         exit(1);
336                 }
337         }
338 }
339
340 int name_id[256];
341
342 #define EXT4_MAX_REC_LEN                ((1<<16)-1)
343
344 static void scramble_dir_block(ext2_filsys fs, blk_t blk, char *buf)
345 {
346         char *p, *end, *cp;
347         struct ext2_dir_entry_2 *dirent;
348         unsigned int rec_len;
349         int id, len;
350
351         end = buf + fs->blocksize;
352         for (p = buf; p < end-8; p += rec_len) {
353                 dirent = (struct ext2_dir_entry_2 *) p;
354                 rec_len = dirent->rec_len;
355 #ifdef WORDS_BIGENDIAN
356                 rec_len = ext2fs_swab16(rec_len);
357 #endif
358                 if (rec_len == EXT4_MAX_REC_LEN || rec_len == 0)
359                         rec_len = fs->blocksize;
360                 else 
361                         rec_len = (rec_len & 65532) | ((rec_len & 3) << 16);
362 #if 0
363                 printf("rec_len = %d, name_len = %d\n", rec_len, dirent->name_len);
364 #endif
365                 if (rec_len < 8 || (rec_len % 4) ||
366                     (p+rec_len > end)) {
367                         printf("Corrupt directory block %lu: "
368                                "bad rec_len (%d)\n", (unsigned long) blk,
369                                rec_len);
370                         rec_len = end - p;
371                         (void) ext2fs_set_rec_len(fs, rec_len,
372                                         (struct ext2_dir_entry *) dirent);
373 #ifdef WORDS_BIGENDIAN
374                         dirent->rec_len = ext2fs_swab16(dirent->rec_len);
375 #endif
376                         continue;
377                 }
378                 if (dirent->name_len + 8 > rec_len) {
379                         printf("Corrupt directory block %lu: "
380                                "bad name_len (%d)\n", (unsigned long) blk,
381                                dirent->name_len);
382                         dirent->name_len = rec_len - 8;
383                         continue;
384                 }
385                 cp = p+8;
386                 len = rec_len - dirent->name_len - 8;
387                 if (len > 0)
388                         memset(cp+dirent->name_len, 0, len);
389                 if (dirent->name_len==1 && cp[0] == '.')
390                         continue;
391                 if (dirent->name_len==2 && cp[0] == '.' && cp[1] == '.')
392                         continue;
393
394                 memset(cp, 'A', dirent->name_len);
395                 len = dirent->name_len;
396                 id = name_id[len]++;
397                 while ((len > 0) && (id > 0)) {
398                         *cp += id % 26;
399                         id = id / 26;
400                         cp++;
401                         len--;
402                 }
403         }
404 }
405
406 static void output_meta_data_blocks(ext2_filsys fs, int fd)
407 {
408         errcode_t       retval;
409         blk_t           blk;
410         char            *buf, *zero_buf;
411         int             sparse = 0;
412
413         buf = malloc(fs->blocksize);
414         if (!buf) {
415                 com_err(program_name, ENOMEM, "while allocating buffer");
416                 exit(1);
417         }
418         zero_buf = malloc(fs->blocksize);
419         if (!zero_buf) {
420                 com_err(program_name, ENOMEM, "while allocating buffer");
421                 exit(1);
422         }
423         memset(zero_buf, 0, fs->blocksize);
424         for (blk = 0; blk < ext2fs_blocks_count(fs->super); blk++) {
425                 if ((blk >= fs->super->s_first_data_block) &&
426                     ext2fs_test_block_bitmap2(meta_block_map, blk)) {
427                         retval = io_channel_read_blk64(fs->io, blk, 1, buf);
428                         if (retval) {
429                                 com_err(program_name, retval,
430                                         "error reading block %u", blk);
431                         }
432                         if (scramble_block_map &&
433                             ext2fs_test_block_bitmap2(scramble_block_map, blk))
434                                 scramble_dir_block(fs, blk, buf);
435                         if ((fd != 1) && check_zero_block(buf, fs->blocksize))
436                                 goto sparse_write;
437                         write_block(fd, buf, sparse, fs->blocksize, blk);
438                         sparse = 0;
439                 } else {
440                 sparse_write:
441                         if (fd == 1) {
442                                 write_block(fd, zero_buf, 0,
443                                             fs->blocksize, blk);
444                                 continue;
445                         }
446                         sparse += fs->blocksize;
447                         if (sparse >= 1024*1024) {
448                                 write_block(fd, 0, sparse, 0, 0);
449                                 sparse = 0;
450                         }
451                 }
452         }
453         if (sparse)
454                 write_block(fd, zero_buf, sparse-1, 1, -1);
455         free(zero_buf);
456         free(buf);
457 }
458
459 static void write_raw_image_file(ext2_filsys fs, int fd, int scramble_flag)
460 {
461         struct process_block_struct     pb;
462         struct ext2_inode               inode;
463         ext2_inode_scan                 scan;
464         ext2_ino_t                      ino;
465         errcode_t                       retval;
466         char *                          block_buf;
467
468         retval = ext2fs_allocate_block_bitmap(fs, "in-use block map",
469                                               &meta_block_map);
470         if (retval) {
471                 com_err(program_name, retval, "while allocating block bitmap");
472                 exit(1);
473         }
474
475         if (scramble_flag) {
476                 retval = ext2fs_allocate_block_bitmap(fs, "scramble block map",
477                                                       &scramble_block_map);
478                 if (retval) {
479                         com_err(program_name, retval,
480                                 "while allocating scramble block bitmap");
481                         exit(1);
482                 }
483         }
484
485         mark_table_blocks(fs);
486
487         retval = ext2fs_open_inode_scan(fs, 0, &scan);
488         if (retval) {
489                 com_err(program_name, retval, _("while opening inode scan"));
490                 exit(1);
491         }
492
493         block_buf = malloc(fs->blocksize * 3);
494         if (!block_buf) {
495                 com_err(program_name, 0, "Can't allocate block buffer");
496                 exit(1);
497         }
498
499         use_inode_shortcuts(fs, 1);
500         stashed_inode = &inode;
501         while (1) {
502                 retval = ext2fs_get_next_inode(scan, &ino, &inode);
503                 if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
504                         continue;
505                 if (retval) {
506                         com_err(program_name, retval,
507                                 _("while getting next inode"));
508                         exit(1);
509                 }
510                 if (ino == 0)
511                         break;
512                 if (!inode.i_links_count)
513                         continue;
514                 if (ext2fs_file_acl_block(&inode)) {
515                         ext2fs_mark_block_bitmap2(meta_block_map,
516                                                  ext2fs_file_acl_block(&inode));
517                 }
518                 if (!ext2fs_inode_has_valid_blocks(&inode))
519                         continue;
520
521                 stashed_ino = ino;
522                 pb.ino = ino;
523                 pb.is_dir = LINUX_S_ISDIR(inode.i_mode);
524                 if (LINUX_S_ISDIR(inode.i_mode) ||
525                     (LINUX_S_ISLNK(inode.i_mode) &&
526                      ext2fs_inode_has_valid_blocks(&inode)) ||
527                     ino == fs->super->s_journal_inum) {
528                         retval = ext2fs_block_iterate2(fs, ino,
529                                         BLOCK_FLAG_READ_ONLY, block_buf,
530                                         process_dir_block, &pb);
531                         if (retval) {
532                                 com_err(program_name, retval,
533                                         "while iterating over inode %u",
534                                         ino);
535                                 exit(1);
536                         }
537                 } else {
538                         if ((inode.i_flags & EXT4_EXTENTS_FL) ||
539                             inode.i_block[EXT2_IND_BLOCK] ||
540                             inode.i_block[EXT2_DIND_BLOCK] ||
541                             inode.i_block[EXT2_TIND_BLOCK]) {
542                                 retval = ext2fs_block_iterate2(fs,
543                                        ino, BLOCK_FLAG_READ_ONLY, block_buf,
544                                        process_file_block, &pb);
545                                 if (retval) {
546                                         com_err(program_name, retval,
547                                         "while iterating over inode %u", ino);
548                                         exit(1);
549                                 }
550                         }
551                 }
552         }
553         use_inode_shortcuts(fs, 0);
554         output_meta_data_blocks(fs, fd);
555         free(block_buf);
556 }
557
558 static void install_image(char *device, char *image_fn, int raw_flag)
559 {
560         errcode_t retval;
561         ext2_filsys fs;
562         int open_flag = EXT2_FLAG_IMAGE_FILE;
563         int fd = 0;
564         io_manager      io_ptr;
565         io_channel      io, image_io;
566
567         if (raw_flag) {
568                 com_err(program_name, 0, "Raw images cannot be installed");
569                 exit(1);
570         }
571
572 #ifdef CONFIG_TESTIO_DEBUG
573         if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) {
574                 io_ptr = test_io_manager;
575                 test_io_backing_manager = unix_io_manager;
576         } else
577 #endif
578                 io_ptr = unix_io_manager;
579
580         retval = ext2fs_open (image_fn, open_flag, 0, 0,
581                               io_ptr, &fs);
582         if (retval) {
583                 com_err (program_name, retval, _("while trying to open %s"),
584                          image_fn);
585                 exit(1);
586         }
587
588         retval = ext2fs_read_bitmaps (fs);
589         if (retval) {
590                 com_err(program_name, retval, "error reading bitmaps");
591                 exit(1);
592         }
593
594 #ifdef HAVE_OPEN64
595         fd = open64(image_fn, O_RDONLY);
596 #else
597         fd = open(image_fn, O_RDONLY);
598 #endif
599         if (fd < 0) {
600                 perror(image_fn);
601                 exit(1);
602         }
603
604         retval = io_ptr->open(device, IO_FLAG_RW, &io);
605         if (retval) {
606                 com_err(device, 0, "while opening device file");
607                 exit(1);
608         }
609
610         image_io = fs->io;
611
612         ext2fs_rewrite_to_io(fs, io);
613
614         if (lseek(fd, fs->image_header->offset_inode, SEEK_SET) < 0) {
615                 perror("lseek");
616                 exit(1);
617         }
618
619         retval = ext2fs_image_inode_read(fs, fd, 0);
620         if (retval) {
621                 com_err(image_fn, 0, "while restoring the image table");
622                 exit(1);
623         }
624
625         ext2fs_close (fs);
626         exit (0);
627 }
628
629 int main (int argc, char ** argv)
630 {
631         int c;
632         errcode_t retval;
633         ext2_filsys fs;
634         char *image_fn;
635         int open_flag = 0;
636         int raw_flag = 0;
637         int install_flag = 0;
638         int scramble_flag = 0;
639         int fd = 0;
640
641 #ifdef ENABLE_NLS
642         setlocale(LC_MESSAGES, "");
643         setlocale(LC_CTYPE, "");
644         bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
645         textdomain(NLS_CAT_NAME);
646 #endif
647         fprintf (stderr, "e2image %s (%s)\n", E2FSPROGS_VERSION,
648                  E2FSPROGS_DATE);
649         if (argc && *argv)
650                 program_name = *argv;
651         add_error_table(&et_ext2_error_table);
652         while ((c = getopt (argc, argv, "rsI")) != EOF)
653                 switch (c) {
654                 case 'r':
655                         raw_flag++;
656                         break;
657                 case 's':
658                         scramble_flag++;
659                         break;
660                 case 'I':
661                         install_flag++;
662                         break;
663                 default:
664                         usage();
665                 }
666         if (optind != argc - 2 )
667                 usage();
668         device_name = argv[optind];
669         image_fn = argv[optind+1];
670
671         if (install_flag) {
672                 install_image(device_name, image_fn, raw_flag);
673                 exit (0);
674         }
675
676         retval = ext2fs_open (device_name, open_flag, 0, 0,
677                               unix_io_manager, &fs);
678         if (retval) {
679                 com_err (program_name, retval, _("while trying to open %s"),
680                          device_name);
681                 fputs(_("Couldn't find valid filesystem superblock.\n"), stdout);
682                 exit(1);
683         }
684
685         if (strcmp(image_fn, "-") == 0)
686                 fd = 1;
687         else {
688 #ifdef HAVE_OPEN64
689                 fd = open64(image_fn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
690 #else
691                 fd = open(image_fn, O_CREAT|O_TRUNC|O_WRONLY, 0600);
692 #endif
693                 if (fd < 0) {
694                         com_err(program_name, errno,
695                                 _("while trying to open %s"), argv[optind+1]);
696                         exit(1);
697                 }
698         }
699
700         if (raw_flag)
701                 write_raw_image_file(fs, fd, scramble_flag);
702         else
703                 write_image_file(fs, fd);
704
705         ext2fs_close (fs);
706         remove_error_table(&et_ext2_error_table);
707         exit (0);
708 }