Whamcloud - gitweb
ChangeLog, Makefile.in, e2image.h, ext2_err.et.in, ext2fs.h, imager.c:
authorTheodore Ts'o <tytso@mit.edu>
Sun, 12 Nov 2000 19:32:20 +0000 (19:32 +0000)
committerTheodore Ts'o <tytso@mit.edu>
Sun, 12 Nov 2000 19:32:20 +0000 (19:32 +0000)
  imager.c (ext2fs_image_{inode,super,bitmap}_{read,write}, ext2_fs.h,
   Makefile.in: New file that has routines that save ext2fs metadata to a
   file.
  ext2_err.et.in (EXT2_ET_MAGIC_E2IMAGE): New error code assigned.
  e2image.h: New file which defines the file format for the ext2 image
   file.  (Saved copy of ext2 metadata to a file as a saving throw
   against worst-case damage.)
ChangeLog, Makefile.in, e2image.c:
  e2image.c, Makefile.in: New program which saves ext2 metadata to a
   file for people who need a last-ditch saving throw.

lib/ext2fs/ChangeLog
lib/ext2fs/Makefile.in
lib/ext2fs/e2image.h [new file with mode: 0644]
lib/ext2fs/ext2_err.et.in
lib/ext2fs/ext2fs.h
lib/ext2fs/imager.c [new file with mode: 0644]
misc/ChangeLog
misc/Makefile.in
misc/e2image.c [new file with mode: 0644]

index 17f47fd..0ade399 100644 (file)
@@ -1,3 +1,15 @@
+2000-11-05    <tytso@snap.thunk.org>
+
+       * imager.c (ext2fs_image_{inode,super,bitmap}_{read,write},
+               ext2_fs.h, Makefile.in: New file that has routines that
+               save ext2fs metadata to a file.
+
+       * ext2_err.et.in (EXT2_ET_MAGIC_E2IMAGE): New error code assigned.
+
+       * e2image.h: New file which defines the file format for the ext2
+               image file.  (Saved copy of ext2 metadata to a file as a
+               saving throw against worst-case damage.)
+
 2000-11-01    <tytso@snap.thunk.org>
 
        * inode.c (ext2fs_flush_icache): Add new function
index 6d21d60..008e48a 100644 (file)
@@ -32,6 +32,7 @@ OBJS= ext2_err.o \
        get_pathname.o \
        getsize.o \
        icount.o \
+       imager.o \
        initialize.o \
        inline.o \
        inode.o \
@@ -82,6 +83,7 @@ SRCS= ext2_err.c \
        $(srcdir)/get_pathname.c \
        $(srcdir)/getsize.c \
        $(srcdir)/icount.c \
+       $(srcdir)/imager.o \
        $(srcdir)/initialize.c \
        $(srcdir)/inline.c \
        $(srcdir)/inode.c \
diff --git a/lib/ext2fs/e2image.h b/lib/ext2fs/e2image.h
new file mode 100644 (file)
index 0000000..c72c29d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * e2image.h --- header file describing the ext2 image format
+ * 
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * Note: this uses the POSIX IO interfaces, unlike most of the other
+ * functions in this library.  So sue me.  
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+
+struct ext2_image_hdr {
+       __u32   magic_number;   /* This must be EXT2_ET_MAGIC_E2IMAGE */
+       char    magic_descriptor[16]; /* "Ext2 Image 1.0", w/ null padding */
+       __u32   fs_hostname[64];/* Hostname of machine of image */
+       char    fs_netaddr[32]; /* Network address */
+       __u32   fs_netaddr_type;/* 0 = IPV4, 1 = IPV6, etc. */
+       __u32   fs_device;      /* Device number of image */
+       char    fs_uuid[16];    /* UUID of filesystem */
+       __u32   fs_reserved[8];
+       
+       __u32   image_device;   /* Device number of image file */
+       __u32   image_inode;    /* Inode number of image file */
+       __u32   image_time;     /* Time of image creation */
+       __u32   image_reserved[8];
+
+       __u32   offset_super;   /* Byte offset of the sb and descriptors */
+       __u32   offset_inode;   /* Byte offset of the inode table  */
+       __u32   offset_inodemap; /* Byte offset of the inode bitmaps */
+       __u32   offset_blockmap; /* Byte offset of the inode bitmaps */
+       __u32   offset_reserved[8];
+};
+
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
index 41a439d..bc79a47 100644 (file)
@@ -56,8 +56,8 @@ ec    EXT2_ET_MAGIC_PQ_IO_CHANNEL,
 ec     EXT2_ET_MAGIC_EXT2_FILE,
        "Wrong magic number for ext2 file structure"
 
-ec     EXT2_ET_MAGIC_RESERVED_7,
-       "Wrong magic number --- RESERVED_7"
+ec     EXT2_ET_MAGIC_E2IMAGE,
+       "Wrong magic number for Ext2 Image Header"
 
 ec     EXT2_ET_MAGIC_RESERVED_8,
        "Wrong magic number --- RESERVED_8"
index 791bd3d..e579386 100644 (file)
@@ -368,6 +368,12 @@ typedef struct ext2_icount *ext2_icount_t;
 #define BMAP_ALLOC     1
 
 /*
+ * Flags for imager.c functions
+ */
+#define IMAGER_FLAG_INODEMAP   1
+#define IMAGER_FLAG_SPARSEWRITE        2
+
+/*
  * For checking structure magic numbers...
  */
 
@@ -724,12 +730,21 @@ extern void ext2fs_badblocks_list_free(badblocks_list bb);
 extern errcode_t ext2fs_get_device_size(const char *file, int blocksize,
                                        blk_t *retblocks);
 
+/* imager.c */
+extern errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags);
+
 /* initialize.c */
 extern errcode_t ext2fs_initialize(const char *name, int flags,
                                   struct ext2_super_block *param,
                                   io_manager manager, ext2_filsys *ret_fs);
 
 /* inode.c */
+extern errcode_t ext2fs_flush_icache(ext2_filsys fs);
 extern errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
                                  ext2_inode_scan *ret_scan);
 extern void ext2fs_close_inode_scan(ext2_inode_scan scan);
diff --git a/lib/ext2fs/imager.c b/lib/ext2fs/imager.c
new file mode 100644 (file)
index 0000000..79b88ee
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * image.c --- writes out the critical parts of the filesystem as a
+ *     flat file.
+ *
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * Note: this uses the POSIX IO interfaces, unlike most of the other
+ * functions in this library.  So sue me.  
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if EXT2_FLAT_INCLUDES
+#include "ext2_fs.h"
+#else
+#include <linux/ext2_fs.h>
+#endif
+
+#include "ext2fs.h"
+
+/*
+ * This function returns 1 if the specified block is all zeros
+ */
+static int check_zero_block(char *buf, int blocksize)
+{
+       char    *cp = buf;
+       int     left = blocksize;
+
+       while (left > 0) {
+               if (*cp++)
+                       return 0;
+               left--;
+       }
+       return 1;
+}
+
+/*
+ * Write the inode table out as a single block.
+ */
+#define BUF_BLOCKS     32
+
+errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags)
+{
+       unsigned int    group, left, c, d;
+       char            *buf, *cp;
+       blk_t           blk;
+       ssize_t         actual;
+       errcode_t       retval;
+
+       buf = malloc(fs->blocksize * BUF_BLOCKS);
+       if (!buf)
+               return ENOMEM;
+       
+       for (group = 0; group < fs->group_desc_count; group++) {
+               blk = fs->group_desc[(unsigned)group].bg_inode_table;
+               if (!blk)
+                       return EXT2_ET_MISSING_INODE_TABLE;
+               left = fs->inode_blocks_per_group;
+               while (left) {
+                       c = BUF_BLOCKS;
+                       if (c > left)
+                               c = left;
+                       retval = io_channel_read_blk(fs->io, blk, c, buf);
+                       if (retval)
+                               goto errout;
+                       cp = buf;
+                       while (c) {
+                               if (!(flags & IMAGER_FLAG_SPARSEWRITE)) {
+                                       d = c;
+                                       goto skip_sparse;
+                               }
+                               /* Skip zero blocks */
+                               if (check_zero_block(cp, fs->blocksize)) {
+                                       c--;
+                                       blk++;
+                                       left--;
+                                       cp += fs->blocksize;
+                                       lseek(fd, fs->blocksize, SEEK_CUR);
+                                       continue;
+                               }
+                               /* Find non-zero blocks */
+                               for (d=1; d < c; d++) {
+                                       if (check_zero_block(cp + d*fs->blocksize, fs->blocksize))
+                                               break;
+                               }
+                       skip_sparse:
+                               actual = write(fd, cp, fs->blocksize * d);
+                               if (actual == -1) {
+                                       errno = retval;
+                                       goto errout;
+                               }
+                               if (actual != fs->blocksize * d) {
+                                       retval = EXT2_ET_SHORT_WRITE;
+                                       goto errout;
+                               }
+                               blk += d;
+                               left -= d;
+                               cp += fs->blocksize * d;
+                               c -= d;
+                       }
+               }
+       }
+       retval = 0;
+
+errout:
+       free(buf);
+       return retval;
+}
+
+/*
+ * Read in the inode table and stuff it into place
+ */
+errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags)
+{
+       unsigned int    group, i, c, left;
+       char            *buf;
+       blk_t           blk;
+       ssize_t         actual;
+       errcode_t       retval;
+
+       buf = malloc(fs->blocksize * BUF_BLOCKS);
+       if (!buf)
+               return ENOMEM;
+       
+       for (group = 0; group < fs->group_desc_count; group++) {
+               blk = fs->group_desc[(unsigned)group].bg_inode_table;
+               if (!blk)
+                       return EXT2_ET_MISSING_INODE_TABLE;
+               left = fs->inode_blocks_per_group;
+               while (left) {
+                       c = BUF_BLOCKS;
+                       if (c > left)
+                               c = left;
+                       actual = read(fd, buf, fs->blocksize * c);
+                       if (actual == -1) {
+                               errno = retval;
+                               goto errout;
+                       }
+                       if (actual != fs->blocksize * c) {
+                               retval = EXT2_ET_SHORT_READ;
+                               goto errout;
+                       }
+                       retval = io_channel_write_blk(fs->io, blk, c, buf);
+                       if (retval)
+                               goto errout;
+                       
+                       blk += c;
+                       left -= c;
+               }
+       }
+       retval = ext2fs_flush_icache(fs);
+
+errout:
+       free(buf);
+       return retval;
+}
+
+/*
+ * Write out superblock and group descriptors
+ */
+errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags)
+{
+       unsigned int    i;
+       char            *buf, *cp;
+       blk_t           blk;
+       ssize_t         actual;
+       errcode_t       retval;
+
+       buf = malloc(fs->blocksize);
+       if (!buf)
+               return ENOMEM;
+
+       /*
+        * Write out the superblock
+        */
+       memset(buf, 0, fs->blocksize);
+       memcpy(buf, fs->super, SUPERBLOCK_SIZE);
+       actual = write(fd, buf, fs->blocksize);
+       if (actual == -1) {
+               errno = retval;
+               goto errout;
+       }
+       if (actual != fs->blocksize) {
+               retval = EXT2_ET_SHORT_WRITE;
+               goto errout;
+       }
+
+       /*
+        * Now write out the block group descriptors
+        */
+       cp = (char *) fs->group_desc;
+       actual = write(fd, cp, fs->blocksize * fs->desc_blocks);
+       if (actual == -1) {
+               errno = retval;
+               goto errout;
+       }
+       if (actual != fs->blocksize * fs->desc_blocks) {
+               retval = EXT2_ET_SHORT_WRITE;
+               goto errout;
+       }
+       
+       retval = 0;
+
+errout:
+       free(buf);
+       return retval;
+}
+
+/*
+ * Read the superblock and group descriptors and overwrite them.
+ */
+errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags)
+{
+       unsigned int    i;
+       char            *buf, *cp;
+       blk_t           blk;
+       ssize_t         actual, size;
+       errcode_t       retval;
+
+       size = fs->blocksize * (fs->group_desc_count + 1);
+       buf = malloc(size);
+       if (!buf)
+               return ENOMEM;
+
+       /*
+        * Read it all in.
+        */
+       actual = read(fd, buf, size);
+       if (actual == -1) {
+               errno = retval;
+               goto errout;
+       }
+       if (actual != size) {
+               retval = EXT2_ET_SHORT_READ;
+               goto errout;
+       }
+
+       /*
+        * Now copy in the superblock and group descriptors
+        */
+       memcpy(fs->super, buf, SUPERBLOCK_SIZE);
+
+       memcpy(fs->group_desc, buf + fs->blocksize,
+              fs->blocksize * fs->group_desc_count);
+
+       retval = 0;
+
+errout:
+       free(buf);
+       return retval;
+}
+
+/*
+ * Write the block/inode bitmaps.
+ */
+errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
+{
+       char            *ptr;
+       int             c, size;
+       char            zero_buf[1024];
+       ssize_t         actual;
+       errcode_t       retval;
+
+       if (flags & IMAGER_FLAG_INODEMAP) {
+               if (!fs->inode_map) {
+                       retval = ext2fs_read_inode_bitmap(fs);
+                       if (retval)
+                               return retval;
+               }
+               ptr = fs->inode_map->bitmap;
+               size = ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8);
+       } else {
+               if (!fs->block_map) {
+                       retval = ext2fs_read_block_bitmap(fs);
+                       if (retval)
+                               return retval;
+               }
+               ptr = fs->block_map->bitmap;
+               size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+       }
+
+       actual = write(fd, ptr, size);
+       if (actual == -1) {
+               errno = retval;
+               goto errout;
+       }
+       if (actual != size) {
+               retval = EXT2_ET_SHORT_WRITE;
+               goto errout;
+       }
+       size = size % fs->blocksize;
+       memset(zero_buf, 0, sizeof(zero_buf));
+       if (size) {
+               size = fs->blocksize - size;
+               while (size) {
+                       c = size;
+                       if (c > sizeof(zero_buf))
+                               c = sizeof(zero_buf);
+                       actual = write(fd, zero_buf, c);
+                       if (actual == -1) {
+                               errno = retval;
+                               goto errout;
+                       }
+                       if (actual != c) {
+                               retval = EXT2_ET_SHORT_WRITE;
+                               goto errout;
+                       }
+                       size -= c;
+               }
+       }
+       retval = 0;
+errout:
+       return (retval);
+}
+
+
+/*
+ * Read the block/inode bitmaps.
+ */
+errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
+{
+       char            *ptr, *buf = 0;
+       int             c, size;
+       char            zero_buf[1024];
+       ssize_t         actual;
+       errcode_t       retval;
+
+       if (flags & IMAGER_FLAG_INODEMAP) {
+               if (!fs->inode_map) {
+                       retval = ext2fs_read_inode_bitmap(fs);
+                       if (retval)
+                               return retval;
+               }
+               ptr = fs->inode_map->bitmap;
+               size = ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8);
+       } else {
+               if (!fs->block_map) {
+                       retval = ext2fs_read_block_bitmap(fs);
+                       if (retval)
+                               return retval;
+               }
+               ptr = fs->block_map->bitmap;
+               size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+       }
+
+       buf = malloc(size);
+       if (!buf)
+               return ENOMEM;
+
+       actual = read(fd, buf, size);
+       if (actual == -1) {
+               errno = retval;
+               goto errout;
+       }
+       if (actual != size) {
+               retval = EXT2_ET_SHORT_WRITE;
+               goto errout;
+       }
+       memcpy(ptr, buf, size);
+       
+       retval = 0;
+errout:
+       if (buf)
+               free(buf);
+       return (retval);
+}
index 5da2e14..6985af1 100644 (file)
@@ -1,3 +1,8 @@
+2000-11-05    <tytso@snap.thunk.org>
+
+       * e2image.c, Makefile.in: New program which saves ext2 metadata to
+               a file for people who need a last-ditch saving throw.
+
 2000-10-24    <tytso@snap.thunk.org>
 
        * mke2fs.c (PRS): Applied Andreas Dilger's patch to make the -r -s
index f196f46..a4d0e44 100644 (file)
@@ -11,7 +11,7 @@ INSTALL = @INSTALL@
 
 @MCONFIG@
 
-SPROGS=                mke2fs badblocks tune2fs dumpe2fs e2label @FSCK_PROG@
+SPROGS=                mke2fs badblocks tune2fs dumpe2fs e2label e2image @FSCK_PROG@
 USPROGS=       mklost+found
 SMANPAGES=     tune2fs.8 mklost+found.8 mke2fs.8 dumpe2fs.8 badblocks.8 \
                        e2label.8 @FSCK_MAN@
@@ -28,6 +28,7 @@ UUIDGEN_OBJS= uuidgen.o
 DUMPE2FS_OBJS= dumpe2fs.o
 BADBLOCKS_OBJS=        badblocks.o
 E2LABEL_OBJS=  e2label.o
+E2IMAGE_OBJS=  e2image.o
 FSCK_OBJS=     fsck.o get_device_by_label.o
 
 SRCS=  $(srcdir)/tune2fs.c $(srcdir)/mklost+found.c $(srcdir)/mke2fs.c \
@@ -57,6 +58,9 @@ tune2fs: $(TUNE2FS_OBJS) $(DEPLIBS_E2P) $(DEPLIBUUID)
 e2label: $(E2LABEL_OBJS) 
        $(CC) $(ALL_LDFLAGS) -o e2label $(E2LABEL_OBJS) 
 
+e2image: $(E2IMAGE_OBJS) $(DEPLIBS)
+       $(CC) $(ALL_LDFLAGS) -o e2image $(E2IMAGE_OBJS) $(LIBS)
+
 mklost+found: $(MKLPF_OBJS)
        $(CC) $(ALL_LDFLAGS) -o mklost+found $(MKLPF_OBJS)
 
diff --git a/misc/e2image.c b/misc/e2image.c
new file mode 100644 (file)
index 0000000..2cdf2fe
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * e2image.c --- Program which writes an image file backing up
+ * critical metadata for the filesystem.
+ *
+ * Copyright 2000 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <fcntl.h>
+#include <grp.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <linux/ext2_fs.h>
+
+#include "ext2fs/ext2fs.h"
+#include "et/com_err.h"
+#include "uuid/uuid.h"
+#include "e2p/e2p.h"
+#include "ext2fs/e2image.h"
+
+#include "../version.h"
+#include "nls-enable.h"
+
+const char * program_name = "e2label";
+char * device_name = NULL;
+
+static void usage(void)
+{
+       fprintf(stderr, _("Usage: %s device file\n"), program_name);
+       exit (1);
+}
+
+static const char *please_fsck = N_("Please run e2fsck on the filesystem.\n");
+
+static void write_header(int fd, struct ext2_image_hdr *hdr)
+{
+       char header_buf[4096];
+       int actual;
+
+       if (lseek(fd, 0, SEEK_SET) < 0) {
+               perror("lseek while writing header");
+               exit(1);
+       }
+       memset(header_buf, 0, sizeof(header_buf));
+       
+       if (hdr)
+               memcpy(header_buf, hdr, sizeof(struct ext2_image_hdr));
+       
+       actual = write(fd, header_buf, sizeof(header_buf));
+       if (actual < 0) {
+               perror("write header");
+               exit(1);
+       }
+       if (actual != sizeof(header_buf)) {
+               fprintf(stderr, _("short write (only %d bytes) for"
+                                 "writing image header"), actual);
+               exit(1);
+       }
+}
+
+
+int main (int argc, char ** argv)
+{
+       int c;
+       char * tmp;
+       errcode_t retval;
+       ext2_filsys fs;
+       struct ext2fs_sb *sb;
+       int open_flag = 0;
+       int raw_flag = 0;
+       int fd = 0;
+       char *features_cmd = 0;
+       struct ext2_image_hdr hdr;
+
+#ifdef ENABLE_NLS
+       setlocale(LC_MESSAGES, "");
+       bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
+       textdomain(NLS_CAT_NAME);
+#endif
+       fprintf (stderr, _("e2image %s, %s for EXT2 FS %s, %s\n"),
+                E2FSPROGS_VERSION, E2FSPROGS_DATE,
+                EXT2FS_VERSION, EXT2FS_DATE);
+       if (argc && *argv)
+               program_name = *argv;
+       initialize_ext2_error_table();
+       while ((c = getopt (argc, argv, "r")) != EOF)
+               switch (c) {
+               case 'r':
+                       raw_flag++;
+                       break;
+               default:
+                       usage();
+               }
+       if (optind != argc - 2 )
+               usage();
+       device_name = argv[optind];
+       retval = ext2fs_open (device_name, open_flag, 0, 0,
+                             unix_io_manager, &fs);
+        if (retval) {
+               com_err (program_name, retval, _("while trying to open %s"),
+                        device_name);
+               printf(_("Couldn't find valid filesystem superblock.\n"));
+               exit(1);
+       }
+       sb = (struct ext2fs_sb *) fs->super;
+
+       fd = open(argv[optind+1], O_CREAT|O_TRUNC|O_RDWR, 0600);
+       if (fd < 0) {
+               com_err(program_name, errno, _("while trying to open %s"),
+                       argv[optind+1]);
+               exit(1);
+       }
+
+       write_header(fd, NULL);
+       memset(&hdr, 0, sizeof(struct ext2_image_hdr));
+       
+       hdr.offset_super = lseek(fd, 0, SEEK_CUR);
+       retval = ext2fs_image_super_write(fs, fd, 0);
+       if (retval) {
+               com_err(program_name, retval, _("while writing superblock"));
+               exit(1);
+       }
+       
+       hdr.offset_inode = lseek(fd, 0, SEEK_CUR);
+       retval = ext2fs_image_inode_write(fs, fd, IMAGER_FLAG_SPARSEWRITE);
+       if (retval) {
+               com_err(program_name, retval, _("while writing inode table"));
+               exit(1);
+       }
+       
+       hdr.offset_blockmap = lseek(fd, 0, SEEK_CUR);
+       retval = ext2fs_image_bitmap_write(fs, fd, 0);
+       if (retval) {
+               com_err(program_name, retval, _("while writing block bitmap"));
+               exit(1);
+       }
+
+       hdr.offset_inodemap = lseek(fd, 0, SEEK_CUR);
+       retval = ext2fs_image_bitmap_write(fs, fd, IMAGER_FLAG_INODEMAP);
+       if (retval) {
+               com_err(program_name, retval, _("while writing inode bitmap"));
+               exit(1);
+       }
+       
+       ext2fs_close (fs);
+       exit (0);
+}