From ee88353f0c125cdc2ee7a56187fde3a10e4ff918 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 18:28:25 -0800 Subject: auto import from //depot/cupcake/@135843 --- genext2fs.c | 2726 ----------------------------------------------------------- 1 file changed, 2726 deletions(-) delete mode 100644 genext2fs.c (limited to 'genext2fs.c') 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 -// -// 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 -// Copyright (C) 2002 Edward Betts -// -// 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 added -// mkfs.jffs2 compatible device table support, -// along with -q, -P, -U - - -#include -#include - -#if HAVE_SYS_TYPES_H -# include -#endif - -#if MAJOR_IN_MKDEV -# include -#elif MAJOR_IN_SYSMACROS -# include -#endif - -#if HAVE_SYS_STAT_H -# include -#endif - -#if STDC_HEADERS -# include -# include -#else -# if HAVE_STDLIB_H -# include -# endif -# if HAVE_STDDEF_H -# include -# endif -#endif - -#if HAVE_STRING_H -# if !STDC_HEADERS && HAVE_MEMORY_H -# include -# endif -# include -#endif - -#if HAVE_STRINGS_H -# include -#endif - -#if HAVE_INTTYPES_H -# include -#else -# if HAVE_STDINT_H -# include -# endif -#endif - -#if HAVE_UNISTD_H -# include -#endif - -#if HAVE_DIRENT_H -# include -# define NAMLEN(dirent) strlen((dirent)->d_name) -#else -# define dirent direct -# define NAMLEN(dirent) (dirent)->d_namlen -# if HAVE_SYS_NDIR_H -# include -# endif -# if HAVE_SYS_DIR_H -# include -# endif -# if HAVE_NDIR_H -# include -# endif -#endif - -#if HAVE_LIBGEN_H -# include -#endif - -#include -#include -#include -#include -#include - -#if HAVE_FCNTL_H -# include -#endif - -#if HAVE_GETOPT_H -# include -#endif - -#if HAVE_LIMITS_H -# include -#endif - -#include -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; ix[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;grpgd[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; grpgd[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: - - /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 ( 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 ( 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;igd[i])); - swap_sb(&fs->sb); -} - -static void -swap_badfs(filesystem *fs) -{ - uint32 i; - swap_sb(&fs->sb); - for(i=0;igd[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 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; igd[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 \n" - " -d, --root \n" - " -D, --devtable \n" - " -b, --size-in-blocks \n" - " -i, --bytes-per-inode \n" - " -N, --number-of-inodes \n" - " -m, --reserved-percentage \n" - " -g, --block-map Generate a block map file for this path.\n" - " -e, --fill-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; -} -- cgit v1.2.3