mirror of
				https://github.com/fspc/gbootroot.git
				synced 2025-11-03 16:05:34 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			1820 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1820 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// genext2fs.c
 | 
						|
//
 | 
						|
// ext2 filesystem generator for embedded systems
 | 
						|
// Copyright (C) 2000 Xavier Bestel <xavier.bestel@free.fr>
 | 
						|
//
 | 
						|
// 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; either version
 | 
						|
// 2 of the License, or (at your option) any later version.
 | 
						|
//
 | 
						|
// 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
 | 
						|
// 	19 Jun 2001	Erik Andersen <andersee@debian.org> added 
 | 
						|
// 	                    the -n (nosquash) option.
 | 
						|
// 	20 Aug 2001	Erik Andersen <andersee@debian.org> added 
 | 
						|
// 	                    device table support
 | 
						|
 | 
						|
 | 
						|
// `genext2fs' is a mean to generate an ext2 filesystem
 | 
						|
// as a normal (non-root) user. It doesn't require you to mount
 | 
						|
// the image file to copy files on it. It doesn't even require
 | 
						|
// you to be the superuser to make device nodes.
 | 
						|
//
 | 
						|
// Warning ! `genext2fs' has been designed for embedded
 | 
						|
// systems. As such, it will generate a filesystem for single-user
 | 
						|
// usage: all files/directories/etc... will belong to UID/GID 0
 | 
						|
//
 | 
						|
// Example usage:
 | 
						|
//
 | 
						|
// # genext2fs -b 1440 -d srcdir /dev/fd0
 | 
						|
//
 | 
						|
// All files in the srcdir directory will be written to /dev/fd0 as
 | 
						|
// a new ext2 filesystem image. You can then mount the floppy as
 | 
						|
// usual.
 | 
						|
//
 | 
						|
// # genext2fs -b 1024 -d builddir -f devices.txt flashdisk.img
 | 
						|
//
 | 
						|
// This one would build a filesystem from all the files in builddir,
 | 
						|
// then would read a devices list and make apropriate nodes. The
 | 
						|
// format for the device list is:
 | 
						|
//
 | 
						|
// drwx            /dev
 | 
						|
// crw-    10,190  /dev/lcd
 | 
						|
// brw-    1,0     /dev/ram0
 | 
						|
// 
 | 
						|
// This device list builds the /dev directory, a character device
 | 
						|
// node /dev/lcd (major 10, minor 190) and a block device node
 | 
						|
// /dev/ram0 (major 1, minor 0)
 | 
						|
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <dirent.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <time.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#define __USE_GNU
 | 
						|
#include <ctype.h>
 | 
						|
#include <fcntl.h>
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
// block size
 | 
						|
 | 
						|
#define BLOCKSIZE         1024
 | 
						|
#define BLOCKS_PER_GROUP  8192
 | 
						|
#define BYTES_PER_INODE   (8*BLOCKSIZE)
 | 
						|
#define RESERVED_INODES   5/100
 | 
						|
 | 
						|
 | 
						|
// inode block size (why is it != BLOCKSIZE ?!?)
 | 
						|
 | 
						|
#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    0xF000	// format mask
 | 
						|
#define FM_IFLNK   0xA000	// socket
 | 
						|
#define FM_IFSOCK  0xC000	// symbolic link
 | 
						|
#define FM_IFREG   0x8000	// regular file
 | 
						|
#define FM_IFBLK   0x6000	// block device
 | 
						|
#define FM_IFDIR   0x4000	// directory
 | 
						|
#define FM_IFCHR   0x2000	// character device
 | 
						|
#define FM_IFIFO   0x1000	// fifo
 | 
						|
 | 
						|
#define FM_ISUID   0x0800	// SUID
 | 
						|
#define FM_ISGID   0x0400	// SGID
 | 
						|
#define FM_ISVTX   0x0200	// sticky bit
 | 
						|
 | 
						|
#define FM_IRWXU   0x01C0	// user mask
 | 
						|
#define FM_IRUSR   0x0100	// read
 | 
						|
#define FM_IWUSR   0x0080	// write
 | 
						|
#define FM_IXUSR   0x0040	// execute
 | 
						|
 | 
						|
#define FM_IRWXG   0x0038	// group mask
 | 
						|
#define FM_IRGRP   0x0020	// read
 | 
						|
#define FM_IWGRP   0x0010	// write
 | 
						|
#define FM_IXGRP   0x0008	// execute
 | 
						|
 | 
						|
#define FM_IRWXO   0x0007	// other mask
 | 
						|
#define FM_IROTH   0x0004	// read
 | 
						|
#define FM_IWOTH   0x0002	// write
 | 
						|
#define FM_IXOTH   0x0001	// execute
 | 
						|
 | 
						|
 | 
						|
// options
 | 
						|
 | 
						|
#define OP_HOLES     0x01       // make files with holes
 | 
						|
 | 
						|
 | 
						|
// 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;
 | 
						|
 | 
						|
 | 
						|
// endianness swap
 | 
						|
 | 
						|
inline uint16 swab16(uint16 val)
 | 
						|
{
 | 
						|
	return (val >> 8) | (val << 8);
 | 
						|
}
 | 
						|
 | 
						|
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];
 | 
						|
	uint32 bg_pad_to_bk[(BLOCKSIZE-32)/sizeof(uint32)];
 | 
						|
} groupdescriptor;
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
	inode_decl
 | 
						|
	uint32 i_reserved2[2];
 | 
						|
} inode;
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
	directory_decl
 | 
						|
	char d_name[0];
 | 
						|
} directory;
 | 
						|
 | 
						|
typedef uint8 block[BLOCKSIZE];
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
	uint32 bnum;
 | 
						|
	uint32 bpdir;
 | 
						|
	uint32 bpind;
 | 
						|
	uint32 bpdind;
 | 
						|
	uint32 bptind;
 | 
						|
} blockwalker;
 | 
						|
 | 
						|
#if BLOCKSIZE == 1024
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
	block zero;          // The famous block 0
 | 
						|
	superblock sb;       // The superblock
 | 
						|
	groupdescriptor gd;  // The group desciptor
 | 
						|
	block bbm;           // The block bitmap
 | 
						|
	block ibm;           // The inode bitmap
 | 
						|
	inode itab[0];       // The inode table
 | 
						|
} 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]); }
 | 
						|
 | 
						|
void swap_sb(superblock *sb)
 | 
						|
{
 | 
						|
#define this sb
 | 
						|
	superblock_decl
 | 
						|
#undef this
 | 
						|
}
 | 
						|
 | 
						|
void swap_gd(groupdescriptor *gd)
 | 
						|
{
 | 
						|
#define this gd
 | 
						|
	groupdescriptor_decl
 | 
						|
#undef this
 | 
						|
}
 | 
						|
 | 
						|
void swap_nod(inode *nod)
 | 
						|
{
 | 
						|
#define this nod
 | 
						|
	inode_decl
 | 
						|
#undef this
 | 
						|
}
 | 
						|
 | 
						|
void swap_dir(directory *dir)
 | 
						|
{
 | 
						|
#define this dir
 | 
						|
	directory_decl
 | 
						|
#undef this
 | 
						|
}
 | 
						|
 | 
						|
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
 | 
						|
 | 
						|
char * argv0;
 | 
						|
int nosquash = 0;
 | 
						|
extern ssize_t getline(char **lineptr, size_t *n, FILE *stream);
 | 
						|
 | 
						|
// error (un)handling
 | 
						|
inline void errexit(const char *fmt, ...)
 | 
						|
{
 | 
						|
	va_list ap;
 | 
						|
	fprintf(stderr, "%s: ", argv0);
 | 
						|
	va_start(ap, fmt);
 | 
						|
	vfprintf(stderr, fmt, ap);
 | 
						|
	va_end(ap);
 | 
						|
	fprintf(stderr, "\n");
 | 
						|
	exit(1);
 | 
						|
}
 | 
						|
 | 
						|
inline void pexit(const char * fname)
 | 
						|
{
 | 
						|
	fprintf(stderr, "%s: ", argv0);
 | 
						|
	perror(fname);
 | 
						|
	exit(1);
 | 
						|
}
 | 
						|
 | 
						|
// printf helper macro
 | 
						|
#define plural(a) (a), ((a) > 1) ? "s" : ""
 | 
						|
 | 
						|
// temporary working block
 | 
						|
inline uint8 * get_workblk(void)
 | 
						|
{
 | 
						|
	static block b;
 | 
						|
	return b;
 | 
						|
}
 | 
						|
inline void free_workblk(block b)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
// rounds a quantity up to a blocksize
 | 
						|
uint32 rndup(uint32 qty, uint32 siz)
 | 
						|
{
 | 
						|
	return (qty + (siz - 1)) & ~(siz - 1);
 | 
						|
}
 | 
						|
 | 
						|
// check if something is allocated in the bitmap
 | 
						|
inline uint32 allocated(block b, uint32 item)
 | 
						|
{
 | 
						|
	return b[(item-1) / 8] & (1 << ((item-1) % 8));
 | 
						|
}
 | 
						|
 | 
						|
// return a given block from a filesystem
 | 
						|
inline uint8 * get_blk(filesystem *fs, uint32 blk)
 | 
						|
{
 | 
						|
	return (uint8*)fs + blk*BLOCKSIZE;
 | 
						|
}
 | 
						|
 | 
						|
// return a given inode from a filesystem
 | 
						|
inline inode * get_nod(filesystem *fs, uint32 nod)
 | 
						|
{
 | 
						|
	return &fs->itab[nod-1];
 | 
						|
}
 | 
						|
 | 
						|
// allocate a given block/inode in the bitmap
 | 
						|
// allocate first free if item == 0
 | 
						|
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
 | 
						|
void deallocate(block b, uint32 item)
 | 
						|
{
 | 
						|
	b[(item-1) / 8] &= ~(1 << ((item-1) % 8));
 | 
						|
}
 | 
						|
 | 
						|
// allocate a block
 | 
						|
uint32 alloc_blk(filesystem *fs)
 | 
						|
{
 | 
						|
	uint32 bk;
 | 
						|
	if(!(bk = allocate(fs->bbm, 0)))
 | 
						|
		errexit("couldn't allocate a block (no free space)");
 | 
						|
	if(!(fs->gd.bg_free_blocks_count--))
 | 
						|
		errexit("group descr. free blocks count == 0 (corrupted fs?)");
 | 
						|
	if(!(fs->sb.s_free_blocks_count--))
 | 
						|
		errexit("superblock free blocks count == 0 (corrupted fs?)");
 | 
						|
	return bk;
 | 
						|
}
 | 
						|
 | 
						|
// allocate an inode
 | 
						|
uint32 alloc_nod(filesystem *fs)
 | 
						|
{
 | 
						|
	uint32 nod;
 | 
						|
	if(!(nod = allocate(fs->ibm, 0)))
 | 
						|
		errexit("couldn't allocate an inode (no free inode)");
 | 
						|
	if(!(fs->gd.bg_free_inodes_count--))
 | 
						|
		errexit("group descr. free blocks count == 0 (corrupted fs?)");
 | 
						|
	if(!(fs->sb.s_free_inodes_count--))
 | 
						|
		errexit("superblock free blocks count == 0 (corrupted fs?)");
 | 
						|
	return nod;
 | 
						|
}
 | 
						|
 | 
						|
// print a bitmap allocation
 | 
						|
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)
 | 
						|
void init_bw(filesystem *fs, uint32 nod, 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 hole!=0, create a hole in the file
 | 
						|
uint32 walk_bw(filesystem *fs, uint32 nod, blockwalker *bw, uint32 *create, uint32 hole)
 | 
						|
{
 | 
						|
	uint32 *bkref = 0;
 | 
						|
	uint32 *b;
 | 
						|
	uint32 extend = 0;
 | 
						|
	if(bw->bnum >= get_nod(fs, nod)->i_blocks / INOBLK)
 | 
						|
	{
 | 
						|
		if(create && (*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);
 | 
						|
	}
 | 
						|
	// 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);
 | 
						|
	}
 | 
						|
	// 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);
 | 
						|
		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);
 | 
						|
	}
 | 
						|
	// 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);
 | 
						|
	}
 | 
						|
	// 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);
 | 
						|
		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);
 | 
						|
		b = (uint32*)get_blk(fs, b[bw->bpind]);
 | 
						|
		bkref = &b[bw->bpdind];
 | 
						|
		if(extend) // allocate first block
 | 
						|
			*bkref = hole ? 0 : alloc_blk(fs);
 | 
						|
	}
 | 
						|
	// 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);
 | 
						|
	}
 | 
						|
	// 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);
 | 
						|
		b = (uint32*)get_blk(fs, b[bw->bpind]);
 | 
						|
		bkref = &b[bw->bpdind];
 | 
						|
		if(extend) // allocate first block
 | 
						|
			*bkref = hole ? 0 : alloc_blk(fs);
 | 
						|
	}
 | 
						|
	// I don't do triple indirect - it's such a small filesystem ...
 | 
						|
	else
 | 
						|
		errexit("file too big ! blocks list for inode %d extends past double indirect blocks!", nod);
 | 
						|
	if(*bkref)
 | 
						|
	{
 | 
						|
		bw->bnum++;
 | 
						|
		if(!allocated(fs->bbm, *bkref))
 | 
						|
			errexit("[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...)
 | 
						|
void extend_blk(filesystem *fs, uint32 nod, block b, int amount)
 | 
						|
{
 | 
						|
	int create = amount;
 | 
						|
	blockwalker bw, lbw;
 | 
						|
	uint32 bk;
 | 
						|
	init_bw(fs, nod, &bw);
 | 
						|
	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
 | 
						|
void add2dir(filesystem *fs, uint32 dnod, uint32 nod, const char* name, uint32 mode, uid_t uid, gid_t gid, time_t ctime)
 | 
						|
{
 | 
						|
	blockwalker bw;
 | 
						|
	uint32 bk;
 | 
						|
	uint8 *b;
 | 
						|
	directory *d;
 | 
						|
	int reclen, nlen;
 | 
						|
	inode *node;
 | 
						|
	inode *pnode;
 | 
						|
 | 
						|
	if (nosquash == 0) {
 | 
						|
	    /* Ok, squash everything */
 | 
						|
	    uid = 0;
 | 
						|
	    gid = 0;
 | 
						|
	    if(!S_ISDIR(mode))
 | 
						|
		mode &= ~(S_IWGRP | S_IWOTH); 
 | 
						|
	    mode &= ~(S_ISUID | S_ISGID); 
 | 
						|
	}
 | 
						|
	pnode = get_nod(fs, dnod);
 | 
						|
 | 
						|
	if(!S_ISDIR(pnode->i_mode))
 | 
						|
		errexit("can't add '%s' to a non-directory", name);
 | 
						|
	if(!*name)
 | 
						|
		errexit("bad name '%s' (not meaningful)", name);
 | 
						|
	if(strchr(name, '/'))
 | 
						|
		errexit("bad name '%s' (contains a slash)", name);
 | 
						|
	nlen = strlen(name);
 | 
						|
	reclen = sizeof(directory) + rndup(nlen, 4);
 | 
						|
	if(reclen > BLOCKSIZE)
 | 
						|
		errexit("bad name '%s' (too long)", name);
 | 
						|
	init_bw(fs, dnod, &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);
 | 
						|
				node->i_mode = mode;
 | 
						|
				node->i_uid = uid;
 | 
						|
				node->i_gid = gid;
 | 
						|
				node->i_atime = ctime;
 | 
						|
				node->i_ctime = ctime;
 | 
						|
				node->i_mtime = ctime;
 | 
						|
				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);
 | 
						|
				node->i_mode = mode;
 | 
						|
				node->i_uid = uid;
 | 
						|
				node->i_gid = gid;
 | 
						|
				node->i_atime = ctime;
 | 
						|
				node->i_ctime = ctime;
 | 
						|
				node->i_mtime = ctime;
 | 
						|
				return;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	// we found no free entry in the directory, so we add a block
 | 
						|
	b = get_workblk();
 | 
						|
	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);
 | 
						|
	node->i_mode = mode;
 | 
						|
	node->i_uid = uid;
 | 
						|
	node->i_gid = gid;
 | 
						|
	node->i_atime = ctime;
 | 
						|
	node->i_ctime = ctime;
 | 
						|
	node->i_mtime = ctime;
 | 
						|
	extend_blk(fs, dnod, b, 1);
 | 
						|
	get_nod(fs, dnod)->i_size += BLOCKSIZE;
 | 
						|
	free_workblk(b);
 | 
						|
}
 | 
						|
 | 
						|
// find an entry in a directory
 | 
						|
uint32 find_dir(filesystem *fs, uint32 nod, const char * name)
 | 
						|
{
 | 
						|
	blockwalker bw;
 | 
						|
	uint32 bk;
 | 
						|
	int nlen = strlen(name);
 | 
						|
	init_bw(fs, nod, &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
 | 
						|
uint32 find_path(filesystem *fs, uint32 nod, const char * name)
 | 
						|
{
 | 
						|
	char *p, *n, *n2 = strdup(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;
 | 
						|
}
 | 
						|
 | 
						|
// make a full-fledged directory (i.e. with "." & "..")
 | 
						|
uint32 mkdir_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, uid_t uid, gid_t gid, time_t ctime)
 | 
						|
{
 | 
						|
	uint32 nod;
 | 
						|
	if((nod = find_dir(fs, parent_nod, name)))
 | 
						|
		return nod;
 | 
						|
       	nod = alloc_nod(fs);
 | 
						|
	if (!(mode & FM_IFDIR))
 | 
						|
	    mode |= FM_IFDIR;
 | 
						|
	add2dir(fs, parent_nod, nod, name, mode, uid, gid, ctime);
 | 
						|
	add2dir(fs, nod, nod, ".", mode, uid, gid, ctime);
 | 
						|
	add2dir(fs, nod, parent_nod, "..", mode, uid, gid, ctime);
 | 
						|
	fs->gd.bg_used_dirs_count++;
 | 
						|
	return nod;
 | 
						|
}
 | 
						|
 | 
						|
// make a symlink
 | 
						|
uint32 mklink_fs(filesystem *fs, uint32 parent_nod, const char *name, size_t size, uint8 * b, uid_t uid, gid_t gid, time_t ctime)
 | 
						|
{
 | 
						|
	uint32 mode;
 | 
						|
	uint32 nod = alloc_nod(fs);
 | 
						|
	mode = FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO; 
 | 
						|
	get_nod(fs, nod)->i_size = size;
 | 
						|
	add2dir(fs, parent_nod, nod, name, mode, uid, gid, ctime);
 | 
						|
	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*
 | 
						|
uint32 mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, size_t size, FILE *f, uid_t uid, gid_t gid, time_t ctime)
 | 
						|
{
 | 
						|
	uint8 * b;
 | 
						|
	uint32 nod = alloc_nod(fs);
 | 
						|
	mode |= FM_IFREG;
 | 
						|
	get_nod(fs, nod)->i_size = size;
 | 
						|
	add2dir(fs, parent_nod, nod, name, mode, uid, gid, ctime);
 | 
						|
	if(!(b = (uint8*)malloc(rndup(size, BLOCKSIZE))))
 | 
						|
		errexit("not enough mem to read file '%s'", name);
 | 
						|
	if(f)
 | 
						|
		fread(b, size, 1, f);
 | 
						|
	else
 | 
						|
		memset(b, 0, size);
 | 
						|
	extend_blk(fs, nod, b, rndup(size, BLOCKSIZE) / BLOCKSIZE);
 | 
						|
	free(b);
 | 
						|
	return nod;
 | 
						|
}
 | 
						|
 | 
						|
// retrieves a mode info from a struct stat
 | 
						|
uint32 get_mode(struct stat *st)
 | 
						|
{
 | 
						|
	uint32 mode = 0;
 | 
						|
	if (nosquash == 1)
 | 
						|
	    return st->st_mode;
 | 
						|
	if(st->st_mode & S_IRUSR)
 | 
						|
		mode |= FM_IRUSR | FM_IRGRP | FM_IROTH;
 | 
						|
	if(st->st_mode & S_IWUSR)
 | 
						|
		mode |= FM_IWUSR | FM_IWGRP | FM_IWOTH;
 | 
						|
	if(st->st_mode & S_IXUSR)
 | 
						|
		mode |= FM_IXUSR | FM_IXGRP | FM_IXOTH;
 | 
						|
	return mode;
 | 
						|
}
 | 
						|
 | 
						|
// retrieves a mode info from a string
 | 
						|
uint32 get_modestr(const char *p)
 | 
						|
{
 | 
						|
	uint32 mode = 0;
 | 
						|
	if(p[0] == 'r')
 | 
						|
		mode |= FM_IRUSR | FM_IRGRP | FM_IROTH;
 | 
						|
	if(p[1] == 'w')
 | 
						|
		mode |= FM_IWUSR | FM_IWGRP | FM_IWOTH;
 | 
						|
	if(p[2] == 'x' || p[2] == 's')
 | 
						|
		mode |= FM_IXUSR | FM_IXGRP | FM_IXOTH;
 | 
						|
	return mode;
 | 
						|
}
 | 
						|
 | 
						|
// basename of a path - free me
 | 
						|
char * basename(const char * fullpath)
 | 
						|
{
 | 
						|
	char * p = strrchr(fullpath, '/');
 | 
						|
	return strdup(p ? p + 1 : fullpath);
 | 
						|
}
 | 
						|
 | 
						|
// dirname of a path - free me
 | 
						|
char * dirname(const char * fullpath)
 | 
						|
{
 | 
						|
	char * p, * n = strdup(fullpath);
 | 
						|
	if((p = strrchr(n, '/')))
 | 
						|
		*(p+1) = 0;
 | 
						|
	else
 | 
						|
		*n = 0;
 | 
						|
	return n;
 | 
						|
}
 | 
						|
 | 
						|
// adds entries to the filesystem from a text file
 | 
						|
void add2fs_from_file(filesystem *fs, uint32 this_nod, FILE * fh)
 | 
						|
{
 | 
						|
	uint32 mode;
 | 
						|
	uint32 nod, nod2;
 | 
						|
	char cmod[11], *path, *name, *dir;
 | 
						|
	int major, minor;
 | 
						|
	while(fscanf(fh, "%10s", cmod))
 | 
						|
	{
 | 
						|
		if(feof(fh))
 | 
						|
			break;
 | 
						|
		mode = get_modestr(cmod + 1);
 | 
						|
		switch(*cmod)
 | 
						|
		{
 | 
						|
			case 'd':
 | 
						|
				fscanf(fh, "%as\n", &path);
 | 
						|
				break;
 | 
						|
			case 'c':
 | 
						|
				mode |= FM_IFCHR;
 | 
						|
				fscanf(fh, "%i, %i %as\n", &major, &minor, &path);
 | 
						|
				break;
 | 
						|
			case 'b':
 | 
						|
				mode |= FM_IFBLK;
 | 
						|
				fscanf(fh, "%i, %i %as\n", &major, &minor, &path);
 | 
						|
				break;
 | 
						|
			case '#':
 | 
						|
				while(fgetc(fh) != '\n');
 | 
						|
				continue;
 | 
						|
			default:
 | 
						|
				errexit("malformed text input file");
 | 
						|
		}
 | 
						|
		name = basename(path);
 | 
						|
		dir = dirname(path);
 | 
						|
		free(path);
 | 
						|
		if(!(nod = find_path(fs, this_nod, dir)))
 | 
						|
			errexit("can't find directory '%s' to create '%s''", dir, name);
 | 
						|
		free(dir);
 | 
						|
		if((!strcmp(name, ".")) || (!strcmp(name, "..")))
 | 
						|
		{
 | 
						|
			free(name);
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		switch(*cmod)
 | 
						|
		{
 | 
						|
			case 'd':
 | 
						|
				// FIXME  - This really needs a get_uid_gid_str()
 | 
						|
				mkdir_fs(fs, nod, name, mode, 0, 0, time(NULL));
 | 
						|
				break;
 | 
						|
			case 'c':
 | 
						|
			case 'b':
 | 
						|
				nod2 = alloc_nod(fs);
 | 
						|
				((uint8*)get_nod(fs, nod2)->i_block)[0] = minor;
 | 
						|
				((uint8*)get_nod(fs, nod2)->i_block)[1] = major;
 | 
						|
				// FIXME  - This really needs a get_uid_gid_str()
 | 
						|
				add2dir(fs, nod, nod2, name, mode, 0, 0, time(NULL));
 | 
						|
				break;
 | 
						|
		}
 | 
						|
		free(name);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// adds a tree of entries to the filesystem from current dir
 | 
						|
void add2fs_from_dir(filesystem *fs, uint32 this_nod)
 | 
						|
{
 | 
						|
	uint32 nod;
 | 
						|
	FILE *fh;
 | 
						|
	DIR *dh;
 | 
						|
	struct dirent *dent;
 | 
						|
	struct stat st;
 | 
						|
	uint8 *b;
 | 
						|
	if(!(dh = opendir(".")))
 | 
						|
		pexit(".");
 | 
						|
	while((dent = readdir(dh)))
 | 
						|
	{
 | 
						|
		if((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, "..")))
 | 
						|
			continue;
 | 
						|
		lstat(dent->d_name, &st);
 | 
						|
		switch(st.st_mode & S_IFMT)
 | 
						|
		{
 | 
						|
			case S_IFCHR:
 | 
						|
			case S_IFBLK:
 | 
						|
				nod = alloc_nod(fs);
 | 
						|
				get_nod(fs, nod)->i_mode = (((st.st_mode & S_IFMT) == S_IFCHR) ? FM_IFCHR : FM_IFBLK) | get_mode(&st);
 | 
						|
				((uint8*)get_nod(fs, nod)->i_block)[0] = (st.st_rdev & 0xff);
 | 
						|
				((uint8*)get_nod(fs, nod)->i_block)[1] = (st.st_rdev >> 8);
 | 
						|
				add2dir(fs, this_nod, nod, dent->d_name, st.st_mode, st.st_uid, st.st_gid, st.st_ctime);
 | 
						|
				break;
 | 
						|
			case S_IFLNK:
 | 
						|
				if(!(b = (uint8*)malloc(rndup(st.st_size, BLOCKSIZE))))
 | 
						|
					errexit("out of memory");
 | 
						|
				if(readlink(dent->d_name, (char*)b, st.st_size) < 0)
 | 
						|
					pexit(dent->d_name);
 | 
						|
				mklink_fs(fs, this_nod, dent->d_name, st.st_size, b, st.st_uid, st.st_gid, st.st_ctime);
 | 
						|
				free(b);
 | 
						|
				break;
 | 
						|
			case S_IFREG:
 | 
						|
				if(!(fh = fopen(dent->d_name, "r")))
 | 
						|
					pexit(dent->d_name);
 | 
						|
				mkfile_fs(fs, this_nod, dent->d_name, st.st_mode, st.st_size, fh, st.st_uid, st.st_gid, st.st_ctime);
 | 
						|
				fclose(fh);
 | 
						|
				break;
 | 
						|
			case S_IFDIR:
 | 
						|
				nod = mkdir_fs(fs, this_nod, dent->d_name, st.st_mode, st.st_uid, st.st_gid, st.st_ctime);
 | 
						|
				if(chdir(dent->d_name) < 0)
 | 
						|
					pexit(dent->d_name);
 | 
						|
				add2fs_from_dir(fs, nod);
 | 
						|
				chdir("..");
 | 
						|
				break;
 | 
						|
			default:
 | 
						|
				fprintf(stderr, "ignoring entry %s", dent->d_name);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	closedir(dh);
 | 
						|
}
 | 
						|
 | 
						|
// endianness swap of x-indirect blocks
 | 
						|
void swap_goodblocks(filesystem *fs, inode *nod)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	int nblk = nod->i_blocks / INOBLK;
 | 
						|
	if(nod->i_size && !nblk)
 | 
						|
		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_IND_BLOCK + BLOCKSIZE/4)
 | 
						|
		return;
 | 
						|
	for(i = 0; i < BLOCKSIZE/4; i++)
 | 
						|
		if(nblk > EXT2_IND_BLOCK + 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;
 | 
						|
	errexit("too big file on the filesystem");
 | 
						|
}
 | 
						|
 | 
						|
void swap_badblocks(filesystem *fs, inode *nod)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	int nblk = nod->i_blocks / INOBLK;
 | 
						|
	if(nod->i_size && !nblk)
 | 
						|
		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_IND_BLOCK + BLOCKSIZE/4)
 | 
						|
		return;
 | 
						|
	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 + 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;
 | 
						|
	errexit("too big file on the filesystem");
 | 
						|
}
 | 
						|
 | 
						|
// endianness swap of the whole filesystem
 | 
						|
void swap_goodfs(filesystem *fs)
 | 
						|
{
 | 
						|
	int 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(fs, i, &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);
 | 
						|
	}
 | 
						|
	swap_gd(&fs->gd);
 | 
						|
	swap_sb(&fs->sb);
 | 
						|
}
 | 
						|
 | 
						|
void swap_badfs(filesystem *fs)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	swap_sb(&fs->sb);
 | 
						|
	swap_gd(&fs->gd);
 | 
						|
	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(fs, i, &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
 | 
						|
filesystem * init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	filesystem *fs;
 | 
						|
	directory *d;
 | 
						|
	uint8 * b;
 | 
						|
	uint32 nod;
 | 
						|
	
 | 
						|
	if(nbblocks < 16) // totally arbitrary
 | 
						|
		errexit("too small filesystem");
 | 
						|
	if(nbblocks >BLOCKS_PER_GROUP) // I build only one group
 | 
						|
		errexit("too big filesystem");
 | 
						|
	if(!(fs = (filesystem*)calloc(nbblocks, BLOCKSIZE)))
 | 
						|
		errexit("not enough memory for filesystem");
 | 
						|
 | 
						|
	// create the superblock for an empty filesystem
 | 
						|
	fs->sb.s_inodes_count = rndup(nbinodes, BLOCKSIZE/sizeof(inode));
 | 
						|
	fs->sb.s_blocks_count = nbblocks;
 | 
						|
	fs->sb.s_r_blocks_count = nbresrvd;
 | 
						|
	fs->sb.s_free_blocks_count = nbblocks;
 | 
						|
	fs->sb.s_free_inodes_count = fs->sb.s_inodes_count - EXT2_FIRST_INO + 1;
 | 
						|
	fs->sb.s_first_data_block = (BLOCKSIZE == 1024);
 | 
						|
	fs->sb.s_log_block_size = BLOCKSIZE >> 11;
 | 
						|
	fs->sb.s_log_frag_size = BLOCKSIZE >> 11;
 | 
						|
	fs->sb.s_blocks_per_group = BLOCKS_PER_GROUP;
 | 
						|
	fs->sb.s_frags_per_group = BLOCKS_PER_GROUP;
 | 
						|
	fs->sb.s_inodes_per_group = fs->sb.s_inodes_count;
 | 
						|
	fs->sb.s_magic = EXT2_MAGIC_NUMBER;
 | 
						|
 | 
						|
	// set up groupdescriptors
 | 
						|
	fs->sb.s_free_blocks_count -= 5 + fs->sb.s_inodes_count * sizeof(inode) / BLOCKSIZE;
 | 
						|
	fs->gd.bg_free_blocks_count = fs->sb.s_free_blocks_count;
 | 
						|
	fs->gd.bg_free_inodes_count = fs->sb.s_free_inodes_count;
 | 
						|
	fs->gd.bg_used_dirs_count = 1;
 | 
						|
	fs->gd.bg_block_bitmap = 3;
 | 
						|
	fs->gd.bg_inode_bitmap = 4;
 | 
						|
	fs->gd.bg_inode_table = 5;
 | 
						|
 | 
						|
	// mark non-filesystem blocks and inodes as allocated
 | 
						|
	for(i = fs->sb.s_blocks_count; i <= BLOCKSIZE * 8; i++)
 | 
						|
		allocate(fs->bbm, i);
 | 
						|
	for(i = fs->sb.s_inodes_count + 1; i <= BLOCKSIZE * 8; i++)
 | 
						|
		allocate(fs->ibm, i);
 | 
						|
 | 
						|
	// mark system blocsk and inodes as allocated
 | 
						|
	for(i = 1; i <= 4 + fs->sb.s_inodes_count * sizeof(inode) / BLOCKSIZE; i++)
 | 
						|
		allocate(fs->bbm, i);
 | 
						|
	for(i = 1; i < EXT2_FIRST_INO; i++)
 | 
						|
		allocate(fs->ibm, i);
 | 
						|
 | 
						|
	// make root inode and directory
 | 
						|
	fs->itab[EXT2_ROOT_INO-1].i_mode = FM_IFDIR | FM_IRWXU | FM_IRWXG | FM_IRWXO;
 | 
						|
	fs->itab[EXT2_ROOT_INO-1].i_size = BLOCKSIZE;
 | 
						|
	fs->itab[EXT2_ROOT_INO-1].i_links_count = 2;
 | 
						|
	b = get_workblk();
 | 
						|
	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", S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH, 0, 0, time(NULL));
 | 
						|
		memset(b, 0, BLOCKSIZE);
 | 
						|
		((directory*)b)->d_rec_len = BLOCKSIZE;
 | 
						|
		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
 | 
						|
filesystem * load_fs(FILE * fh, int swapit)
 | 
						|
{
 | 
						|
	size_t fssize;
 | 
						|
	filesystem *fs;
 | 
						|
	if((fseek(fh, 0, SEEK_END) < 0) || ((fssize = ftell(fh)) < 0))
 | 
						|
		pexit("input filesystem image");
 | 
						|
	rewind(fh);
 | 
						|
	fssize = (fssize + BLOCKSIZE - 1) / BLOCKSIZE;
 | 
						|
	if(fssize < 16) // totally arbitrary
 | 
						|
		errexit("too small filesystem");
 | 
						|
	if(fssize > BLOCKS_PER_GROUP) // I build only one group
 | 
						|
		errexit("too big filesystem");
 | 
						|
	if(!(fs = (filesystem*)calloc(fssize, BLOCKSIZE)))
 | 
						|
		errexit("not enough memory for filesystem");
 | 
						|
	if(fread(fs, BLOCKSIZE, fssize, fh) != fssize)
 | 
						|
		pexit("input filesystem image");
 | 
						|
	if(swapit)
 | 
						|
		swap_badfs(fs);
 | 
						|
	if(fs->sb.s_rev_level || (fs->sb.s_magic != EXT2_MAGIC_NUMBER))
 | 
						|
		errexit("not a suitable ext2 filesystem");
 | 
						|
	return fs;
 | 
						|
}
 | 
						|
 | 
						|
void free_fs(filesystem *fs)
 | 
						|
{
 | 
						|
	free(fs);
 | 
						|
}
 | 
						|
 | 
						|
// just walk through blocks list
 | 
						|
void flist_blocks(filesystem *fs, uint32 nod, FILE *fh)
 | 
						|
{
 | 
						|
	blockwalker bw;
 | 
						|
	uint32 bk;
 | 
						|
	init_bw(fs, nod, &bw);
 | 
						|
	while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
 | 
						|
		fprintf(fh, " %d", bk);
 | 
						|
	fprintf(fh, "\n");
 | 
						|
}
 | 
						|
 | 
						|
// walk through blocks list
 | 
						|
void list_blocks(filesystem *fs, uint32 nod)
 | 
						|
{
 | 
						|
	int bn = 0;
 | 
						|
	blockwalker bw;
 | 
						|
	uint32 bk;
 | 
						|
	init_bw(fs, nod, &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*
 | 
						|
void write_blocks(filesystem *fs, uint32 nod, FILE* f)
 | 
						|
{
 | 
						|
	blockwalker bw;
 | 
						|
	uint32 bk;
 | 
						|
	int32 fsize = get_nod(fs, nod)->i_size;
 | 
						|
	init_bw(fs, nod, &bw);
 | 
						|
	while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
 | 
						|
	{
 | 
						|
		if(fsize <= 0)
 | 
						|
			errexit("wrong size while saving inode %d", nod);
 | 
						|
		if(fwrite(get_blk(fs, bk), (fsize > BLOCKSIZE) ? BLOCKSIZE : fsize, 1, f) != 1)
 | 
						|
			errexit("error while saving inode %d", nod);
 | 
						|
		fsize -= BLOCKSIZE;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// hexdumps blocks to a FILE*
 | 
						|
void hexdump_blocks(filesystem *fs, uint32 nod, FILE* f)
 | 
						|
{
 | 
						|
	blockwalker bw;
 | 
						|
	uint32 bk;
 | 
						|
	uint8 *b;
 | 
						|
	int32 fsize = get_nod(fs, nod)->i_size;
 | 
						|
	init_bw(fs, nod, &bw);
 | 
						|
	printf("block: offset: data:                                ascii:\n");
 | 
						|
	while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
 | 
						|
	{
 | 
						|
		int i, j;
 | 
						|
		if(fsize <= 0)
 | 
						|
			errexit("wrong size while saving inode %d", nod);
 | 
						|
		b = get_blk(fs, bk);
 | 
						|
		for(i = 0; i < 64; i++)
 | 
						|
		{
 | 
						|
			int dmp = 0;
 | 
						|
			for(j = 0; j < 4; j++)
 | 
						|
				if(*(int32*)&b[i * 16 + j * 4])
 | 
						|
					dmp = 1;
 | 
						|
			if(!dmp)
 | 
						|
				continue;
 | 
						|
			printf("%5d:    %03X:", bk, i * 16);
 | 
						|
			for(j = 0; j < 4; j++)
 | 
						|
				printf(" %08x", *(int32*)&b[i * 16 + j * 4]);
 | 
						|
			printf("  ");
 | 
						|
			for(j = 0; j < 16; j++)
 | 
						|
				printf("%c", (b[i * 16 + j] >= ' ' && b[i * 16 + j] < 127) ? b[i * 16 + j] : ' ');
 | 
						|
			printf("\n");
 | 
						|
		}
 | 
						|
		fsize -= BLOCKSIZE;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// print block/char device minor and major
 | 
						|
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
 | 
						|
void print_dir(filesystem *fs, uint32 nod)
 | 
						|
{
 | 
						|
	blockwalker bw;
 | 
						|
	uint32 bk;
 | 
						|
	init_bw(fs, nod, &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
 | 
						|
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
 | 
						|
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
 | 
						|
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(fs->ibm, 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);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// describes various fields in a filesystem
 | 
						|
void print_fs(filesystem *fs)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	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("%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("block bitmap: block %d, inode bitmap: block %d, inode table: block %d\n", fs->gd.bg_block_bitmap, fs->gd.bg_inode_bitmap, fs->gd.bg_inode_table);
 | 
						|
	printf("block bitmap allocation:\n");
 | 
						|
	print_bm(fs->bbm, fs->sb.s_blocks_count);
 | 
						|
	printf("inode bitmap allocation:\n");
 | 
						|
	print_bm(fs->ibm, fs->sb.s_inodes_count);
 | 
						|
	for(i=1; i<=fs->sb.s_inodes_count; i++)
 | 
						|
		if(allocated(fs->ibm, i))
 | 
						|
			print_inode(fs, i);
 | 
						|
}
 | 
						|
 | 
						|
void dump_fs(filesystem *fs, FILE * fh, int swapit)
 | 
						|
{
 | 
						|
	int nbblocks = fs->sb.s_blocks_count;
 | 
						|
	fs->sb.s_reserved[200] = 0;
 | 
						|
	if(swapit)
 | 
						|
		swap_goodfs(fs);
 | 
						|
	if(fwrite(fs, BLOCKSIZE, nbblocks, fh) < nbblocks)
 | 
						|
		pexit("output filesystem image");
 | 
						|
	if(swapit)
 | 
						|
		swap_badfs(fs);
 | 
						|
}
 | 
						|
 | 
						|
char *simple_ltoa(unsigned long i)
 | 
						|
{
 | 
						|
    /* 21 digits plus null terminator, good for 64-bit or smaller ints */
 | 
						|
    static char local[21];
 | 
						|
    char *p = local;
 | 
						|
    *p-- = '\0';
 | 
						|
    do {
 | 
						|
	*p-- = '0' + i % 10;
 | 
						|
	i /= 10;
 | 
						|
    } while (i > 0);
 | 
						|
    return p + 1;
 | 
						|
}
 | 
						|
 | 
						|
//# device list table
 | 
						|
//# <path>	<type> <mode>	<uid>	<gid>	<major>	<minor>	<start>	<inc>	<count>
 | 
						|
///dev/mem	c	640	0	0	1	1	0	0	-
 | 
						|
static int interpret_table_entry(filesystem *fs, char *line)
 | 
						|
{
 | 
						|
	int status;
 | 
						|
	char type, path[40];
 | 
						|
	uint32 nod, nod2;
 | 
						|
	char *name, *dir;
 | 
						|
	unsigned long uid=0, gid=0, mode=755;
 | 
						|
	unsigned long major=0, minor=0, start=0, increment=1, count=0;
 | 
						|
 | 
						|
	status = sscanf(line, "%40s %c %lo %lu %lu %lu %lu %lu %lu %lu", path, 
 | 
						|
			&type, &mode, &uid, &gid, &major, &minor, &start, &increment, &count); 
 | 
						|
	if (status<0)
 | 
						|
		return status;
 | 
						|
	
 | 
						|
	name = basename(path);
 | 
						|
	dir = dirname(path);
 | 
						|
	if(!(nod = find_path(fs, EXT2_ROOT_INO, dir)))
 | 
						|
		errexit("can't find directory '%s' to create '%s''", dir, name);
 | 
						|
	free(dir);
 | 
						|
	if((!strcmp(name, ".")) || (!strcmp(name, "..")))
 | 
						|
	{
 | 
						|
		free(name);
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	
 | 
						|
	switch(type)
 | 
						|
	{
 | 
						|
		case 'd':
 | 
						|
			mkdir_fs(fs, nod, name, mode|FM_IFDIR, uid, gid, time(NULL));
 | 
						|
			break;
 | 
						|
		case 'c':
 | 
						|
		case 'b':
 | 
						|
			mode|= (type=='c')? FM_IFCHR : FM_IFBLK;
 | 
						|
			if (count > 0) {
 | 
						|
			    int i, last;
 | 
						|
			    char buf[80];
 | 
						|
			    last = start+(count*increment);
 | 
						|
			    for(i = start; i<last; i++) {
 | 
						|
				nod2 = alloc_nod(fs);
 | 
						|
				((uint8*)get_nod(fs, nod2)->i_block)[0] = minor+(i*increment-start);
 | 
						|
				((uint8*)get_nod(fs, nod2)->i_block)[1] = major;
 | 
						|
				strncpy(buf, name, sizeof(buf)-1);
 | 
						|
				strncat(buf, simple_ltoa(i*increment), sizeof(buf)-1);
 | 
						|
				buf[79]='\0';
 | 
						|
				//fprintf(stderr, "start=%ld, i=%d adding '%s'(%ld, %ld)\n", start, i, buf, major, minor+(i*increment-start));
 | 
						|
				add2dir(fs, nod, nod2, buf, mode, uid, gid, time(NULL));
 | 
						|
			    }
 | 
						|
			}
 | 
						|
			else {
 | 
						|
			    nod2 = alloc_nod(fs);
 | 
						|
			    ((uint8*)get_nod(fs, nod2)->i_block)[0] = minor;
 | 
						|
			    ((uint8*)get_nod(fs, nod2)->i_block)[1] = major;
 | 
						|
			    add2dir(fs, nod, nod2, name, mode, uid, gid, time(NULL));
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			errexit("%s: Type '%c' is not supported", path, type);
 | 
						|
 | 
						|
	}
 | 
						|
	free(name);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int parse_device_table(filesystem *fs, char *filename)
 | 
						|
{
 | 
						|
	FILE *file;
 | 
						|
	char *line;
 | 
						|
	int status = 0;
 | 
						|
	size_t length = 0;
 | 
						|
	struct stat statbuf;
 | 
						|
 | 
						|
	if (!filename) {
 | 
						|
		fprintf(stderr, "No filename specified.\n");
 | 
						|
		return(1);
 | 
						|
	}
 | 
						|
	if (!(file = fopen(filename, "r"))) {
 | 
						|
		perror(filename);
 | 
						|
		return(1);
 | 
						|
	}
 | 
						|
	if (fstat(fileno(file), &statbuf) < 0) {
 | 
						|
		perror(filename);
 | 
						|
		return(1);
 | 
						|
	}
 | 
						|
 | 
						|
	if (statbuf.st_size < 10) {
 | 
						|
		fprintf(stderr, "%s: not a proper device table file\n", filename);
 | 
						|
		return(1);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Looks ok so far.  The general plan now is to read in one
 | 
						|
	 * line at a time, check for leading comment delimiters ('#'),
 | 
						|
	 * then try and parse the line as a device table.  If we fail
 | 
						|
	 * to parse things, try and help the poor fool to fix their
 | 
						|
	 * device table with a useful error msg... */
 | 
						|
	line = NULL;
 | 
						|
	while (getline(&line, &length, file) != -1) {
 | 
						|
		/* First trim off any whitespace */
 | 
						|
		int len = strlen(line);
 | 
						|
		/* trim trailing whitespace */
 | 
						|
		while ( len > 0 && isspace(line[len-1]))
 | 
						|
		    line[--len]='\0';
 | 
						|
		/* trim leading whitespace */
 | 
						|
		memmove(line, &line[strspn(line, " \n\r\t\v")], len);
 | 
						|
 | 
						|
		/* If this is NOT a comment line, try to interpret it */
 | 
						|
		if (length && *line!='#') {
 | 
						|
			if (interpret_table_entry(fs, line))
 | 
						|
				status=1;
 | 
						|
		}
 | 
						|
 | 
						|
		free(line);
 | 
						|
		line = NULL;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	return status;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
Local Variables:
 | 
						|
c-file-style: "linux"
 | 
						|
c-basic-offset: 4
 | 
						|
tab-width: 4
 | 
						|
End:
 | 
						|
*/
 | 
						|
 | 
						|
void showhelp(void)
 | 
						|
{
 | 
						|
	fprintf(stderr, "Usage: %s [options] image\n"
 | 
						|
	"Create an ext2 filesystem image from directories/files\n\n"
 | 
						|
	"  -x image                Use this image as a starting point\n"
 | 
						|
	"  -d directory            Add this directory as source\n"
 | 
						|
	"  -f file                 Add nodes (e.g. devices) from this spec file\n"
 | 
						|
	"  -b blocks               Size in blocks\n"
 | 
						|
	"  -i inodes               Number of inodes\n"
 | 
						|
	"  -r reserved             Number of reserved blocks\n"
 | 
						|
	"  -g path                 Generate a block map file for this path\n"
 | 
						|
	"  -e value                Fill unallocated blocks with value\n"
 | 
						|
	"  -z                      Make files with holes\n"
 | 
						|
	"  -n                      Do not squash permissions and owners.  By default everythig is\n"
 | 
						|
	"                              owned by root.  This perserves file permissions and owners.\n"
 | 
						|
	"  -v                      Print resulting filesystem structure\n"
 | 
						|
	"  -h                      Show this help\n\n"
 | 
						|
	"Example of spec file:\n"
 | 
						|
	"drwx            /dev\n"
 | 
						|
	"crw-    10,190  /dev/lcd\n"
 | 
						|
	"brw-    1,0     /dev/ram0\n\n"
 | 
						|
	"Report bugs to xavier.bestel@free.fr\n", argv0);
 | 
						|
}
 | 
						|
 | 
						|
#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;
 | 
						|
	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;
 | 
						|
	uint16 endian = 1;
 | 
						|
	int bigendian = !*(char*)&endian;
 | 
						|
	filesystem *fs;
 | 
						|
	int i;
 | 
						|
	char c;
 | 
						|
	char * devtable = NULL;
 | 
						|
 | 
						|
	argv0 = argv[0];
 | 
						|
	while((c = getopt(argc, argv, "D:x:f:d:b:i:r:g:e:zvhn")) != EOF)
 | 
						|
		switch(c)
 | 
						|
		{
 | 
						|
			case 'x':
 | 
						|
				fsin = optarg;
 | 
						|
				break;
 | 
						|
			case 'D':
 | 
						|
				devtable = optarg;
 | 
						|
				break;
 | 
						|
			case 'd':
 | 
						|
			case 'f':
 | 
						|
				dopt[didx++] = optarg;
 | 
						|
				break;
 | 
						|
			case 'b':
 | 
						|
				nbblocks = atoi(optarg);
 | 
						|
				break;
 | 
						|
			case 'i':
 | 
						|
				nbinodes = atoi(optarg);
 | 
						|
				break;
 | 
						|
			case 'r':
 | 
						|
				nbresrvd = atoi(optarg);
 | 
						|
				break;
 | 
						|
			case 'g':
 | 
						|
				gopt[gidx++] = optarg;
 | 
						|
				break;
 | 
						|
			case 'e':
 | 
						|
				emptyval = atoi(optarg);
 | 
						|
				break;
 | 
						|
			case 'z':
 | 
						|
				holes = 1;
 | 
						|
				break;
 | 
						|
			case 'n':
 | 
						|
				nosquash = 1;
 | 
						|
				break;
 | 
						|
			case 'v':
 | 
						|
				verbose = 1;
 | 
						|
				break;
 | 
						|
			case 'h':
 | 
						|
				showhelp();
 | 
						|
				exit(0);
 | 
						|
			default:
 | 
						|
				exit(1);
 | 
						|
		}
 | 
						|
	if(optind < (argc - 1))
 | 
						|
		errexit("too many arguments");
 | 
						|
	if(optind == (argc - 1))
 | 
						|
		fsout = argv[optind];
 | 
						|
	if(fsin)
 | 
						|
	{
 | 
						|
		if(strcmp(fsin, "-"))
 | 
						|
		{
 | 
						|
			FILE * fh = fopen(fsin, "r");
 | 
						|
			if(!fh)
 | 
						|
				pexit(fsin);
 | 
						|
			fs = load_fs(fh, bigendian);
 | 
						|
			fclose(fh);
 | 
						|
		}
 | 
						|
		else
 | 
						|
			fs = load_fs(stdin, bigendian);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		if(nbblocks == -1)
 | 
						|
			errexit("filesystem size unspecified");
 | 
						|
		if(nbinodes == -1)
 | 
						|
			nbinodes = nbblocks * BLOCKSIZE / rndup(BYTES_PER_INODE, BLOCKSIZE);
 | 
						|
		if(nbresrvd == -1)
 | 
						|
			nbresrvd = nbblocks * RESERVED_INODES;
 | 
						|
		fs = init_fs(nbblocks, nbinodes, nbresrvd, holes);
 | 
						|
	}
 | 
						|
	for(i = 0; i < didx; i++)
 | 
						|
	{
 | 
						|
		struct stat st;
 | 
						|
		FILE *fh;
 | 
						|
		char *pdir;
 | 
						|
		stat(dopt[i], &st);
 | 
						|
		switch(st.st_mode & S_IFMT)
 | 
						|
		{
 | 
						|
			case S_IFREG:
 | 
						|
				if(!(fh = fopen(dopt[i], "r")))
 | 
						|
					pexit(dopt[i]);
 | 
						|
				add2fs_from_file(fs, EXT2_ROOT_INO, fh);
 | 
						|
				fclose(fh);
 | 
						|
				break;
 | 
						|
			case S_IFDIR:
 | 
						|
				if(!(pdir = getcwd(0, 0)))
 | 
						|
					pexit(dopt[i]);
 | 
						|
				if(chdir(dopt[i]) < 0)
 | 
						|
					pexit(dopt[i]);
 | 
						|
				add2fs_from_dir(fs, EXT2_ROOT_INO);
 | 
						|
				if(chdir(pdir) < 0)
 | 
						|
					pexit(pdir);
 | 
						|
				free(pdir);
 | 
						|
				break;
 | 
						|
			default:
 | 
						|
				errexit("%s in neither a file nor a directory", dopt[i]);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if(emptyval)
 | 
						|
		for(i = 1; i < fs->sb.s_blocks_count; i++)
 | 
						|
			if(!allocated(fs->bbm, i))
 | 
						|
				memset(get_blk(fs, i), emptyval, BLOCKSIZE);
 | 
						|
	if(devtable)
 | 
						|
	    parse_device_table(fs, devtable);
 | 
						|
	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])))
 | 
						|
			errexit("path %s not found in filesystem", gopt[i]);
 | 
						|
		while((p = strchr(gopt[i], '/')))
 | 
						|
			*p = '_';
 | 
						|
		snprintf(fname, MAX_FILENAME-1, "%s.blk", gopt[i]);
 | 
						|
		if(!(fh = fopen(fname, "w")))
 | 
						|
			pexit(fname);
 | 
						|
		fprintf(fh, "%d:", get_nod(fs, nod)->i_size);
 | 
						|
		flist_blocks(fs, nod, fh);
 | 
						|
		fclose(fh);
 | 
						|
	}
 | 
						|
	if(strcmp(fsout, "-"))
 | 
						|
	{
 | 
						|
		FILE * fh = fopen(fsout, "w");
 | 
						|
		if(!fh)
 | 
						|
			pexit(fsout);
 | 
						|
		dump_fs(fs, fh, bigendian);
 | 
						|
		fclose(fh);
 | 
						|
	}
 | 
						|
	else
 | 
						|
		dump_fs(fs, stdout, bigendian);
 | 
						|
	return 0;
 | 
						|
}
 |