diff --git a/genext2fs/Makefile b/genext2fs/Makefile new file mode 100644 index 0000000..e2dc777 --- /dev/null +++ b/genext2fs/Makefile @@ -0,0 +1,19 @@ +CC=gcc +CFLAGS=-Wall -O2 + +SRC=genext2fs.c +OBJS=$(patsubst %.c,%.o, $(SRC)) + + +all: genext2fs + +genext2fs: $(OBJS) + $(CC) $(CFLAGS) -o $@ $(OBJS) -o $@ + +$(OBJS): %.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + +$(OBJS): Makefile + +clean: + rm -f *.o *.a core genext2fs diff --git a/genext2fs/dev.txt b/genext2fs/dev.txt new file mode 100644 index 0000000..eeafbca --- /dev/null +++ b/genext2fs/dev.txt @@ -0,0 +1,6 @@ +drwx /dev +crw- 4,0 /dev/console +crw- 5,64 /dev/cua0 +crw- 5,65 /dev/cua1 +brw- 1,0 /dev/ram0 +brw- 1,1 /dev/ram1 diff --git a/genext2fs/device_table.txt b/genext2fs/device_table.txt new file mode 100644 index 0000000..90d90c6 --- /dev/null +++ b/genext2fs/device_table.txt @@ -0,0 +1,76 @@ +# device list table +# +/dev d 755 0 0 - - - - - +/dev/mem c 640 0 0 1 1 0 0 - +/dev/kmem c 640 0 0 1 2 0 0 - +/dev/null c 640 0 0 1 3 0 0 - +/dev/zero c 640 0 0 1 5 0 0 - +/dev/random c 640 0 0 1 8 0 0 - +/dev/urandom c 640 0 0 1 9 0 0 - +/dev/tty c 640 0 0 5 0 0 0 - +/dev/tty c 640 0 0 4 0 0 1 6 +/dev/console c 640 0 0 5 1 0 0 - +/dev/ram b 640 0 0 1 1 0 0 - +/dev/ram b 640 0 0 1 0 0 1 4 +/dev/loop b 640 0 0 7 0 0 1 2 +# +# +#/dev/ttyS c 640 0 0 4 64 0 1 4 +#/dev/psaux c 640 0 0 10 1 0 0 - +#/dev/rtc c 640 0 0 10 135 0 0 - +#/dev/fd b 640 0 0 2 0 0 0 1 +# +# IDE Devices +#/dev/hda b 640 0 0 3 0 0 0 - +#/dev/hda b 640 0 0 3 1 1 1 1 +#/dev/hdb b 640 0 0 3 64 0 0 - +#/dev/hdb b 640 0 0 3 65 1 1 1 +#/dev/hdc b 640 0 0 22 0 0 0 - +#/dev/hdc b 640 0 0 22 1 1 1 1 +#/dev/hdd b 640 0 0 22 64 0 0 - +#/dev/hdd b 640 0 0 22 65 1 1 1 +#/dev/hde b 640 0 0 33 0 0 0 - +#/dev/hde b 640 0 0 33 1 1 1 1 +#/dev/hdf b 640 0 0 33 64 0 0 - +#/dev/hdf b 640 0 0 33 65 1 1 1 +#/dev/hdg b 640 0 0 34 64 0 0 - +#/dev/hdg b 640 0 0 34 65 1 1 1 +#/dev/hdh b 640 0 0 34 64 0 0 - +#/dev/hdh b 640 0 0 34 65 1 1 1 +# SCSI Devices +#/dev/sda b 640 0 0 8 0 0 0 - +#/dev/sda b 640 0 0 8 1 1 1 1 +#/dev/sdb b 640 0 0 8 16 0 0 - +#/dev/sdb b 640 0 0 8 17 1 1 1 +#/dev/sdc b 640 0 0 8 32 0 0 - +#/dev/sdc b 640 0 0 8 33 1 1 1 +##/dev/sdd b 640 0 0 8 48 0 0 - +#/dev/sdd b 640 0 0 8 49 1 1 1 +#/dev/sde b 640 0 0 8 64 0 0 - +##/dev/sde b 640 0 0 8 65 1 1 1 +#/dev/sdf b 640 0 0 8 80 0 0 - +#/dev/sdf b 640 0 0 8 81 1 1 1 +#/dev/sdg b 640 0 0 8 96 0 0 - +#/dev/sdg b 640 0 0 8 97 1 1 1 +#/dev/sdh b 640 0 0 8 112 0 0 - +#/dev/sdh b 640 0 0 8 113 1 1 1 +#/dev/sg c 640 0 0 21 0 0 1 1 +#/dev/scd b 640 0 0 11 0 0 1 1 +#/dev/st b 640 0 0 9 0 1 1 4 +#/dev/st b 640 0 0 9 32 1 1 4 +#/dev/st b 640 0 0 9 64 1 1 4 +#/dev/st b 640 0 0 9 96 1 1 4 +# All the proprietary cdrom devices in the world +#/dev/aztcd b 640 0 0 29 0 0 0 - +#/dev/bpcd b 640 0 0 41 0 0 0 - +#/dev/capi20 c 640 0 0 68 0 0 1 2 +#/dev/cdu31a b 640 0 0 15 0 0 0 - +#/dev/cdu535 b 640 0 0 24 0 0 0 - +#/dev/cm206cd b 640 0 0 32 0 0 0 - +#/dev/sjcd b 640 0 0 18 0 0 0 - +#/dev/sonycd b 640 0 0 15 0 0 0 - +#/dev/gscd b 640 0 0 16 0 0 0 - +#/dev/sbpcd b 640 0 0 25 0 0 0 - +#/dev/sbpcd b 640 0 0 25 0 0 1 4 +#/dev/mcd b 640 0 0 23 0 0 0 - +#/dev/optcd b 640 0 0 17 0 0 0 - diff --git a/genext2fs/genext2fs b/genext2fs/genext2fs new file mode 100755 index 0000000..ca3379d Binary files /dev/null and b/genext2fs/genext2fs differ diff --git a/genext2fs/genext2fs.c b/genext2fs/genext2fs.c new file mode 100644 index 0000000..b328740 --- /dev/null +++ b/genext2fs/genext2fs.c @@ -0,0 +1,1819 @@ +// genext2fs.c +// +// ext2 filesystem generator for embedded systems +// Copyright (C) 2000 Xavier Bestel +// +// 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 added +// the -n (nosquash) option. +// 20 Aug 2001 Erik Andersen 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 +#include +#include +#include +#include +#include +#include +#include +#define __USE_GNU +#include +#include + + + + + +// 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; ix[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 +//# +///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; ii_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; +} diff --git a/genext2fs/genext2fs.o b/genext2fs/genext2fs.o new file mode 100644 index 0000000..82cbd50 Binary files /dev/null and b/genext2fs/genext2fs.o differ diff --git a/genext2fs/gpl.txt b/genext2fs/gpl.txt new file mode 100644 index 0000000..c4ccd33 --- /dev/null +++ b/genext2fs/gpl.txt @@ -0,0 +1,342 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + +