aboutsummaryrefslogtreecommitdiff
path: root/genext2fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'genext2fs.c')
-rw-r--r--genext2fs.c2726
1 files changed, 0 insertions, 2726 deletions
diff --git a/genext2fs.c b/genext2fs.c
deleted file mode 100644
index 11c9c37..0000000
--- a/genext2fs.c
+++ /dev/null
@@ -1,2726 +0,0 @@
-/* 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;
-}