aboutsummaryrefslogtreecommitdiff
path: root/genext2fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'genext2fs.c')
-rw-r--r--genext2fs.c2726
1 files changed, 2726 insertions, 0 deletions
diff --git a/genext2fs.c b/genext2fs.c
new file mode 100644
index 0000000..11c9c37
--- /dev/null
+++ b/genext2fs.c
@@ -0,0 +1,2726 @@
+/* vi: set sw=8 ts=8: */
+// genext2fs.c
+//
+// ext2 filesystem generator for embedded systems
+// Copyright (C) 2000 Xavier Bestel <xavier.bestel@free.fr>
+//
+// Please direct support requests to genext2fs-devel@lists.sourceforge.net
+//
+// 'du' portions taken from coreutils/du.c in busybox:
+// Copyright (C) 1999,2000 by Lineo, inc. and John Beppu
+// Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+// Copyright (C) 2002 Edward Betts <edward@debian.org>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; version
+// 2 of the License.
+//
+// Changes:
+// 3 Jun 2000 Initial release
+// 6 Jun 2000 Bugfix: fs size multiple of 8
+// Bugfix: fill blocks with inodes
+// 14 Jun 2000 Bugfix: bad chdir() with -d option
+// Bugfix: removed size=8n constraint
+// Changed -d file to -f file
+// Added -e option
+// 22 Jun 2000 Changed types for 64bits archs
+// 24 Jun 2000 Added endianness swap
+// Bugfix: bad dir name lookup
+// 03 Aug 2000 Bugfix: ind. blocks endian swap
+// 09 Aug 2000 Bugfix: symlinks endian swap
+// 01 Sep 2000 Bugfix: getopt returns int, not char proski@gnu.org
+// 10 Sep 2000 Bugfix: device nodes endianness xavier.gueguen@col.bsf.alcatel.fr
+// Bugfix: getcwd values for Solaris xavier.gueguen@col.bsf.alcatel.fr
+// Bugfix: ANSI scanf for non-GNU C xavier.gueguen@col.bsf.alcatel.fr
+// 28 Jun 2001 Bugfix: getcwd differs for Solaris/GNU mike@sowbug.com
+// 8 Mar 2002 Bugfix: endianness swap of x-indirects
+// 23 Mar 2002 Bugfix: test for IFCHR or IFBLK was flawed
+// 10 Oct 2002 Added comments,makefile targets, vsundar@ixiacom.com
+// endianess swap assert check.
+// Copyright (C) 2002 Ixia communications
+// 12 Oct 2002 Added support for triple indirection vsundar@ixiacom.com
+// Copyright (C) 2002 Ixia communications
+// 14 Oct 2002 Added support for groups vsundar@ixiacom.com
+// Copyright (C) 2002 Ixia communications
+// 5 Jan 2003 Bugfixes: reserved inodes should be set vsundar@usc.edu
+// only in the first group; directory names
+// need to be null padded at the end; and
+// number of blocks per group should be a
+// multiple of 8. Updated md5 values.
+// 6 Jan 2003 Erik Andersen <andersee@debian.org> added
+// mkfs.jffs2 compatible device table support,
+// along with -q, -P, -U
+
+
+#include <config.h>
+#include <stdio.h>
+
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#if MAJOR_IN_MKDEV
+# include <sys/mkdev.h>
+#elif MAJOR_IN_SYSMACROS
+# include <sys/sysmacros.h>
+#endif
+
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+# if HAVE_STDDEF_H
+# include <stddef.h>
+# endif
+#endif
+
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+# include <stdint.h>
+# endif
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+#endif
+
+#if HAVE_LIBGEN_H
+# include <libgen.h>
+#endif
+
+#include <stdarg.h>
+#include <assert.h>
+#include <time.h>
+#include <ctype.h>
+#include <errno.h>
+
+#if HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+
+#if HAVE_GETOPT_H
+# include <getopt.h>
+#endif
+
+#if HAVE_LIMITS_H
+# include <limits.h>
+#endif
+
+#include <private/android_filesystem_config.h>
+unsigned source_path_len = 0;
+
+struct stats {
+ unsigned long nblocks;
+ unsigned long ninodes;
+};
+
+// block size
+
+#define BLOCKSIZE 1024
+#define BLOCKS_PER_GROUP 8192
+#define INODES_PER_GROUP 8192
+/* Percentage of blocks that are reserved.*/
+#define RESERVED_BLOCKS 5/100
+#define MAX_RESERVED_BLOCKS 25/100
+
+
+// inode block size (why is it != BLOCKSIZE ?!?)
+/* The field i_blocks in the ext2 inode stores the number of data blocks
+ but in terms of 512 bytes. That is what INODE_BLOCKSIZE represents.
+ INOBLK is the number of such blocks in an actual disk block */
+
+#define INODE_BLOCKSIZE 512
+#define INOBLK (BLOCKSIZE / INODE_BLOCKSIZE)
+
+// reserved inodes
+
+#define EXT2_BAD_INO 1 // Bad blocks inode
+#define EXT2_ROOT_INO 2 // Root inode
+#define EXT2_ACL_IDX_INO 3 // ACL inode
+#define EXT2_ACL_DATA_INO 4 // ACL inode
+#define EXT2_BOOT_LOADER_INO 5 // Boot loader inode
+#define EXT2_UNDEL_DIR_INO 6 // Undelete directory inode
+#define EXT2_FIRST_INO 11 // First non reserved inode
+
+// magic number for ext2
+
+#define EXT2_MAGIC_NUMBER 0xEF53
+
+
+// direct/indirect block addresses
+
+#define EXT2_NDIR_BLOCKS 11 // direct blocks
+#define EXT2_IND_BLOCK 12 // indirect block
+#define EXT2_DIND_BLOCK 13 // double indirect block
+#define EXT2_TIND_BLOCK 14 // triple indirect block
+#define EXT2_INIT_BLOCK 0xFFFFFFFF // just initialized (not really a block address)
+
+// end of a block walk
+
+#define WALK_END 0xFFFFFFFE
+
+// file modes
+
+#define FM_IFMT 0170000 // format mask
+#define FM_IFSOCK 0140000 // socket
+#define FM_IFLNK 0120000 // symbolic link
+#define FM_IFREG 0100000 // regular file
+
+#define FM_IFBLK 0060000 // block device
+#define FM_IFDIR 0040000 // directory
+#define FM_IFCHR 0020000 // character device
+#define FM_IFIFO 0010000 // fifo
+
+#define FM_IMASK 0007777 // *all* perms mask for everything below
+
+#define FM_ISUID 0004000 // SUID
+#define FM_ISGID 0002000 // SGID
+#define FM_ISVTX 0001000 // sticky bit
+
+#define FM_IRWXU 0000700 // entire "user" mask
+#define FM_IRUSR 0000400 // read
+#define FM_IWUSR 0000200 // write
+#define FM_IXUSR 0000100 // execute
+
+#define FM_IRWXG 0000070 // entire "group" mask
+#define FM_IRGRP 0000040 // read
+#define FM_IWGRP 0000020 // write
+#define FM_IXGRP 0000010 // execute
+
+#define FM_IRWXO 0000007 // entire "other" mask
+#define FM_IROTH 0000004 // read
+#define FM_IWOTH 0000002 // write
+#define FM_IXOTH 0000001 // execute
+
+// options
+
+#define OP_HOLES 0x01 // make files with holes
+
+/* Defines for accessing group details */
+
+// Number of groups in the filesystem
+#define GRP_NBGROUPS(fs) \
+ (((fs)->sb.s_blocks_count - fs->sb.s_first_data_block + \
+ (fs)->sb.s_blocks_per_group - 1) / (fs)->sb.s_blocks_per_group)
+
+// Get group block bitmap (bbm) given the group number
+#define GRP_GET_GROUP_BBM(fs,grp) ( get_blk((fs),(fs)->gd[(grp)].bg_block_bitmap) )
+
+// Get group inode bitmap (ibm) given the group number
+#define GRP_GET_GROUP_IBM(fs,grp) ( get_blk((fs),(fs)->gd[(grp)].bg_inode_bitmap) )
+
+// Given an inode number find the group it belongs to
+#define GRP_GROUP_OF_INODE(fs,nod) ( ((nod)-1) / (fs)->sb.s_inodes_per_group)
+
+//Given an inode number get the inode bitmap that covers it
+#define GRP_GET_INODE_BITMAP(fs,nod) \
+ ( GRP_GET_GROUP_IBM((fs),GRP_GROUP_OF_INODE((fs),(nod))) )
+
+//Given an inode number find its offset within the inode bitmap that covers it
+#define GRP_IBM_OFFSET(fs,nod) \
+ ( (nod) - GRP_GROUP_OF_INODE((fs),(nod))*(fs)->sb.s_inodes_per_group )
+
+// Given a block number find the group it belongs to
+#define GRP_GROUP_OF_BLOCK(fs,blk) ( ((blk)-1) / (fs)->sb.s_blocks_per_group)
+
+//Given a block number get the block bitmap that covers it
+#define GRP_GET_BLOCK_BITMAP(fs,blk) \
+ ( GRP_GET_GROUP_BBM((fs),GRP_GROUP_OF_BLOCK((fs),(blk))) )
+
+//Given a block number find its offset within the block bitmap that covers it
+#define GRP_BBM_OFFSET(fs,blk) \
+ ( (blk) - GRP_GROUP_OF_BLOCK((fs),(blk))*(fs)->sb.s_blocks_per_group )
+
+
+// used types
+
+typedef signed char int8;
+typedef unsigned char uint8;
+typedef signed short int16;
+typedef unsigned short uint16;
+typedef signed int int32;
+typedef unsigned int uint32;
+
+
+// the GNU C library has a wonderful scanf("%as", string) which will
+// allocate the string with the right size, good to avoid buffer
+// overruns. the following macros use it if available or use a
+// hacky workaround
+// moreover it will define a snprintf() like a sprintf(), i.e.
+// without the buffer overrun checking, to work around bugs in
+// older solaris. Note that this is still not very portable, in that
+// the return value cannot be trusted.
+
+#if 0 // SCANF_CAN_MALLOC
+// C99 define "a" for floating point, so you can have runtime surprise
+// according the library versions
+# define SCANF_PREFIX "a"
+# define SCANF_STRING(s) (&s)
+#else
+# define SCANF_PREFIX "511"
+# define SCANF_STRING(s) (s = malloc(512))
+#endif /* SCANF_CAN_MALLOC */
+
+#if PREFER_PORTABLE_SNPRINTF
+static inline int
+portable_snprintf(char *str, size_t n, const char *fmt, ...)
+{
+ int ret;
+ va_list ap;
+ va_start(ap, fmt);
+ ret = vsprintf(str, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+# define SNPRINTF portable_snprintf
+#else
+# define SNPRINTF snprintf
+#endif /* PREFER_PORTABLE_SNPRINTF */
+
+#if !HAVE_GETLINE
+// getline() replacement for Darwin and Solaris etc.
+// This code uses backward seeks (unless rchunk is set to 1) which can't work
+// on pipes etc. However, add2fs_from_file() only calls getline() for
+// regular files, so a larger rchunk and backward seeks are okay.
+
+ssize_t
+getdelim(char **lineptr, size_t *n, int delim, FILE *stream)
+{
+ char *p; // reads stored here
+ size_t const rchunk = 512; // number of bytes to read
+ size_t const mchunk = 512; // number of extra bytes to malloc
+ size_t m = rchunk + 1; // initial buffer size
+
+ if (*lineptr) {
+ if (*n < m) {
+ *lineptr = (char*)realloc(*lineptr, m);
+ if (!*lineptr) return -1;
+ *n = m;
+ }
+ } else {
+ *lineptr = (char*)malloc(m);
+ if (!*lineptr) return -1;
+ *n = m;
+ }
+
+ m = 0; // record length including seperator
+
+ do {
+ size_t i; // number of bytes read etc
+ size_t j = 0; // number of bytes searched
+
+ p = *lineptr + m;
+
+ i = fread(p, 1, rchunk, stream);
+ if (i < rchunk && ferror(stream))
+ return -1;
+ while (j < i) {
+ ++j;
+ if (*p++ == (char)delim) {
+ *p = '\0';
+ if (j != i) {
+ if (fseek(stream, j - i, SEEK_CUR))
+ return -1;
+ if (feof(stream))
+ clearerr(stream);
+ }
+ m += j;
+ return m;
+ }
+ }
+
+ m += j;
+ if (feof(stream)) {
+ if (m) return m;
+ if (!i) return -1;
+ }
+
+ // allocate space for next read plus possible null terminator
+ i = ((m + (rchunk + 1 > mchunk ? rchunk + 1 : mchunk) +
+ mchunk - 1) / mchunk) * mchunk;
+ if (i != *n) {
+ *lineptr = (char*)realloc(*lineptr, i);
+ if (!*lineptr) return -1;
+ *n = i;
+ }
+ } while (1);
+}
+#define getline(a,b,c) getdelim(a,b,'\n',c)
+#endif /* HAVE_GETLINE */
+
+// Convert a numerical string to a float, and multiply the result by an
+// IEC or SI multiplier if provided; supported multipliers are Ki, Mi, Gi, k, M
+// and G.
+
+float
+SI_atof(const char *nptr)
+{
+ float f = 0;
+ float m = 1;
+ char *suffixptr;
+
+#if HAVE_STRTOF
+ f = strtof(nptr, &suffixptr);
+#else
+ f = (float)strtod(nptr, &suffixptr);
+#endif /* HAVE_STRTOF */
+
+ if (*suffixptr) {
+ if (!strcmp(suffixptr, "Ki"))
+ m = 1 << 10;
+ else if (!strcmp(suffixptr, "Mi"))
+ m = 1 << 20;
+ else if (!strcmp(suffixptr, "Gi"))
+ m = 1 << 30;
+ else if (!strcmp(suffixptr, "k"))
+ m = 1000;
+ else if (!strcmp(suffixptr, "M"))
+ m = 1000 * 1000;
+ else if (!strcmp(suffixptr, "G"))
+ m = 1000 * 1000 * 1000;
+ }
+ return f * m;
+}
+
+// endianness swap
+
+static inline uint16
+swab16(uint16 val)
+{
+ return (val >> 8) | (val << 8);
+}
+
+static inline uint32
+swab32(uint32 val)
+{
+ return ((val>>24) | ((val>>8)&0xFF00) |
+ ((val<<8)&0xFF0000) | (val<<24));
+}
+
+
+// on-disk structures
+// this trick makes me declare things only once
+// (once for the structures, once for the endianness swap)
+
+#define superblock_decl \
+ udecl32(s_inodes_count) /* Count of inodes in the filesystem */ \
+ udecl32(s_blocks_count) /* Count of blocks in the filesystem */ \
+ udecl32(s_r_blocks_count) /* Count of the number of reserved blocks */ \
+ udecl32(s_free_blocks_count) /* Count of the number of free blocks */ \
+ udecl32(s_free_inodes_count) /* Count of the number of free inodes */ \
+ udecl32(s_first_data_block) /* The first block which contains data */ \
+ udecl32(s_log_block_size) /* Indicator of the block size */ \
+ decl32(s_log_frag_size) /* Indicator of the size of the fragments */ \
+ udecl32(s_blocks_per_group) /* Count of the number of blocks in each block group */ \
+ udecl32(s_frags_per_group) /* Count of the number of fragments in each block group */ \
+ udecl32(s_inodes_per_group) /* Count of the number of inodes in each block group */ \
+ udecl32(s_mtime) /* The time that the filesystem was last mounted */ \
+ udecl32(s_wtime) /* The time that the filesystem was last written to */ \
+ udecl16(s_mnt_count) /* The number of times the file system has been mounted */ \
+ decl16(s_max_mnt_count) /* The number of times the file system can be mounted */ \
+ udecl16(s_magic) /* Magic number indicating ex2fs */ \
+ udecl16(s_state) /* Flags indicating the current state of the filesystem */ \
+ udecl16(s_errors) /* Flags indicating the procedures for error reporting */ \
+ udecl16(s_minor_rev_level) /* The minor revision level of the filesystem */ \
+ udecl32(s_lastcheck) /* The time that the filesystem was last checked */ \
+ udecl32(s_checkinterval) /* The maximum time permissable between checks */ \
+ udecl32(s_creator_os) /* Indicator of which OS created the filesystem */ \
+ udecl32(s_rev_level) /* The revision level of the filesystem */ \
+ udecl16(s_def_resuid) /* The default uid for reserved blocks */ \
+ udecl16(s_def_resgid) /* The default gid for reserved blocks */
+
+#define groupdescriptor_decl \
+ udecl32(bg_block_bitmap) /* Block number of the block bitmap */ \
+ udecl32(bg_inode_bitmap) /* Block number of the inode bitmap */ \
+ udecl32(bg_inode_table) /* Block number of the inode table */ \
+ udecl16(bg_free_blocks_count) /* Free blocks in the group */ \
+ udecl16(bg_free_inodes_count) /* Free inodes in the group */ \
+ udecl16(bg_used_dirs_count) /* Number of directories in the group */ \
+ udecl16(bg_pad)
+
+#define inode_decl \
+ udecl16(i_mode) /* Entry type and file mode */ \
+ udecl16(i_uid) /* User id */ \
+ udecl32(i_size) /* File/dir size in bytes */ \
+ udecl32(i_atime) /* Last access time */ \
+ udecl32(i_ctime) /* Creation time */ \
+ udecl32(i_mtime) /* Last modification time */ \
+ udecl32(i_dtime) /* Deletion time */ \
+ udecl16(i_gid) /* Group id */ \
+ udecl16(i_links_count) /* Number of (hard) links to this inode */ \
+ udecl32(i_blocks) /* Number of blocks used (1 block = 512 bytes) */ \
+ udecl32(i_flags) /* ??? */ \
+ udecl32(i_reserved1) \
+ utdecl32(i_block,15) /* Blocks table */ \
+ udecl32(i_version) /* ??? */ \
+ udecl32(i_file_acl) /* File access control list */ \
+ udecl32(i_dir_acl) /* Directory access control list */ \
+ udecl32(i_faddr) /* Fragment address */ \
+ udecl8(i_frag) /* Fragments count*/ \
+ udecl8(i_fsize) /* Fragment size */ \
+ udecl16(i_pad1)
+
+#define directory_decl \
+ udecl32(d_inode) /* Inode entry */ \
+ udecl16(d_rec_len) /* Total size on record */ \
+ udecl16(d_name_len) /* Size of entry name */
+
+#define decl8(x) int8 x;
+#define udecl8(x) uint8 x;
+#define decl16(x) int16 x;
+#define udecl16(x) uint16 x;
+#define decl32(x) int32 x;
+#define udecl32(x) uint32 x;
+#define utdecl32(x,n) uint32 x[n];
+
+typedef struct
+{
+ superblock_decl
+ uint32 s_reserved[235]; // Reserved
+} superblock;
+
+typedef struct
+{
+ groupdescriptor_decl
+ uint32 bg_reserved[3];
+} groupdescriptor;
+
+typedef struct
+{
+ inode_decl
+ uint32 i_reserved2[2];
+} inode;
+
+typedef struct
+{
+ directory_decl
+ char d_name[0];
+} directory;
+
+typedef uint8 block[BLOCKSIZE];
+
+/* blockwalker fields:
+ The blockwalker is used to access all the blocks of a file (including
+ the indirection blocks) through repeated calls to walk_bw.
+
+ bpdir -> index into the inode->i_block[]. Indicates level of indirection.
+ bnum -> total number of blocks so far accessed. including indirection
+ blocks.
+ bpind,bpdind,bptind -> index into indirection blocks.
+
+ bpind, bpdind, bptind do *NOT* index into single, double and triple
+ indirect blocks resp. as you might expect from their names. Instead
+ they are in order the 1st, 2nd & 3rd index to be used
+
+ As an example..
+ To access data block number 70000:
+ bpdir: 15 (we are doing triple indirection)
+ bpind: 0 ( index into the triple indirection block)
+ bpdind: 16 ( index into the double indirection block)
+ bptind: 99 ( index into the single indirection block)
+ 70000 = 12 + 256 + 256*256 + 16*256 + 100 (indexing starts from zero)
+
+ So,for double indirection bpind will index into the double indirection
+ block and bpdind into the single indirection block. For single indirection
+ only bpind will be used.
+*/
+
+typedef struct
+{
+ uint32 bnum;
+ uint32 bpdir;
+ uint32 bpind;
+ uint32 bpdind;
+ uint32 bptind;
+} blockwalker;
+
+
+/* Filesystem structure that support groups */
+#if BLOCKSIZE == 1024
+typedef struct
+{
+ block zero; // The famous block 0
+ superblock sb; // The superblock
+ groupdescriptor gd[0]; // The group descriptors
+} filesystem;
+#else
+#error UNHANDLED BLOCKSIZE
+#endif
+
+// now the endianness swap
+
+#undef decl8
+#undef udecl8
+#undef decl16
+#undef udecl16
+#undef decl32
+#undef udecl32
+#undef utdecl32
+
+#define decl8(x)
+#define udecl8(x)
+#define decl16(x) this->x = swab16(this->x);
+#define udecl16(x) this->x = swab16(this->x);
+#define decl32(x) this->x = swab32(this->x);
+#define udecl32(x) this->x = swab32(this->x);
+#define utdecl32(x,n) { int i; for(i=0; i<n; i++) this->x[i] = swab32(this->x[i]); }
+
+#define HDLINK_CNT 16
+static int32 hdlink_cnt = HDLINK_CNT;
+struct hdlink_s
+{
+ uint32 src_inode;
+ uint32 dst_nod;
+};
+
+struct hdlinks_s
+{
+ int32 count;
+ struct hdlink_s *hdl;
+};
+
+static struct hdlinks_s hdlinks;
+
+static void
+swap_sb(superblock *sb)
+{
+#define this sb
+ superblock_decl
+#undef this
+}
+
+static void
+swap_gd(groupdescriptor *gd)
+{
+#define this gd
+ groupdescriptor_decl
+#undef this
+}
+
+static void
+swap_nod(inode *nod)
+{
+#define this nod
+ inode_decl
+#undef this
+}
+
+static void
+swap_dir(directory *dir)
+{
+#define this dir
+ directory_decl
+#undef this
+}
+
+static void
+swap_block(block b)
+{
+ int i;
+ uint32 *blk = (uint32*)b;
+ for(i = 0; i < BLOCKSIZE/4; i++)
+ blk[i] = swab32(blk[i]);
+}
+
+#undef decl8
+#undef udecl8
+#undef decl16
+#undef udecl16
+#undef decl32
+#undef udecl32
+#undef utdecl32
+
+static char * app_name;
+static const char *const memory_exhausted = "memory exhausted";
+
+// error (un)handling
+static void
+verror_msg(const char *s, va_list p)
+{
+ fflush(stdout);
+ fprintf(stderr, "%s: ", app_name);
+ vfprintf(stderr, s, p);
+}
+static void
+error_msg(const char *s, ...)
+{
+ va_list p;
+ va_start(p, s);
+ verror_msg(s, p);
+ va_end(p);
+ putc('\n', stderr);
+}
+
+static void
+error_msg_and_die(const char *s, ...)
+{
+ va_list p;
+ va_start(p, s);
+ verror_msg(s, p);
+ va_end(p);
+ putc('\n', stderr);
+ exit(EXIT_FAILURE);
+}
+
+static void
+vperror_msg(const char *s, va_list p)
+{
+ int err = errno;
+ if (s == 0)
+ s = "";
+ verror_msg(s, p);
+ if (*s)
+ s = ": ";
+ fprintf(stderr, "%s%s\n", s, strerror(err));
+}
+
+static void
+perror_msg_and_die(const char *s, ...)
+{
+ va_list p;
+ va_start(p, s);
+ vperror_msg(s, p);
+ va_end(p);
+ exit(EXIT_FAILURE);
+}
+
+static FILE *
+xfopen(const char *path, const char *mode)
+{
+ FILE *fp;
+ if ((fp = fopen(path, mode)) == NULL)
+ perror_msg_and_die("%s", path);
+ return fp;
+}
+
+static char *
+xstrdup(const char *s)
+{
+ char *t;
+
+ if (s == NULL)
+ return NULL;
+ t = strdup(s);
+ if (t == NULL)
+ error_msg_and_die(memory_exhausted);
+ return t;
+}
+
+static void *
+xrealloc(void *ptr, size_t size)
+{
+ ptr = realloc(ptr, size);
+ if (ptr == NULL && size != 0)
+ error_msg_and_die(memory_exhausted);
+ return ptr;
+}
+
+static char *
+xreadlink(const char *path)
+{
+ static const int GROWBY = 80; /* how large we will grow strings by */
+
+ char *buf = NULL;
+ int bufsize = 0, readsize = 0;
+
+ do {
+ buf = xrealloc(buf, bufsize += GROWBY);
+ readsize = readlink(path, buf, bufsize); /* 1st try */
+ if (readsize == -1) {
+ perror_msg_and_die("%s:%s", app_name, path);
+ }
+ }
+ while (bufsize < readsize + 1);
+
+ buf[readsize] = '\0';
+ return buf;
+}
+
+int
+is_hardlink(ino_t inode)
+{
+ int i;
+
+ for(i = 0; i < hdlinks.count; i++) {
+ if(hdlinks.hdl[i].src_inode == inode)
+ return i;
+ }
+ return -1;
+}
+
+// printf helper macro
+#define plural(a) (a), ((a) > 1) ? "s" : ""
+
+// temporary working block
+static inline uint8 *
+get_workblk(void)
+{
+ unsigned char* b=calloc(1,BLOCKSIZE);
+ return b;
+}
+static inline void
+free_workblk(block b)
+{
+ free(b);
+}
+
+/* Rounds qty upto a multiple of siz. siz should be a power of 2 */
+static inline uint32
+rndup(uint32 qty, uint32 siz)
+{
+ return (qty + (siz - 1)) & ~(siz - 1);
+}
+
+// check if something is allocated in the bitmap
+static inline uint32
+allocated(block b, uint32 item)
+{
+ return b[(item-1) / 8] & (1 << ((item-1) % 8));
+}
+
+// return a given block from a filesystem
+static inline uint8 *
+get_blk(filesystem *fs, uint32 blk)
+{
+ return (uint8*)fs + blk*BLOCKSIZE;
+}
+
+// return a given inode from a filesystem
+static inline inode *
+get_nod(filesystem *fs, uint32 nod)
+{
+ int grp,offset;
+ inode *itab;
+
+ offset = GRP_IBM_OFFSET(fs,nod);
+ grp = GRP_GROUP_OF_INODE(fs,nod);
+ itab = (inode *)get_blk(fs, fs->gd[grp].bg_inode_table);
+ return itab+offset-1;
+}
+
+// allocate a given block/inode in the bitmap
+// allocate first free if item == 0
+static uint32
+allocate(block b, uint32 item)
+{
+ if(!item)
+ {
+ int i;
+ uint8 bits;
+ for(i = 0; i < BLOCKSIZE; i++)
+ if((bits = b[i]) != (uint8)-1)
+ {
+ int j;
+ for(j = 0; j < 8; j++)
+ if(!(bits & (1 << j)))
+ break;
+ item = i * 8 + j + 1;
+ break;
+ }
+ if(i == BLOCKSIZE)
+ return 0;
+ }
+ b[(item-1) / 8] |= (1 << ((item-1) % 8));
+ return item;
+}
+
+// deallocate a given block/inode
+static void
+deallocate(block b, uint32 item)
+{
+ b[(item-1) / 8] &= ~(1 << ((item-1) % 8));
+}
+
+// allocate a block
+static uint32
+alloc_blk(filesystem *fs, uint32 nod)
+{
+ uint32 bk=0;
+ uint32 grp,nbgroups;
+
+ grp = GRP_GROUP_OF_INODE(fs,nod);
+ nbgroups = GRP_NBGROUPS(fs);
+ if(!(bk = allocate(get_blk(fs,fs->gd[grp].bg_block_bitmap), 0))) {
+ for(grp=0;grp<nbgroups && !bk;grp++)
+ bk=allocate(get_blk(fs,fs->gd[grp].bg_block_bitmap),0);
+ grp--;
+ }
+ if (!bk)
+ error_msg_and_die("couldn't allocate a block (no free space)");
+ if(!(fs->gd[grp].bg_free_blocks_count--))
+ error_msg_and_die("group descr %d. free blocks count == 0 (corrupted fs?)",grp);
+ if(!(fs->sb.s_free_blocks_count--))
+ error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)");
+ return fs->sb.s_blocks_per_group*grp + bk;
+}
+
+// free a block
+static void
+free_blk(filesystem *fs, uint32 bk)
+{
+ uint32 grp;
+
+ grp = bk / fs->sb.s_blocks_per_group;
+ bk %= fs->sb.s_blocks_per_group;
+ deallocate(get_blk(fs,fs->gd[grp].bg_block_bitmap), bk);
+ fs->gd[grp].bg_free_blocks_count++;
+ fs->sb.s_free_blocks_count++;
+}
+
+// allocate an inode
+static uint32
+alloc_nod(filesystem *fs)
+{
+ uint32 nod,best_group=0;
+ uint32 grp,nbgroups,avefreei;
+
+ nbgroups = GRP_NBGROUPS(fs);
+
+ /* Distribute inodes amongst all the blocks */
+ /* For every block group with more than average number of free inodes */
+ /* find the one with the most free blocks and allocate node there */
+ /* Idea from find_group_dir in fs/ext2/ialloc.c in 2.4.19 kernel */
+ /* We do it for all inodes. */
+ avefreei = fs->sb.s_free_inodes_count / nbgroups;
+ for(grp=0; grp<nbgroups; grp++) {
+ if (fs->gd[grp].bg_free_inodes_count < avefreei ||
+ fs->gd[grp].bg_free_inodes_count == 0)
+ continue;
+ if (!best_group ||
+ fs->gd[grp].bg_free_blocks_count > fs->gd[best_group].bg_free_blocks_count)
+ best_group = grp;
+ }
+ if (!(nod = allocate(get_blk(fs,fs->gd[best_group].bg_inode_bitmap),0)))
+ error_msg_and_die("couldn't allocate an inode (no free inode)");
+ if(!(fs->gd[best_group].bg_free_inodes_count--))
+ error_msg_and_die("group descr. free blocks count == 0 (corrupted fs?)");
+ if(!(fs->sb.s_free_inodes_count--))
+ error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)");
+ return fs->sb.s_inodes_per_group*best_group+nod;
+}
+
+// print a bitmap allocation
+static void
+print_bm(block b, uint32 max)
+{
+ uint32 i;
+ printf("----+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0\n");
+ for(i=1; i <= max; i++)
+ {
+ putchar(allocated(b, i) ? '*' : '.');
+ if(!(i % 100))
+ printf("\n");
+ }
+ if((i-1) % 100)
+ printf("\n");
+}
+
+// initalize a blockwalker (iterator for blocks list)
+static inline void
+init_bw(blockwalker *bw)
+{
+ bw->bnum = 0;
+ bw->bpdir = EXT2_INIT_BLOCK;
+}
+
+// return next block of inode (WALK_END for end)
+// if *create>0, append a newly allocated block at the end
+// if *create<0, free the block - warning, the metadata blocks contents is
+// used after being freed, so once you start
+// freeing blocks don't stop until the end of
+// the file. moreover, i_blocks isn't updated.
+// in fact, don't do that, just use extend_blk
+// if hole!=0, create a hole in the file
+static uint32
+walk_bw(filesystem *fs, uint32 nod, blockwalker *bw, int32 *create, uint32 hole)
+{
+ uint32 *bkref = 0;
+ uint32 *b;
+ int extend = 0, reduce = 0;
+ if(create && (*create) < 0)
+ reduce = 1;
+ if(bw->bnum >= get_nod(fs, nod)->i_blocks / INOBLK)
+ {
+ if(create && (*create) > 0)
+ {
+ (*create)--;
+ extend = 1;
+ }
+ else
+ return WALK_END;
+ }
+ // first direct block
+ if(bw->bpdir == EXT2_INIT_BLOCK)
+ {
+ bkref = &get_nod(fs, nod)->i_block[bw->bpdir = 0];
+ if(extend) // allocate first block
+ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ if(reduce) // free first block
+ free_blk(fs, *bkref);
+ }
+ // direct block
+ else if(bw->bpdir < EXT2_NDIR_BLOCKS)
+ {
+ bkref = &get_nod(fs, nod)->i_block[++bw->bpdir];
+ if(extend) // allocate block
+ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ if(reduce) // free block
+ free_blk(fs, *bkref);
+ }
+ // first block in indirect block
+ else if(bw->bpdir == EXT2_NDIR_BLOCKS)
+ {
+ bw->bnum++;
+ bw->bpdir = EXT2_IND_BLOCK;
+ bw->bpind = 0;
+ if(extend) // allocate indirect block
+ get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
+ if(reduce) // free indirect block
+ free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+ b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+ bkref = &b[bw->bpind];
+ if(extend) // allocate first block
+ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ if(reduce) // free first block
+ free_blk(fs, *bkref);
+ }
+ // block in indirect block
+ else if((bw->bpdir == EXT2_IND_BLOCK) && (bw->bpind < BLOCKSIZE/4 - 1))
+ {
+ bw->bpind++;
+ b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+ bkref = &b[bw->bpind];
+ if(extend) // allocate block
+ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ if(reduce) // free block
+ free_blk(fs, *bkref);
+ }
+ // first block in first indirect block in first double indirect block
+ else if(bw->bpdir == EXT2_IND_BLOCK)
+ {
+ bw->bnum += 2;
+ bw->bpdir = EXT2_DIND_BLOCK;
+ bw->bpind = 0;
+ bw->bpdind = 0;
+ if(extend) // allocate double indirect block
+ get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
+ if(reduce) // free double indirect block
+ free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+ b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+ if(extend) // allocate first indirect block
+ b[bw->bpind] = alloc_blk(fs,nod);
+ if(reduce) // free firstindirect block
+ free_blk(fs, b[bw->bpind]);
+ b = (uint32*)get_blk(fs, b[bw->bpind]);
+ bkref = &b[bw->bpdind];
+ if(extend) // allocate first block
+ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ if(reduce) // free first block
+ free_blk(fs, *bkref);
+ }
+ // block in indirect block in double indirect block
+ else if((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpdind < BLOCKSIZE/4 - 1))
+ {
+ bw->bpdind++;
+ b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+ b = (uint32*)get_blk(fs, b[bw->bpind]);
+ bkref = &b[bw->bpdind];
+ if(extend) // allocate block
+ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ if(reduce) // free block
+ free_blk(fs, *bkref);
+ }
+ // first block in indirect block in double indirect block
+ else if((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpind < BLOCKSIZE/4 - 1))
+ {
+ bw->bnum++;
+ bw->bpdind = 0;
+ bw->bpind++;
+ b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+ if(extend) // allocate indirect block
+ b[bw->bpind] = alloc_blk(fs,nod);
+ if(reduce) // free indirect block
+ free_blk(fs, b[bw->bpind]);
+ b = (uint32*)get_blk(fs, b[bw->bpind]);
+ bkref = &b[bw->bpdind];
+ if(extend) // allocate first block
+ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ if(reduce) // free first block
+ free_blk(fs, *bkref);
+ }
+
+ /* Adding support for triple indirection */
+ /* Just starting triple indirection. Allocate the indirection
+ blocks and the first data block
+ */
+ else if (bw->bpdir == EXT2_DIND_BLOCK)
+ {
+ bw->bnum += 3;
+ bw->bpdir = EXT2_TIND_BLOCK;
+ bw->bpind = 0;
+ bw->bpdind = 0;
+ bw->bptind = 0;
+ if(extend) // allocate triple indirect block
+ get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
+ if(reduce) // free triple indirect block
+ free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+ b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+ if(extend) // allocate first double indirect block
+ b[bw->bpind] = alloc_blk(fs,nod);
+ if(reduce) // free first double indirect block
+ free_blk(fs, b[bw->bpind]);
+ b = (uint32*)get_blk(fs, b[bw->bpind]);
+ if(extend) // allocate first indirect block
+ b[bw->bpdind] = alloc_blk(fs,nod);
+ if(reduce) // free first indirect block
+ free_blk(fs, b[bw->bpind]);
+ b = (uint32*)get_blk(fs, b[bw->bpdind]);
+ bkref = &b[bw->bptind];
+ if(extend) // allocate first data block
+ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ if(reduce) // free first block
+ free_blk(fs, *bkref);
+ }
+ /* Still processing a single indirect block down the indirection
+ chain.Allocate a data block for it
+ */
+ else if ( (bw->bpdir == EXT2_TIND_BLOCK) &&
+ (bw->bptind < BLOCKSIZE/4 -1) )
+ {
+ bw->bptind++;
+ b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+ b = (uint32*)get_blk(fs, b[bw->bpind]);
+ b = (uint32*)get_blk(fs, b[bw->bpdind]);
+ bkref = &b[bw->bptind];
+ if(extend) // allocate data block
+ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ if(reduce) // free block
+ free_blk(fs, *bkref);
+ }
+ /* Finished processing a single indirect block. But still in the
+ same double indirect block. Allocate new single indirect block
+ for it and a data block
+ */
+ else if ( (bw->bpdir == EXT2_TIND_BLOCK) &&
+ (bw->bpdind < BLOCKSIZE/4 -1) )
+ {
+ bw->bnum++;
+ bw->bptind = 0;
+ bw->bpdind++;
+ b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+ b = (uint32*)get_blk(fs, b[bw->bpind]);
+ if(extend) // allocate single indirect block
+ b[bw->bpdind] = alloc_blk(fs,nod);
+ if(reduce) // free indirect block
+ free_blk(fs, b[bw->bpind]);
+ b = (uint32*)get_blk(fs, b[bw->bpdind]);
+ bkref = &b[bw->bptind];
+ if(extend) // allocate first data block
+ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ if(reduce) // free first block
+ free_blk(fs, *bkref);
+ }
+ /* Finished processing a double indirect block. Allocate the next
+ double indirect block and the single,data blocks for it
+ */
+ else if ( (bw->bpdir == EXT2_TIND_BLOCK) &&
+ (bw->bpind < BLOCKSIZE/4 - 1) )
+ {
+ bw->bnum += 2;
+ bw->bpdind = 0;
+ bw->bptind = 0;
+ bw->bpind++;
+ b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
+ if(extend) // allocate double indirect block
+ b[bw->bpind] = alloc_blk(fs,nod);
+ if(reduce) // free double indirect block
+ free_blk(fs, b[bw->bpind]);
+ b = (uint32*)get_blk(fs, b[bw->bpind]);
+ if(extend) // allocate single indirect block
+ b[bw->bpdind] = alloc_blk(fs,nod);
+ if(reduce) // free indirect block
+ free_blk(fs, b[bw->bpind]);
+ b = (uint32*)get_blk(fs, b[bw->bpdind]);
+ bkref = &b[bw->bptind];
+ if(extend) // allocate first block
+ *bkref = hole ? 0 : alloc_blk(fs,nod);
+ if(reduce) // free first block
+ free_blk(fs, *bkref);
+ }
+ else
+ error_msg_and_die("file too big !");
+ /* End change for walking triple indirection */
+
+ if(*bkref)
+ {
+ bw->bnum++;
+ if(!reduce && !allocated(GRP_GET_BLOCK_BITMAP(fs,*bkref), GRP_BBM_OFFSET(fs,*bkref)))
+ error_msg_and_die("[block %d of inode %d is unallocated !]", *bkref, nod);
+ }
+ if(extend)
+ get_nod(fs, nod)->i_blocks = bw->bnum * INOBLK;
+ return *bkref;
+}
+
+// add blocks to an inode (file/dir/etc...)
+static void
+extend_blk(filesystem *fs, uint32 nod, block b, int amount)
+{
+ int create = amount;
+ blockwalker bw, lbw;
+ uint32 bk;
+ init_bw(&bw);
+ if(amount < 0)
+ {
+ uint32 i;
+ for(i = 0; i < get_nod(fs, nod)->i_blocks / INOBLK + amount; i++)
+ walk_bw(fs, nod, &bw, 0, 0);
+ while(walk_bw(fs, nod, &bw, &create, 0) != WALK_END)
+ /*nop*/;
+ get_nod(fs, nod)->i_blocks += amount * INOBLK;
+ }
+ else
+ {
+ lbw = bw;
+ while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
+ lbw = bw;
+ bw = lbw;
+ while(create)
+ {
+ int i, copyb = 0;
+ if(!(fs->sb.s_reserved[200] & OP_HOLES))
+ copyb = 1;
+ else
+ for(i = 0; i < BLOCKSIZE / 4; i++)
+ if(((int32*)(b + BLOCKSIZE * (amount - create)))[i])
+ {
+ copyb = 1;
+ break;
+ }
+ if((bk = walk_bw(fs, nod, &bw, &create, !copyb)) == WALK_END)
+ break;
+ if(copyb)
+ memcpy(get_blk(fs, bk), b + BLOCKSIZE * (amount - create - 1), BLOCKSIZE);
+ }
+ }
+}
+
+// link an entry (inode #) to a directory
+static void
+add2dir(filesystem *fs, uint32 dnod, uint32 nod, const char* name)
+{
+ blockwalker bw;
+ uint32 bk;
+ uint8 *b;
+ directory *d;
+ int reclen, nlen;
+ inode *node;
+ inode *pnode;
+
+ pnode = get_nod(fs, dnod);
+ if((pnode->i_mode & FM_IFMT) != FM_IFDIR)
+ error_msg_and_die("can't add '%s' to a non-directory", name);
+ if(!*name)
+ error_msg_and_die("can't create an inode with an empty name");
+ if(strchr(name, '/'))
+ error_msg_and_die("bad name '%s' (contains a slash)", name);
+ nlen = strlen(name);
+ reclen = sizeof(directory) + rndup(nlen, 4);
+ if(reclen > BLOCKSIZE)
+ error_msg_and_die("bad name '%s' (too long)", name);
+ init_bw(&bw);
+ while((bk = walk_bw(fs, dnod, &bw, 0, 0)) != WALK_END) // for all blocks in dir
+ {
+ b = get_blk(fs, bk);
+ // for all dir entries in block
+ for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
+ {
+ // if empty dir entry, large enough, use it
+ if((!d->d_inode) && (d->d_rec_len >= reclen))
+ {
+ d->d_inode = nod;
+ node = get_nod(fs, nod);
+ node->i_links_count++;
+ d->d_name_len = nlen;
+ strncpy(d->d_name, name, nlen);
+ return;
+ }
+ // if entry with enough room (last one?), shrink it & use it
+ if(d->d_rec_len >= (sizeof(directory) + rndup(d->d_name_len, 4) + reclen))
+ {
+ reclen = d->d_rec_len;
+ d->d_rec_len = sizeof(directory) + rndup(d->d_name_len, 4);
+ reclen -= d->d_rec_len;
+ d = (directory*) (((int8*)d) + d->d_rec_len);
+ d->d_rec_len = reclen;
+ d->d_inode = nod;
+ node = get_nod(fs, nod);
+ node->i_links_count++;
+ d->d_name_len = nlen;
+ strncpy(d->d_name, name, nlen);
+ return;
+ }
+ }
+ }
+ // we found no free entry in the directory, so we add a block
+ if(!(b = get_workblk()))
+ error_msg_and_die("get_workblk() failed.");
+ d = (directory*)b;
+ d->d_inode = nod;
+ node = get_nod(fs, nod);
+ node->i_links_count++;
+ d->d_rec_len = BLOCKSIZE;
+ d->d_name_len = nlen;
+ strncpy(d->d_name, name, nlen);
+ extend_blk(fs, dnod, b, 1);
+ get_nod(fs, dnod)->i_size += BLOCKSIZE;
+ free_workblk(b);
+}
+
+// find an entry in a directory
+static uint32
+find_dir(filesystem *fs, uint32 nod, const char * name)
+{
+ blockwalker bw;
+ uint32 bk;
+ int nlen = strlen(name);
+ init_bw(&bw);
+ while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
+ {
+ directory *d;
+ uint8 *b;
+ b = get_blk(fs, bk);
+ for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
+ if(d->d_inode && (nlen == d->d_name_len) && !strncmp(d->d_name, name, nlen))
+ return d->d_inode;
+ }
+ return 0;
+}
+
+// find the inode of a full path
+static uint32
+find_path(filesystem *fs, uint32 nod, const char * name)
+{
+ char *p, *n, *n2 = xstrdup(name);
+ n = n2;
+ while(*n == '/')
+ {
+ nod = EXT2_ROOT_INO;
+ n++;
+ }
+ while(*n)
+ {
+ if((p = strchr(n, '/')))
+ (*p) = 0;
+ if(!(nod = find_dir(fs, nod, n)))
+ break;
+ if(p)
+ n = p + 1;
+ else
+ break;
+ }
+ free(n2);
+ return nod;
+}
+
+// chmod an inode
+void
+chmod_fs(filesystem *fs, uint32 nod, uint16 mode, uint16 uid, uint16 gid)
+{
+ inode *node;
+ node = get_nod(fs, nod);
+ node->i_mode = (node->i_mode & ~FM_IMASK) | (mode & FM_IMASK);
+ node->i_uid = uid;
+ node->i_gid = gid;
+}
+
+// create a simple inode
+static uint32
+mknod_fs(filesystem *fs, uint32 parent_nod, const char *name, uint16 mode, uint16 uid, uint16 gid, uint8 major, uint8 minor, uint32 ctime, uint32 mtime)
+{
+ uint32 nod;
+ inode *node;
+ {
+ nod = alloc_nod(fs);
+ node = get_nod(fs, nod);
+ node->i_mode = mode;
+ add2dir(fs, parent_nod, nod, name);
+ switch(mode & FM_IFMT)
+ {
+ case FM_IFLNK:
+ mode = FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO;
+ break;
+ case FM_IFBLK:
+ case FM_IFCHR:
+ ((uint8*)get_nod(fs, nod)->i_block)[0] = minor;
+ ((uint8*)get_nod(fs, nod)->i_block)[1] = major;
+ break;
+ case FM_IFDIR:
+ add2dir(fs, nod, nod, ".");
+ add2dir(fs, nod, parent_nod, "..");
+ fs->gd[GRP_GROUP_OF_INODE(fs,nod)].bg_used_dirs_count++;
+ break;
+ }
+ }
+ node->i_uid = uid;
+ node->i_gid = gid;
+ node->i_atime = mtime;
+ node->i_ctime = ctime;
+ node->i_mtime = mtime;
+ return nod;
+}
+
+// make a full-fledged directory (i.e. with "." & "..")
+static inline uint32
+mkdir_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode,
+ uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
+{
+ return mknod_fs(fs, parent_nod, name, mode|FM_IFDIR, uid, gid, 0, 0, ctime, mtime);
+}
+
+// make a symlink
+static uint32
+mklink_fs(filesystem *fs, uint32 parent_nod, const char *name, size_t size, uint8 *b, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
+{
+ uint32 nod = mknod_fs(fs, parent_nod, name, FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO, uid, gid, 0, 0, ctime, mtime);
+ extend_blk(fs, nod, 0, - (int)get_nod(fs, nod)->i_blocks / INOBLK);
+ get_nod(fs, nod)->i_size = size;
+ if(size <= 4 * (EXT2_TIND_BLOCK+1))
+ {
+ strncpy((char*)get_nod(fs, nod)->i_block, (char*)b, size);
+ return nod;
+ }
+ extend_blk(fs, nod, b, rndup(size, BLOCKSIZE) / BLOCKSIZE);
+ return nod;
+}
+
+// make a file from a FILE*
+static uint32
+mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, size_t size, FILE *f, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
+{
+ uint8 * b;
+ uint32 nod = mknod_fs(fs, parent_nod, name, mode|FM_IFREG, uid, gid, 0, 0, ctime, mtime);
+ extend_blk(fs, nod, 0, - (int)get_nod(fs, nod)->i_blocks / INOBLK);
+ get_nod(fs, nod)->i_size = size;
+ if (size) {
+ if(!(b = (uint8*)calloc(rndup(size, BLOCKSIZE), 1)))
+ error_msg_and_die("not enough mem to read file '%s'", name);
+ if(f)
+ fread(b, size, 1, f); // FIXME: ugly. use mmap() ...
+ extend_blk(fs, nod, b, rndup(size, BLOCKSIZE) / BLOCKSIZE);
+ free(b);
+ }
+ return nod;
+}
+
+// retrieves a mode info from a struct stat
+static uint32
+get_mode(struct stat *st)
+{
+ uint32 mode = 0;
+
+ if(st->st_mode & S_IRUSR)
+ mode |= FM_IRUSR;
+ if(st->st_mode & S_IWUSR)
+ mode |= FM_IWUSR;
+ if(st->st_mode & S_IXUSR)
+ mode |= FM_IXUSR;
+ if(st->st_mode & S_IRGRP)
+ mode |= FM_IRGRP;
+ if(st->st_mode & S_IWGRP)
+ mode |= FM_IWGRP;
+ if(st->st_mode & S_IXGRP)
+ mode |= FM_IXGRP;
+ if(st->st_mode & S_IROTH)
+ mode |= FM_IROTH;
+ if(st->st_mode & S_IWOTH)
+ mode |= FM_IWOTH;
+ if(st->st_mode & S_IXOTH)
+ mode |= FM_IXOTH;
+ if(st->st_mode & S_ISUID)
+ mode |= FM_ISUID;
+ if(st->st_mode & S_ISGID)
+ mode |= FM_ISGID;
+ if(st->st_mode & S_ISVTX)
+ mode |= FM_ISVTX;
+ return mode;
+}
+
+// add or fixup entries to the filesystem from a text file
+/* device table entries take the form of:
+ <path> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
+ /dev/mem c 640 0 0 1 1 0 0 -
+
+ type can be one of:
+ f A regular file
+ d Directory
+ c Character special device file
+ b Block special device file
+ p Fifo (named pipe)
+
+ I don't bother with symlinks (permissions are irrelevant), hard
+ links (special cases of regular files), or sockets (why bother).
+
+ Regular files must exist in the target root directory. If a char,
+ block, fifo, or directory does not exist, it will be created.
+*/
+
+static void
+add2fs_from_file(filesystem *fs, uint32 this_nod, FILE * fh, uint32 fs_timestamp, struct stats *stats)
+{
+ unsigned long mode, uid, gid, major, minor;
+ unsigned long start, increment, count;
+ uint32 nod, ctime, mtime;
+ char *c, type, *path = NULL, *path2 = NULL, *dir, *name, *line = NULL;
+ size_t len;
+ struct stat st;
+ int nbargs, lineno = 0;
+
+ fstat(fileno(fh), &st);
+ ctime = fs_timestamp;
+ mtime = st.st_mtime;
+ while(getline(&line, &len, fh) >= 0)
+ {
+ mode = uid = gid = major = minor = 0;
+ start = 0; increment = 1; count = 0;
+ lineno++;
+ if((c = strchr(line, '#')))
+ *c = 0;
+ if (path) {
+ free(path);
+ path = NULL;
+ }
+ if (path2) {
+ free(path2);
+ path2 = NULL;
+ }
+ nbargs = sscanf (line, "%" SCANF_PREFIX "s %c %lo %lu %lu %lu %lu %lu %lu %lu",
+ SCANF_STRING(path), &type, &mode, &uid, &gid, &major, &minor,
+ &start, &increment, &count);
+ if(nbargs < 3)
+ {
+ if(nbargs > 0)
+ error_msg("device table line %d skipped: bad format for entry '%s'", lineno, path);
+ continue;
+ }
+ mode &= FM_IMASK;
+ path2 = strdup(path);
+ name = basename(path);
+ dir = dirname(path2);
+ if((!strcmp(name, ".")) || (!strcmp(name, "..")))
+ {
+ error_msg("device table line %d skipped", lineno);
+ continue;
+ }
+ if(fs)
+ {
+ if(!(nod = find_path(fs, this_nod, dir)))
+ {
+ error_msg("device table line %d skipped: can't find directory '%s' to create '%s''", lineno, dir, name);
+ continue;
+ }
+ }
+ else
+ nod = 0;
+ switch (type)
+ {
+ case 'd':
+ mode |= FM_IFDIR;
+ break;
+ case 'f':
+ mode |= FM_IFREG;
+ break;
+ case 'p':
+ mode |= FM_IFIFO;
+ break;
+ case 's':
+ mode |= FM_IFSOCK;
+ break;
+ case 'c':
+ mode |= FM_IFCHR;
+ break;
+ case 'b':
+ mode |= FM_IFBLK;
+ break;
+ default:
+ error_msg("device table line %d skipped: bad type '%c' for entry '%s'", lineno, type, name);
+ continue;
+ }
+ if(stats) {
+ if(count > 0)
+ stats->ninodes += count - start;
+ else
+ stats->ninodes++;
+ } else {
+ if(count > 0)
+ {
+ char *dname;
+ unsigned long i;
+ unsigned len;
+ len = strlen(name) + 10;
+ dname = malloc(len + 1);
+ for(i = start; i < count; i++)
+ {
+ uint32 oldnod;
+ SNPRINTF(dname, len, "%s%lu", name, i);
+ oldnod = find_dir(fs, nod, dname);
+ if(oldnod)
+ chmod_fs(fs, oldnod, mode, uid, gid);
+ else
+ mknod_fs(fs, nod, dname, mode, uid, gid, major, minor + (i * increment - start), ctime, mtime);
+ }
+ free(dname);
+ }
+ else
+ {
+ uint32 oldnod = find_dir(fs, nod, name);
+ if(oldnod)
+ chmod_fs(fs, oldnod, mode, uid, gid);
+ else
+ mknod_fs(fs, nod, name, mode, uid, gid, major, minor, ctime, mtime);
+ }
+ }
+ }
+ if (line)
+ free(line);
+ if (path)
+ free(path);
+ if (path2)
+ free(path2);
+}
+
+static void
+prep_stat(const char *root_path)
+{
+ int len = strlen(root_path);
+
+ if((len >= 4) && (!strcmp(root_path + len - 4, "data"))) {
+ source_path_len = len - 4;
+ } else if((len >= 7) && (!strcmp(root_path + len - 6, "system"))) {
+ source_path_len = len - 6;
+ } else {
+ error_msg_and_die("Fixstats (-a) option requested but "
+ "filesystem is not data or android!");
+ }
+}
+
+static void
+fix_stat(const char *path, struct stat *s)
+{
+ path += source_path_len;
+ fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode);
+}
+
+// adds a tree of entries to the filesystem from current dir
+static void
+add2fs_from_dir(filesystem *fs, const char *path, uint32 this_nod, int squash_uids, int squash_perms, int fixstats, uint32 fs_timestamp, struct stats *stats)
+{
+ uint32 nod;
+ uint32 uid, gid, mode, ctime, mtime;
+ const char *name;
+ FILE *fh;
+ DIR *dh;
+ struct dirent *dent;
+ struct stat st;
+ char *lnk;
+ uint32 save_nod;
+ char full_name[2048];
+
+ if(!(dh = opendir(".")))
+ perror_msg_and_die(".");
+ while((dent = readdir(dh)))
+ {
+ if((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, "..")))
+ continue;
+
+ lstat(dent->d_name, &st);
+
+ if(fixstats) {
+ int tmp = snprintf(full_name, sizeof(full_name),
+ "%s/%s", path, dent->d_name);
+ if(tmp >= (int)sizeof(full_name))
+ error_msg_and_die("Path too long!");
+ fix_stat(full_name, &st);
+ } else
+ full_name[0] = '\0';
+ uid = st.st_uid;
+ gid = st.st_gid;
+ ctime = fs_timestamp;
+ mtime = st.st_mtime;
+ name = dent->d_name;
+ mode = get_mode(&st);
+ if(squash_uids)
+ uid = gid = 0;
+ if(squash_perms)
+ mode &= ~(FM_IRWXG | FM_IRWXO);
+ if(stats)
+ switch(st.st_mode & S_IFMT)
+ {
+ case S_IFLNK:
+ case S_IFREG:
+ if((st.st_mode & S_IFMT) == S_IFREG || st.st_size > 4 * (EXT2_TIND_BLOCK+1))
+ stats->nblocks += (st.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
+ case S_IFCHR:
+ case S_IFBLK:
+ case S_IFIFO:
+ case S_IFSOCK:
+ stats->ninodes++;
+ break;
+ case S_IFDIR:
+ stats->ninodes++;
+ if(chdir(dent->d_name) < 0)
+ perror_msg_and_die(dent->d_name);
+ add2fs_from_dir(fs, full_name, this_nod, squash_uids, squash_perms, fixstats, fs_timestamp, stats);
+ chdir("..");
+ break;
+ default:
+ break;
+ }
+ else
+ {
+ if((nod = find_dir(fs, this_nod, name)))
+ {
+ error_msg("ignoring duplicate entry %s", name);
+ if(S_ISDIR(st.st_mode)) {
+ if(chdir(dent->d_name) < 0)
+ perror_msg_and_die(name);
+ add2fs_from_dir(fs, full_name, nod, squash_uids, squash_perms, fixstats, fs_timestamp, stats);
+ chdir("..");
+ }
+ continue;
+ }
+ save_nod = 0;
+ /* Check for hardlinks */
+ if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) && st.st_nlink > 1) {
+ int32 hdlink = is_hardlink(st.st_ino);
+ if (hdlink >= 0) {
+ add2dir(fs, this_nod, hdlinks.hdl[hdlink].dst_nod, name);
+ continue;
+ } else {
+ save_nod = 1;
+ }
+ }
+ switch(st.st_mode & S_IFMT)
+ {
+#if HAVE_STRUCT_STAT_ST_RDEV
+ case S_IFCHR:
+ nod = mknod_fs(fs, this_nod, name, mode|FM_IFCHR, uid, gid, major(st.st_rdev), minor(st.st_rdev), ctime, mtime);
+ break;
+ case S_IFBLK:
+ nod = mknod_fs(fs, this_nod, name, mode|FM_IFBLK, uid, gid, major(st.st_rdev), minor(st.st_rdev), ctime, mtime);
+ break;
+#endif
+ case S_IFIFO:
+ nod = mknod_fs(fs, this_nod, name, mode|FM_IFIFO, uid, gid, 0, 0, ctime, mtime);
+ break;
+ case S_IFSOCK:
+ nod = mknod_fs(fs, this_nod, name, mode|FM_IFSOCK, uid, gid, 0, 0, ctime, mtime);
+ break;
+ case S_IFLNK:
+ lnk = xreadlink(dent->d_name);
+ mklink_fs(fs, this_nod, name, st.st_size, (uint8*)lnk, uid, gid, ctime, mtime);
+ free(lnk);
+ break;
+ case S_IFREG:
+ fh = xfopen(dent->d_name, "rb");
+ nod = mkfile_fs(fs, this_nod, name, mode, st.st_size, fh, uid, gid, ctime, mtime);
+ fclose(fh);
+ break;
+ case S_IFDIR:
+ nod = mkdir_fs(fs, this_nod, name, mode, uid, gid, ctime, mtime);
+ if(chdir(dent->d_name) < 0)
+ perror_msg_and_die(name);
+ add2fs_from_dir(fs, full_name, nod, squash_uids, squash_perms, fixstats, fs_timestamp, stats);
+ chdir("..");
+ break;
+ default:
+ error_msg("ignoring entry %s", name);
+ }
+ if (save_nod) {
+ if (hdlinks.count == hdlink_cnt) {
+ if ((hdlinks.hdl =
+ realloc (hdlinks.hdl, (hdlink_cnt + HDLINK_CNT) *
+ sizeof (struct hdlink_s))) == NULL) {
+ error_msg_and_die("Not enough memory");
+ }
+ hdlink_cnt += HDLINK_CNT;
+ }
+ hdlinks.hdl[hdlinks.count].src_inode = st.st_ino;
+ hdlinks.hdl[hdlinks.count].dst_nod = nod;
+ hdlinks.count++;
+ }
+ }
+ }
+ closedir(dh);
+}
+
+// endianness swap of x-indirect blocks
+static void
+swap_goodblocks(filesystem *fs, inode *nod)
+{
+ uint32 i,j;
+ int done=0;
+ uint32 *b,*b2;
+
+ uint32 nblk = nod->i_blocks / INOBLK;
+ if((nod->i_size && !nblk) || ((nod->i_mode & FM_IFBLK) == FM_IFBLK) || ((nod->i_mode & FM_IFCHR) == FM_IFCHR))
+ for(i = 0; i <= EXT2_TIND_BLOCK; i++)
+ nod->i_block[i] = swab32(nod->i_block[i]);
+ if(nblk <= EXT2_IND_BLOCK)
+ return;
+ swap_block(get_blk(fs, nod->i_block[EXT2_IND_BLOCK]));
+ if(nblk <= EXT2_DIND_BLOCK + BLOCKSIZE/4)
+ return;
+ /* Currently this will fail b'cos the number of blocks as stored
+ in i_blocks also includes the indirection blocks (see
+ walk_bw). But this function assumes that i_blocks only
+ stores the count of data blocks ( Actually according to
+ "Understanding the Linux Kernel" (Table 17-3 p502 1st Ed)
+ i_blocks IS supposed to store the count of data blocks). so
+ with a file of size 268K nblk would be 269.The above check
+ will be false even though double indirection hasn't been
+ started.This is benign as 0 means block 0 which has been
+ zeroed out and therefore points back to itself from any offset
+ */
+ // FIXME: I have fixed that, but I have the feeling the rest of
+ // ths function needs to be fixed for the same reasons - Xav
+ assert(nod->i_block[EXT2_DIND_BLOCK] != 0);
+ for(i = 0; i < BLOCKSIZE/4; i++)
+ if(nblk > EXT2_IND_BLOCK + BLOCKSIZE/4 + (BLOCKSIZE/4)*i )
+ swap_block(get_blk(fs, ((uint32*)get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]))[i]));
+ swap_block(get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]));
+ if(nblk <= EXT2_IND_BLOCK + BLOCKSIZE/4 + BLOCKSIZE/4 * BLOCKSIZE/4)
+ return;
+ /* Adding support for triple indirection */
+ b = (uint32*)get_blk(fs,nod->i_block[EXT2_TIND_BLOCK]);
+ for(i=0;i < BLOCKSIZE/4 && !done ; i++) {
+ b2 = (uint32*)get_blk(fs,b[i]);
+ for(j=0; j<BLOCKSIZE/4;j++) {
+ if (nblk > ( EXT2_IND_BLOCK + BLOCKSIZE/4 +
+ (BLOCKSIZE/4)*(BLOCKSIZE/4) +
+ i*(BLOCKSIZE/4)*(BLOCKSIZE/4) +
+ j*(BLOCKSIZE/4)) )
+ swap_block(get_blk(fs,b2[j]));
+ else {
+ done = 1;
+ break;
+ }
+ }
+ swap_block((uint8 *)b2);
+ }
+ swap_block((uint8 *)b);
+ return;
+}
+
+static void
+swap_badblocks(filesystem *fs, inode *nod)
+{
+ uint32 i,j;
+ int done=0;
+ uint32 *b,*b2;
+
+ uint32 nblk = nod->i_blocks / INOBLK;
+ if((nod->i_size && !nblk) || ((nod->i_mode & FM_IFBLK) == FM_IFBLK) || ((nod->i_mode & FM_IFCHR) == FM_IFCHR))
+ for(i = 0; i <= EXT2_TIND_BLOCK; i++)
+ nod->i_block[i] = swab32(nod->i_block[i]);
+ if(nblk <= EXT2_IND_BLOCK)
+ return;
+ swap_block(get_blk(fs, nod->i_block[EXT2_IND_BLOCK]));
+ if(nblk <= EXT2_DIND_BLOCK + BLOCKSIZE/4)
+ return;
+ /* See comment in swap_goodblocks */
+ assert(nod->i_block[EXT2_DIND_BLOCK] != 0);
+ swap_block(get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]));
+ for(i = 0; i < BLOCKSIZE/4; i++)
+ if(nblk > EXT2_IND_BLOCK + BLOCKSIZE/4 + (BLOCKSIZE/4)*i )
+ swap_block(get_blk(fs, ((uint32*)get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]))[i]));
+ if(nblk <= EXT2_IND_BLOCK + BLOCKSIZE/4 + BLOCKSIZE/4 * BLOCKSIZE/4)
+ return;
+ /* Adding support for triple indirection */
+ b = (uint32*)get_blk(fs,nod->i_block[EXT2_TIND_BLOCK]);
+ swap_block((uint8 *)b);
+ for(i=0;i < BLOCKSIZE/4 && !done ; i++) {
+ b2 = (uint32*)get_blk(fs,b[i]);
+ swap_block((uint8 *)b2);
+ for(j=0; j<BLOCKSIZE/4;j++) {
+ if (nblk > ( EXT2_IND_BLOCK + BLOCKSIZE/4 +
+ (BLOCKSIZE/4)*(BLOCKSIZE/4) +
+ i*(BLOCKSIZE/4)*(BLOCKSIZE/4) +
+ j*(BLOCKSIZE/4)) )
+ swap_block(get_blk(fs,b2[j]));
+ else {
+ done = 1;
+ break;
+ }
+ }
+ }
+ return;
+}
+
+// endianness swap of the whole filesystem
+static void
+swap_goodfs(filesystem *fs)
+{
+ uint32 i;
+ for(i = 1; i < fs->sb.s_inodes_count; i++)
+ {
+ inode *nod = get_nod(fs, i);
+ if(nod->i_mode & FM_IFDIR)
+ {
+ blockwalker bw;
+ uint32 bk;
+ init_bw(&bw);
+ while((bk = walk_bw(fs, i, &bw, 0, 0)) != WALK_END)
+ {
+ directory *d;
+ uint8 *b;
+ b = get_blk(fs, bk);
+ for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + swab16(d->d_rec_len)))
+ swap_dir(d);
+ }
+ }
+ swap_goodblocks(fs, nod);
+ swap_nod(nod);
+ }
+ for(i=0;i<GRP_NBGROUPS(fs);i++)
+ swap_gd(&(fs->gd[i]));
+ swap_sb(&fs->sb);
+}
+
+static void
+swap_badfs(filesystem *fs)
+{
+ uint32 i;
+ swap_sb(&fs->sb);
+ for(i=0;i<GRP_NBGROUPS(fs);i++)
+ swap_gd(&(fs->gd[i]));
+ for(i = 1; i < fs->sb.s_inodes_count; i++)
+ {
+ inode *nod = get_nod(fs, i);
+ swap_nod(nod);
+ swap_badblocks(fs, nod);
+ if(nod->i_mode & FM_IFDIR)
+ {
+ blockwalker bw;
+ uint32 bk;
+ init_bw(&bw);
+ while((bk = walk_bw(fs, i, &bw, 0, 0)) != WALK_END)
+ {
+ directory *d;
+ uint8 *b;
+ b = get_blk(fs, bk);
+ for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
+ swap_dir(d);
+ }
+ }
+ }
+}
+
+// initialize an empty filesystem
+static filesystem *
+init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes, uint32 fs_timestamp)
+{
+ uint32 i;
+ filesystem *fs;
+ directory *d;
+ uint8 * b;
+ uint32 nod, first_block;
+ uint32 nbgroups,nbinodes_per_group,overhead_per_group,free_blocks,
+ free_blocks_per_group,nbblocks_per_group,min_nbgroups;
+ uint32 gdsz,itblsz,bbmpos,ibmpos,itblpos;
+ uint32 j;
+ uint8 *bbm,*ibm;
+ inode *itab0;
+
+ if(nbresrvd < 0)
+ error_msg_and_die("reserved blocks value is invalid. Note: options have changed, see --help or the man page.");
+ if(nbinodes < EXT2_FIRST_INO - 1 + (nbresrvd ? 1 : 0))
+ error_msg_and_die("too few inodes. Note: options have changed, see --help or the man page.");
+ if(nbblocks < 8)
+ error_msg_and_die("too few blocks. Note: options have changed, see --help or the man page.");
+
+ /* nbinodes is the total number of inodes in the system.
+ * a block group can have no more than 8192 inodes.
+ */
+ min_nbgroups = (nbinodes + INODES_PER_GROUP - 1) / INODES_PER_GROUP;
+
+ /* nbblocks is the total number of blocks in the filesystem.
+ * a block group can have no more than 8192 blocks.
+ */
+ first_block = (BLOCKSIZE == 1024);
+ nbgroups = (nbblocks - first_block + BLOCKS_PER_GROUP - 1) / BLOCKS_PER_GROUP;
+ if(nbgroups < min_nbgroups) nbgroups = min_nbgroups;
+ nbblocks_per_group = rndup((nbblocks - first_block + nbgroups - 1)/nbgroups, 8);
+ nbinodes_per_group = rndup((nbinodes + nbgroups - 1)/nbgroups,
+ (BLOCKSIZE/sizeof(inode)));
+ if (nbinodes_per_group < 16)
+ nbinodes_per_group = 16; //minimum number b'cos the first 10 are reserved
+
+ gdsz = rndup(nbgroups*sizeof(groupdescriptor),BLOCKSIZE)/BLOCKSIZE;
+ itblsz = nbinodes_per_group * sizeof(inode)/BLOCKSIZE;
+ overhead_per_group = 3 /*sb,bbm,ibm*/ + gdsz + itblsz;
+ if((uint32)nbblocks - 1 < overhead_per_group * nbgroups)
+ error_msg_and_die("too much overhead, try fewer inodes or more blocks. Note: options have changed, see --help or the man page.");
+ free_blocks = nbblocks - overhead_per_group*nbgroups - 1 /*boot block*/;
+ free_blocks_per_group = nbblocks_per_group - overhead_per_group;
+
+ if(!(fs = (filesystem*)calloc(nbblocks, BLOCKSIZE)))
+ error_msg_and_die("not enough memory for filesystem");
+
+ // create the superblock for an empty filesystem
+ fs->sb.s_inodes_count = nbinodes_per_group * nbgroups;
+ fs->sb.s_blocks_count = nbblocks;
+ fs->sb.s_r_blocks_count = nbresrvd;
+ fs->sb.s_free_blocks_count = free_blocks;
+ fs->sb.s_free_inodes_count = fs->sb.s_inodes_count - EXT2_FIRST_INO + 1;
+ fs->sb.s_first_data_block = first_block;
+ fs->sb.s_log_block_size = BLOCKSIZE >> 11;
+ fs->sb.s_log_frag_size = BLOCKSIZE >> 11;
+ fs->sb.s_blocks_per_group = nbblocks_per_group;
+ fs->sb.s_frags_per_group = nbblocks_per_group;
+ fs->sb.s_inodes_per_group = nbinodes_per_group;
+ fs->sb.s_wtime = fs_timestamp;
+ fs->sb.s_magic = EXT2_MAGIC_NUMBER;
+ fs->sb.s_lastcheck = fs_timestamp;
+
+ // set up groupdescriptors
+ for(i=0, bbmpos=gdsz+2, ibmpos=bbmpos+1, itblpos=ibmpos+1;
+ i<nbgroups;
+ i++, bbmpos+=nbblocks_per_group, ibmpos+=nbblocks_per_group, itblpos+=nbblocks_per_group)
+ {
+ if(free_blocks > free_blocks_per_group) {
+ fs->gd[i].bg_free_blocks_count = free_blocks_per_group;
+ free_blocks -= free_blocks_per_group;
+ } else {
+ fs->gd[i].bg_free_blocks_count = free_blocks;
+ free_blocks = 0; // this is the last block group
+ }
+ if(i)
+ fs->gd[i].bg_free_inodes_count = nbinodes_per_group;
+ else
+ fs->gd[i].bg_free_inodes_count = nbinodes_per_group -
+ EXT2_FIRST_INO + 2;
+ fs->gd[i].bg_used_dirs_count = 0;
+ fs->gd[i].bg_block_bitmap = bbmpos;
+ fs->gd[i].bg_inode_bitmap = ibmpos;
+ fs->gd[i].bg_inode_table = itblpos;
+ }
+
+ /* Mark non-filesystem blocks and inodes as allocated */
+ /* Mark system blocks and inodes as allocated */
+ for(i = 0; i<nbgroups;i++) {
+
+ /* Block bitmap */
+ bbm = get_blk(fs,fs->gd[i].bg_block_bitmap);
+ //non-filesystem blocks
+ for(j = fs->gd[i].bg_free_blocks_count
+ + overhead_per_group + 1; j <= BLOCKSIZE * 8; j++)
+ allocate(bbm, j);
+ //system blocks
+ for(j = 1; j <= overhead_per_group; j++)
+ allocate(bbm, j);
+
+ /* Inode bitmap */
+ ibm = get_blk(fs,fs->gd[i].bg_inode_bitmap);
+ //non-filesystem inodes
+ for(j = fs->sb.s_inodes_per_group+1; j <= BLOCKSIZE * 8; j++)
+ allocate(ibm, j);
+
+ //system inodes
+ if(i == 0)
+ for(j = 1; j < EXT2_FIRST_INO; j++)
+ allocate(ibm, j);
+ }
+
+ // make root inode and directory
+ /* We have groups now. Add the root filesystem in group 0 */
+ /* Also increment the directory count for group 0 */
+ fs->gd[0].bg_free_inodes_count--;
+ fs->gd[0].bg_used_dirs_count = 1;
+ itab0 = (inode *)get_blk(fs,fs->gd[0].bg_inode_table);
+ itab0[EXT2_ROOT_INO-1].i_mode = FM_IFDIR | FM_IRWXU | FM_IRGRP | FM_IROTH | FM_IXGRP | FM_IXOTH;
+ itab0[EXT2_ROOT_INO-1].i_ctime = fs_timestamp;
+ itab0[EXT2_ROOT_INO-1].i_mtime = fs_timestamp;
+ itab0[EXT2_ROOT_INO-1].i_atime = fs_timestamp;
+ itab0[EXT2_ROOT_INO-1].i_size = BLOCKSIZE;
+ itab0[EXT2_ROOT_INO-1].i_links_count = 2;
+
+ if(!(b = get_workblk()))
+ error_msg_and_die("get_workblk() failed.");
+ d = (directory*)b;
+ d->d_inode = EXT2_ROOT_INO;
+ d->d_rec_len = sizeof(directory)+4;
+ d->d_name_len = 1;
+ strcpy(d->d_name, ".");
+ d = (directory*)(b + d->d_rec_len);
+ d->d_inode = EXT2_ROOT_INO;
+ d->d_rec_len = BLOCKSIZE - (sizeof(directory)+4);
+ d->d_name_len = 2;
+ strcpy(d->d_name, "..");
+ extend_blk(fs, EXT2_ROOT_INO, b, 1);
+
+ // make lost+found directory and reserve blocks
+ if(fs->sb.s_r_blocks_count)
+ {
+ nod = mkdir_fs(fs, EXT2_ROOT_INO, "lost+found", FM_IRWXU, 0, 0, fs_timestamp, fs_timestamp);
+ memset(b, 0, BLOCKSIZE);
+ ((directory*)b)->d_rec_len = BLOCKSIZE;
+ /* We run into problems with e2fsck if directory lost+found grows
+ * bigger than this. Need to find out why this happens - sundar
+ */
+ if (fs->sb.s_r_blocks_count > fs->sb.s_blocks_count * MAX_RESERVED_BLOCKS )
+ fs->sb.s_r_blocks_count = fs->sb.s_blocks_count * MAX_RESERVED_BLOCKS;
+ for(i = 1; i < fs->sb.s_r_blocks_count; i++)
+ extend_blk(fs, nod, b, 1);
+ get_nod(fs, nod)->i_size = fs->sb.s_r_blocks_count * BLOCKSIZE;
+ }
+ free_workblk(b);
+
+ // administrative info
+ fs->sb.s_state = 1;
+ fs->sb.s_max_mnt_count = 20;
+
+ // options for me
+ if(holes)
+ fs->sb.s_reserved[200] |= OP_HOLES;
+
+ return fs;
+}
+
+// loads a filesystem from disk
+static filesystem *
+load_fs(FILE * fh, int swapit)
+{
+ size_t fssize;
+ filesystem *fs;
+ if((fseek(fh, 0, SEEK_END) < 0) || ((ssize_t)(fssize = ftell(fh)) == -1))
+ perror_msg_and_die("input filesystem image");
+ rewind(fh);
+ fssize = (fssize + BLOCKSIZE - 1) / BLOCKSIZE;
+ if(fssize < 16) // totally arbitrary
+ error_msg_and_die("too small filesystem");
+ if(!(fs = (filesystem*)calloc(fssize, BLOCKSIZE)))
+ error_msg_and_die("not enough memory for filesystem");
+ if(fread(fs, BLOCKSIZE, fssize, fh) != fssize)
+ perror_msg_and_die("input filesystem image");
+ if(swapit)
+ swap_badfs(fs);
+ if(fs->sb.s_rev_level || (fs->sb.s_magic != EXT2_MAGIC_NUMBER))
+ error_msg_and_die("not a suitable ext2 filesystem");
+ return fs;
+}
+
+static void
+free_fs(filesystem *fs)
+{
+ free(fs);
+}
+
+// just walk through blocks list
+static void
+flist_blocks(filesystem *fs, uint32 nod, FILE *fh)
+{
+ blockwalker bw;
+ uint32 bk;
+ init_bw(&bw);
+ while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
+ fprintf(fh, " %d", bk);
+ fprintf(fh, "\n");
+}
+
+// walk through blocks list
+static void
+list_blocks(filesystem *fs, uint32 nod)
+{
+ int bn = 0;
+ blockwalker bw;
+ uint32 bk;
+ init_bw(&bw);
+ printf("blocks in inode %d:", nod);
+ while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
+ printf(" %d", bk), bn++;
+ printf("\n%d blocks (%d bytes)\n", bn, bn * BLOCKSIZE);
+}
+
+// saves blocks to FILE*
+static void
+write_blocks(filesystem *fs, uint32 nod, FILE* f)
+{
+ blockwalker bw;
+ uint32 bk;
+ int32 fsize = get_nod(fs, nod)->i_size;
+ init_bw(&bw);
+ while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
+ {
+ if(fsize <= 0)
+ error_msg_and_die("wrong size while saving inode %d", nod);
+ if(fwrite(get_blk(fs, bk), (fsize > BLOCKSIZE) ? BLOCKSIZE : fsize, 1, f) != 1)
+ error_msg_and_die("error while saving inode %d", nod);
+ fsize -= BLOCKSIZE;
+ }
+}
+
+
+// print block/char device minor and major
+static void
+print_dev(filesystem *fs, uint32 nod)
+{
+ int minor, major;
+ minor = ((uint8*)get_nod(fs, nod)->i_block)[0];
+ major = ((uint8*)get_nod(fs, nod)->i_block)[1];
+ printf("major: %d, minor: %d\n", major, minor);
+}
+
+// print an inode as a directory
+static void
+print_dir(filesystem *fs, uint32 nod)
+{
+ blockwalker bw;
+ uint32 bk;
+ init_bw(&bw);
+ printf("directory for inode %d:\n", nod);
+ while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
+ {
+ directory *d;
+ uint8 *b;
+ b = get_blk(fs, bk);
+ for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
+ if(d->d_inode)
+ {
+ int i;
+ printf("entry '");
+ for(i = 0; i < d->d_name_len; i++)
+ putchar(d->d_name[i]);
+ printf("' (inode %d): rec_len: %d (name_len: %d)\n", d->d_inode, d->d_rec_len, d->d_name_len);
+ }
+ }
+}
+
+// print a symbolic link
+static void
+print_link(filesystem *fs, uint32 nod)
+{
+ if(!get_nod(fs, nod)->i_blocks)
+ printf("links to '%s'\n", (char*)get_nod(fs, nod)->i_block);
+ else
+ {
+ printf("links to '");
+ write_blocks(fs, nod, stdout);
+ printf("'\n");
+ }
+}
+
+// make a ls-like printout of permissions
+static void
+make_perms(uint32 mode, char perms[11])
+{
+ strcpy(perms, "----------");
+ if(mode & FM_IRUSR)
+ perms[1] = 'r';
+ if(mode & FM_IWUSR)
+ perms[2] = 'w';
+ if(mode & FM_IXUSR)
+ perms[3] = 'x';
+ if(mode & FM_IRGRP)
+ perms[4] = 'r';
+ if(mode & FM_IWGRP)
+ perms[5] = 'w';
+ if(mode & FM_IXGRP)
+ perms[6] = 'x';
+ if(mode & FM_IROTH)
+ perms[7] = 'r';
+ if(mode & FM_IWOTH)
+ perms[8] = 'w';
+ if(mode & FM_IXOTH)
+ perms[9] = 'x';
+ if(mode & FM_ISUID)
+ perms[3] = 's';
+ if(mode & FM_ISGID)
+ perms[6] = 's';
+ if(mode & FM_ISVTX)
+ perms[9] = 't';
+ switch(mode & FM_IFMT)
+ {
+ case 0:
+ *perms = '0';
+ break;
+ case FM_IFSOCK:
+ *perms = 's';
+ break;
+ case FM_IFLNK:
+ *perms = 'l';
+ break;
+ case FM_IFREG:
+ *perms = '-';
+ break;
+ case FM_IFBLK:
+ *perms = 'b';
+ break;
+ case FM_IFDIR:
+ *perms = 'd';
+ break;
+ case FM_IFCHR:
+ *perms = 'c';
+ break;
+ case FM_IFIFO:
+ *perms = 'p';
+ break;
+ default:
+ *perms = '?';
+ }
+}
+
+// print an inode
+static void
+print_inode(filesystem *fs, uint32 nod)
+{
+ char *s;
+ char perms[11];
+ if(!get_nod(fs, nod)->i_mode)
+ return;
+ switch(nod)
+ {
+ case EXT2_BAD_INO:
+ s = "bad blocks";
+ break;
+ case EXT2_ROOT_INO:
+ s = "root";
+ break;
+ case EXT2_ACL_IDX_INO:
+ case EXT2_ACL_DATA_INO:
+ s = "ACL";
+ break;
+ case EXT2_BOOT_LOADER_INO:
+ s = "boot loader";
+ break;
+ case EXT2_UNDEL_DIR_INO:
+ s = "undelete directory";
+ break;
+ default:
+ s = (nod >= EXT2_FIRST_INO) ? "normal" : "unknown reserved";
+ }
+ printf("inode %d (%s, %d links): ", nod, s, get_nod(fs, nod)->i_links_count);
+ if(!allocated(GRP_GET_INODE_BITMAP(fs,nod), GRP_IBM_OFFSET(fs,nod)))
+ {
+ printf("unallocated\n");
+ return;
+ }
+ make_perms(get_nod(fs, nod)->i_mode, perms);
+ printf("%s, size: %d byte%s (%d block%s)\n", perms, plural(get_nod(fs, nod)->i_size), plural(get_nod(fs, nod)->i_blocks / INOBLK));
+ switch(get_nod(fs, nod)->i_mode & FM_IFMT)
+ {
+ case FM_IFSOCK:
+ list_blocks(fs, nod);
+ break;
+ case FM_IFLNK:
+ print_link(fs, nod);
+ break;
+ case FM_IFREG:
+ list_blocks(fs, nod);
+ break;
+ case FM_IFBLK:
+ print_dev(fs, nod);
+ break;
+ case FM_IFDIR:
+ list_blocks(fs, nod);
+ print_dir(fs, nod);
+ break;
+ case FM_IFCHR:
+ print_dev(fs, nod);
+ break;
+ case FM_IFIFO:
+ list_blocks(fs, nod);
+ break;
+ default:
+ list_blocks(fs, nod);
+ }
+ printf("Done with inode %d\n",nod);
+}
+
+// describes various fields in a filesystem
+static void
+print_fs(filesystem *fs)
+{
+ uint32 i;
+ uint8 *ibm;
+
+ printf("%d blocks (%d free, %d reserved), first data block: %d\n",
+ fs->sb.s_blocks_count, fs->sb.s_free_blocks_count,
+ fs->sb.s_r_blocks_count, fs->sb.s_first_data_block);
+ printf("%d inodes (%d free)\n", fs->sb.s_inodes_count,
+ fs->sb.s_free_inodes_count);
+ printf("block size = %d, frag size = %d\n",
+ fs->sb.s_log_block_size ? (fs->sb.s_log_block_size << 11) : 1024,
+ fs->sb.s_log_frag_size ? (fs->sb.s_log_frag_size << 11) : 1024);
+ printf("number of groups: %d\n",GRP_NBGROUPS(fs));
+ printf("%d blocks per group,%d frags per group,%d inodes per group\n",
+ fs->sb.s_blocks_per_group, fs->sb.s_frags_per_group,
+ fs->sb.s_inodes_per_group);
+ printf("Size of inode table: %d blocks\n",
+ (int)(fs->sb.s_inodes_per_group * sizeof(inode) / BLOCKSIZE));
+ for (i = 0; i < GRP_NBGROUPS(fs); i++) {
+ printf("Group No: %d\n", i+1);
+ printf("block bitmap: block %d,inode bitmap: block %d, inode table: block %d\n",
+ fs->gd[i].bg_block_bitmap, fs->gd[i].bg_inode_bitmap,
+ fs->gd[i].bg_inode_table);
+ printf("block bitmap allocation:\n");
+ print_bm(GRP_GET_GROUP_BBM(fs, i),fs->sb.s_blocks_per_group);
+ printf("inode bitmap allocation:\n");
+ ibm = GRP_GET_GROUP_IBM(fs, i);
+ print_bm(ibm, fs->sb.s_inodes_per_group);
+ for (i = 1; i <= fs->sb.s_inodes_per_group; i++)
+ if (allocated(ibm, i))
+ print_inode(fs, i);
+ }
+}
+
+static void
+dump_fs(filesystem *fs, FILE * fh, int swapit)
+{
+ uint32 nbblocks = fs->sb.s_blocks_count;
+ fs->sb.s_reserved[200] = 0;
+ if(swapit)
+ swap_goodfs(fs);
+ if(fwrite(fs, BLOCKSIZE, nbblocks, fh) < nbblocks)
+ perror_msg_and_die("output filesystem image");
+ if(swapit)
+ swap_badfs(fs);
+}
+
+static void
+populate_fs(filesystem *fs, char **dopt, int didx, int squash_uids, int squash_perms, int fixstats, uint32 fs_timestamp, struct stats *stats)
+{
+ int i;
+ for(i = 0; i < didx; i++)
+ {
+ struct stat st;
+ FILE *fh;
+ int pdir;
+ char *pdest;
+ uint32 nod = EXT2_ROOT_INO;
+ if(fs)
+ if((pdest = strchr(dopt[i], ':')))
+ {
+ *(pdest++) = 0;
+ if(!(nod = find_path(fs, EXT2_ROOT_INO, pdest)))
+ error_msg_and_die("path %s not found in filesystem", pdest);
+ }
+ stat(dopt[i], &st);
+ switch(st.st_mode & S_IFMT)
+ {
+ case S_IFREG:
+ fh = xfopen(dopt[i], "rb");
+ add2fs_from_file(fs, nod, fh, fs_timestamp, stats);
+ fclose(fh);
+ break;
+ case S_IFDIR:
+ if((pdir = open(".", O_RDONLY)) < 0)
+ perror_msg_and_die(".");
+ if(chdir(dopt[i]) < 0)
+ perror_msg_and_die(dopt[i]);
+ if (fixstats)
+ prep_stat(dopt[i]);
+ add2fs_from_dir(fs, dopt[i], nod, squash_uids, squash_perms, fixstats, fs_timestamp, stats);
+ if(fchdir(pdir) < 0)
+ perror_msg_and_die("fchdir");
+ if(close(pdir) < 0)
+ perror_msg_and_die("close");
+ break;
+ default:
+ error_msg_and_die("%s is neither a file nor a directory", dopt[i]);
+ }
+ }
+}
+
+static void
+showversion(void)
+{
+ printf("genext2fs " VERSION "\n");
+}
+
+static void
+showhelp(void)
+{
+ fprintf(stderr, "Usage: %s [options] image\n"
+ "Create an ext2 filesystem image from directories/files\n\n"
+ " -x, --starting-image <image>\n"
+ " -d, --root <directory>\n"
+ " -D, --devtable <file>\n"
+ " -b, --size-in-blocks <blocks>\n"
+ " -i, --bytes-per-inode <bytes per inode>\n"
+ " -N, --number-of-inodes <number of inodes>\n"
+ " -m, --reserved-percentage <percentage of blocks to reserve>\n"
+ " -g, --block-map <path> Generate a block map file for this path.\n"
+ " -e, --fill-value <value> Fill unallocated blocks with value.\n"
+ " -z, --allow-holes Allow files with holes.\n"
+ " -f, --faketime Set filesystem timestamps to 0 (for testing).\n"
+ " -q, --squash Same as \"-U -P\".\n"
+ " -U, --squash-uids Squash owners making all files be owned by root.\n"
+ " -P, --squash-perms Squash permissions on all files.\n"
+ " -a, --fix-android-stats Fix-up file stats (user, perms, ...)\n"
+ " -h, --help\n"
+ " -V, --version\n"
+ " -v, --verbose\n\n"
+ "Report bugs to genext2fs-devel@lists.sourceforge.net\n", app_name);
+}
+
+#define MAX_DOPT 128
+#define MAX_GOPT 128
+
+#define MAX_FILENAME 255
+
+extern char* optarg;
+extern int optind, opterr, optopt;
+
+int
+main(int argc, char **argv)
+{
+ int nbblocks = -1;
+ int nbinodes = -1;
+ int nbresrvd = -1;
+ float bytes_per_inode = -1;
+ float reserved_frac = -1;
+ int fs_timestamp = -1;
+ char * fsout = "-";
+ char * fsin = 0;
+ char * dopt[MAX_DOPT];
+ int didx = 0;
+ char * gopt[MAX_GOPT];
+ int gidx = 0;
+ int verbose = 0;
+ int holes = 0;
+ int emptyval = 0;
+ int squash_uids = 0;
+ int squash_perms = 0;
+ int fix_android_stats = 0;
+ uint16 endian = 1;
+ int bigendian = !*(char*)&endian;
+ filesystem *fs;
+ int i;
+ int c;
+ struct stats stats;
+
+#if HAVE_GETOPT_LONG
+ struct option longopts[] = {
+ { "starting-image", required_argument, NULL, 'x' },
+ { "root", required_argument, NULL, 'd' },
+ { "devtable", required_argument, NULL, 'D' },
+ { "size-in-blocks", required_argument, NULL, 'b' },
+ { "bytes-per-inode", required_argument, NULL, 'i' },
+ { "number-of-inodes", required_argument, NULL, 'N' },
+ { "reserved-percentage", required_argument, NULL, 'm' },
+ { "block-map", required_argument, NULL, 'g' },
+ { "fill-value", required_argument, NULL, 'e' },
+ { "allow-holes", no_argument, NULL, 'z' },
+ { "faketime", no_argument, NULL, 'f' },
+ { "squash", no_argument, NULL, 'q' },
+ { "squash-uids", no_argument, NULL, 'U' },
+ { "squash-perms", no_argument, NULL, 'P' },
+ { "fix-android-stats",no_argument, NULL, 'a' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { "verbose", no_argument, NULL, 'v' },
+ { 0, 0, 0, 0}
+ } ;
+
+ app_name = argv[0];
+
+ while((c = getopt_long(argc, argv, "x:d:D:b:i:N:m:g:e:zfqUPahVv", longopts, NULL)) != EOF) {
+#else
+ app_name = argv[0];
+
+ while((c = getopt(argc, argv, "x:d:D:b:i:N:m:g:e:zfqUPahVv")) != EOF) {
+#endif /* HAVE_GETOPT_LONG */
+ switch(c)
+ {
+ case 'x':
+ fsin = optarg;
+ break;
+ case 'd':
+ case 'D':
+ dopt[didx++] = optarg;
+ break;
+ case 'b':
+ nbblocks = SI_atof(optarg);
+ break;
+ case 'i':
+ bytes_per_inode = SI_atof(optarg);
+ break;
+ case 'N':
+ nbinodes = SI_atof(optarg);
+ break;
+ case 'm':
+ reserved_frac = SI_atof(optarg) / 100;
+ break;
+ case 'g':
+ gopt[gidx++] = optarg;
+ break;
+ case 'e':
+ emptyval = atoi(optarg);
+ break;
+ case 'z':
+ holes = 1;
+ break;
+ case 'f':
+ fs_timestamp = 0;
+ break;
+ case 'q':
+ squash_uids = 1;
+ squash_perms = 1;
+ break;
+ case 'U':
+ squash_uids = 1;
+ break;
+ case 'P':
+ squash_perms = 1;
+ break;
+ case 'a':
+ fix_android_stats = 1;
+ break;
+ case 'h':
+ showhelp();
+ exit(0);
+ case 'V':
+ showversion();
+ exit(0);
+ case 'v':
+ verbose = 1;
+ showversion();
+ break;
+ default:
+ error_msg_and_die("Note: options have changed, see --help or the man page.");
+ }
+ }
+
+ if(optind < (argc - 1))
+ error_msg_and_die("Too many arguments. Try --help or else see the man page.");
+ if(optind > (argc - 1))
+ error_msg_and_die("Not enough arguments. Try --help or else see the man page.");
+
+ if(fix_android_stats && (squash_uids || squash_perms))
+ error_msg_and_die("Cannot squash uid/perms and fix them up for Android at the same time.");
+
+ fsout = argv[optind];
+
+ hdlinks.hdl = (struct hdlink_s *)malloc(hdlink_cnt * sizeof(struct hdlink_s));
+ if (!hdlinks.hdl)
+ error_msg_and_die("Not enough memory");
+ hdlinks.count = 0 ;
+
+ if(fsin)
+ {
+ if(strcmp(fsin, "-"))
+ {
+ FILE * fh = xfopen(fsin, "rb");
+ fs = load_fs(fh, bigendian);
+ fclose(fh);
+ }
+ else
+ fs = load_fs(stdin, bigendian);
+ }
+ else
+ {
+ if(reserved_frac == -1)
+ nbresrvd = nbblocks * RESERVED_BLOCKS;
+ else
+ nbresrvd = nbblocks * reserved_frac;
+
+ stats.ninodes = EXT2_FIRST_INO - 1 + (nbresrvd ? 1 : 0);
+ stats.nblocks = 0;
+
+ populate_fs(NULL, dopt, didx, squash_uids, squash_perms, 0, fs_timestamp, &stats);
+
+ if(nbinodes == -1)
+ nbinodes = stats.ninodes;
+ else
+ if(stats.ninodes > (unsigned long)nbinodes)
+ {
+ fprintf(stderr, "number of inodes too low, increasing to %ld\n", stats.ninodes);
+ nbinodes = stats.ninodes;
+ }
+
+ if(bytes_per_inode != -1) {
+ int tmp_nbinodes = nbblocks * BLOCKSIZE / bytes_per_inode;
+ if(tmp_nbinodes > nbinodes)
+ nbinodes = tmp_nbinodes;
+ }
+ if(fs_timestamp == -1)
+ fs_timestamp = time(NULL);
+ fs = init_fs(nbblocks, nbinodes, nbresrvd, holes, fs_timestamp);
+ }
+
+ populate_fs(fs, dopt, didx, squash_uids, squash_perms, fix_android_stats, fs_timestamp, NULL);
+
+ if(emptyval) {
+ uint32 b;
+ for(b = 1; b < fs->sb.s_blocks_count; b++)
+ if(!allocated(GRP_GET_BLOCK_BITMAP(fs,b),GRP_BBM_OFFSET(fs,b)))
+ memset(get_blk(fs, b), emptyval, BLOCKSIZE);
+ }
+ if(verbose)
+ print_fs(fs);
+ for(i = 0; i < gidx; i++)
+ {
+ uint32 nod;
+ char fname[MAX_FILENAME];
+ char *p;
+ FILE *fh;
+ if(!(nod = find_path(fs, EXT2_ROOT_INO, gopt[i])))
+ error_msg_and_die("path %s not found in filesystem", gopt[i]);
+ while((p = strchr(gopt[i], '/')))
+ *p = '_';
+ SNPRINTF(fname, MAX_FILENAME-1, "%s.blk", gopt[i]);
+ fh = xfopen(fname, "wb");
+ fprintf(fh, "%d:", get_nod(fs, nod)->i_size);
+ flist_blocks(fs, nod, fh);
+ fclose(fh);
+ }
+ if(strcmp(fsout, "-"))
+ {
+ FILE * fh = xfopen(fsout, "wb");
+ dump_fs(fs, fh, bigendian);
+ fclose(fh);
+ }
+ else
+ dump_fs(fs, stdout, bigendian);
+ free_fs(fs);
+ return 0;
+}