| [PATCH] update genext2fs.c to CVS rev 1.118 |
| |
| See http://genext2fs.cvs.sourceforge.net/viewvc/genext2fs/genext2fs/genext2fs.c?view=log |
| for details. |
| |
| Numerous bugfixes, large file and filesystem support, rev 1 filesystems, |
| volume id support, block size, .. |
| |
| Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk> |
| --- |
| cache.h | 128 ++++ |
| genext2fs.c | 1870 ++++++++++++++++++++++++++++++++++++++++++------------------ |
| list.h | 78 ++ |
| 3 files changed, 1527 insertions(+), 549 deletions(-) |
| |
| Index: genext2fs-1.4.1/genext2fs.c |
| =================================================================== |
| --- genext2fs-1.4.1.orig/genext2fs.c |
| +++ genext2fs-1.4.1/genext2fs.c |
| @@ -53,6 +53,12 @@ |
| // along with -q, -P, -U |
| |
| |
| +/* |
| + * Allow fseeko/off_t to be 64-bit offsets to allow filesystems and |
| + * individual files >2GB. |
| + */ |
| +#define _FILE_OFFSET_BITS 64 |
| + |
| #include <config.h> |
| #include <stdio.h> |
| |
| @@ -107,10 +113,8 @@ |
| |
| #if HAVE_DIRENT_H |
| # include <dirent.h> |
| -# define NAMLEN(dirent) strlen((dirent)->d_name) |
| #else |
| # define dirent direct |
| -# define NAMLEN(dirent) (dirent)->d_namlen |
| # if HAVE_SYS_NDIR_H |
| # include <sys/ndir.h> |
| # endif |
| @@ -144,6 +148,8 @@ |
| # include <limits.h> |
| #endif |
| |
| +#include "cache.h" |
| + |
| struct stats { |
| unsigned long nblocks; |
| unsigned long ninodes; |
| @@ -151,13 +157,42 @@ |
| |
| // block size |
| |
| -#define BLOCKSIZE 1024 |
| +static int blocksize = 1024; |
| + |
| +#define SUPERBLOCK_OFFSET 1024 |
| +#define SUPERBLOCK_SIZE 1024 |
| + |
| +#define BLOCKSIZE blocksize |
| #define BLOCKS_PER_GROUP 8192 |
| #define INODES_PER_GROUP 8192 |
| /* Percentage of blocks that are reserved.*/ |
| #define RESERVED_BLOCKS 5/100 |
| #define MAX_RESERVED_BLOCKS 25/100 |
| |
| +/* The default value for s_creator_os. */ |
| +#if defined(__linux__) && defined(EXT2_OS_LINUX) |
| +#define CREATOR_OS EXT2_OS_LINUX |
| +#define CREATOR_OS_NAME "linux" |
| +#else |
| +#if defined(__GNU__) && defined(EXT2_OS_HURD) |
| +#define CREATOR_OS EXT2_OS_HURD |
| +#define CREATOR_OS_NAME "hurd" |
| +#else |
| +#if defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) |
| +#define CREATOR_OS EXT2_OS_FREEBSD |
| +#define CREATOR_OS_NAME "freebsd" |
| +#else |
| +#if defined(LITES) && defined(EXT2_OS_LITES) |
| +#define CREATOR_OS EXT2_OS_LITES |
| +#define CREATOR_OS_NAME "lites" |
| +#else |
| +#define CREATOR_OS EXT2_OS_LINUX /* by default */ |
| +#define CREATOR_OS_NAME "linux" |
| +#endif /* defined(LITES) && defined(EXT2_OS_LITES) */ |
| +#endif /* defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) */ |
| +#endif /* defined(__GNU__) && defined(EXT2_OS_HURD) */ |
| +#endif /* defined(__linux__) && defined(EXT2_OS_LINUX) */ |
| + |
| |
| // inode block size (why is it != BLOCKSIZE ?!?) |
| /* The field i_blocks in the ext2 inode stores the number of data blocks |
| @@ -190,6 +225,14 @@ |
| #define EXT2_TIND_BLOCK 14 // triple indirect block |
| #define EXT2_INIT_BLOCK 0xFFFFFFFF // just initialized (not really a block address) |
| |
| +// codes for operating systems |
| + |
| +#define EXT2_OS_LINUX 0 |
| +#define EXT2_OS_HURD 1 |
| +#define EXT2_OS_MASIX 2 |
| +#define EXT2_OS_FREEBSD 3 |
| +#define EXT2_OS_LITES 4 |
| + |
| // end of a block walk |
| |
| #define WALK_END 0xFFFFFFFE |
| @@ -227,44 +270,46 @@ |
| #define FM_IWOTH 0000002 // write |
| #define FM_IXOTH 0000001 // execute |
| |
| -// options |
| - |
| -#define OP_HOLES 0x01 // make files with holes |
| - |
| /* Defines for accessing group details */ |
| |
| // Number of groups in the filesystem |
| #define GRP_NBGROUPS(fs) \ |
| - (((fs)->sb.s_blocks_count - fs->sb.s_first_data_block + \ |
| - (fs)->sb.s_blocks_per_group - 1) / (fs)->sb.s_blocks_per_group) |
| + (((fs)->sb->s_blocks_count - fs->sb->s_first_data_block + \ |
| + (fs)->sb->s_blocks_per_group - 1) / (fs)->sb->s_blocks_per_group) |
| |
| // Get group block bitmap (bbm) given the group number |
| -#define GRP_GET_GROUP_BBM(fs,grp) ( get_blk((fs),(fs)->gd[(grp)].bg_block_bitmap) ) |
| +#define GRP_GET_GROUP_BBM(fs,grp,bi) (get_blk((fs),(grp)->bg_block_bitmap,(bi))) |
| +#define GRP_PUT_GROUP_BBM(bi) ( put_blk((bi)) ) |
| |
| // Get group inode bitmap (ibm) given the group number |
| -#define GRP_GET_GROUP_IBM(fs,grp) ( get_blk((fs),(fs)->gd[(grp)].bg_inode_bitmap) ) |
| - |
| +#define GRP_GET_GROUP_IBM(fs,grp,bi) (get_blk((fs), (grp)->bg_inode_bitmap,(bi))) |
| +#define GRP_PUT_GROUP_IBM(bi) ( put_blk((bi)) ) |
| + |
| // Given an inode number find the group it belongs to |
| -#define GRP_GROUP_OF_INODE(fs,nod) ( ((nod)-1) / (fs)->sb.s_inodes_per_group) |
| +#define GRP_GROUP_OF_INODE(fs,nod) ( ((nod)-1) / (fs)->sb->s_inodes_per_group) |
| |
| //Given an inode number get the inode bitmap that covers it |
| -#define GRP_GET_INODE_BITMAP(fs,nod) \ |
| - ( GRP_GET_GROUP_IBM((fs),GRP_GROUP_OF_INODE((fs),(nod))) ) |
| +#define GRP_GET_INODE_BITMAP(fs,nod,bi,gi) \ |
| + ( GRP_GET_GROUP_IBM((fs),get_gd(fs,GRP_GROUP_OF_INODE((fs),(nod)),gi),bi) ) |
| +#define GRP_PUT_INODE_BITMAP(bi,gi) \ |
| + ( GRP_PUT_GROUP_IBM((bi)),put_gd((gi)) ) |
| |
| //Given an inode number find its offset within the inode bitmap that covers it |
| #define GRP_IBM_OFFSET(fs,nod) \ |
| - ( (nod) - GRP_GROUP_OF_INODE((fs),(nod))*(fs)->sb.s_inodes_per_group ) |
| + ( (nod) - GRP_GROUP_OF_INODE((fs),(nod))*(fs)->sb->s_inodes_per_group ) |
| |
| // Given a block number find the group it belongs to |
| -#define GRP_GROUP_OF_BLOCK(fs,blk) ( ((blk)-1) / (fs)->sb.s_blocks_per_group) |
| +#define GRP_GROUP_OF_BLOCK(fs,blk) ( ((blk)-1) / (fs)->sb->s_blocks_per_group) |
| |
| -//Given a block number get the block bitmap that covers it |
| -#define GRP_GET_BLOCK_BITMAP(fs,blk) \ |
| - ( GRP_GET_GROUP_BBM((fs),GRP_GROUP_OF_BLOCK((fs),(blk))) ) |
| +//Given a block number get/put the block bitmap that covers it |
| +#define GRP_GET_BLOCK_BITMAP(fs,blk,bi,gi) \ |
| + ( GRP_GET_GROUP_BBM((fs),get_gd(fs,GRP_GROUP_OF_BLOCK((fs),(blk)),(gi)),(bi)) ) |
| +#define GRP_PUT_BLOCK_BITMAP(bi,gi) \ |
| + ( GRP_PUT_GROUP_BBM((bi)),put_gd((gi)) ) |
| |
| //Given a block number find its offset within the block bitmap that covers it |
| #define GRP_BBM_OFFSET(fs,blk) \ |
| - ( (blk) - GRP_GROUP_OF_BLOCK((fs),(blk))*(fs)->sb.s_blocks_per_group ) |
| + ( (blk) - GRP_GROUP_OF_BLOCK((fs),(blk))*(fs)->sb->s_blocks_per_group ) |
| |
| |
| // used types |
| @@ -286,7 +331,9 @@ |
| // older solaris. Note that this is still not very portable, in that |
| // the return value cannot be trusted. |
| |
| -#if SCANF_CAN_MALLOC |
| +#if 0 // SCANF_CAN_MALLOC |
| +// C99 define "a" for floating point, so you can have runtime surprise |
| +// according the library versions |
| # define SCANF_PREFIX "a" |
| # define SCANF_STRING(s) (&s) |
| #else |
| @@ -430,6 +477,17 @@ |
| ((val<<8)&0xFF0000) | (val<<24)); |
| } |
| |
| +static inline int |
| +is_blk_empty(uint8 *b) |
| +{ |
| + uint32 i; |
| + uint32 *v = (uint32 *) b; |
| + |
| + for(i = 0; i < BLOCKSIZE / 4; i++) |
| + if (*v++) |
| + return 0; |
| + return 1; |
| +} |
| |
| // on-disk structures |
| // this trick makes me declare things only once |
| @@ -460,7 +518,22 @@ |
| 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 */ |
| + udecl16(s_def_resgid) /* The default gid for reserved blocks */ \ |
| + /* rev 1 version fields start here */ \ |
| + udecl32(s_first_ino) /* First non-reserved inode */ \ |
| + udecl16(s_inode_size) /* size of inode structure */ \ |
| + udecl16(s_block_group_nr) /* block group # of this superblock */ \ |
| + udecl32(s_feature_compat) /* compatible feature set */ \ |
| + udecl32(s_feature_incompat) /* incompatible feature set */ \ |
| + udecl32(s_feature_ro_compat) /* readonly-compatible feature set */ \ |
| + utdecl8(s_uuid,16) /* 128-bit uuid for volume */ \ |
| + utdecl8(s_volume_name,16) /* volume name */ \ |
| + utdecl8(s_last_mounted,64) /* directory where last mounted */ \ |
| + udecl32(s_algorithm_usage_bitmap) /* For compression */ |
| + |
| +#define EXT2_GOOD_OLD_FIRST_INO 11 |
| +#define EXT2_GOOD_OLD_INODE_SIZE 128 |
| +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 |
| |
| #define groupdescriptor_decl \ |
| udecl32(bg_block_bitmap) /* Block number of the block bitmap */ \ |
| @@ -500,6 +573,7 @@ |
| |
| #define decl8(x) int8 x; |
| #define udecl8(x) uint8 x; |
| +#define utdecl8(x,n) uint8 x[n]; |
| #define decl16(x) int16 x; |
| #define udecl16(x) uint16 x; |
| #define decl32(x) int32 x; |
| @@ -509,7 +583,7 @@ |
| typedef struct |
| { |
| superblock_decl |
| - uint32 s_reserved[235]; // Reserved |
| + uint32 s_reserved[205]; // Reserved |
| } superblock; |
| |
| typedef struct |
| @@ -527,10 +601,9 @@ |
| typedef struct |
| { |
| directory_decl |
| - char d_name[0]; |
| } directory; |
| |
| -typedef uint8 block[BLOCKSIZE]; |
| +typedef uint8 *block; |
| |
| /* blockwalker fields: |
| The blockwalker is used to access all the blocks of a file (including |
| @@ -567,23 +640,41 @@ |
| uint32 bptind; |
| } blockwalker; |
| |
| +#define HDLINK_CNT 16 |
| +struct hdlink_s |
| +{ |
| + uint32 src_inode; |
| + uint32 dst_nod; |
| +}; |
| + |
| +struct hdlinks_s |
| +{ |
| + int32 count; |
| + struct hdlink_s *hdl; |
| +}; |
| |
| /* Filesystem structure that support groups */ |
| -#if BLOCKSIZE == 1024 |
| typedef struct |
| { |
| - block zero; // The famous block 0 |
| - superblock sb; // The superblock |
| - groupdescriptor gd[0]; // The group descriptors |
| + FILE *f; |
| + superblock *sb; |
| + int swapit; |
| + int32 hdlink_cnt; |
| + struct hdlinks_s hdlinks; |
| + |
| + int holes; |
| + |
| + listcache blks; |
| + listcache gds; |
| + listcache inodes; |
| + listcache blkmaps; |
| } filesystem; |
| -#else |
| -#error UNHANDLED BLOCKSIZE |
| -#endif |
| |
| // now the endianness swap |
| |
| #undef decl8 |
| #undef udecl8 |
| +#undef utdecl8 |
| #undef decl16 |
| #undef udecl16 |
| #undef decl32 |
| @@ -592,28 +683,13 @@ |
| |
| #define decl8(x) |
| #define udecl8(x) |
| +#define utdecl8(x,n) |
| #define decl16(x) this->x = swab16(this->x); |
| #define udecl16(x) this->x = swab16(this->x); |
| #define decl32(x) this->x = swab32(this->x); |
| #define udecl32(x) this->x = swab32(this->x); |
| #define utdecl32(x,n) { int i; for(i=0; i<n; i++) this->x[i] = swab32(this->x[i]); } |
| |
| -#define HDLINK_CNT 16 |
| -static int32 hdlink_cnt = HDLINK_CNT; |
| -struct hdlink_s |
| -{ |
| - uint32 src_inode; |
| - uint32 dst_nod; |
| -}; |
| - |
| -struct hdlinks_s |
| -{ |
| - int32 count; |
| - struct hdlink_s *hdl; |
| -}; |
| - |
| -static struct hdlinks_s hdlinks; |
| - |
| static void |
| swap_sb(superblock *sb) |
| { |
| @@ -633,9 +709,24 @@ |
| static void |
| swap_nod(inode *nod) |
| { |
| + uint32 nblk; |
| + |
| #define this nod |
| inode_decl |
| #undef this |
| + |
| + // block and character inodes store the major and minor in the |
| + // i_block, so we need to unswap to get those. Also, if it's |
| + // zero iblocks, put the data back like it belongs. |
| + nblk = nod->i_blocks / INOBLK; |
| + if ((nod->i_size && !nblk) |
| + || ((nod->i_mode & FM_IFBLK) == FM_IFBLK) |
| + || ((nod->i_mode & FM_IFCHR) == FM_IFCHR)) |
| + { |
| + int i; |
| + for(i = 0; i <= EXT2_TIND_BLOCK; i++) |
| + nod->i_block[i] = swab32(nod->i_block[i]); |
| + } |
| } |
| |
| static void |
| @@ -657,6 +748,7 @@ |
| |
| #undef decl8 |
| #undef udecl8 |
| +#undef utdecl8 |
| #undef decl16 |
| #undef udecl16 |
| #undef decl32 |
| @@ -770,15 +862,15 @@ |
| } |
| |
| int |
| -is_hardlink(ino_t inode) |
| +is_hardlink(filesystem *fs, ino_t inode) |
| { |
| int i; |
| |
| - for(i = 0; i < hdlinks.count; i++) { |
| - if(hdlinks.hdl[i].src_inode == inode) |
| + for(i = 0; i < fs->hdlinks.count; i++) { |
| + if(fs->hdlinks.hdl[i].src_inode == inode) |
| return i; |
| } |
| - return -1; |
| + return -1; |
| } |
| |
| // printf helper macro |
| @@ -789,6 +881,8 @@ |
| get_workblk(void) |
| { |
| unsigned char* b=calloc(1,BLOCKSIZE); |
| + if (!b) |
| + error_msg_and_die("get_workblk() failed, out of memory"); |
| return b; |
| } |
| static inline void |
| @@ -811,24 +905,464 @@ |
| return b[(item-1) / 8] & (1 << ((item-1) % 8)); |
| } |
| |
| -// return a given block from a filesystem |
| +// Used by get_blk/put_blk to hold information about a block owned |
| +// by the user. |
| +typedef struct |
| +{ |
| + cache_link link; |
| + |
| + filesystem *fs; |
| + uint32 blk; |
| + uint8 *b; |
| + uint32 usecount; |
| +} blk_info; |
| + |
| +#define MAX_FREE_CACHE_BLOCKS 100 |
| + |
| +static uint32 |
| +blk_elem_val(cache_link *elem) |
| +{ |
| + blk_info *bi = container_of(elem, blk_info, link); |
| + return bi->blk; |
| +} |
| + |
| +static void |
| +blk_freed(cache_link *elem) |
| +{ |
| + blk_info *bi = container_of(elem, blk_info, link); |
| + |
| + if (fseeko(bi->fs->f, ((off_t) bi->blk) * BLOCKSIZE, SEEK_SET)) |
| + perror_msg_and_die("fseek"); |
| + if (fwrite(bi->b, BLOCKSIZE, 1, bi->fs->f) != 1) |
| + perror_msg_and_die("get_blk: write"); |
| + free(bi->b); |
| + free(bi); |
| +} |
| + |
| +// Return a given block from a filesystem. Make sure to call |
| +// put_blk when you are done with it. |
| static inline uint8 * |
| -get_blk(filesystem *fs, uint32 blk) |
| +get_blk(filesystem *fs, uint32 blk, blk_info **rbi) |
| { |
| - return (uint8*)fs + blk*BLOCKSIZE; |
| + cache_link *curr; |
| + blk_info *bi; |
| + |
| + if (blk >= fs->sb->s_blocks_count) |
| + error_msg_and_die("Internal error, block out of range"); |
| + |
| + curr = cache_find(&fs->blks, blk); |
| + if (curr) { |
| + bi = container_of(curr, blk_info, link); |
| + bi->usecount++; |
| + goto out; |
| + } |
| + |
| + bi = malloc(sizeof(*bi)); |
| + if (!bi) |
| + error_msg_and_die("get_blk: out of memory"); |
| + bi->fs = fs; |
| + bi->blk = blk; |
| + bi->usecount = 1; |
| + bi->b = malloc(BLOCKSIZE); |
| + if (!bi->b) |
| + error_msg_and_die("get_blk: out of memory"); |
| + cache_add(&fs->blks, &bi->link); |
| + if (fseeko(fs->f, ((off_t) blk) * BLOCKSIZE, SEEK_SET)) |
| + perror_msg_and_die("fseek"); |
| + if (fread(bi->b, BLOCKSIZE, 1, fs->f) != 1) { |
| + if (ferror(fs->f)) |
| + perror_msg_and_die("fread"); |
| + memset(bi->b, 0, BLOCKSIZE); |
| + } |
| + |
| +out: |
| + *rbi = bi; |
| + return bi->b; |
| } |
| |
| // return a given inode from a filesystem |
| -static inline inode * |
| -get_nod(filesystem *fs, uint32 nod) |
| +static inline void |
| +put_blk(blk_info *bi) |
| +{ |
| + if (bi->usecount == 0) |
| + error_msg_and_die("Internal error: put_blk usecount zero"); |
| + bi->usecount--; |
| + if (bi->usecount == 0) |
| + /* Free happens in the cache code */ |
| + cache_item_set_unused(&bi->fs->blks, &bi->link); |
| +} |
| + |
| +typedef struct |
| { |
| - int grp,offset; |
| + cache_link link; |
| + |
| + filesystem *fs; |
| + int gds; |
| + blk_info *bi; |
| + groupdescriptor *gd; |
| + uint32 usecount; |
| +} gd_info; |
| + |
| +#define MAX_FREE_CACHE_GDS 100 |
| + |
| +static uint32 |
| +gd_elem_val(cache_link *elem) |
| +{ |
| + gd_info *gi = container_of(elem, gd_info, link); |
| + return gi->gds; |
| +} |
| + |
| +static void |
| +gd_freed(cache_link *elem) |
| +{ |
| + gd_info *gi = container_of(elem, gd_info, link); |
| + |
| + if (gi->fs->swapit) |
| + swap_gd(gi->gd); |
| + put_blk(gi->bi); |
| + free(gi); |
| +} |
| + |
| +#define GDS_START ((SUPERBLOCK_OFFSET + SUPERBLOCK_SIZE + BLOCKSIZE - 1) / BLOCKSIZE) |
| +#define GDS_PER_BLOCK (BLOCKSIZE / sizeof(groupdescriptor)) |
| +// the group descriptors are aligned on the block size |
| +static inline groupdescriptor * |
| +get_gd(filesystem *fs, uint32 no, gd_info **rgi) |
| +{ |
| + uint32 gdblk; |
| + uint32 offset; |
| + gd_info *gi; |
| + cache_link *curr; |
| + |
| + curr = cache_find(&fs->gds, no); |
| + if (curr) { |
| + gi = container_of(curr, gd_info, link); |
| + gi->usecount++; |
| + goto out; |
| + } |
| + |
| + gi = malloc(sizeof(*gi)); |
| + if (!gi) |
| + error_msg_and_die("get_gd: out of memory"); |
| + gi->fs = fs; |
| + gi->gds = no; |
| + gi->usecount = 1; |
| + gdblk = GDS_START + (no / GDS_PER_BLOCK); |
| + offset = no % GDS_PER_BLOCK; |
| + gi->gd = ((groupdescriptor *) get_blk(fs, gdblk, &gi->bi)) + offset; |
| + cache_add(&fs->gds, &gi->link); |
| + if (fs->swapit) |
| + swap_gd(gi->gd); |
| + out: |
| + *rgi = gi; |
| + |
| + return gi->gd; |
| +} |
| + |
| +static inline void |
| +put_gd(gd_info *gi) |
| +{ |
| + if (gi->usecount == 0) |
| + error_msg_and_die("Internal error: put_gd usecount zero"); |
| + |
| + gi->usecount--; |
| + if (gi->usecount == 0) |
| + /* Free happens in the cache code */ |
| + cache_item_set_unused(&gi->fs->gds, &gi->link); |
| +} |
| + |
| +// Used by get_blkmap/put_blkmap to hold information about an block map |
| +// owned by the user. |
| +typedef struct |
| +{ |
| + cache_link link; |
| + |
| + filesystem *fs; |
| + uint32 blk; |
| + uint8 *b; |
| + blk_info *bi; |
| + uint32 usecount; |
| +} blkmap_info; |
| + |
| +#define MAX_FREE_CACHE_BLOCKMAPS 100 |
| + |
| +static uint32 |
| +blkmap_elem_val(cache_link *elem) |
| +{ |
| + blkmap_info *bmi = container_of(elem, blkmap_info, link); |
| + return bmi->blk; |
| +} |
| + |
| +static void |
| +blkmap_freed(cache_link *elem) |
| +{ |
| + blkmap_info *bmi = container_of(elem, blkmap_info, link); |
| + |
| + if (bmi->fs->swapit) |
| + swap_block(bmi->b); |
| + put_blk(bmi->bi); |
| + free(bmi); |
| +} |
| + |
| +// Return a given block map from a filesystem. Make sure to call |
| +// put_blkmap when you are done with it. |
| +static inline uint32 * |
| +get_blkmap(filesystem *fs, uint32 blk, blkmap_info **rbmi) |
| +{ |
| + blkmap_info *bmi; |
| + cache_link *curr; |
| + |
| + curr = cache_find(&fs->blkmaps, blk); |
| + if (curr) { |
| + bmi = container_of(curr, blkmap_info, link); |
| + bmi->usecount++; |
| + goto out; |
| + } |
| + |
| + bmi = malloc(sizeof(*bmi)); |
| + if (!bmi) |
| + error_msg_and_die("get_blkmap: out of memory"); |
| + bmi->fs = fs; |
| + bmi->blk = blk; |
| + bmi->b = get_blk(fs, blk, &bmi->bi); |
| + bmi->usecount = 1; |
| + cache_add(&fs->blkmaps, &bmi->link); |
| + |
| + if (fs->swapit) |
| + swap_block(bmi->b); |
| + out: |
| + *rbmi = bmi; |
| + return (uint32 *) bmi->b; |
| +} |
| + |
| +static inline void |
| +put_blkmap(blkmap_info *bmi) |
| +{ |
| + if (bmi->usecount == 0) |
| + error_msg_and_die("Internal error: put_blkmap usecount zero"); |
| + |
| + bmi->usecount--; |
| + if (bmi->usecount == 0) |
| + /* Free happens in the cache code */ |
| + cache_item_set_unused(&bmi->fs->blkmaps, &bmi->link); |
| +} |
| + |
| +// Used by get_nod/put_nod to hold information about an inode owned |
| +// by the user. |
| +typedef struct |
| +{ |
| + cache_link link; |
| + |
| + filesystem *fs; |
| + uint32 nod; |
| + uint8 *b; |
| + blk_info *bi; |
| inode *itab; |
| + uint32 usecount; |
| +} nod_info; |
| + |
| +#define MAX_FREE_CACHE_INODES 100 |
| + |
| +static uint32 |
| +inode_elem_val(cache_link *elem) |
| +{ |
| + nod_info *ni = container_of(elem, nod_info, link); |
| + return ni->nod; |
| +} |
| + |
| +static void |
| +inode_freed(cache_link *elem) |
| +{ |
| + nod_info *ni = container_of(elem, nod_info, link); |
| + |
| + if (ni->fs->swapit) |
| + swap_nod(ni->itab); |
| + put_blk(ni->bi); |
| + free(ni); |
| +} |
| + |
| +#define INODES_PER_BLOCK (BLOCKSIZE / sizeof(inode)) |
| |
| - offset = GRP_IBM_OFFSET(fs,nod); |
| +// return a given inode from a filesystem |
| +static inline inode * |
| +get_nod(filesystem *fs, uint32 nod, nod_info **rni) |
| +{ |
| + uint32 grp, boffset, offset; |
| + cache_link *curr; |
| + groupdescriptor *gd; |
| + gd_info *gi; |
| + nod_info *ni; |
| + |
| + curr = cache_find(&fs->inodes, nod); |
| + if (curr) { |
| + ni = container_of(curr, nod_info, link); |
| + ni->usecount++; |
| + goto out; |
| + } |
| + |
| + ni = malloc(sizeof(*ni)); |
| + if (!ni) |
| + error_msg_and_die("get_nod: out of memory"); |
| + ni->fs = fs; |
| + ni->nod = nod; |
| + ni->usecount = 1; |
| + cache_add(&fs->inodes, &ni->link); |
| + |
| + offset = GRP_IBM_OFFSET(fs,nod) - 1; |
| + boffset = offset / INODES_PER_BLOCK; |
| + offset %= INODES_PER_BLOCK; |
| grp = GRP_GROUP_OF_INODE(fs,nod); |
| - itab = (inode *)get_blk(fs, fs->gd[grp].bg_inode_table); |
| - return itab+offset-1; |
| + gd = get_gd(fs, grp, &gi); |
| + ni->b = get_blk(fs, gd->bg_inode_table + boffset, &ni->bi); |
| + ni->itab = ((inode *) ni->b) + offset; |
| + if (fs->swapit) |
| + swap_nod(ni->itab); |
| + put_gd(gi); |
| + out: |
| + *rni = ni; |
| + return ni->itab; |
| +} |
| + |
| +static inline void |
| +put_nod(nod_info *ni) |
| +{ |
| + if (ni->usecount == 0) |
| + error_msg_and_die("Internal error: put_nod usecount zero"); |
| + |
| + ni->usecount--; |
| + if (ni->usecount == 0) |
| + /* Free happens in the cache code */ |
| + cache_item_set_unused(&ni->fs->inodes, &ni->link); |
| +} |
| + |
| +// Used to hold state information while walking a directory inode. |
| +typedef struct |
| +{ |
| + directory d; |
| + filesystem *fs; |
| + uint32 nod; |
| + directory *last_d; |
| + uint8 *b; |
| + blk_info *bi; |
| +} dirwalker; |
| + |
| +// Start a directory walk on the given inode. You must pass in a |
| +// dirwalker structure, then use that dirwalker for future operations. |
| +// Call put_dir when you are done walking the directory. |
| +static inline directory * |
| +get_dir(filesystem *fs, uint32 nod, dirwalker *dw) |
| +{ |
| + dw->fs = fs; |
| + dw->b = get_blk(fs, nod, &dw->bi); |
| + dw->nod = nod; |
| + dw->last_d = (directory *) dw->b; |
| + |
| + memcpy(&dw->d, dw->last_d, sizeof(directory)); |
| + if (fs->swapit) |
| + swap_dir(&dw->d); |
| + return &dw->d; |
| +} |
| + |
| +// Move to the next directory. |
| +static inline directory * |
| +next_dir(dirwalker *dw) |
| +{ |
| + directory *next_d = (directory *)((int8*)dw->last_d + dw->d.d_rec_len); |
| + |
| + if (dw->fs->swapit) |
| + swap_dir(&dw->d); |
| + memcpy(dw->last_d, &dw->d, sizeof(directory)); |
| + |
| + if (((int8 *) next_d) >= ((int8 *) dw->b + BLOCKSIZE)) |
| + return NULL; |
| + |
| + dw->last_d = next_d; |
| + memcpy(&dw->d, next_d, sizeof(directory)); |
| + if (dw->fs->swapit) |
| + swap_dir(&dw->d); |
| + return &dw->d; |
| +} |
| + |
| +// Call then when you are done with the directory walk. |
| +static inline void |
| +put_dir(dirwalker *dw) |
| +{ |
| + if (dw->fs->swapit) |
| + swap_dir(&dw->d); |
| + memcpy(dw->last_d, &dw->d, sizeof(directory)); |
| + |
| + if (dw->nod == 0) |
| + free_workblk(dw->b); |
| + else |
| + put_blk(dw->bi); |
| +} |
| + |
| +// Create a new directory block with the given inode as it's destination |
| +// and append it to the current dirwalker. |
| +static directory * |
| +new_dir(filesystem *fs, uint32 dnod, const char *name, int nlen, dirwalker *dw) |
| +{ |
| + directory *d; |
| + |
| + dw->fs = fs; |
| + dw->b = get_workblk(); |
| + dw->nod = 0; |
| + dw->last_d = (directory *) dw->b; |
| + d = &dw->d; |
| + d->d_inode = dnod; |
| + d->d_rec_len = BLOCKSIZE; |
| + d->d_name_len = nlen; |
| + strncpy(((char *) dw->last_d) + sizeof(directory), name, nlen); |
| + return d; |
| +} |
| + |
| +// Shrink the current directory entry, make a new one with the free |
| +// space, and return the new directory entry (making it current). |
| +static inline directory * |
| +shrink_dir(dirwalker *dw, uint32 nod, const char *name, int nlen) |
| +{ |
| + int reclen, preclen; |
| + directory *d = &dw->d; |
| + |
| + reclen = d->d_rec_len; |
| + d->d_rec_len = sizeof(directory) + rndup(d->d_name_len, 4); |
| + preclen = d->d_rec_len; |
| + reclen -= preclen; |
| + if (dw->fs->swapit) |
| + swap_dir(&dw->d); |
| + memcpy(dw->last_d, &dw->d, sizeof(directory)); |
| + |
| + dw->last_d = (directory *) (((int8 *) dw->last_d) + preclen); |
| + d->d_rec_len = reclen; |
| + d->d_inode = nod; |
| + d->d_name_len = nlen; |
| + strncpy(((char *) dw->last_d) + sizeof(directory), name, nlen); |
| + |
| + return d; |
| +} |
| + |
| +// Return the current block the directory is walking |
| +static inline uint8 * |
| +dir_data(dirwalker *dw) |
| +{ |
| + return dw->b; |
| +} |
| + |
| +// Return the pointer to the name for the current directory |
| +static inline char * |
| +dir_name(dirwalker *dw) |
| +{ |
| + return ((char *) dw->last_d) + sizeof(directory); |
| +} |
| + |
| +// Set the name for the current directory. Note that this doesn't |
| +// verify that there is space for the directory name, you must do |
| +// that yourself. |
| +static void |
| +dir_set_name(dirwalker *dw, const char *name, int nlen) |
| +{ |
| + dw->d.d_name_len = nlen; |
| + strncpy(((char *) dw->last_d) + sizeof(directory), name, nlen); |
| } |
| |
| // allocate a given block/inode in the bitmap |
| @@ -870,21 +1404,34 @@ |
| { |
| uint32 bk=0; |
| uint32 grp,nbgroups; |
| + blk_info *bi; |
| + groupdescriptor *gd; |
| + gd_info *gi; |
| |
| grp = GRP_GROUP_OF_INODE(fs,nod); |
| nbgroups = GRP_NBGROUPS(fs); |
| - if(!(bk = allocate(get_blk(fs,fs->gd[grp].bg_block_bitmap), 0))) { |
| - for(grp=0;grp<nbgroups && !bk;grp++) |
| - bk=allocate(get_blk(fs,fs->gd[grp].bg_block_bitmap),0); |
| + gd = get_gd(fs, grp, &gi); |
| + bk = allocate(GRP_GET_GROUP_BBM(fs, gd, &bi), 0); |
| + GRP_PUT_GROUP_BBM(bi); |
| + put_gd(gi); |
| + if (!bk) { |
| + for (grp=0; grp<nbgroups && !bk; grp++) { |
| + gd = get_gd(fs, grp, &gi); |
| + bk = allocate(GRP_GET_GROUP_BBM(fs, gd, &bi), 0); |
| + GRP_PUT_GROUP_BBM(bi); |
| + put_gd(gi); |
| + } |
| grp--; |
| } |
| if (!bk) |
| error_msg_and_die("couldn't allocate a block (no free space)"); |
| - if(!(fs->gd[grp].bg_free_blocks_count--)) |
| + gd = get_gd(fs, grp, &gi); |
| + if(!(gd->bg_free_blocks_count--)) |
| error_msg_and_die("group descr %d. free blocks count == 0 (corrupted fs?)",grp); |
| - if(!(fs->sb.s_free_blocks_count--)) |
| + put_gd(gi); |
| + if(!(fs->sb->s_free_blocks_count--)) |
| error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)"); |
| - return fs->sb.s_blocks_per_group*grp + bk; |
| + return fs->sb->s_first_data_block + fs->sb->s_blocks_per_group*grp + (bk-1); |
| } |
| |
| // free a block |
| @@ -892,12 +1439,18 @@ |
| free_blk(filesystem *fs, uint32 bk) |
| { |
| uint32 grp; |
| - |
| - grp = bk / fs->sb.s_blocks_per_group; |
| - bk %= fs->sb.s_blocks_per_group; |
| - deallocate(get_blk(fs,fs->gd[grp].bg_block_bitmap), bk); |
| - fs->gd[grp].bg_free_blocks_count++; |
| - fs->sb.s_free_blocks_count++; |
| + blk_info *bi; |
| + gd_info *gi; |
| + groupdescriptor *gd; |
| + |
| + grp = bk / fs->sb->s_blocks_per_group; |
| + bk %= fs->sb->s_blocks_per_group; |
| + gd = get_gd(fs, grp, &gi); |
| + deallocate(GRP_GET_GROUP_BBM(fs, gd, &bi), bk); |
| + GRP_PUT_GROUP_BBM(bi); |
| + gd->bg_free_blocks_count++; |
| + put_gd(gi); |
| + fs->sb->s_free_blocks_count++; |
| } |
| |
| // allocate an inode |
| @@ -906,6 +1459,9 @@ |
| { |
| uint32 nod,best_group=0; |
| uint32 grp,nbgroups,avefreei; |
| + blk_info *bi; |
| + gd_info *gi, *bestgi; |
| + groupdescriptor *gd, *bestgd; |
| |
| nbgroups = GRP_NBGROUPS(fs); |
| |
| @@ -914,22 +1470,32 @@ |
| /* find the one with the most free blocks and allocate node there */ |
| /* Idea from find_group_dir in fs/ext2/ialloc.c in 2.4.19 kernel */ |
| /* We do it for all inodes. */ |
| - avefreei = fs->sb.s_free_inodes_count / nbgroups; |
| + avefreei = fs->sb->s_free_inodes_count / nbgroups; |
| + bestgd = get_gd(fs, best_group, &bestgi); |
| for(grp=0; grp<nbgroups; grp++) { |
| - if (fs->gd[grp].bg_free_inodes_count < avefreei || |
| - fs->gd[grp].bg_free_inodes_count == 0) |
| + gd = get_gd(fs, grp, &gi); |
| + if (gd->bg_free_inodes_count < avefreei || |
| + gd->bg_free_inodes_count == 0) { |
| + put_gd(gi); |
| continue; |
| - if (!best_group || |
| - fs->gd[grp].bg_free_blocks_count > fs->gd[best_group].bg_free_blocks_count) |
| + } |
| + if (!best_group || gd->bg_free_blocks_count > bestgd->bg_free_blocks_count) { |
| + put_gd(bestgi); |
| best_group = grp; |
| + bestgd = gd; |
| + bestgi = gi; |
| + } else |
| + put_gd(gi); |
| } |
| - if (!(nod = allocate(get_blk(fs,fs->gd[best_group].bg_inode_bitmap),0))) |
| + if (!(nod = allocate(GRP_GET_GROUP_IBM(fs, bestgd, &bi), 0))) |
| error_msg_and_die("couldn't allocate an inode (no free inode)"); |
| - if(!(fs->gd[best_group].bg_free_inodes_count--)) |
| + GRP_PUT_GROUP_IBM(bi); |
| + if(!(bestgd->bg_free_inodes_count--)) |
| error_msg_and_die("group descr. free blocks count == 0 (corrupted fs?)"); |
| - if(!(fs->sb.s_free_inodes_count--)) |
| + put_gd(bestgi); |
| + if(!(fs->sb->s_free_inodes_count--)) |
| error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)"); |
| - return fs->sb.s_inodes_per_group*best_group+nod; |
| + return fs->sb->s_inodes_per_group*best_group+nod; |
| } |
| |
| // print a bitmap allocation |
| @@ -962,30 +1528,40 @@ |
| // used after being freed, so once you start |
| // freeing blocks don't stop until the end of |
| // the file. moreover, i_blocks isn't updated. |
| -// in fact, don't do that, just use extend_blk |
| // if hole!=0, create a hole in the file |
| static uint32 |
| walk_bw(filesystem *fs, uint32 nod, blockwalker *bw, int32 *create, uint32 hole) |
| { |
| uint32 *bkref = 0; |
| + uint32 bk = 0; |
| + blkmap_info *bmi1 = NULL, *bmi2 = NULL, *bmi3 = NULL; |
| uint32 *b; |
| int extend = 0, reduce = 0; |
| + inode *inod; |
| + nod_info *ni; |
| + uint32 *iblk; |
| + |
| if(create && (*create) < 0) |
| reduce = 1; |
| - if(bw->bnum >= get_nod(fs, nod)->i_blocks / INOBLK) |
| + inod = get_nod(fs, nod, &ni); |
| + if(bw->bnum >= inod->i_blocks / INOBLK) |
| { |
| if(create && (*create) > 0) |
| { |
| (*create)--; |
| extend = 1; |
| } |
| - else |
| + else |
| + { |
| + put_nod(ni); |
| return WALK_END; |
| + } |
| } |
| + iblk = inod->i_block; |
| // first direct block |
| if(bw->bpdir == EXT2_INIT_BLOCK) |
| { |
| - bkref = &get_nod(fs, nod)->i_block[bw->bpdir = 0]; |
| + bkref = &iblk[bw->bpdir = 0]; |
| if(extend) // allocate first block |
| *bkref = hole ? 0 : alloc_blk(fs,nod); |
| if(reduce) // free first block |
| @@ -994,7 +1570,7 @@ |
| // direct block |
| else if(bw->bpdir < EXT2_NDIR_BLOCKS) |
| { |
| - bkref = &get_nod(fs, nod)->i_block[++bw->bpdir]; |
| + bkref = &iblk[++bw->bpdir]; |
| if(extend) // allocate block |
| *bkref = hole ? 0 : alloc_blk(fs,nod); |
| if(reduce) // free block |
| @@ -1007,10 +1583,10 @@ |
| bw->bpdir = EXT2_IND_BLOCK; |
| bw->bpind = 0; |
| if(extend) // allocate indirect block |
| - get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod); |
| + iblk[bw->bpdir] = alloc_blk(fs,nod); |
| if(reduce) // free indirect block |
| - free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]); |
| - b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]); |
| + free_blk(fs, iblk[bw->bpdir]); |
| + b = get_blkmap(fs, iblk[bw->bpdir], &bmi1); |
| bkref = &b[bw->bpind]; |
| if(extend) // allocate first block |
| *bkref = hole ? 0 : alloc_blk(fs,nod); |
| @@ -1021,7 +1597,7 @@ |
| 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]); |
| + b = get_blkmap(fs, iblk[bw->bpdir], &bmi1); |
| bkref = &b[bw->bpind]; |
| if(extend) // allocate block |
| *bkref = hole ? 0 : alloc_blk(fs,nod); |
| @@ -1036,15 +1612,15 @@ |
| bw->bpind = 0; |
| bw->bpdind = 0; |
| if(extend) // allocate double indirect block |
| - get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod); |
| + iblk[bw->bpdir] = alloc_blk(fs,nod); |
| if(reduce) // free double indirect block |
| - free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]); |
| - b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]); |
| + free_blk(fs, iblk[bw->bpdir]); |
| + b = get_blkmap(fs, iblk[bw->bpdir], &bmi1); |
| if(extend) // allocate first indirect block |
| b[bw->bpind] = alloc_blk(fs,nod); |
| if(reduce) // free firstindirect block |
| free_blk(fs, b[bw->bpind]); |
| - b = (uint32*)get_blk(fs, b[bw->bpind]); |
| + b = get_blkmap(fs, b[bw->bpind], &bmi2); |
| bkref = &b[bw->bpdind]; |
| if(extend) // allocate first block |
| *bkref = hole ? 0 : alloc_blk(fs,nod); |
| @@ -1055,8 +1631,8 @@ |
| 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]); |
| + b = get_blkmap(fs, iblk[bw->bpdir], &bmi1); |
| + b = get_blkmap(fs, b[bw->bpind], &bmi2); |
| bkref = &b[bw->bpdind]; |
| if(extend) // allocate block |
| *bkref = hole ? 0 : alloc_blk(fs,nod); |
| @@ -1069,12 +1645,12 @@ |
| bw->bnum++; |
| bw->bpdind = 0; |
| bw->bpind++; |
| - b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]); |
| + b = get_blkmap(fs, iblk[bw->bpdir], &bmi1); |
| if(extend) // allocate indirect block |
| b[bw->bpind] = alloc_blk(fs,nod); |
| if(reduce) // free indirect block |
| free_blk(fs, b[bw->bpind]); |
| - b = (uint32*)get_blk(fs, b[bw->bpind]); |
| + b = get_blkmap(fs, b[bw->bpind], &bmi2); |
| bkref = &b[bw->bpdind]; |
| if(extend) // allocate first block |
| *bkref = hole ? 0 : alloc_blk(fs,nod); |
| @@ -1094,20 +1670,20 @@ |
| bw->bpdind = 0; |
| bw->bptind = 0; |
| if(extend) // allocate triple indirect block |
| - get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod); |
| + iblk[bw->bpdir] = alloc_blk(fs,nod); |
| if(reduce) // free triple indirect block |
| - free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]); |
| - b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]); |
| + free_blk(fs, iblk[bw->bpdir]); |
| + b = get_blkmap(fs, iblk[bw->bpdir], &bmi1); |
| if(extend) // allocate first double indirect block |
| b[bw->bpind] = alloc_blk(fs,nod); |
| if(reduce) // free first double indirect block |
| free_blk(fs, b[bw->bpind]); |
| - b = (uint32*)get_blk(fs, b[bw->bpind]); |
| + b = get_blkmap(fs, b[bw->bpind], &bmi2); |
| if(extend) // allocate first indirect block |
| b[bw->bpdind] = alloc_blk(fs,nod); |
| if(reduce) // free first indirect block |
| free_blk(fs, b[bw->bpind]); |
| - b = (uint32*)get_blk(fs, b[bw->bpdind]); |
| + b = get_blkmap(fs, b[bw->bpdind], &bmi3); |
| bkref = &b[bw->bptind]; |
| if(extend) // allocate first data block |
| *bkref = hole ? 0 : alloc_blk(fs,nod); |
| @@ -1121,9 +1697,9 @@ |
| (bw->bptind < BLOCKSIZE/4 -1) ) |
| { |
| bw->bptind++; |
| - b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]); |
| - b = (uint32*)get_blk(fs, b[bw->bpind]); |
| - b = (uint32*)get_blk(fs, b[bw->bpdind]); |
| + b = get_blkmap(fs, iblk[bw->bpdir], &bmi1); |
| + b = get_blkmap(fs, b[bw->bpind], &bmi2); |
| + b = get_blkmap(fs, b[bw->bpdind], &bmi3); |
| bkref = &b[bw->bptind]; |
| if(extend) // allocate data block |
| *bkref = hole ? 0 : alloc_blk(fs,nod); |
| @@ -1140,13 +1716,13 @@ |
| bw->bnum++; |
| bw->bptind = 0; |
| bw->bpdind++; |
| - b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]); |
| - b = (uint32*)get_blk(fs, b[bw->bpind]); |
| + b = get_blkmap(fs, iblk[bw->bpdir], &bmi1); |
| + b = get_blkmap(fs, b[bw->bpind], &bmi2); |
| if(extend) // allocate single indirect block |
| b[bw->bpdind] = alloc_blk(fs,nod); |
| if(reduce) // free indirect block |
| free_blk(fs, b[bw->bpind]); |
| - b = (uint32*)get_blk(fs, b[bw->bpdind]); |
| + b = get_blkmap(fs, b[bw->bpdind], &bmi3); |
| bkref = &b[bw->bptind]; |
| if(extend) // allocate first data block |
| *bkref = hole ? 0 : alloc_blk(fs,nod); |
| @@ -1163,17 +1739,17 @@ |
| bw->bpdind = 0; |
| bw->bptind = 0; |
| bw->bpind++; |
| - b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]); |
| + b = get_blkmap(fs, iblk[bw->bpdir], &bmi1); |
| if(extend) // allocate double indirect block |
| b[bw->bpind] = alloc_blk(fs,nod); |
| if(reduce) // free double indirect block |
| free_blk(fs, b[bw->bpind]); |
| - b = (uint32*)get_blk(fs, b[bw->bpind]); |
| + b = get_blkmap(fs, b[bw->bpind], &bmi2); |
| if(extend) // allocate single indirect block |
| b[bw->bpdind] = alloc_blk(fs,nod); |
| if(reduce) // free indirect block |
| free_blk(fs, b[bw->bpind]); |
| - b = (uint32*)get_blk(fs, b[bw->bpdind]); |
| + b = get_blkmap(fs, b[bw->bpdind], &bmi3); |
| bkref = &b[bw->bptind]; |
| if(extend) // allocate first block |
| *bkref = hole ? 0 : alloc_blk(fs,nod); |
| @@ -1184,56 +1760,105 @@ |
| error_msg_and_die("file too big !"); |
| /* End change for walking triple indirection */ |
| |
| - if(*bkref) |
| - { |
| + bk = *bkref; |
| + if (bmi3) |
| + put_blkmap(bmi3); |
| + if (bmi2) |
| + put_blkmap(bmi2); |
| + if (bmi1) |
| + put_blkmap(bmi1); |
| + |
| + if(bk) |
| + { |
| + blk_info *bi; |
| + gd_info *gi; |
| + uint8 *block; |
| bw->bnum++; |
| - if(!reduce && !allocated(GRP_GET_BLOCK_BITMAP(fs,*bkref), GRP_BBM_OFFSET(fs,*bkref))) |
| - error_msg_and_die("[block %d of inode %d is unallocated !]", *bkref, nod); |
| + block = GRP_GET_BLOCK_BITMAP(fs,bk,&bi,&gi); |
| + if(!reduce && !allocated(block, GRP_BBM_OFFSET(fs,bk))) |
| + error_msg_and_die("[block %d of inode %d is unallocated !]", bk, nod); |
| + GRP_PUT_BLOCK_BITMAP(bi, gi); |
| } |
| if(extend) |
| - get_nod(fs, nod)->i_blocks = bw->bnum * INOBLK; |
| - return *bkref; |
| + inod->i_blocks = bw->bnum * INOBLK; |
| + put_nod(ni); |
| + return bk; |
| } |
| |
| -// add blocks to an inode (file/dir/etc...) |
| -static void |
| -extend_blk(filesystem *fs, uint32 nod, block b, int amount) |
| +typedef struct |
| { |
| - int create = amount; |
| - blockwalker bw, lbw; |
| - uint32 bk; |
| - init_bw(&bw); |
| - if(amount < 0) |
| - { |
| - uint32 i; |
| - for(i = 0; i < get_nod(fs, nod)->i_blocks / INOBLK + amount; i++) |
| - walk_bw(fs, nod, &bw, 0, 0); |
| - while(walk_bw(fs, nod, &bw, &create, 0) != WALK_END) |
| + blockwalker bw; |
| + uint32 nod; |
| + nod_info *ni; |
| + inode *inod; |
| +} inode_pos; |
| +#define INODE_POS_TRUNCATE 0 |
| +#define INODE_POS_EXTEND 1 |
| + |
| +// Call this to set up an ipos structure for future use with |
| +// extend_inode_blk to append blocks to the given inode. If |
| +// op is INODE_POS_TRUNCATE, the inode is truncated to zero size. |
| +// If op is INODE_POS_EXTEND, the position is moved to the end |
| +// of the inode's data blocks. |
| +// Call inode_pos_finish when done with the inode_pos structure. |
| +static void |
| +inode_pos_init(filesystem *fs, inode_pos *ipos, uint32 nod, int op, |
| + blockwalker *endbw) |
| +{ |
| + blockwalker lbw; |
| + |
| + init_bw(&ipos->bw); |
| + ipos->nod = nod; |
| + ipos->inod = get_nod(fs, nod, &ipos->ni); |
| + if (op == INODE_POS_TRUNCATE) { |
| + int32 create = -1; |
| + while(walk_bw(fs, nod, &ipos->bw, &create, 0) != WALK_END) |
| /*nop*/; |
| - get_nod(fs, nod)->i_blocks += amount * INOBLK; |
| + ipos->inod->i_blocks = 0; |
| } |
| - else |
| + |
| + if (endbw) |
| + ipos->bw = *endbw; |
| + else { |
| + /* Seek to the end */ |
| + init_bw(&ipos->bw); |
| + lbw = ipos->bw; |
| + while(walk_bw(fs, nod, &ipos->bw, 0, 0) != WALK_END) |
| + lbw = ipos->bw; |
| + ipos->bw = lbw; |
| + } |
| +} |
| + |
| +// Clean up the inode_pos structure. |
| +static void |
| +inode_pos_finish(filesystem *fs, inode_pos *ipos) |
| +{ |
| + put_nod(ipos->ni); |
| +} |
| + |
| +// add blocks to an inode (file/dir/etc...) at the given position. |
| +// This will only work when appending to the end of an inode. |
| +static void |
| +extend_inode_blk(filesystem *fs, inode_pos *ipos, block b, int amount) |
| +{ |
| + uint32 bk; |
| + uint32 pos; |
| + |
| + if (amount < 0) |
| + error_msg_and_die("extend_inode_blk: Got negative amount"); |
| + |
| + for (pos = 0; amount; pos += BLOCKSIZE) |
| { |
| - 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); |
| + int hole = (fs->holes && is_blk_empty(b + pos)); |
| + |
| + bk = walk_bw(fs, ipos->nod, &ipos->bw, &amount, hole); |
| + if (bk == WALK_END) |
| + error_msg_and_die("extend_inode_blk: extend failed"); |
| + if (!hole) { |
| + blk_info *bi; |
| + uint8 *block = get_blk(fs, bk, &bi); |
| + memcpy(block, b + pos, BLOCKSIZE); |
| + put_blk(bi); |
| } |
| } |
| } |
| @@ -1242,15 +1867,17 @@ |
| static void |
| add2dir(filesystem *fs, uint32 dnod, uint32 nod, const char* name) |
| { |
| - blockwalker bw; |
| + blockwalker bw, lbw; |
| uint32 bk; |
| - uint8 *b; |
| directory *d; |
| + dirwalker dw; |
| int reclen, nlen; |
| inode *node; |
| inode *pnode; |
| + nod_info *dni, *ni; |
| + inode_pos ipos; |
| |
| - pnode = get_nod(fs, dnod); |
| + pnode = get_nod(fs, dnod, &dni); |
| if((pnode->i_mode & FM_IFMT) != FM_IFDIR) |
| error_msg_and_die("can't add '%s' to a non-directory", name); |
| if(!*name) |
| @@ -1262,52 +1889,52 @@ |
| if(reclen > BLOCKSIZE) |
| error_msg_and_die("bad name '%s' (too long)", name); |
| init_bw(&bw); |
| + lbw = 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)) |
| + for(d = get_dir(fs, bk, &dw); d; d = next_dir(&dw)) |
| { |
| // 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 = get_nod(fs, nod, &ni); |
| + dir_set_name(&dw, name, nlen); |
| + put_dir(&dw); |
| node->i_links_count++; |
| - d->d_name_len = nlen; |
| - strncpy(d->d_name, name, nlen); |
| - return; |
| + put_nod(ni); |
| + goto out; |
| } |
| // 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); |
| + d = shrink_dir(&dw, nod, name, nlen); |
| + put_dir(&dw); |
| + node = get_nod(fs, nod, &ni); |
| node->i_links_count++; |
| - d->d_name_len = nlen; |
| - strncpy(d->d_name, name, nlen); |
| - return; |
| + put_nod(ni); |
| + goto out; |
| } |
| } |
| + put_dir(&dw); |
| + lbw = bw; |
| } |
| // we found no free entry in the directory, so we add a block |
| - if(!(b = get_workblk())) |
| - error_msg_and_die("get_workblk() failed."); |
| - d = (directory*)b; |
| - d->d_inode = nod; |
| - node = get_nod(fs, nod); |
| + node = get_nod(fs, nod, &ni); |
| + d = new_dir(fs, nod, name, nlen, &dw); |
| node->i_links_count++; |
| - d->d_rec_len = BLOCKSIZE; |
| - d->d_name_len = nlen; |
| - strncpy(d->d_name, name, nlen); |
| - extend_blk(fs, dnod, b, 1); |
| - get_nod(fs, dnod)->i_size += BLOCKSIZE; |
| - free_workblk(b); |
| + put_nod(ni); |
| + next_dir(&dw); // Force the data into the buffer |
| + |
| + inode_pos_init(fs, &ipos, dnod, INODE_POS_EXTEND, &lbw); |
| + extend_inode_blk(fs, &ipos, dir_data(&dw), 1); |
| + inode_pos_finish(fs, &ipos); |
| + |
| + put_dir(&dw); |
| + pnode->i_size += BLOCKSIZE; |
| +out: |
| + put_nod(dni); |
| } |
| |
| // find an entry in a directory |
| @@ -1321,11 +1948,13 @@ |
| 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)) |
| + dirwalker dw; |
| + for (d = get_dir(fs, bk, &dw); d; d=next_dir(&dw)) |
| + if(d->d_inode && (nlen == d->d_name_len) && !strncmp(dir_name(&dw), name, nlen)) { |
| + put_dir(&dw); |
| return d->d_inode; |
| + } |
| + put_dir(&dw); |
| } |
| return 0; |
| } |
| @@ -1356,47 +1985,55 @@ |
| return nod; |
| } |
| |
| +// chmod an inode |
| +void |
| +chmod_fs(filesystem *fs, uint32 nod, uint16 mode, uint16 uid, uint16 gid) |
| +{ |
| + inode *node; |
| + nod_info *ni; |
| + node = get_nod(fs, nod, &ni); |
| + node->i_mode = (node->i_mode & ~FM_IMASK) | (mode & FM_IMASK); |
| + node->i_uid = uid; |
| + node->i_gid = gid; |
| + put_nod(ni); |
| +} |
| + |
| // create a simple inode |
| static uint32 |
| mknod_fs(filesystem *fs, uint32 parent_nod, const char *name, uint16 mode, uint16 uid, uint16 gid, uint8 major, uint8 minor, uint32 ctime, uint32 mtime) |
| { |
| uint32 nod; |
| inode *node; |
| - if((nod = find_dir(fs, parent_nod, name))) |
| - { |
| - node = get_nod(fs, nod); |
| - if((node->i_mode & FM_IFMT) != (mode & FM_IFMT)) |
| - error_msg_and_die("node '%s' already exists and isn't of the same type", name); |
| - node->i_mode = mode; |
| - } |
| - else |
| + nod_info *ni; |
| + gd_info *gi; |
| + |
| + nod = alloc_nod(fs); |
| + node = get_nod(fs, nod, &ni); |
| + node->i_mode = mode; |
| + add2dir(fs, parent_nod, nod, name); |
| + switch(mode & FM_IFMT) |
| { |
| - nod = alloc_nod(fs); |
| - node = get_nod(fs, nod); |
| - node->i_mode = mode; |
| - add2dir(fs, parent_nod, nod, name); |
| - switch(mode & FM_IFMT) |
| - { |
| - case FM_IFLNK: |
| - mode = FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO; |
| - break; |
| - case FM_IFBLK: |
| - case FM_IFCHR: |
| - ((uint8*)get_nod(fs, nod)->i_block)[0] = minor; |
| - ((uint8*)get_nod(fs, nod)->i_block)[1] = major; |
| - break; |
| - case FM_IFDIR: |
| - add2dir(fs, nod, nod, "."); |
| - add2dir(fs, nod, parent_nod, ".."); |
| - fs->gd[GRP_GROUP_OF_INODE(fs,nod)].bg_used_dirs_count++; |
| - break; |
| - } |
| + case FM_IFLNK: |
| + mode = FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO; |
| + break; |
| + case FM_IFBLK: |
| + case FM_IFCHR: |
| + ((uint8*)node->i_block)[0] = minor; |
| + ((uint8*)node->i_block)[1] = major; |
| + break; |
| + case FM_IFDIR: |
| + add2dir(fs, nod, nod, "."); |
| + add2dir(fs, nod, parent_nod, ".."); |
| + get_gd(fs,GRP_GROUP_OF_INODE(fs,nod),&gi)->bg_used_dirs_count++; |
| + put_gd(gi); |
| + break; |
| } |
| node->i_uid = uid; |
| node->i_gid = gid; |
| node->i_atime = mtime; |
| node->i_ctime = ctime; |
| node->i_mtime = mtime; |
| + put_nod(ni); |
| return nod; |
| } |
| |
| @@ -1413,33 +2050,73 @@ |
| mklink_fs(filesystem *fs, uint32 parent_nod, const char *name, size_t size, uint8 *b, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime) |
| { |
| uint32 nod = mknod_fs(fs, parent_nod, name, FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO, uid, gid, 0, 0, ctime, mtime); |
| - extend_blk(fs, nod, 0, - (int)get_nod(fs, nod)->i_blocks / INOBLK); |
| - get_nod(fs, nod)->i_size = size; |
| - if(size <= 4 * (EXT2_TIND_BLOCK+1)) |
| - { |
| - strncpy((char*)get_nod(fs, nod)->i_block, (char*)b, size); |
| + nod_info *ni; |
| + inode *node = get_nod(fs, nod, &ni); |
| + inode_pos ipos; |
| + |
| + inode_pos_init(fs, &ipos, nod, INODE_POS_TRUNCATE, NULL); |
| + node->i_size = size; |
| + if(size < 4 * (EXT2_TIND_BLOCK+1)) |
| + { |
| + strncpy((char*)node->i_block, (char*)b, size); |
| + ((char*)node->i_block)[size+1] = '\0'; |
| + inode_pos_finish(fs, &ipos); |
| + put_nod(ni); |
| return nod; |
| } |
| - extend_blk(fs, nod, b, rndup(size, BLOCKSIZE) / BLOCKSIZE); |
| + extend_inode_blk(fs, &ipos, b, rndup(size, BLOCKSIZE) / BLOCKSIZE); |
| + inode_pos_finish(fs, &ipos); |
| + put_nod(ni); |
| return nod; |
| } |
| |
| +static void |
| +fs_upgrade_rev1_largefile(filesystem *fs) |
| +{ |
| + fs->sb->s_rev_level = 1; |
| + fs->sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; |
| + fs->sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; |
| +} |
| + |
| +#define COPY_BLOCKS 16 |
| +#define CB_SIZE (COPY_BLOCKS * BLOCKSIZE) |
| + |
| // make a file from a FILE* |
| static uint32 |
| -mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, size_t size, FILE *f, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime) |
| +mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, FILE *f, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime) |
| { |
| uint8 * b; |
| uint32 nod = mknod_fs(fs, parent_nod, name, mode|FM_IFREG, uid, gid, 0, 0, ctime, mtime); |
| - extend_blk(fs, nod, 0, - (int)get_nod(fs, nod)->i_blocks / INOBLK); |
| - get_nod(fs, nod)->i_size = size; |
| - if (size) { |
| - if(!(b = (uint8*)calloc(rndup(size, BLOCKSIZE), 1))) |
| - error_msg_and_die("not enough mem to read file '%s'", name); |
| - if(f) |
| - fread(b, size, 1, f); // FIXME: ugly. use mmap() ... |
| - extend_blk(fs, nod, b, rndup(size, BLOCKSIZE) / BLOCKSIZE); |
| - free(b); |
| - } |
| + nod_info *ni; |
| + inode *node = get_nod(fs, nod, &ni); |
| + off_t size = 0; |
| + size_t readbytes; |
| + inode_pos ipos; |
| + int fullsize; |
| + |
| + b = malloc(CB_SIZE); |
| + if (!b) |
| + error_msg_and_die("mkfile_fs: out of memory"); |
| + inode_pos_init(fs, &ipos, nod, INODE_POS_TRUNCATE, NULL); |
| + readbytes = fread(b, 1, CB_SIZE, f); |
| + while (readbytes) { |
| + fullsize = rndup(readbytes, BLOCKSIZE); |
| + // Fill to end of block with zeros. |
| + memset(b + readbytes, 0, fullsize - readbytes); |
| + extend_inode_blk(fs, &ipos, b, fullsize / BLOCKSIZE); |
| + size += readbytes; |
| + readbytes = fread(b, 1, CB_SIZE, f); |
| + } |
| + if (size > 0x7fffffff) { |
| + if (fs->sb->s_rev_level < 1) |
| + fs_upgrade_rev1_largefile(fs); |
| + fs->sb->s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_LARGE_FILE; |
| + } |
| + node->i_dir_acl = size >> 32; |
| + node->i_size = size; |
| + inode_pos_finish(fs, &ipos); |
| + put_nod(ni); |
| + free(b); |
| return nod; |
| } |
| |
| @@ -1591,13 +2268,24 @@ |
| dname = malloc(len + 1); |
| for(i = start; i < count; i++) |
| { |
| + uint32 oldnod; |
| SNPRINTF(dname, len, "%s%lu", name, i); |
| - mknod_fs(fs, nod, dname, mode, uid, gid, major, minor + (i * increment - start), ctime, mtime); |
| + oldnod = find_dir(fs, nod, dname); |
| + if(oldnod) |
| + chmod_fs(fs, oldnod, mode, uid, gid); |
| + else |
| + mknod_fs(fs, nod, dname, mode, uid, gid, major, minor + (i * increment - start), ctime, mtime); |
| } |
| free(dname); |
| } |
| else |
| - mknod_fs(fs, nod, name, mode, uid, gid, major, minor, ctime, mtime); |
| + { |
| + uint32 oldnod = find_dir(fs, nod, name); |
| + if(oldnod) |
| + chmod_fs(fs, oldnod, mode, uid, gid); |
| + else |
| + mknod_fs(fs, nod, name, mode, uid, gid, major, minor, ctime, mtime); |
| + } |
| } |
| } |
| if (line) |
| @@ -1643,6 +2331,10 @@ |
| switch(st.st_mode & S_IFMT) |
| { |
| case S_IFLNK: |
| + if((st.st_mode & S_IFMT) == S_IFREG || st.st_size >= 4 * (EXT2_TIND_BLOCK+1)) |
| + stats->nblocks += (st.st_size + BLOCKSIZE - 1) / BLOCKSIZE; |
| + stats->ninodes++; |
| + break; |
| case S_IFREG: |
| if((st.st_mode & S_IFMT) == S_IFREG || st.st_size > 4 * (EXT2_TIND_BLOCK+1)) |
| stats->nblocks += (st.st_size + BLOCKSIZE - 1) / BLOCKSIZE; |
| @@ -1657,19 +2349,33 @@ |
| if(chdir(dent->d_name) < 0) |
| perror_msg_and_die(dent->d_name); |
| add2fs_from_dir(fs, this_nod, squash_uids, squash_perms, fs_timestamp, stats); |
| - chdir(".."); |
| + if (chdir("..") == -1) |
| + perror_msg_and_die(".."); |
| + |
| break; |
| default: |
| break; |
| } |
| else |
| { |
| + if((nod = find_dir(fs, this_nod, name))) |
| + { |
| + error_msg("ignoring duplicate entry %s", name); |
| + if(S_ISDIR(st.st_mode)) { |
| + if(chdir(dent->d_name) < 0) |
| + perror_msg_and_die(name); |
| + add2fs_from_dir(fs, nod, squash_uids, squash_perms, fs_timestamp, stats); |
| + if (chdir("..") == -1) |
| + perror_msg_and_die(".."); |
| + } |
| + continue; |
| + } |
| save_nod = 0; |
| /* Check for hardlinks */ |
| if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) && st.st_nlink > 1) { |
| - int32 hdlink = is_hardlink(st.st_ino); |
| + int32 hdlink = is_hardlink(fs, st.st_ino); |
| if (hdlink >= 0) { |
| - add2dir(fs, this_nod, hdlinks.hdl[hdlink].dst_nod, name); |
| + add2dir(fs, this_nod, fs->hdlinks.hdl[hdlink].dst_nod, name); |
| continue; |
| } else { |
| save_nod = 1; |
| @@ -1697,8 +2403,12 @@ |
| free(lnk); |
| break; |
| case S_IFREG: |
| - fh = xfopen(dent->d_name, "rb"); |
| - nod = mkfile_fs(fs, this_nod, name, mode, st.st_size, fh, uid, gid, ctime, mtime); |
| + fh = fopen(dent->d_name, "rb"); |
| + if (!fh) { |
| + error_msg("Unable to open file %s", dent->d_name); |
| + break; |
| + } |
| + nod = mkfile_fs(fs, this_nod, name, mode, fh, uid, gid, ctime, mtime); |
| fclose(fh); |
| break; |
| case S_IFDIR: |
| @@ -1706,199 +2416,128 @@ |
| if(chdir(dent->d_name) < 0) |
| perror_msg_and_die(name); |
| add2fs_from_dir(fs, nod, squash_uids, squash_perms, fs_timestamp, stats); |
| - chdir(".."); |
| + if (chdir("..") == -1) |
| + perror_msg_and_die(".."); |
| break; |
| default: |
| error_msg("ignoring entry %s", name); |
| } |
| if (save_nod) { |
| - if (hdlinks.count == hdlink_cnt) { |
| - if ((hdlinks.hdl = |
| - realloc (hdlinks.hdl, (hdlink_cnt + HDLINK_CNT) * |
| + if (fs->hdlinks.count == fs->hdlink_cnt) { |
| + if ((fs->hdlinks.hdl = |
| + realloc (fs->hdlinks.hdl, (fs->hdlink_cnt + HDLINK_CNT) * |
| sizeof (struct hdlink_s))) == NULL) { |
| error_msg_and_die("Not enough memory"); |
| } |
| - hdlink_cnt += HDLINK_CNT; |
| + fs->hdlink_cnt += HDLINK_CNT; |
| } |
| - hdlinks.hdl[hdlinks.count].src_inode = st.st_ino; |
| - hdlinks.hdl[hdlinks.count].dst_nod = nod; |
| - hdlinks.count++; |
| + fs->hdlinks.hdl[fs->hdlinks.count].src_inode = st.st_ino; |
| + fs->hdlinks.hdl[fs->hdlinks.count].dst_nod = nod; |
| + fs->hdlinks.count++; |
| } |
| } |
| } |
| closedir(dh); |
| } |
| |
| -// endianness swap of x-indirect blocks |
| +// Copy size blocks from src to dst, putting holes in the output |
| +// file (if possible) if the input block is all zeros. |
| +// Copy size blocks from src to dst, putting holes in the output |
| +// file (if possible) if the input block is all zeros. |
| static void |
| -swap_goodblocks(filesystem *fs, inode *nod) |
| +copy_file(filesystem *fs, FILE *dst, FILE *src, size_t size) |
| { |
| - uint32 i,j; |
| - int done=0; |
| - uint32 *b,*b2; |
| + uint8 *b; |
| |
| - uint32 nblk = nod->i_blocks / INOBLK; |
| - if((nod->i_size && !nblk) || ((nod->i_mode & FM_IFBLK) == FM_IFBLK) || ((nod->i_mode & FM_IFCHR) == FM_IFCHR)) |
| - for(i = 0; i <= EXT2_TIND_BLOCK; i++) |
| - nod->i_block[i] = swab32(nod->i_block[i]); |
| - if(nblk <= EXT2_IND_BLOCK) |
| - return; |
| - swap_block(get_blk(fs, nod->i_block[EXT2_IND_BLOCK])); |
| - if(nblk <= EXT2_DIND_BLOCK + BLOCKSIZE/4) |
| - return; |
| - /* Currently this will fail b'cos the number of blocks as stored |
| - in i_blocks also includes the indirection blocks (see |
| - walk_bw). But this function assumes that i_blocks only |
| - stores the count of data blocks ( Actually according to |
| - "Understanding the Linux Kernel" (Table 17-3 p502 1st Ed) |
| - i_blocks IS supposed to store the count of data blocks). so |
| - with a file of size 268K nblk would be 269.The above check |
| - will be false even though double indirection hasn't been |
| - started.This is benign as 0 means block 0 which has been |
| - zeroed out and therefore points back to itself from any offset |
| - */ |
| - // FIXME: I have fixed that, but I have the feeling the rest of |
| - // ths function needs to be fixed for the same reasons - Xav |
| - assert(nod->i_block[EXT2_DIND_BLOCK] != 0); |
| - for(i = 0; i < BLOCKSIZE/4; i++) |
| - if(nblk > EXT2_IND_BLOCK + BLOCKSIZE/4 + (BLOCKSIZE/4)*i ) |
| - swap_block(get_blk(fs, ((uint32*)get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]))[i])); |
| - swap_block(get_blk(fs, nod->i_block[EXT2_DIND_BLOCK])); |
| - if(nblk <= EXT2_IND_BLOCK + BLOCKSIZE/4 + BLOCKSIZE/4 * BLOCKSIZE/4) |
| - return; |
| - /* Adding support for triple indirection */ |
| - b = (uint32*)get_blk(fs,nod->i_block[EXT2_TIND_BLOCK]); |
| - for(i=0;i < BLOCKSIZE/4 && !done ; i++) { |
| - b2 = (uint32*)get_blk(fs,b[i]); |
| - for(j=0; j<BLOCKSIZE/4;j++) { |
| - if (nblk > ( EXT2_IND_BLOCK + BLOCKSIZE/4 + |
| - (BLOCKSIZE/4)*(BLOCKSIZE/4) + |
| - i*(BLOCKSIZE/4)*(BLOCKSIZE/4) + |
| - j*(BLOCKSIZE/4)) ) |
| - swap_block(get_blk(fs,b2[j])); |
| - else { |
| - done = 1; |
| - break; |
| - } |
| + b = malloc(BLOCKSIZE); |
| + if (!b) |
| + error_msg_and_die("copy_file: out of memory"); |
| + if (fseek(src, 0, SEEK_SET)) |
| + perror_msg_and_die("fseek"); |
| + if (ftruncate(fileno(dst), 0)) |
| + perror_msg_and_die("copy_file: ftruncate"); |
| + while (size > 0) { |
| + if (fread(b, BLOCKSIZE, 1, src) != 1) |
| + perror_msg_and_die("copy failed on read"); |
| + if ((dst != stdout) && fs->holes && is_blk_empty(b)) { |
| + /* Empty block, just skip it */ |
| + if (fseek(dst, BLOCKSIZE, SEEK_CUR)) |
| + perror_msg_and_die("fseek"); |
| + } else { |
| + if (fwrite(b, BLOCKSIZE, 1, dst) != 1) |
| + perror_msg_and_die("copy failed on write"); |
| } |
| - swap_block((uint8 *)b2); |
| + size--; |
| } |
| - swap_block((uint8 *)b); |
| - return; |
| + free(b); |
| } |
| |
| -static void |
| -swap_badblocks(filesystem *fs, inode *nod) |
| +// Allocate a new filesystem structure, allocate internal memory, |
| +// and initialize the contents. |
| +static filesystem * |
| +alloc_fs(int swapit, char *fname, uint32 nbblocks, FILE *srcfile) |
| { |
| - uint32 i,j; |
| - int done=0; |
| - uint32 *b,*b2; |
| + filesystem *fs; |
| + struct stat srcstat, dststat; |
| |
| - uint32 nblk = nod->i_blocks / INOBLK; |
| - if((nod->i_size && !nblk) || ((nod->i_mode & FM_IFBLK) == FM_IFBLK) || ((nod->i_mode & FM_IFCHR) == FM_IFCHR)) |
| - for(i = 0; i <= EXT2_TIND_BLOCK; i++) |
| - nod->i_block[i] = swab32(nod->i_block[i]); |
| - if(nblk <= EXT2_IND_BLOCK) |
| - return; |
| - swap_block(get_blk(fs, nod->i_block[EXT2_IND_BLOCK])); |
| - if(nblk <= EXT2_DIND_BLOCK + BLOCKSIZE/4) |
| - return; |
| - /* See comment in swap_goodblocks */ |
| - assert(nod->i_block[EXT2_DIND_BLOCK] != 0); |
| - swap_block(get_blk(fs, nod->i_block[EXT2_DIND_BLOCK])); |
| - for(i = 0; i < BLOCKSIZE/4; i++) |
| - if(nblk > EXT2_IND_BLOCK + BLOCKSIZE/4 + (BLOCKSIZE/4)*i ) |
| - swap_block(get_blk(fs, ((uint32*)get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]))[i])); |
| - if(nblk <= EXT2_IND_BLOCK + BLOCKSIZE/4 + BLOCKSIZE/4 * BLOCKSIZE/4) |
| - return; |
| - /* Adding support for triple indirection */ |
| - b = (uint32*)get_blk(fs,nod->i_block[EXT2_TIND_BLOCK]); |
| - swap_block((uint8 *)b); |
| - for(i=0;i < BLOCKSIZE/4 && !done ; i++) { |
| - b2 = (uint32*)get_blk(fs,b[i]); |
| - swap_block((uint8 *)b2); |
| - for(j=0; j<BLOCKSIZE/4;j++) { |
| - if (nblk > ( EXT2_IND_BLOCK + BLOCKSIZE/4 + |
| - (BLOCKSIZE/4)*(BLOCKSIZE/4) + |
| - i*(BLOCKSIZE/4)*(BLOCKSIZE/4) + |
| - j*(BLOCKSIZE/4)) ) |
| - swap_block(get_blk(fs,b2[j])); |
| - else { |
| - done = 1; |
| - break; |
| - } |
| - } |
| - } |
| - return; |
| -} |
| + fs = malloc(sizeof(*fs)); |
| + if (!fs) |
| + error_msg_and_die("not enough memory for filesystem"); |
| + memset(fs, 0, sizeof(*fs)); |
| + fs->swapit = swapit; |
| + cache_init(&fs->blks, MAX_FREE_CACHE_BLOCKS, blk_elem_val, blk_freed); |
| + cache_init(&fs->gds, MAX_FREE_CACHE_GDS, gd_elem_val, gd_freed); |
| + cache_init(&fs->blkmaps, MAX_FREE_CACHE_BLOCKMAPS, |
| + blkmap_elem_val, blkmap_freed); |
| + cache_init(&fs->inodes, MAX_FREE_CACHE_INODES, |
| + inode_elem_val, inode_freed); |
| + fs->hdlink_cnt = HDLINK_CNT; |
| + fs->hdlinks.hdl = calloc(sizeof(struct hdlink_s), fs->hdlink_cnt); |
| + if (!fs->hdlinks.hdl) |
| + error_msg_and_die("Not enough memory"); |
| + fs->hdlinks.count = 0 ; |
| |
| -// endianness swap of the whole filesystem |
| -static void |
| -swap_goodfs(filesystem *fs) |
| -{ |
| - uint32 i; |
| - for(i = 1; i < fs->sb.s_inodes_count; i++) |
| - { |
| - inode *nod = get_nod(fs, i); |
| - if(nod->i_mode & FM_IFDIR) |
| - { |
| - blockwalker bw; |
| - uint32 bk; |
| - init_bw(&bw); |
| - while((bk = walk_bw(fs, i, &bw, 0, 0)) != WALK_END) |
| - { |
| - directory *d; |
| - uint8 *b; |
| - b = get_blk(fs, bk); |
| - for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + swab16(d->d_rec_len))) |
| - swap_dir(d); |
| - } |
| - } |
| - swap_goodblocks(fs, nod); |
| - swap_nod(nod); |
| - } |
| - for(i=0;i<GRP_NBGROUPS(fs);i++) |
| - swap_gd(&(fs->gd[i])); |
| - swap_sb(&fs->sb); |
| + if (strcmp(fname, "-") == 0) |
| + fs->f = tmpfile(); |
| + else if (srcfile) { |
| + if (fstat(fileno(srcfile), &srcstat)) |
| + perror_msg_and_die("fstat srcfile"); |
| + if (stat(fname, &dststat) == 0 |
| + && srcstat.st_ino == dststat.st_ino |
| + && srcstat.st_dev == dststat.st_dev) |
| + { |
| + // source and destination are the same file, don't |
| + // truncate or copy, just use the file. |
| + fs->f = fopen(fname, "r+b"); |
| + } else { |
| + fs->f = fopen(fname, "w+b"); |
| + if (fs->f) |
| + copy_file(fs, fs->f, srcfile, nbblocks); |
| + } |
| + } else |
| + fs->f = fopen(fname, "w+b"); |
| + if (!fs->f) |
| + perror_msg_and_die("opening %s", fname); |
| + return fs; |
| } |
| |
| +/* Make sure the output file is the right size */ |
| static void |
| -swap_badfs(filesystem *fs) |
| +set_file_size(filesystem *fs) |
| { |
| - uint32 i; |
| - swap_sb(&fs->sb); |
| - for(i=0;i<GRP_NBGROUPS(fs);i++) |
| - swap_gd(&(fs->gd[i])); |
| - for(i = 1; i < fs->sb.s_inodes_count; i++) |
| - { |
| - inode *nod = get_nod(fs, i); |
| - swap_nod(nod); |
| - swap_badblocks(fs, nod); |
| - if(nod->i_mode & FM_IFDIR) |
| - { |
| - blockwalker bw; |
| - uint32 bk; |
| - init_bw(&bw); |
| - while((bk = walk_bw(fs, i, &bw, 0, 0)) != WALK_END) |
| - { |
| - directory *d; |
| - uint8 *b; |
| - b = get_blk(fs, bk); |
| - for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len)) |
| - swap_dir(d); |
| - } |
| - } |
| - } |
| + if (ftruncate(fileno(fs->f), |
| + ((off_t) fs->sb->s_blocks_count) * BLOCKSIZE)) |
| + perror_msg_and_die("set_file_size: ftruncate"); |
| } |
| |
| // initialize an empty filesystem |
| static filesystem * |
| -init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes, uint32 fs_timestamp) |
| +init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes, |
| + uint32 fs_timestamp, uint32 creator_os, int swapit, char *fname) |
| { |
| uint32 i; |
| filesystem *fs; |
| - directory *d; |
| - uint8 * b; |
| + dirwalker dw; |
| uint32 nod, first_block; |
| uint32 nbgroups,nbinodes_per_group,overhead_per_group,free_blocks, |
| free_blocks_per_group,nbblocks_per_group,min_nbgroups; |
| @@ -1906,6 +2545,11 @@ |
| uint32 j; |
| uint8 *bbm,*ibm; |
| inode *itab0; |
| + blk_info *bi; |
| + nod_info *ni; |
| + groupdescriptor *gd; |
| + gd_info *gi; |
| + inode_pos ipos; |
| |
| if(nbresrvd < 0) |
| error_msg_and_die("reserved blocks value is invalid. Note: options have changed, see --help or the man page."); |
| @@ -1919,10 +2563,14 @@ |
| */ |
| min_nbgroups = (nbinodes + INODES_PER_GROUP - 1) / INODES_PER_GROUP; |
| |
| + /* On filesystems with 1k block size, the bootloader area uses a full |
| + * block. For 2048 and up, the superblock can be fitted into block 0. |
| + */ |
| + first_block = (BLOCKSIZE == 1024); |
| + |
| /* nbblocks is the total number of blocks in the filesystem. |
| * a block group can have no more than 8192 blocks. |
| */ |
| - first_block = (BLOCKSIZE == 1024); |
| nbgroups = (nbblocks - first_block + BLOCKS_PER_GROUP - 1) / BLOCKS_PER_GROUP; |
| if(nbgroups < min_nbgroups) nbgroups = min_nbgroups; |
| nbblocks_per_group = rndup((nbblocks - first_block + nbgroups - 1)/nbgroups, 8); |
| @@ -1934,51 +2582,59 @@ |
| gdsz = rndup(nbgroups*sizeof(groupdescriptor),BLOCKSIZE)/BLOCKSIZE; |
| itblsz = nbinodes_per_group * sizeof(inode)/BLOCKSIZE; |
| overhead_per_group = 3 /*sb,bbm,ibm*/ + gdsz + itblsz; |
| - if((uint32)nbblocks - 1 < overhead_per_group * nbgroups) |
| - error_msg_and_die("too much overhead, try fewer inodes or more blocks. Note: options have changed, see --help or the man page."); |
| - free_blocks = nbblocks - overhead_per_group*nbgroups - 1 /*boot block*/; |
| + free_blocks = nbblocks - overhead_per_group*nbgroups - first_block; |
| free_blocks_per_group = nbblocks_per_group - overhead_per_group; |
| + if(free_blocks < 0) |
| + error_msg_and_die("too much overhead, try fewer inodes or more blocks. Note: options have changed, see --help or the man page."); |
| |
| - if(!(fs = (filesystem*)calloc(nbblocks, BLOCKSIZE))) |
| - error_msg_and_die("not enough memory for filesystem"); |
| + fs = alloc_fs(swapit, fname, nbblocks, NULL); |
| + fs->sb = calloc(1, SUPERBLOCK_SIZE); |
| + if (!fs->sb) |
| + error_msg_and_die("error allocating header memory"); |
| |
| // create the superblock for an empty filesystem |
| - fs->sb.s_inodes_count = nbinodes_per_group * nbgroups; |
| - fs->sb.s_blocks_count = nbblocks; |
| - fs->sb.s_r_blocks_count = nbresrvd; |
| - fs->sb.s_free_blocks_count = free_blocks; |
| - fs->sb.s_free_inodes_count = fs->sb.s_inodes_count - EXT2_FIRST_INO + 1; |
| - fs->sb.s_first_data_block = first_block; |
| - fs->sb.s_log_block_size = BLOCKSIZE >> 11; |
| - fs->sb.s_log_frag_size = BLOCKSIZE >> 11; |
| - fs->sb.s_blocks_per_group = nbblocks_per_group; |
| - fs->sb.s_frags_per_group = nbblocks_per_group; |
| - fs->sb.s_inodes_per_group = nbinodes_per_group; |
| - fs->sb.s_wtime = fs_timestamp; |
| - fs->sb.s_magic = EXT2_MAGIC_NUMBER; |
| - fs->sb.s_lastcheck = fs_timestamp; |
| + fs->sb->s_inodes_count = nbinodes_per_group * nbgroups; |
| + fs->sb->s_blocks_count = nbblocks; |
| + fs->sb->s_r_blocks_count = nbresrvd; |
| + fs->sb->s_free_blocks_count = free_blocks; |
| + fs->sb->s_free_inodes_count = fs->sb->s_inodes_count - EXT2_FIRST_INO + 1; |
| + fs->sb->s_first_data_block = first_block; |
| + fs->sb->s_log_block_size = BLOCKSIZE >> 11; |
| + fs->sb->s_log_frag_size = BLOCKSIZE >> 11; |
| + fs->sb->s_blocks_per_group = nbblocks_per_group; |
| + fs->sb->s_frags_per_group = nbblocks_per_group; |
| + fs->sb->s_inodes_per_group = nbinodes_per_group; |
| + fs->sb->s_wtime = fs_timestamp; |
| + fs->sb->s_magic = EXT2_MAGIC_NUMBER; |
| + fs->sb->s_lastcheck = fs_timestamp; |
| + fs->sb->s_creator_os = creator_os; |
| + |
| + set_file_size(fs); |
| |
| // set up groupdescriptors |
| - for(i=0, bbmpos=gdsz+2, ibmpos=bbmpos+1, itblpos=ibmpos+1; |
| + for(i=0, bbmpos=first_block+1+gdsz, ibmpos=bbmpos+1, itblpos=ibmpos+1; |
| i<nbgroups; |
| i++, bbmpos+=nbblocks_per_group, ibmpos+=nbblocks_per_group, itblpos+=nbblocks_per_group) |
| { |
| + gd = get_gd(fs, i, &gi); |
| + |
| if(free_blocks > free_blocks_per_group) { |
| - fs->gd[i].bg_free_blocks_count = free_blocks_per_group; |
| + gd->bg_free_blocks_count = free_blocks_per_group; |
| free_blocks -= free_blocks_per_group; |
| } else { |
| - fs->gd[i].bg_free_blocks_count = free_blocks; |
| + gd->bg_free_blocks_count = free_blocks; |
| free_blocks = 0; // this is the last block group |
| } |
| if(i) |
| - fs->gd[i].bg_free_inodes_count = nbinodes_per_group; |
| + gd->bg_free_inodes_count = nbinodes_per_group; |
| else |
| - fs->gd[i].bg_free_inodes_count = nbinodes_per_group - |
| + gd->bg_free_inodes_count = nbinodes_per_group - |
| EXT2_FIRST_INO + 2; |
| - fs->gd[i].bg_used_dirs_count = 0; |
| - fs->gd[i].bg_block_bitmap = bbmpos; |
| - fs->gd[i].bg_inode_bitmap = ibmpos; |
| - fs->gd[i].bg_inode_table = itblpos; |
| + gd->bg_used_dirs_count = 0; |
| + gd->bg_block_bitmap = bbmpos; |
| + gd->bg_inode_bitmap = ibmpos; |
| + gd->bg_inode_table = itblpos; |
| + put_gd(gi); |
| } |
| |
| /* Mark non-filesystem blocks and inodes as allocated */ |
| @@ -1984,110 +2640,143 @@ |
| /* Mark non-filesystem blocks and inodes as allocated */ |
| /* Mark system blocks and inodes as allocated */ |
| for(i = 0; i<nbgroups;i++) { |
| - |
| /* Block bitmap */ |
| - bbm = get_blk(fs,fs->gd[i].bg_block_bitmap); |
| + gd = get_gd(fs, i, &gi); |
| + bbm = GRP_GET_GROUP_BBM(fs, gd, &bi); |
| //non-filesystem blocks |
| - for(j = fs->gd[i].bg_free_blocks_count |
| + for(j = gd->bg_free_blocks_count |
| + overhead_per_group + 1; j <= BLOCKSIZE * 8; j++) |
| allocate(bbm, j); |
| //system blocks |
| for(j = 1; j <= overhead_per_group; j++) |
| allocate(bbm, j); |
| - |
| + GRP_PUT_GROUP_BBM(bi); |
| + |
| /* Inode bitmap */ |
| - ibm = get_blk(fs,fs->gd[i].bg_inode_bitmap); |
| + ibm = GRP_GET_GROUP_IBM(fs, gd, &bi); |
| //non-filesystem inodes |
| - for(j = fs->sb.s_inodes_per_group+1; j <= BLOCKSIZE * 8; j++) |
| + for(j = fs->sb->s_inodes_per_group+1; j <= BLOCKSIZE * 8; j++) |
| allocate(ibm, j); |
| |
| //system inodes |
| if(i == 0) |
| for(j = 1; j < EXT2_FIRST_INO; j++) |
| allocate(ibm, j); |
| + GRP_PUT_GROUP_IBM(bi); |
| + put_gd(gi); |
| } |
| |
| // make root inode and directory |
| /* We have groups now. Add the root filesystem in group 0 */ |
| /* Also increment the directory count for group 0 */ |
| - fs->gd[0].bg_free_inodes_count--; |
| - fs->gd[0].bg_used_dirs_count = 1; |
| - itab0 = (inode *)get_blk(fs,fs->gd[0].bg_inode_table); |
| - itab0[EXT2_ROOT_INO-1].i_mode = FM_IFDIR | FM_IRWXU | FM_IRGRP | FM_IROTH | FM_IXGRP | FM_IXOTH; |
| - itab0[EXT2_ROOT_INO-1].i_ctime = fs_timestamp; |
| - itab0[EXT2_ROOT_INO-1].i_mtime = fs_timestamp; |
| - itab0[EXT2_ROOT_INO-1].i_atime = fs_timestamp; |
| - itab0[EXT2_ROOT_INO-1].i_size = BLOCKSIZE; |
| - itab0[EXT2_ROOT_INO-1].i_links_count = 2; |
| - |
| - if(!(b = get_workblk())) |
| - error_msg_and_die("get_workblk() failed."); |
| - d = (directory*)b; |
| - d->d_inode = EXT2_ROOT_INO; |
| - d->d_rec_len = sizeof(directory)+4; |
| - d->d_name_len = 1; |
| - strcpy(d->d_name, "."); |
| - d = (directory*)(b + d->d_rec_len); |
| - d->d_inode = EXT2_ROOT_INO; |
| - d->d_rec_len = BLOCKSIZE - (sizeof(directory)+4); |
| - d->d_name_len = 2; |
| - strcpy(d->d_name, ".."); |
| - extend_blk(fs, EXT2_ROOT_INO, b, 1); |
| + gd = get_gd(fs, 0, &gi); |
| + gd->bg_free_inodes_count--; |
| + gd->bg_used_dirs_count = 1; |
| + put_gd(gi); |
| + itab0 = get_nod(fs, EXT2_ROOT_INO, &ni); |
| + itab0->i_mode = FM_IFDIR | FM_IRWXU | FM_IRGRP | FM_IROTH | FM_IXGRP | FM_IXOTH; |
| + itab0->i_ctime = fs_timestamp; |
| + itab0->i_mtime = fs_timestamp; |
| + itab0->i_atime = fs_timestamp; |
| + itab0->i_size = BLOCKSIZE; |
| + itab0->i_links_count = 2; |
| + put_nod(ni); |
| + |
| + new_dir(fs, EXT2_ROOT_INO, ".", 1, &dw); |
| + shrink_dir(&dw, EXT2_ROOT_INO, "..", 2); |
| + next_dir(&dw); // Force the data into the buffer |
| + inode_pos_init(fs, &ipos, EXT2_ROOT_INO, INODE_POS_EXTEND, NULL); |
| + extend_inode_blk(fs, &ipos, dir_data(&dw), 1); |
| + inode_pos_finish(fs, &ipos); |
| + put_dir(&dw); |
| |
| - // make lost+found directory and reserve blocks |
| - if(fs->sb.s_r_blocks_count) |
| + // make lost+found directory |
| + if(fs->sb->s_r_blocks_count) |
| { |
| - nod = mkdir_fs(fs, EXT2_ROOT_INO, "lost+found", FM_IRWXU, 0, 0, fs_timestamp, fs_timestamp); |
| + inode *node; |
| + uint8 *b; |
| + |
| + nod = mkdir_fs(fs, EXT2_ROOT_INO, "lost+found", FM_IRWXU, |
| + 0, 0, fs_timestamp, fs_timestamp); |
| + b = get_workblk(); |
| memset(b, 0, BLOCKSIZE); |
| ((directory*)b)->d_rec_len = BLOCKSIZE; |
| - /* We run into problems with e2fsck if directory lost+found grows |
| - * bigger than this. Need to find out why this happens - sundar |
| - */ |
| - if (fs->sb.s_r_blocks_count > fs->sb.s_blocks_count * MAX_RESERVED_BLOCKS ) |
| - fs->sb.s_r_blocks_count = fs->sb.s_blocks_count * MAX_RESERVED_BLOCKS; |
| - for(i = 1; i < fs->sb.s_r_blocks_count; i++) |
| - extend_blk(fs, nod, b, 1); |
| - get_nod(fs, nod)->i_size = fs->sb.s_r_blocks_count * BLOCKSIZE; |
| + inode_pos_init(fs, &ipos, nod, INODE_POS_EXTEND, NULL); |
| + // It is always 16 blocks to start out with |
| + for(i = 1; i < 16; i++) |
| + extend_inode_blk(fs, &ipos, b, 1); |
| + inode_pos_finish(fs, &ipos); |
| + free_workblk(b); |
| + node = get_nod(fs, nod, &ni); |
| + node->i_size = 16 * BLOCKSIZE; |
| + put_nod(ni); |
| } |
| - free_workblk(b); |
| |
| // administrative info |
| - fs->sb.s_state = 1; |
| - fs->sb.s_max_mnt_count = 20; |
| + fs->sb->s_state = 1; |
| + fs->sb->s_max_mnt_count = 20; |
| |
| // options for me |
| - if(holes) |
| - fs->sb.s_reserved[200] |= OP_HOLES; |
| + fs->holes = holes; |
| |
| return fs; |
| } |
| |
| // loads a filesystem from disk |
| static filesystem * |
| -load_fs(FILE * fh, int swapit) |
| +load_fs(FILE *fh, int swapit, char *fname) |
| { |
| - size_t fssize; |
| + off_t fssize; |
| filesystem *fs; |
| - if((fseek(fh, 0, SEEK_END) < 0) || ((ssize_t)(fssize = ftell(fh)) == -1)) |
| + |
| + if((fseek(fh, 0, SEEK_END) < 0) || ((fssize = ftello(fh)) == -1)) |
| perror_msg_and_die("input filesystem image"); |
| rewind(fh); |
| - fssize = (fssize + BLOCKSIZE - 1) / BLOCKSIZE; |
| + if ((fssize % BLOCKSIZE) != 0) |
| + error_msg_and_die("Input file not a multiple of block size"); |
| + fssize /= BLOCKSIZE; |
| if(fssize < 16) // totally arbitrary |
| error_msg_and_die("too small filesystem"); |
| - if(!(fs = (filesystem*)calloc(fssize, BLOCKSIZE))) |
| - error_msg_and_die("not enough memory for filesystem"); |
| - if(fread(fs, BLOCKSIZE, fssize, fh) != fssize) |
| - perror_msg_and_die("input filesystem image"); |
| + fs = alloc_fs(swapit, fname, fssize, fh); |
| + |
| + /* Read and check the superblock, then read the superblock |
| + * and all the group descriptors */ |
| + fs->sb = malloc(SUPERBLOCK_SIZE); |
| + if (!fs->sb) |
| + error_msg_and_die("error allocating header memory"); |
| + if (fseek(fs->f, SUPERBLOCK_OFFSET, SEEK_SET)) |
| + perror_msg_and_die("fseek"); |
| + if (fread(fs->sb, SUPERBLOCK_SIZE, 1, fs->f) != 1) |
| + perror_msg_and_die("fread filesystem image superblock"); |
| if(swapit) |
| - swap_badfs(fs); |
| - if(fs->sb.s_rev_level || (fs->sb.s_magic != EXT2_MAGIC_NUMBER)) |
| + swap_sb(fs->sb); |
| + |
| + if((fs->sb->s_rev_level > 1) || (fs->sb->s_magic != EXT2_MAGIC_NUMBER)) |
| error_msg_and_die("not a suitable ext2 filesystem"); |
| + if (fs->sb->s_rev_level > 0) { |
| + if (fs->sb->s_first_ino != EXT2_GOOD_OLD_FIRST_INO) |
| + error_msg_and_die("First inode incompatible"); |
| + if (fs->sb->s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) |
| + error_msg_and_die("inode size incompatible"); |
| + if (fs->sb->s_feature_compat) |
| + error_msg_and_die("Unsupported compat features"); |
| + if (fs->sb->s_feature_incompat) |
| + error_msg_and_die("Unsupported incompat features"); |
| + if (fs->sb->s_feature_ro_compat |
| + & ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE) |
| + error_msg_and_die("Unsupported ro compat features"); |
| + } |
| + |
| + set_file_size(fs); |
| return fs; |
| } |
| |
| static void |
| free_fs(filesystem *fs) |
| { |
| + free(fs->hdlinks.hdl); |
| + fclose(fs->f); |
| + free(fs->sb); |
| free(fs); |
| } |
| |
| @@ -2123,16 +2812,23 @@ |
| { |
| blockwalker bw; |
| uint32 bk; |
| - int32 fsize = get_nod(fs, nod)->i_size; |
| + nod_info *ni; |
| + inode *node = get_nod(fs, nod, &ni); |
| + int32 fsize = node->i_size; |
| + blk_info *bi; |
| + |
| init_bw(&bw); |
| while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END) |
| { |
| if(fsize <= 0) |
| error_msg_and_die("wrong size while saving inode %d", nod); |
| - if(fwrite(get_blk(fs, bk), (fsize > BLOCKSIZE) ? BLOCKSIZE : fsize, 1, f) != 1) |
| + if(fwrite(get_blk(fs, bk, &bi), |
| + (fsize > BLOCKSIZE) ? BLOCKSIZE : fsize, 1, f) != 1) |
| error_msg_and_die("error while saving inode %d", nod); |
| + put_blk(bi); |
| fsize -= BLOCKSIZE; |
| } |
| + put_nod(ni); |
| } |
| |
| |
| @@ -2141,8 +2837,11 @@ |
| 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]; |
| + nod_info *ni; |
| + inode *node = get_nod(fs, nod, &ni); |
| + minor = ((uint8*)node->i_block)[0]; |
| + major = ((uint8*)node->i_block)[1]; |
| + put_nod(ni); |
| printf("major: %d, minor: %d\n", major, minor); |
| } |
| |
| @@ -2157,17 +2856,15 @@ |
| 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)) |
| + dirwalker dw; |
| + for (d = get_dir(fs, bk, &dw); d; d = next_dir(&dw)) |
| if(d->d_inode) |
| { |
| - int i; |
| printf("entry '"); |
| - for(i = 0; i < d->d_name_len; i++) |
| - putchar(d->d_name[i]); |
| + fwrite(dir_name(&dw), 1, d->d_name_len, stdout); |
| printf("' (inode %d): rec_len: %d (name_len: %d)\n", d->d_inode, d->d_rec_len, d->d_name_len); |
| } |
| + put_dir(&dw); |
| } |
| } |
| |
| @@ -2175,14 +2872,18 @@ |
| static void |
| print_link(filesystem *fs, uint32 nod) |
| { |
| - if(!get_nod(fs, nod)->i_blocks) |
| - printf("links to '%s'\n", (char*)get_nod(fs, nod)->i_block); |
| + nod_info *ni; |
| + inode *node = get_nod(fs, nod, &ni); |
| + |
| + if(!node->i_blocks) |
| + printf("links to '%s'\n", (char*)node->i_block); |
| else |
| { |
| printf("links to '"); |
| write_blocks(fs, nod, stdout); |
| printf("'\n"); |
| } |
| + put_nod(ni); |
| } |
| |
| // make a ls-like printout of permissions |
| @@ -2251,8 +2952,13 @@ |
| { |
| char *s; |
| char perms[11]; |
| - if(!get_nod(fs, nod)->i_mode) |
| - return; |
| + nod_info *ni; |
| + inode *node = get_nod(fs, nod, &ni); |
| + blk_info *bi; |
| + gd_info *gi; |
| + |
| + if(!node->i_mode) |
| + goto out; |
| switch(nod) |
| { |
| case EXT2_BAD_INO: |
| @@ -2274,15 +2980,18 @@ |
| default: |
| s = (nod >= EXT2_FIRST_INO) ? "normal" : "unknown reserved"; |
| } |
| - printf("inode %d (%s, %d links): ", nod, s, get_nod(fs, nod)->i_links_count); |
| - if(!allocated(GRP_GET_INODE_BITMAP(fs,nod), GRP_IBM_OFFSET(fs,nod))) |
| + printf("inode %d (%s, %d links): ", nod, s, node->i_links_count); |
| + if(!allocated(GRP_GET_INODE_BITMAP(fs,nod,&bi,&gi), GRP_IBM_OFFSET(fs,nod))) |
| { |
| + GRP_PUT_INODE_BITMAP(bi,gi); |
| printf("unallocated\n"); |
| - return; |
| + goto out; |
| } |
| - 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) |
| + GRP_PUT_INODE_BITMAP(bi,gi); |
| + make_perms(node->i_mode, perms); |
| + printf("%s, size: %d byte%s (%d block%s)\n", perms, |
| + plural(node->i_size), plural(node->i_blocks / INOBLK)); |
| + switch(node->i_mode & FM_IFMT) |
| { |
| case FM_IFSOCK: |
| list_blocks(fs, nod); |
| @@ -2310,6 +3019,8 @@ |
| list_blocks(fs, nod); |
| } |
| printf("Done with inode %d\n",nod); |
| +out: |
| + put_nod(ni); |
| } |
| |
| // describes various fields in a filesystem |
| @@ -2317,49 +3028,65 @@ |
| print_fs(filesystem *fs) |
| { |
| uint32 i; |
| + blk_info *bi; |
| + groupdescriptor *gd; |
| + gd_info *gi; |
| uint8 *ibm; |
| |
| printf("%d blocks (%d free, %d reserved), first data block: %d\n", |
| - fs->sb.s_blocks_count, fs->sb.s_free_blocks_count, |
| - fs->sb.s_r_blocks_count, fs->sb.s_first_data_block); |
| - printf("%d inodes (%d free)\n", fs->sb.s_inodes_count, |
| - fs->sb.s_free_inodes_count); |
| + 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); |
| + fs->sb->s_log_block_size ? (fs->sb->s_log_block_size << 11) : 1024, |
| + fs->sb->s_log_frag_size ? (fs->sb->s_log_frag_size << 11) : 1024); |
| printf("number of groups: %d\n",GRP_NBGROUPS(fs)); |
| printf("%d blocks per group,%d frags per group,%d inodes per group\n", |
| - fs->sb.s_blocks_per_group, fs->sb.s_frags_per_group, |
| - fs->sb.s_inodes_per_group); |
| + fs->sb->s_blocks_per_group, fs->sb->s_frags_per_group, |
| + fs->sb->s_inodes_per_group); |
| printf("Size of inode table: %d blocks\n", |
| - (int)(fs->sb.s_inodes_per_group * sizeof(inode) / BLOCKSIZE)); |
| + (int)(fs->sb->s_inodes_per_group * sizeof(inode) / BLOCKSIZE)); |
| for (i = 0; i < GRP_NBGROUPS(fs); i++) { |
| printf("Group No: %d\n", i+1); |
| + gd = get_gd(fs, i, &gi); |
| printf("block bitmap: block %d,inode bitmap: block %d, inode table: block %d\n", |
| - fs->gd[i].bg_block_bitmap, fs->gd[i].bg_inode_bitmap, |
| - fs->gd[i].bg_inode_table); |
| + gd->bg_block_bitmap, |
| + gd->bg_inode_bitmap, |
| + gd->bg_inode_table); |
| printf("block bitmap allocation:\n"); |
| - print_bm(GRP_GET_GROUP_BBM(fs, i),fs->sb.s_blocks_per_group); |
| + print_bm(GRP_GET_GROUP_BBM(fs, gd, &bi),fs->sb->s_blocks_per_group); |
| + GRP_PUT_GROUP_BBM(bi); |
| printf("inode bitmap allocation:\n"); |
| - ibm = GRP_GET_GROUP_IBM(fs, i); |
| - print_bm(ibm, fs->sb.s_inodes_per_group); |
| - for (i = 1; i <= fs->sb.s_inodes_per_group; i++) |
| + ibm = GRP_GET_GROUP_IBM(fs, gd, &bi); |
| + print_bm(ibm, fs->sb->s_inodes_per_group); |
| + for (i = 1; i <= fs->sb->s_inodes_per_group; i++) |
| if (allocated(ibm, i)) |
| print_inode(fs, i); |
| + GRP_PUT_GROUP_IBM(bi); |
| + put_gd(gi); |
| } |
| } |
| |
| static void |
| -dump_fs(filesystem *fs, FILE * fh, int swapit) |
| +finish_fs(filesystem *fs) |
| { |
| - uint32 nbblocks = fs->sb.s_blocks_count; |
| - fs->sb.s_reserved[200] = 0; |
| - if(swapit) |
| - swap_goodfs(fs); |
| - if(fwrite(fs, BLOCKSIZE, nbblocks, fh) < nbblocks) |
| - perror_msg_and_die("output filesystem image"); |
| - if(swapit) |
| - swap_badfs(fs); |
| + if (cache_flush(&fs->inodes)) |
| + error_msg_and_die("entry mismatch on inode cache flush"); |
| + if (cache_flush(&fs->blkmaps)) |
| + error_msg_and_die("entry mismatch on blockmap cache flush"); |
| + if (cache_flush(&fs->gds)) |
| + error_msg_and_die("entry mismatch on gd cache flush"); |
| + if (cache_flush(&fs->blks)) |
| + error_msg_and_die("entry mismatch on block cache flush"); |
| + if(fs->swapit) |
| + swap_sb(fs->sb); |
| + if (fseek(fs->f, SUPERBLOCK_OFFSET, SEEK_SET)) |
| + perror_msg_and_die("fseek"); |
| + if(fwrite(fs->sb, SUPERBLOCK_SIZE, 1, fs->f) != 1) |
| + perror_msg_and_die("output filesystem superblock"); |
| + if(fs->swapit) |
| + swap_sb(fs->sb); |
| } |
| |
| static void |
| @@ -2419,10 +3146,12 @@ |
| " -x, --starting-image <image>\n" |
| " -d, --root <directory>\n" |
| " -D, --devtable <file>\n" |
| + " -B, --block-size <bytes>\n" |
| " -b, --size-in-blocks <blocks>\n" |
| " -i, --bytes-per-inode <bytes per inode>\n" |
| " -N, --number-of-inodes <number of inodes>\n" |
| " -m, --reserved-percentage <percentage of blocks to reserve>\n" |
| + " -o, --creator-os <os> 'linux' (default), 'hurd', 'freebsd' or number.\n" |
| " -g, --block-map <path> Generate a block map file for this path.\n" |
| " -e, --fill-value <value> Fill unallocated blocks with value.\n" |
| " -z, --allow-holes Allow files with holes.\n" |
| @@ -2444,15 +3173,34 @@ |
| extern char* optarg; |
| extern int optind, opterr, optopt; |
| |
| +// parse the value for -o <os> |
| +int |
| +lookup_creator_os(const char *name) |
| +{ |
| + if (isdigit (*name)) |
| + return atoi(name); |
| + else if (strcasecmp(name, "linux") == 0) |
| + return EXT2_OS_LINUX; |
| + else if (strcasecmp(name, "GNU") == 0 || strcasecmp(name, "hurd") == 0) |
| + return EXT2_OS_HURD; |
| + else if (strcasecmp(name, "freebsd") == 0) |
| + return EXT2_OS_FREEBSD; |
| + else if (strcasecmp(name, "lites") == 0) |
| + return EXT2_OS_LITES; |
| + else |
| + return EXT2_OS_LINUX; |
| +} |
| + |
| int |
| main(int argc, char **argv) |
| { |
| - int nbblocks = -1; |
| + long long nbblocks = -1; |
| int nbinodes = -1; |
| int nbresrvd = -1; |
| float bytes_per_inode = -1; |
| float reserved_frac = -1; |
| int fs_timestamp = -1; |
| + int creator_os = CREATOR_OS; |
| char * fsout = "-"; |
| char * fsin = 0; |
| char * dopt[MAX_DOPT]; |
| @@ -2466,6 +3214,7 @@ |
| int squash_perms = 0; |
| uint16 endian = 1; |
| int bigendian = !*(char*)&endian; |
| + char *volumelabel = NULL; |
| filesystem *fs; |
| int i; |
| int c; |
| @@ -2476,13 +3225,16 @@ |
| { "starting-image", required_argument, NULL, 'x' }, |
| { "root", required_argument, NULL, 'd' }, |
| { "devtable", required_argument, NULL, 'D' }, |
| + { "block-size", required_argument, NULL, 'B' }, |
| { "size-in-blocks", required_argument, NULL, 'b' }, |
| { "bytes-per-inode", required_argument, NULL, 'i' }, |
| { "number-of-inodes", required_argument, NULL, 'N' }, |
| + { "volume-label", required_argument, NULL, 'L' }, |
| { "reserved-percentage", required_argument, NULL, 'm' }, |
| + { "creator-os", required_argument, NULL, 'o' }, |
| { "block-map", required_argument, NULL, 'g' }, |
| { "fill-value", required_argument, NULL, 'e' }, |
| - { "allow-holes", no_argument, NULL, 'z' }, |
| + { "allow-holes", no_argument, NULL, 'z' }, |
| { "faketime", no_argument, NULL, 'f' }, |
| { "squash", no_argument, NULL, 'q' }, |
| { "squash-uids", no_argument, NULL, 'U' }, |
| @@ -2495,11 +3247,11 @@ |
| |
| app_name = argv[0]; |
| |
| - while((c = getopt_long(argc, argv, "x:d:D:b:i:N:m:g:e:zfqUPhVv", longopts, NULL)) != EOF) { |
| + while((c = getopt_long(argc, argv, "x:d:D:B:b:i:N:L:m:o:g:e:zfqUPhVv", longopts, NULL)) != EOF) { |
| #else |
| app_name = argv[0]; |
| |
| - while((c = getopt(argc, argv, "x:d:D:b:i:N:m:g:e:zfqUPhVv")) != EOF) { |
| + while((c = getopt(argc, argv, "x:d:D:B:b:i:N:L:m:o:g:e:zfqUPhVv")) != EOF) { |
| #endif /* HAVE_GETOPT_LONG */ |
| switch(c) |
| { |
| @@ -2510,6 +3262,9 @@ |
| case 'D': |
| dopt[didx++] = optarg; |
| break; |
| + case 'B': |
| + blocksize = SI_atof(optarg); |
| + break; |
| case 'b': |
| nbblocks = SI_atof(optarg); |
| break; |
| @@ -2519,9 +3274,15 @@ |
| case 'N': |
| nbinodes = SI_atof(optarg); |
| break; |
| + case 'L': |
| + volumelabel = optarg; |
| + break; |
| case 'm': |
| reserved_frac = SI_atof(optarg) / 100; |
| break; |
| + case 'o': |
| + creator_os = lookup_creator_os(optarg); |
| + break; |
| case 'g': |
| gopt[gidx++] = optarg; |
| break; |
| @@ -2565,21 +3326,21 @@ |
| error_msg_and_die("Not enough arguments. Try --help or else see the man page."); |
| fsout = argv[optind]; |
| |
| - hdlinks.hdl = (struct hdlink_s *)malloc(hdlink_cnt * sizeof(struct hdlink_s)); |
| - if (!hdlinks.hdl) |
| - error_msg_and_die("Not enough memory"); |
| - hdlinks.count = 0 ; |
| + if(blocksize != 1024 && blocksize != 2048 && blocksize != 4096) |
| + error_msg_and_die("Valid block sizes: 1024, 2048 or 4096."); |
| + if(creator_os < 0) |
| + error_msg_and_die("Creator OS unknown."); |
| |
| if(fsin) |
| { |
| if(strcmp(fsin, "-")) |
| { |
| FILE * fh = xfopen(fsin, "rb"); |
| - fs = load_fs(fh, bigendian); |
| + fs = load_fs(fh, bigendian, fsout); |
| fclose(fh); |
| } |
| else |
| - fs = load_fs(stdin, bigendian); |
| + fs = load_fs(stdin, bigendian, fsout); |
| } |
| else |
| { |
| @@ -2609,16 +3370,29 @@ |
| } |
| if(fs_timestamp == -1) |
| fs_timestamp = time(NULL); |
| - fs = init_fs(nbblocks, nbinodes, nbresrvd, holes, fs_timestamp); |
| + fs = init_fs(nbblocks, nbinodes, nbresrvd, holes, |
| + fs_timestamp, creator_os, bigendian, fsout); |
| } |
| + if (volumelabel != NULL) |
| + strncpy((char *)fs->sb->s_volume_name, volumelabel, |
| + sizeof(fs->sb->s_volume_name)); |
| |
| populate_fs(fs, dopt, didx, squash_uids, squash_perms, fs_timestamp, NULL); |
| |
| if(emptyval) { |
| uint32 b; |
| - for(b = 1; b < fs->sb.s_blocks_count; b++) |
| - if(!allocated(GRP_GET_BLOCK_BITMAP(fs,b),GRP_BBM_OFFSET(fs,b))) |
| - memset(get_blk(fs, b), emptyval, BLOCKSIZE); |
| + for(b = 1; b < fs->sb->s_blocks_count; b++) { |
| + blk_info *bi; |
| + gd_info *gi; |
| + if(!allocated(GRP_GET_BLOCK_BITMAP(fs,b,&bi,&gi), |
| + GRP_BBM_OFFSET(fs,b))) { |
| + blk_info *bi2; |
| + memset(get_blk(fs, b, &bi2), emptyval, |
| + BLOCKSIZE); |
| + put_blk(bi2); |
| + } |
| + GRP_PUT_BLOCK_BITMAP(bi,gi); |
| + } |
| } |
| if(verbose) |
| print_fs(fs); |
| @@ -2628,24 +3402,22 @@ |
| char fname[MAX_FILENAME]; |
| char *p; |
| FILE *fh; |
| + nod_info *ni; |
| if(!(nod = find_path(fs, EXT2_ROOT_INO, gopt[i]))) |
| error_msg_and_die("path %s not found in filesystem", gopt[i]); |
| while((p = strchr(gopt[i], '/'))) |
| *p = '_'; |
| SNPRINTF(fname, MAX_FILENAME-1, "%s.blk", gopt[i]); |
| fh = xfopen(fname, "wb"); |
| - fprintf(fh, "%d:", get_nod(fs, nod)->i_size); |
| + fprintf(fh, "%d:", get_nod(fs, nod, &ni)->i_size); |
| + put_nod(ni); |
| flist_blocks(fs, nod, fh); |
| fclose(fh); |
| } |
| - if(strcmp(fsout, "-")) |
| - { |
| - FILE * fh = xfopen(fsout, "wb"); |
| - dump_fs(fs, fh, bigendian); |
| - fclose(fh); |
| - } |
| - else |
| - dump_fs(fs, stdout, bigendian); |
| + finish_fs(fs); |
| + if(strcmp(fsout, "-") == 0) |
| + copy_file(fs, stdout, fs->f, fs->sb->s_blocks_count); |
| + |
| free_fs(fs); |
| return 0; |
| } |
| Index: genext2fs-1.4.1/cache.h |
| =================================================================== |
| --- /dev/null |
| +++ genext2fs-1.4.1/cache.h |
| @@ -0,0 +1,128 @@ |
| +#ifndef __CACHE_H__ |
| +#define __CACHE_H__ |
| + |
| +#include "list.h" |
| + |
| +#define CACHE_LISTS 256 |
| + |
| +typedef struct |
| +{ |
| + list_elem link; |
| + list_elem lru_link; |
| +} cache_link; |
| + |
| +typedef struct |
| +{ |
| + /* LRU list holds unused items */ |
| + unsigned int lru_entries; |
| + list_elem lru_list; |
| + unsigned int max_free_entries; |
| + |
| + unsigned int entries; |
| + list_elem lists[CACHE_LISTS]; |
| + unsigned int (*elem_val)(cache_link *elem); |
| + void (*freed)(cache_link *elem); |
| +} listcache; |
| + |
| +static inline void |
| +cache_add(listcache *c, cache_link *elem) |
| +{ |
| + unsigned int hash = c->elem_val(elem) % CACHE_LISTS; |
| + int delcount = c->lru_entries - c->max_free_entries; |
| + |
| + if (delcount > 0) { |
| + /* Delete some unused items. */ |
| + list_elem *lru, *next; |
| + cache_link *l; |
| + list_for_each_elem_safe(&c->lru_list, lru, next) { |
| + l = container_of(lru, cache_link, lru_link); |
| + list_del(lru); |
| + list_del(&l->link); |
| + c->entries--; |
| + c->lru_entries--; |
| + c->freed(l); |
| + delcount--; |
| + if (delcount <= 0) |
| + break; |
| + } |
| + } |
| + |
| + c->entries++; |
| + list_item_init(&elem->lru_link); /* Mark it not in the LRU list */ |
| + list_add_after(&c->lists[hash], &elem->link); |
| +} |
| + |
| +static inline void |
| +cache_item_set_unused(listcache *c, cache_link *elem) |
| +{ |
| + list_add_before(&c->lru_list, &elem->lru_link); |
| + c->lru_entries++; |
| +} |
| + |
| +static inline cache_link * |
| +cache_find(listcache *c, unsigned int val) |
| +{ |
| + unsigned int hash = val % CACHE_LISTS; |
| + list_elem *elem; |
| + |
| + list_for_each_elem(&c->lists[hash], elem) { |
| + cache_link *l = container_of(elem, cache_link, link); |
| + if (c->elem_val(l) == val) { |
| + if (!list_empty(&l->lru_link)) { |
| + /* It's in the unused list, remove it. */ |
| + list_del(&l->lru_link); |
| + list_item_init(&l->lru_link); |
| + c->lru_entries--; |
| + } |
| + return l; |
| + } |
| + } |
| + return NULL; |
| +} |
| + |
| +static inline int |
| +cache_flush(listcache *c) |
| +{ |
| + list_elem *elem, *next; |
| + cache_link *l; |
| + int i; |
| + |
| + list_for_each_elem_safe(&c->lru_list, elem, next) { |
| + l = container_of(elem, cache_link, lru_link); |
| + list_del(elem); |
| + list_del(&l->link); |
| + c->entries--; |
| + c->lru_entries--; |
| + c->freed(l); |
| + } |
| + |
| + for (i = 0; i < CACHE_LISTS; i++) { |
| + list_for_each_elem_safe(&c->lists[i], elem, next) { |
| + l = container_of(elem, cache_link, link); |
| + list_del(&l->link); |
| + c->entries--; |
| + c->freed(l); |
| + } |
| + } |
| + |
| + return c->entries || c->lru_entries; |
| +} |
| + |
| +static inline void |
| +cache_init(listcache *c, unsigned int max_free_entries, |
| + unsigned int (*elem_val)(cache_link *elem), |
| + void (*freed)(cache_link *elem)) |
| +{ |
| + int i; |
| + |
| + c->entries = 0; |
| + c->lru_entries = 0; |
| + c->max_free_entries = max_free_entries; |
| + list_init(&c->lru_list); |
| + for (i = 0; i < CACHE_LISTS; i++) |
| + list_init(&c->lists[i]); |
| + c->elem_val = elem_val; |
| + c->freed = freed; |
| +} |
| + |
| +#endif /* __CACHE_H__ */ |
| Index: genext2fs-1.4.1/list.h |
| =================================================================== |
| --- /dev/null |
| +++ genext2fs-1.4.1/list.h |
| @@ -0,0 +1,78 @@ |
| +#ifndef __LIST_H__ |
| +#define __LIST_H__ |
| + |
| +#if STDC_HEADERS |
| +# include <stdlib.h> |
| +# include <stddef.h> |
| +#else |
| +# if HAVE_STDLIB_H |
| +# include <stdlib.h> |
| +# endif |
| +# if HAVE_STDDEF_H |
| +# include <stddef.h> |
| +# endif |
| +#endif |
| + |
| +#ifndef offsetof |
| +#define offsetof(st, m) \ |
| + ((size_t) ( (char *)&((st *)(0))->m - (char *)0 )) |
| +#endif |
| + |
| +#define container_of(ptr, type, member) ({ \ |
| + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ |
| + (type *)( (char *)__mptr - offsetof(type,member) );}) |
| + |
| +typedef struct list_elem |
| +{ |
| + struct list_elem *next; |
| + struct list_elem *prev; |
| +} list_elem; |
| + |
| +static inline void list_init(list_elem *list) |
| +{ |
| + list->next = list; |
| + list->prev = list; |
| +} |
| + |
| +static inline void list_add_after(list_elem *pos, list_elem *elem) |
| +{ |
| + elem->next = pos->next; |
| + elem->prev = pos; |
| + pos->next->prev = elem; |
| + pos->next = elem; |
| +} |
| + |
| +static inline void list_add_before(list_elem *pos, list_elem *elem) |
| +{ |
| + elem->prev = pos->prev; |
| + elem->next = pos; |
| + pos->prev->next = elem; |
| + pos->prev = elem; |
| +} |
| + |
| +static inline void list_del(list_elem *elem) |
| +{ |
| + elem->next->prev = elem->prev; |
| + elem->prev->next = elem->next; |
| +} |
| + |
| +static inline void list_item_init(list_elem *elem) |
| +{ |
| + elem->next = elem; |
| + elem->prev = elem; |
| +} |
| + |
| +static inline int list_empty(list_elem *elem) |
| +{ |
| + return elem->next == elem; |
| +} |
| + |
| +#define list_for_each_elem(list, curr) \ |
| + for ((curr) = (list)->next; (curr) != (list); (curr) = (curr)->next) |
| + |
| +#define list_for_each_elem_safe(list, curr, next) \ |
| + for ((curr) = (list)->next, (next) = (curr)->next; \ |
| + (curr) != (list); \ |
| + (curr) = (next), (next) = (curr)->next) |
| + |
| +#endif /* __LIST_H__ */ |