mirror of https://github.com/fspc/gbootroot.git
1819 lines
47 KiB
1819 lines
47 KiB
// 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;
|
|
}
|
|
|