+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
get_pathname.o \
getsize.o \
icount.o \
+ imager.o \
initialize.o \
inline.o \
inode.o \
$(srcdir)/get_pathname.c \
$(srcdir)/getsize.c \
$(srcdir)/icount.c \
+ $(srcdir)/imager.o \
$(srcdir)/initialize.c \
$(srcdir)/inline.c \
$(srcdir)/inode.c \
--- /dev/null
+/*
+ * 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];
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
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"
#define BMAP_ALLOC 1
/*
+ * Flags for imager.c functions
+ */
+#define IMAGER_FLAG_INODEMAP 1
+#define IMAGER_FLAG_SPARSEWRITE 2
+
+/*
* For checking structure magic numbers...
*/
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);
--- /dev/null
+/*
+ * 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);
+}
+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
@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@
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 \
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)
--- /dev/null
+/*
+ * 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);
+}