diff options
author | H. Peter Anvin <hpa@zytor.com> | 2009-12-28 17:38:48 -0800 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2009-12-28 17:38:48 -0800 |
commit | 22d83c14f4d916e65ffee040468a7f944fdd30d7 (patch) | |
tree | 0396b906e78c6f21a55b20b932df95b36caa148f /core | |
parent | 078bd1b56a512c1dac52020d23382c1bdb37056d (diff) | |
parent | b5839067e5fa11af7e76a6f6f87ef914ba9913d1 (diff) | |
download | syslinux-22d83c14f4d916e65ffee040468a7f944fdd30d7.tar.gz |
Merge commit 'liu/master' into fsc
Resolved Conflicts:
core/fs.c
core/fs/ext2/ext2.c
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'core')
-rw-r--r-- | core/cache.c | 51 | ||||
-rw-r--r-- | core/comboot.inc | 14 | ||||
-rw-r--r-- | core/dir.c | 7 | ||||
-rw-r--r-- | core/diskio.c | 56 | ||||
-rw-r--r-- | core/diskstart.inc | 1 | ||||
-rw-r--r-- | core/fs.c | 104 | ||||
-rw-r--r-- | core/fs/ext2/bmap.c | 185 | ||||
-rw-r--r-- | core/fs/ext2/ext2.c | 848 | ||||
-rw-r--r-- | core/fs/ext2/ext2_fs.h | 69 | ||||
-rw-r--r-- | core/fs/fat/fat.c | 1045 | ||||
-rw-r--r-- | core/fs/fat/fat_fs.h | 33 | ||||
-rw-r--r-- | core/fs/iso9660/iso9660.c | 745 | ||||
-rw-r--r-- | core/fs/iso9660/iso9660_fs.h | 8 | ||||
-rw-r--r-- | core/fs/pxe/dhcp_option.c | 40 | ||||
-rw-r--r-- | core/fs/pxe/pxe.c | 3 | ||||
-rw-r--r-- | core/include/core.h | 5 | ||||
-rw-r--r-- | core/include/disk.h | 1 | ||||
-rw-r--r-- | core/include/fs.h | 89 | ||||
-rw-r--r-- | core/malloc.c | 216 |
19 files changed, 1827 insertions, 1693 deletions
diff --git a/core/cache.c b/core/cache.c index c55f1603..415becff 100644 --- a/core/cache.c +++ b/core/cache.c @@ -1,24 +1,18 @@ -#include "core.h" -#include "cache.h" +/* + * core/cache.c: A simple LRU-based cache implementation. + * + */ + #include <stdio.h> #include <string.h> +#include "core.h" +#include "cache.h" -/** - * Each CachePtr contains: - * - Block pointer - * - LRU previous pointer - * - LRU next pointer - * - Block data buffer address - * - * The cache buffer are pointed to by a cache_head structure. - */ - -/** - * cache_init: - * - * Initialize the cache data structres. - * regs->eax.l stores the block size(in bits not bytes) +/* + * Initialize the cache data structres. the _block_size_shift_ specify + * the block size, which is 512 byte for FAT fs of the current + * implementation since the block(cluster) size in FAT is a bit big. * */ void cache_init(struct device *dev, int block_size_shift) @@ -50,25 +44,10 @@ void cache_init(struct device *dev, int block_size_shift) } -/** - * get_cache_block: - * +/* * Check for a particular BLOCK in the block cache, * and if it is already there, just do nothing and return; - * otherwise load it and updata the relative cache - * structre with data pointer. - * - * it's a test version for my start of merging extlinux into core. - * and after I have figured out how to handle the relations between - * rm and pm, c and asm, we call call it from C file, so no need - * com32sys_t *regs any more. - * - * I just found that I was tring to do a stupid thing! - * I haven't change the fs code to c, so for now the cache is based - * on SECTOR SIZE but not block size. While we can fix it easily by - * make the block size be the sector size. - * - * @return: the data stores at gs:si + * otherwise load it from disk and updata the LRU link. * */ struct cache_struct* get_cache_block(struct device *dev, block_t block) @@ -136,7 +115,7 @@ struct cache_struct* get_cache_block(struct device *dev, block_t block) } -/** +/* * Just print the sector, and according the LRU algorithm, * Left most value is the most least secotr, and Right most * value is the most Recent sector. I see it's a Left Right Used @@ -148,7 +127,7 @@ void print_cache(struct device *dev) struct cache_struct *cs = dev->cache_head; for (; i < dev->cache_entries; i++) { cs = cs->next; - printf("%d(%p) ", cs->block, cs->data); + printf("%d(%p)\n", cs->block, cs->data); } printf("\n"); diff --git a/core/comboot.inc b/core/comboot.inc index 72f642e4..25409595 100644 --- a/core/comboot.inc +++ b/core/comboot.inc @@ -899,7 +899,10 @@ comapi_getcwd: ; ; INT 22h AX=0020h Open directory ; -%if IS_SYSLINUX +%if IS_PXELINUX +comapi_opendir equ comapi_err + +%else comapi_opendir: mov es,P_ES mov si,P_SI @@ -911,21 +914,20 @@ comapi_opendir: mov P_EAX,eax clc ret -%else -comapi_opendir equ comapi_err %endif ; ; INT 22h AX=0021h Read directory ; -%if IS_SYSLINUX +%if IS_PXELINUX +comapi_readdir equ comapi_err + +%else comapi_readdir: mov esi,P_ESI ; The address of DIR structure pm_call readdir mov P_EAX,eax ; The address of newly read dirent structure ret -%else -comapi_readdir equ comapi_err %endif ; @@ -10,8 +10,11 @@ extern struct fs_info *this_fs; * open dir, return the file structure pointer in _eax_, or NULL if failed */ void opendir(com32sys_t *regs) -{ - this_fs->fs_ops->opendir(regs); +{ + char *src = MK_PTR(regs->es, regs->esi.w[0]); + char *dst = MK_PTR(regs->ds, regs->edi.w[0]); + strcpy(dst, src); + searchdir(regs); regs->eax.l = (uint32_t)handle_to_file(regs->esi.w[0]); } diff --git a/core/diskio.c b/core/diskio.c index c51f7225..911e4ddf 100644 --- a/core/diskio.c +++ b/core/diskio.c @@ -2,9 +2,9 @@ #include <string.h> #include <stdbool.h> #include <klibc/compiler.h> -#include "core.h" -#include "fs.h" -#include "disk.h" +#include <core.h> +#include <fs.h> +#include <disk.h> #define RETRY_COUNT 6 @@ -17,7 +17,7 @@ static int chs_rdwr_sectors(struct disk *disk, void *buf, char *tptr; size_t chunk, freeseg; int sector_shift = disk->sector_shift; - uint32_t xlba = lba + disk->part_start; /* Truncated LBA (CHS is << 2 TB) */ + uint32_t xlba = lba + disk->part_start; /* Truncated LBA (CHS is << 2 TB) */ uint32_t t; uint16_t c, h, s; com32sys_t ireg, oreg; @@ -227,25 +227,11 @@ static int ilog2(uint32_t num) void getoneblk(struct disk *disk, char *buf, block_t block, int block_size) { - int sec_per_block = block_size >> SECTOR_SHIFT; + int sec_per_block = block_size / disk->sector_size; disk->rdwr_sectors(disk, buf, block * sec_per_block, sec_per_block, 0); } -static void dump_disk(struct disk *disk) -{ - printf("drive number: 0x%x\n", disk->disk_number); - printf("disk type: %s(%d)\n", disk->type ? "EDD" : "CHS", disk->type); - printf("sector size: %d(%d)\n", disk->sector_size, disk->sector_shift); - printf("h: %d\ts: %d\n", disk->h, disk->s); - printf("offset: %d\n", disk->part_start); - printf("%s\n", disk->rdwr_sectors == edd_rdwr_sectors ? "EDD_RDWR_SECTORS" : - "CHS_RDWR_SECTORS"); - printf("--------------------------------\n"); - printf("disk->rdwr_sectors@: %p\n", disk->rdwr_sectors); - printf("edd_rdwr_sectors @: %p\n", edd_rdwr_sectors); - printf("chs_rdwr_sectors @: %p\n", chs_rdwr_sectors); -} struct disk *disk_init(uint8_t devno, bool cdrom, sector_t part_start, uint16_t bsHeads, uint16_t bsSecPerTrack) @@ -305,9 +291,6 @@ struct disk *disk_init(uint8_t devno, bool cdrom, sector_t part_start, disk.part_start = part_start; disk.rdwr_sectors = ebios ? edd_rdwr_sectors : chs_rdwr_sectors; -#if 0 - dump_disk(&disk); -#endif return &disk; } @@ -322,31 +305,8 @@ struct device * device_init(uint8_t devno, bool cdrom, sector_t part_start, dev.disk = disk_init(devno, cdrom, part_start, bsHeads, bsSecPerTrack); - /* for now, isolinux doesn't use cache */ - if (!cdrom) { - /* - * FIX!! I can't use __lowmem here, 'cause it will cause the error: - * "auxseg/lowmem region collides with xfer_buf_seg". - * - * static __lowmem char cache_buf[65536]; - */ - dev.cache_data = core_cache_buf; - dev.cache_size = sizeof core_cache_buf; - } else - dev.cache_data = NULL; - + dev.cache_data = core_cache_buf; + dev.cache_size = sizeof core_cache_buf; + return &dev; } - - -/* debug function */ -static void dump_dev(struct device *dev) -{ - printf("device type:%s\n", dev->disk->type ? "EDD" : "CHS"); - printf("drive number: 0x%x\n", dev->disk->disk_number); - printf("cache_data: %p\n", dev->cache_data); - printf("cache_head: %p\n", dev->cache_head); - printf("cache_block_size: %d\n", dev->cache_block_size); - printf("cache_entries: %d\n", dev->cache_entries); - printf("cache_size: %d\n", dev->cache_size); -} diff --git a/core/diskstart.inc b/core/diskstart.inc index 4c4e8ee9..8bb4f78a 100644 --- a/core/diskstart.inc +++ b/core/diskstart.inc @@ -411,6 +411,7 @@ getlinsec_cbios: ; ; kaboom: write a message and bail out. ; + global kaboom disk_error: kaboom: xor si,si @@ -1,12 +1,13 @@ #include <stdio.h> #include <stdbool.h> #include <string.h> -#include "fs.h" -#include "cache.h" +#include <fs.h> +#include <cache.h> /* The currently mounted filesystem */ struct fs_info *this_fs = NULL; static struct fs_info fs; +struct inode *this_inode = NULL; /* Actual file structures (we don't have malloc yet...) */ struct file files[MAX_OPEN]; @@ -20,7 +21,7 @@ static struct file *alloc_file(void) struct file *file = files; for (i = 0; i < MAX_OPEN; i++) { - if (!file->open_file) + if (!file->fs) return file; file++; } @@ -38,7 +39,7 @@ static inline void free_file(struct file *file) void _close_file(struct file *file) { - if (file->open_file) + if (file->fs) file->fs->fs_ops->close_file(file); free_file(file); } @@ -118,18 +119,25 @@ void getfssec(com32sys_t *regs) void searchdir(com32sys_t *regs) { - char *filename = (char *)MK_PTR(regs->ds, regs->edi.w[0]);; + char *name = MK_PTR(regs->ds, regs->edi.w[0]); + struct inode *inode; + struct inode *parent; struct file *file; - + char part[256]; + char *p; + int symlink_count = 6; + #if 0 - printf("filename: %s\n", filename); + printf("filename: %s\n", name); #endif - file = alloc_file(); - - if (file) { - file->fs = this_fs; - file->fs->fs_ops->searchdir(filename, file); + if (!(file = alloc_file())) + goto err_no_close; + file->fs = this_fs; + + /* if we have ->searchdir method, call it */ + if (file->fs->fs_ops->searchdir) { + file->fs->fs_ops->searchdir(name, file); if (file->open_file) { regs->esi.w[0] = file_to_handle(file); @@ -137,9 +145,68 @@ void searchdir(com32sys_t *regs) regs->eflags.l &= ~EFLAGS_ZF; return; } + + goto err; + } + + + /* else, try the generic-path-lookup method */ + if (*name == '/') { + inode = this_fs->fs_ops->iget_root(); + while(*name == '/') + name++; + } else { + inode = this_inode; } + parent = inode; - /* failure... */ + while (*name) { + p = part; + while(*name && *name != '/') + *p++ = *name++; + *p = '\0'; + inode = this_fs->fs_ops->iget(part, parent); + if (!inode) + goto err; + if (inode->mode == I_SYMLINK) { + if (!this_fs->fs_ops->follow_symlink || + --symlink_count == 0 || /* limit check */ + inode->size >= BLOCK_SIZE(this_fs)) + goto err; + name = this_fs->fs_ops->follow_symlink(inode, name); + free_inode(inode); + continue; + } + + /* + * For the relative path searching used in FAT and ISO fs. + */ + if ((this_fs->fs_ops->fs_flags & FS_THISIND) && (this_inode != parent)){ + if (this_inode) + free_inode(this_inode); + this_inode = parent; + } + + if (parent != this_inode) + free_inode(parent); + parent = inode; + if (! *name) + break; + while(*name == '/') + name++; + } + + file->inode = inode; + file->offset = 0; + + regs->esi.w[0] = file_to_handle(file); + regs->eax.l = inode->size; + regs->eflags.l &= ~EFLAGS_ZF; + return; + +err: + _close_file(file); +err_no_close: regs->esi.w[0] = 0; regs->eax.l = 0; regs->eflags.l |= EFLAGS_ZF; @@ -158,10 +225,12 @@ void close_file(com32sys_t *regs) /* * it will do: + * initialize the memory management function; * set up the vfs fs structure; * initialize the device structure; * invoke the fs-specific init function; - * finally, initialize the cache + * initialize the cache if we need one; + * finally, get the current inode for relative path looking. * */ void fs_init(com32sys_t *regs) @@ -176,6 +245,9 @@ void fs_init(com32sys_t *regs) /* ops is a ptr list for several fs_ops */ const struct fs_ops **ops = (const struct fs_ops **)regs->eax.l; + /* Initialize malloc() */ + mem_init(); + while ((blk_shift < 0) && *ops) { /* set up the fs stucture */ fs.fs_ops = *ops; @@ -202,7 +274,11 @@ void fs_init(com32sys_t *regs) ; } this_fs = &fs; + /* initialize the cache */ if (fs.fs_dev && fs.fs_dev->cache_data) cache_init(fs.fs_dev, blk_shift); + + if (fs.fs_ops->iget_current) + this_inode = fs.fs_ops->iget_current(); } diff --git a/core/fs/ext2/bmap.c b/core/fs/ext2/bmap.c new file mode 100644 index 00000000..e38bcd2c --- /dev/null +++ b/core/fs/ext2/bmap.c @@ -0,0 +1,185 @@ +/* + * The logical block -> physical block routine. + * + * Copyright (C) 2009 Liu Aleaxander -- All rights reserved. This file + * may be redistributed under the terms of the GNU Public License. + */ + +#include <stdio.h> +#include <fs.h> +#include <disk.h> +#include <cache.h> +#include "ext2_fs.h" + + +static struct ext4_extent_header * +ext4_find_leaf(struct fs_info *fs, struct ext4_extent_header *eh, block_t block) +{ + struct ext4_extent_idx *index; + struct cache_struct *cs; + block_t blk; + int i; + + while (1) { + if (eh->eh_magic != EXT4_EXT_MAGIC) + return NULL; + if (eh->eh_depth == 0) + return eh; + + index = EXT4_FIRST_INDEX(eh); + for (i = 0; i < (int)eh->eh_entries; i++) { + if (block < index[i].ei_block) + break; + } + if (--i < 0) + return NULL; + + blk = index[i].ei_leaf_hi; + blk = (blk << 32) + index[i].ei_leaf_lo; + cs = get_cache_block(fs->fs_dev, blk); + eh = (struct ext4_extent_header *)cs->data; + } +} + +/* handle the ext4 extents to get the phsical block number */ +static uint64_t bmap_extent(struct fs_info *fs, + struct inode *inode, + uint32_t block) +{ + struct ext4_extent_header *leaf; + struct ext4_extent *ext; + int i; + block_t start; + + leaf = ext4_find_leaf(fs, (struct ext4_extent_header *)inode->data, block); + if (!leaf) { + printf("ERROR, extent leaf not found\n"); + return 0; + } + + ext = EXT4_FIRST_EXTENT(leaf); + for (i = 0; i < leaf->eh_entries; i++) { + if (block < ext[i].ee_block) + break; + } + if (--i < 0) { + printf("ERROR, not find the right block\n"); + return 0; + } + + /* got it */ + block -= ext[i].ee_block; + if (block >= ext[i].ee_len) + return 0; + start = ext[i].ee_start_hi; + start = (start << 32) + ext[i].ee_start_lo; + + return start + block; +} + + +/* + * handle the traditional block map, like indirect, double indirect + * and triple indirect + */ +static unsigned int bmap_traditional(struct fs_info *fs, + struct inode *inode, + uint32_t block) +{ + int addr_per_block = BLOCK_SIZE(fs) >> 2; + uint32_t direct_blocks = EXT2_NDIR_BLOCKS, + indirect_blocks = addr_per_block, + double_blocks = addr_per_block * addr_per_block, + triple_blocks = double_blocks * addr_per_block; + struct cache_struct *cs; + + /* direct blocks */ + if (block < direct_blocks) + return inode->data[block]; + + /* indirect blocks */ + block -= direct_blocks; + if (block < indirect_blocks) { + block_t ind_block = inode->data[EXT2_IND_BLOCK]; + + if (!ind_block) + return 0; + cs = get_cache_block(fs->fs_dev, ind_block); + + return ((uint32_t *)cs->data)[block]; + } + + + /* double indirect blocks */ + block -= indirect_blocks; + if (block < double_blocks) { + block_t dou_block = inode->data[EXT2_DIND_BLOCK]; + + if (!dou_block) + return 0; + cs = get_cache_block(fs->fs_dev, dou_block); + + dou_block = ((uint32_t *)cs->data)[block / indirect_blocks]; + if (!dou_block) + return 0; + cs = get_cache_block(fs->fs_dev, dou_block); + + return ((uint32_t *)cs->data)[block % addr_per_block]; + } + + + /* triple indirect block */ + block -= double_blocks; + if (block < triple_blocks) { + block_t tri_block = inode->data[EXT2_TIND_BLOCK]; + + if (!tri_block) + return 0; + cs = get_cache_block(fs->fs_dev, tri_block); + + tri_block = ((uint32_t *)cs->data)[block / double_blocks]; + if (!tri_block) + return 0; + cs = get_cache_block(fs->fs_dev, tri_block); + + tri_block = (block / addr_per_block) % addr_per_block; + tri_block = ((uint32_t *)cs->data)[tri_block]; + if (!tri_block) + return 0; + cs = get_cache_block(fs->fs_dev, tri_block); + + return ((uint32_t *)cs->data)[block % addr_per_block]; + } + + + /* File too big, can not handle */ + printf("ERROR, file too big\n"); + return 0; +} + + +/** + * Map the logical block to physic block where the file data stores. + * In EXT4, there are two ways to handle the map process, extents and indirect. + * EXT4 uses a inode flag to mark extent file and indirect block file. + * + * @fs: the fs_info structure. + * @inode: the inode structure. + * @block: the logical blcok needed to be maped. + * @retrun: the physic block number. + * + */ +block_t bmap(struct fs_info *fs, struct inode * inode, int block) +{ + block_t ret; + + if (block < 0) + return 0; + + if (inode->flags & EXT4_EXTENTS_FLAG) + ret = bmap_extent(fs, inode, block); + else + ret = bmap_traditional(fs, inode, block); + + return ret; +} diff --git a/core/fs/ext2/ext2.c b/core/fs/ext2/ext2.c index dde5571e..9d4d9ec5 100644 --- a/core/fs/ext2/ext2.c +++ b/core/fs/ext2/ext2.c @@ -1,41 +1,17 @@ #include <stdio.h> #include <string.h> +#include <sys/dirent.h> #include <cache.h> #include <core.h> #include <disk.h> #include <fs.h> #include "ext2_fs.h" -#define MAX_SYMLINKS 64 -#define SYMLINK_SECTORS 2 -static char SymlinkBuf[SYMLINK_SECTORS * SECTOR_SIZE + 64]; - -/* - * File structure, This holds the information for each currently open file - */ -struct open_file_t { - uint32_t file_bytesleft; /* Number of bytes left (0 = free) */ - uint32_t file_sector; /* Next linear sector to read */ - sector_t file_in_sec; /* Sector where inode lives */ - uint16_t file_in_off; - uint16_t file_mode; - uint32_t pad[3]; /* pad to 2^5 == 0x20 bytes */ -}; -static struct open_file_t Files[MAX_OPEN]; - -static struct ext2_inode this_inode; -static struct ext2_super_block sb; - -static uint16_t ClustByteShift, ClustShift; -static uint32_t SecPerClust, ClustSize, ClustMask; -static uint32_t PtrsPerBlock1, PtrsPerBlock2, PtrsPerBlock3; -static int DescPerBlock, InodePerBlock; - /* * just like the function strcpy(), except it returns non-zero if overflow. * */ -static int strecpy(char *dst, char *src, char *end) +static int strecpy(char *dst, const char *src, char *end) { while (*src != '\0') *dst++ = *src++; @@ -47,278 +23,34 @@ static int strecpy(char *dst, char *src, char *end) return 0; } - -/* - * Allocate a file structure, if successful return the file pointer, or NULL. - * - */ -static struct open_file_t *allocate_file(void) -{ - struct open_file_t *file = Files; - int i; - - for (i = 0; i < MAX_OPEN; i++) { - if (file->file_bytesleft == 0) /* found it */ - return file; - file++; - } - - return NULL; /* not found */ -} - - -/** - * ext2_close_file: - * - * Deallocates a file structure point by FILE - * - * @param: file, the file structure we want deallocate - * - */ -static inline void close_pvt(struct open_file_t *of) -{ - of->file_bytesleft = 0; -} - static void ext2_close_file(struct file *file) { - close_pvt(file->open_file); + if (file->inode) { + file->offset = 0; + free_inode(file->inode); + } } -/** +/* * get the group's descriptor of group_num - * - * @param: group_num, the group number; - * - * @return: the pointer of the group's descriptor - * - */ -static struct ext2_group_desc * -get_group_desc(struct fs_info *fs, uint32_t group_num) -{ - block_t block_num; - uint32_t offset; - struct ext2_group_desc *desc; - struct cache_struct *cs; - - block_num = group_num / DescPerBlock; - offset = group_num % DescPerBlock; - - block_num += sb.s_first_data_block + 1; - cs = get_cache_block(fs->fs_dev, block_num); - desc = (struct ext2_group_desc *)cs->data + offset; - - return desc; -} - - -/** - * read the right inode structure to _dst_. - * - * @param: inode_offset, the inode offset within a group; - * @prarm: dst, wher we will store the inode structure; - * @param: desc, the pointer to the group's descriptor - * @param: block, a pointer used for retruning the blk number for file structure - * @param: offset, same as block - * */ -static void read_inode(struct fs_info *fs, uint32_t inode_offset, - struct ext2_inode *dst, struct ext2_group_desc *desc, - block_t *block, uint32_t *offset) +struct ext2_group_desc * ext2_get_group_desc(uint32_t group_num) { - struct cache_struct *cs; - struct ext2_inode *inode; - - *block = inode_offset / InodePerBlock + desc->bg_inode_table; - *offset = inode_offset % InodePerBlock; - - cs = get_cache_block(fs->fs_dev, *block); - - /* well, in EXT4, the inode structure usually be 256 */ - inode = (struct ext2_inode *)(cs->data + (*offset * (sb.s_inode_size))); - memcpy(dst, inode, EXT2_GOOD_OLD_INODE_SIZE); - - /* for file structure */ - *offset = (inode_offset * sb.s_inode_size) % ClustSize; + struct ext2_sb_info *sbi = EXT2_SB(this_fs); + + if (group_num >= sbi->s_groups_count) { + printf ("ext2_get_group_desc" + "block_group >= groups_count - " + "block_group = %d, groups_count = %d", + group_num, sbi->s_groups_count); + + return NULL; + } + + return sbi->s_group_desc[group_num]; } -/** - * open a file indicated by an inode number in INR - * - * @param : inr, the inode number - * @return: a open_file_t structure pointer - * file length in bytes - * the first 128 bytes of the inode, stores in ThisInode - * - */ -static struct open_file_t * -open_inode(struct fs_info *fs, uint32_t inr, uint32_t *file_len) -{ - struct open_file_t *file; - struct ext2_group_desc *desc; - - uint32_t inode_group, inode_offset; - block_t block_num; - uint32_t block_off; - - file = allocate_file(); - if (!file) - return NULL; - - file->file_sector = 0; - - inr --; - inode_group = inr / sb.s_inodes_per_group; - - /* get the group desc */ - desc = get_group_desc(fs, inode_group); - - inode_offset = inr % sb.s_inodes_per_group; - read_inode(fs, inode_offset, &this_inode, desc, &block_num, &block_off); - - /* Finally, we need to convet it to sector for now */ - file->file_in_sec = (block_num<<ClustShift) + (block_off>>SECTOR_SHIFT); - file->file_in_off = block_off & (SECTOR_SIZE - 1); - file->file_mode = this_inode.i_mode; - *file_len = file->file_bytesleft = this_inode.i_size; - - if (*file_len == 0) - return NULL; - - return file; -} - - - -static struct ext4_extent_header * -ext4_find_leaf(struct fs_info *fs, struct ext4_extent_header *eh, block_t block) -{ - struct ext4_extent_idx *index; - struct cache_struct *cs; - block_t blk; - int i; - - while (1) { - if (eh->eh_magic != EXT4_EXT_MAGIC) - return NULL; - - /* got it */ - if (eh->eh_depth == 0) - return eh; - - index = EXT4_FIRST_INDEX(eh); - for (i = 0; i < eh->eh_entries; i++) { - if (block < index[i].ei_block) - break; - } - if (--i < 0) - return NULL; - - blk = index[i].ei_leaf_hi; - blk = (blk << 32) + index[i].ei_leaf_lo; - - /* read the blk to memeory */ - cs = get_cache_block(fs->fs_dev, blk); - eh = (struct ext4_extent_header *)(cs->data); - } -} - -/* handle the ext4 extents to get the phsical block number */ -static block_t linsector_extent(struct fs_info *fs, block_t block, - struct ext2_inode *inode) -{ - struct ext4_extent_header *leaf; - struct ext4_extent *ext; - int i; - block_t start; - - leaf = ext4_find_leaf(fs, (struct ext4_extent_header*)inode->i_block, block); - if (!leaf) { - printf("ERROR, extent leaf not found\n"); - return 0; - } - - ext = EXT4_FIRST_EXTENT(leaf); - for (i = 0; i < leaf->eh_entries; i++) { - if (block < ext[i].ee_block) - break; - } - if (--i < 0) { - printf("ERROR, not find the right block\n"); - return 0; - } - - /* got it */ - block -= ext[i].ee_block; - if (block >= ext[i].ee_len) - return 0; - - start = ext[i].ee_start_hi; - start = (start << 32) + ext[i].ee_start_lo; - - return start + block; -} - - -/** - * linsector_direct: - * - * @param: block, the block index - * @param: inode, the inode structure - * - * @return: the physic block number - */ -static block_t linsector_direct(struct fs_info *fs, uint32_t block, struct ext2_inode *inode) -{ - struct cache_struct *cs; - - /* direct blocks */ - if (block < EXT2_NDIR_BLOCKS) - return inode->i_block[block]; - - - /* indirect blocks */ - block -= EXT2_NDIR_BLOCKS; - if (block < PtrsPerBlock1) { - block_t ind_block = inode->i_block[EXT2_IND_BLOCK]; - cs = get_cache_block(fs->fs_dev, ind_block); - - return ((uint32_t *)cs->data)[block]; - } - - /* double indirect blocks */ - block -= PtrsPerBlock1; - if (block < PtrsPerBlock2) { - block_t dou_block = inode->i_block[EXT2_DIND_BLOCK]; - cs = get_cache_block(fs->fs_dev, dou_block); - - dou_block = ((uint32_t *)cs->data)[block / PtrsPerBlock1]; - cs = get_cache_block(fs->fs_dev, dou_block); - - return ((uint32_t*)cs->data)[block % PtrsPerBlock1]; - } - - /* triple indirect block */ - block -= PtrsPerBlock2; - if (block < PtrsPerBlock3) { - block_t tri_block = inode->i_block[EXT2_TIND_BLOCK]; - cs = get_cache_block(fs->fs_dev, tri_block); - - tri_block = ((uint32_t *)cs->data)[block / PtrsPerBlock2]; - cs = get_cache_block(fs->fs_dev, tri_block); - - tri_block = ((uint32_t *)cs->data)[block % PtrsPerBlock2]; - cs = get_cache_block(fs->fs_dev, tri_block); - - return ((uint32_t*)cs->data)[block % PtrsPerBlock1]; - } - - /* File too big, can not handle */ - printf("ERROR, file too big\n"); - return 0; -} - /** * linsector: @@ -332,52 +64,17 @@ static block_t linsector_direct(struct fs_info *fs, uint32_t block, struct ext2_ * * @return: physic sector number */ -static sector_t linsector(struct fs_info *fs, uint32_t lin_sector) +static sector_t linsector(struct fs_info *fs, + struct inode *inode, + uint32_t lin_sector) { - uint32_t block = lin_sector >> ClustShift; - block_t ret; - struct ext2_inode *inode; - - /* well, this is what I think the variable this_inode used for */ - inode = &this_inode; - - if (inode->i_flags & EXT4_EXTENTS_FLAG) - ret = linsector_extent(fs, block, inode); - else - ret = linsector_direct(fs, block, inode); + int blk_bits = fs->block_shift - fs->sector_shift; + block_t block = bmap(fs, inode, lin_sector >> blk_bits); - if (!ret) { - printf("ERROR: something error happend at linsector..\n"); - return 0; - } - - /* finally convert it to sector */ - return ((ret << ClustShift) + (lin_sector & ClustMask)); -} - - -/* - * NOTE! unlike strncmp, ext2_match_entry returns 1 for success, 0 for failure. - * - * len <= EXT2_NAME_LEN and de != NULL are guaranteed by caller. - */ -static inline int ext2_match_entry (const char * const name, - struct ext2_dir_entry * de) -{ - if (!de->d_inode) - return 0; - return !strncmp(name, de->d_name, de->d_name_len); + return (block << blk_bits) + (lin_sector & ((1 << blk_bits) - 1)); } -/* - * p is at least 6 bytes before the end of page - */ -static inline struct ext2_dir_entry *ext2_next_entry(struct ext2_dir_entry *p) -{ - return (struct ext2_dir_entry *)((char*)p + p->d_rec_len); -} - /** * getlinsec_ext: * @@ -387,15 +84,16 @@ static inline struct ext2_dir_entry *ext2_next_entry(struct ext2_dir_entry *p) * */ static void getlinsec_ext(struct fs_info *fs, char *buf, - sector_t sector, int sector_cnt) + sector_t sector, int sector_cnt) { int ext_cnt = 0; + int sec_per_block = 1 << (fs->block_shift - fs->sector_shift); struct disk *disk = fs->fs_dev->disk; - if (sector < SecPerClust) { - ext_cnt = SecPerClust - sector; - memset(buf, 0, ext_cnt << SECTOR_SHIFT); - buf += ext_cnt << SECTOR_SHIFT; + if (sector < sec_per_block) { + ext_cnt = sec_per_block - sector; + memset(buf, 0, ext_cnt << fs->sector_shift); + buf += ext_cnt << fs->sector_shift; } sector += ext_cnt; @@ -403,9 +101,7 @@ static void getlinsec_ext(struct fs_info *fs, char *buf, disk->rdwr_sectors(disk, buf, sector, sector_cnt, 0); } -/** - * getfssec: - * +/* * Get multiple sectors from a file * * Alought we have made the buffer data based on block size, @@ -413,35 +109,27 @@ static void getlinsec_ext(struct fs_info *fs, char *buf, * sectors (then can be multiple blocks) is what the function * do. So, let it be based on sectors. * - * This function can be called from C function, and either from - * ASM function. - * - * @param: ES:BX(of regs), the buffer to store data - * @param: DS:SI(of regs), the pointer to open_file_t - * @param: CX(of regs), number of sectors to read - * - * @return: ECX(of regs), number of bytes read - * */ -static uint32_t ext2_getfssec(struct file *gfile, char *buf, +static uint32_t ext2_getfssec(struct file *file, char *buf, int sectors, bool *have_more) { + struct inode *inode = file->inode; + struct fs_info *fs = file->fs; int sector_left, next_sector, sector_idx; int frag_start, con_sec_cnt; - int bytes_read = sectors << SECTOR_SHIFT; - struct open_file_t *file = gfile->open_file; - struct fs_info *fs = gfile->fs; + int bytes_read = sectors << fs->sector_shift; + uint32_t bytesleft = inode->size - file->offset; - sector_left = (file->file_bytesleft + SECTOR_SIZE - 1) >> SECTOR_SHIFT; + sector_left = (bytesleft + SECTOR_SIZE(fs) - 1) >> fs->sector_shift; if (sectors > sector_left) sectors = sector_left; + sector_idx = file->offset >> fs->sector_shift; while (sectors) { /* * get the frament */ - sector_idx = file->file_sector; - next_sector = frag_start = linsector(fs, sector_idx); + next_sector = frag_start = linsector(fs, inode, sector_idx); con_sec_cnt = 0; /* get the consective sectors count */ @@ -457,226 +145,246 @@ static uint32_t ext2_getfssec(struct file *gfile, char *buf, sector_idx ++; next_sector ++; - } while (next_sector == linsector(fs, sector_idx)); + } while (next_sector == linsector(fs, inode, sector_idx)); -#if 0 +#if 0 printf("You are reading data stored at sector --0x%x--0x%x\n", frag_start, frag_start + con_sec_cnt -1); #endif getlinsec_ext(fs, buf, frag_start, con_sec_cnt); - buf += con_sec_cnt << 9; - file->file_sector += con_sec_cnt; /* next sector index */ + buf += con_sec_cnt << fs->sector_shift; } while(sectors); - if (bytes_read >= file->file_bytesleft) { - bytes_read = file->file_bytesleft; + if (bytes_read >= bytesleft) { + bytes_read = bytesleft; *have_more = 0; } else { *have_more = 1; } - file->file_bytesleft -= bytes_read; - + file->offset += bytes_read; + return bytes_read; } - +/* + * Unlike strncmp, ext2_match_entry returns 1 for success, 0 for failure. + */ +static inline int ext2_match_entry (const char * const name, + struct ext2_dir_entry * de) +{ + if (!de->d_inode) + return 0; + if (strlen(name) != de->d_name_len) + return 0; + return !strncmp(name, de->d_name, de->d_name_len); +} -/** - * find_dir_entry: - * - * find a dir entry, if find return it or return NULL - * + +/* + * p is at least 6 bytes before the end of page */ -static struct ext2_dir_entry* -find_dir_entry(struct fs_info *fs, struct open_file_t *file, char *filename) +static inline struct ext2_dir_entry *ext2_next_entry(struct ext2_dir_entry *p) { - bool have_more; - char *EndBlock = trackbuf + (SecPerClust << SECTOR_SHIFT);; - struct ext2_dir_entry *de; - struct file xfile; + return (struct ext2_dir_entry *)((char*)p + p->d_rec_len); +} - /* Fake out a VFS file structure */ - xfile.fs = fs; - xfile.open_file = file; +/* + * find a dir entry, return it if found, or return NULL. + */ +static struct ext2_dir_entry * +ext2_find_entry(struct fs_info *fs, struct inode *inode, char *dname) +{ + int index = 0; + block_t block; + uint32_t i = 0; + struct ext2_dir_entry *de; + struct cache_struct *cs; - /* read a clust at a time */ - ext2_getfssec(&xfile, trackbuf, SecPerClust, &have_more); - de = (struct ext2_dir_entry *)trackbuf; + if (!(block = bmap(fs, inode, index++))) + return NULL; + cs = get_cache_block(fs->fs_dev, block); + de = (struct ext2_dir_entry *)cs->data; + + while(i < (int)inode->size) { + if (ext2_match_entry(dname, de)) + return de; + i += de->d_rec_len; + if (i >= (int)inode->size) + break; + if ((char *)de >= (char *)cs->data + BLOCK_SIZE(fs)) { + if (!(block = bmap(fs, inode, index++))) + break; + cs = get_cache_block(fs->fs_dev, block); + de = (struct ext2_dir_entry *)cs->data; + continue; + } + + de = ext2_next_entry(de); + } - while (1) { - if ((char *)de >= (char *)EndBlock) { - if (!have_more) - return NULL; - ext2_getfssec(&xfile, trackbuf, SecPerClust, &have_more); - de = (struct ext2_dir_entry *)trackbuf; - } - - /* Zero inode == void entry */ - if (de->d_inode == 0) { - de = ext2_next_entry(de); - continue; - } - - if (ext2_match_entry (filename, de)) { - filename += de->d_name_len; - if ((*filename == 0) || (*filename == '/')) - return de; /* got it */ - - /* not match, restore the filename then try next */ - filename -= de->d_name_len; - } - - de = ext2_next_entry(de); - } + return NULL; } - -static char *do_symlink(struct fs_info *fs, struct open_file_t *file, - uint32_t file_len, char *filename) +static struct ext2_inode * get_inode(int inr) { - int flag; - bool have_more; - - char *SymlinkTmpBuf = trackbuf; - char *lnk_end; - char *SymlinkTmpBufEnd = trackbuf + SYMLINK_SECTORS * SECTOR_SIZE+64; - struct file xfile; - xfile.fs = fs; - xfile.open_file = file; - - flag = this_inode.i_file_acl ? SecPerClust : 0; - if (this_inode.i_blocks == flag) { - /* fast symlink */ - close_pvt(file); /* we've got all we need */ - memcpy(SymlinkTmpBuf, this_inode.i_block, file_len); - lnk_end = SymlinkTmpBuf + file_len; - - } else { - /* slow symlink */ - ext2_getfssec(&xfile, SymlinkTmpBuf, SYMLINK_SECTORS, &have_more); - lnk_end = SymlinkTmpBuf + file_len; - } + struct ext2_group_desc *desc; + struct cache_struct *cs; + uint32_t inode_group, inode_offset; + uint32_t block_num, block_off; - if (*filename != 0) - *lnk_end++ = '/'; + inr--; + inode_group = inr / EXT2_INODES_PER_GROUP(this_fs); + inode_offset = inr % EXT2_INODES_PER_GROUP(this_fs); + desc = ext2_get_group_desc (inode_group); + if (!desc) + return NULL; - if (strecpy(lnk_end, filename, SymlinkTmpBufEnd)) - return NULL; /* buffer overflow */ + block_num = desc->bg_inode_table + + inode_offset / EXT2_INODES_PER_BLOCK(this_fs); + block_off = inode_offset % EXT2_INODES_PER_BLOCK(this_fs); - /* - * now copy it to the "real" buffer; we need to have - * two buffers so we avoid overwriting the tail on - * the next copy. - */ - strcpy(SymlinkBuf, SymlinkTmpBuf); + cs = get_cache_block(this_fs->fs_dev, block_num); - /* return the new path */ - return SymlinkBuf; + return cs->data + block_off * EXT2_SB(this_fs)->s_inode_size; } +static inline int get_inode_mode(int mode) +{ + mode >>= S_IFSHIFT; + if (mode == T_IFDIR) + mode = I_DIR; + else if (mode == T_IFLNK) + mode = I_SYMLINK; + else + mode = I_FILE; /* we treat others as FILE */ + return mode; +} +static void fill_inode(struct inode *inode, struct ext2_inode *e_inode) +{ + inode->mode = get_inode_mode(e_inode->i_mode); + inode->size = e_inode->i_size; + inode->atime = e_inode->i_atime; + inode->ctime = e_inode->i_ctime; + inode->mtime = e_inode->i_mtime; + inode->dtime = e_inode->i_dtime; + inode->blocks = e_inode->i_blocks; + inode->flags = e_inode->i_flags; + inode->file_acl = e_inode->i_file_acl; + + inode->data = malloc(EXT2_N_BLOCKS * sizeof(uint32_t *)); + if (!inode->data) { + malloc_error("inode data filed"); + return ; + } + memcpy(inode->data, e_inode->i_block, EXT2_N_BLOCKS * sizeof(uint32_t *)); +} +static struct inode *ext2_iget_by_inr(uint32_t inr) +{ + struct ext2_inode *e_inode; + struct inode *inode; + + e_inode = get_inode(inr); + if (!(inode = malloc(sizeof(*inode)))) + return NULL; + fill_inode(inode, e_inode); + inode->ino = inr; + + return inode; +} -/** - * Search the root directory for a pre-mangle filename in FILENAME. - * - * @param: filename, the filename we want to search. - * - * @out : a open_file_t structure pointer, stores in file->open_file - * @out : file lenght in bytes, stores in file->file_len - * - */ -static void ext2_searchdir(char *filename, struct file *file) +static struct inode *ext2_iget_root() +{ + return ext2_iget_by_inr(EXT2_ROOT_INO); +} + +static struct inode *ext2_iget_current() { extern int CurrentDir; - struct open_file_t *open_file; - struct ext2_dir_entry *de; - uint8_t file_mode; - uint8_t SymlinkCtr = MAX_SYMLINKS; - uint32_t inr = CurrentDir; - uint32_t ThisDir = CurrentDir; - uint32_t file_len; + return ext2_iget_by_inr(CurrentDir); +} + +static struct inode *ext2_iget(char *dname, struct inode *parent) +{ + struct ext2_dir_entry *de; - begin_path: - while (*filename == '/') { /* Absolute filename */ - inr = EXT2_ROOT_INO; - filename ++; - } - open: - if ((open_file = open_inode(file->fs, inr, &file_len)) == NULL) - goto err_noclose; + de = ext2_find_entry(this_fs, parent, dname); + if (!de) + return NULL; - file_mode = open_file->file_mode >> S_IFSHIFT; + return ext2_iget_by_inr(de->d_inode); +} + + +static char * ext2_follow_symlink(struct inode *inode, const char *name_left) +{ + int sec_per_block = 1 << (this_fs->block_shift - this_fs->sector_shift); + int fast_symlink; + char *symlink_buf; + char *p; + struct cache_struct *cs; - /* It's a file */ - if (file_mode == T_IFREG) { - if (*filename == '\0') - goto done; - else - goto err; - } + symlink_buf = malloc(BLOCK_SIZE(this_fs)); + if (!symlink_buf) { + malloc_error("symlink buffer"); + return NULL; + } + fast_symlink = (inode->file_acl ? sec_per_block : 0) == inode->blocks; + if (fast_symlink) { + memcpy(symlink_buf, inode->data, inode->size); + } else { + cs = get_cache_block(this_fs->fs_dev, *(uint32_t *)inode->data); + memcpy(symlink_buf, cs->data, inode->size); + } + p = symlink_buf + inode->size; + if (*name_left) + *p++ = '/'; + if (strecpy(p, name_left, symlink_buf + BLOCK_SIZE(this_fs))) { + free(symlink_buf); + return NULL; + } + if(!(p = strdup(symlink_buf))) + return symlink_buf; - /* It's a directory */ - if (file_mode == T_IFDIR) { - ThisDir = inr; - - if (*filename == 0) - goto err; - while (*filename == '/') - filename ++; - - de = find_dir_entry(file->fs, open_file, filename); - if (!de) - goto err; - - inr = de->d_inode; - filename += de->d_name_len; - close_pvt(open_file); - goto open; - } + free(symlink_buf); + return p; +} + +/* + * Read one directory entry at a time + */ +static struct dirent * ext2_readdir(struct file *file) +{ + struct fs_info *fs = file->fs; + struct inode *inode = file->inode; + struct dirent *dirent; + struct ext2_dir_entry *de; + struct cache_struct *cs; + int index = file->offset >> fs->block_shift; + block_t block; - - /* - * It's a symlink. We have to determine if it's a fast symlink - * (data stored in the inode) or not (data stored as a regular - * file.) Either which way, we start from the directory - * which we just visited if relative, or from the root directory - * if absolute, and append any remaining part of the path. - */ - if (file_mode == T_IFLNK) { - if (--SymlinkCtr==0 || file_len>=SYMLINK_SECTORS*SECTOR_SIZE) - goto err; /* too many links or symlink too long */ - - filename = do_symlink(file->fs, open_file, file_len, filename); - if (!filename) - goto err_noclose;/* buffer overflow */ - - inr = ThisDir; - goto begin_path; /* we got a new path, so search it again */ + if (!(block = bmap(fs, inode, index))) + return NULL; + cs = get_cache_block(fs->fs_dev, block); + de = (struct ext2_dir_entry *)(cs->data + (file->offset & (BLOCK_SIZE(fs) - 1))); + + if (!(dirent = malloc(sizeof(*dirent)))) { + malloc_error("dirent structure in ext2_readdir"); + return NULL; } + dirent->d_ino = de->d_inode; + dirent->d_off = file->offset; + dirent->d_reclen = de->d_rec_len; + dirent->d_type = de->d_file_type; + memcpy(dirent->d_name, de->d_name, de->d_name_len); + dirent->d_name[de->d_name_len] = '\0'; - /* Otherwise, something bad ... */ - err: - close_pvt(open_file); - err_noclose: - file_len = 0; - open_file = NULL; - done: + file->offset += de->d_rec_len; /* Update for next reading */ - file->file_len = file_len; - file->open_file = (void*)open_file; - -#if 0 - if (open_file) { - printf("file bytesleft: %d\n", open_file->file_bytesleft); - printf("file sector : %d\n", open_file->file_sector); - printf("file in sector: %d\n", open_file->file_in_sec); - printf("file offsector: %d\n", open_file->file_in_off); - } -#endif - + return dirent; } /* Load the config file, return 1 if failed, or 0 */ @@ -702,38 +410,86 @@ static int ext2_load_config(void) static int ext2_fs_init(struct fs_info *fs) { struct disk *disk = fs->fs_dev->disk; - + struct ext2_sb_info *sbi; + struct ext2_super_block sb; + int blk_bits; + int db_count; + int i; + int desc_block; + char *desc_buffer; + /* read the super block */ disk->rdwr_sectors(disk, &sb, 2, 2, 0); /* check if it is ext2, since we also support btrfs now */ if (sb.s_magic != EXT2_SUPER_MAGIC) return -1; - ClustByteShift = sb.s_log_block_size + 10; - ClustSize = 1 << ClustByteShift; - ClustShift = ClustByteShift - SECTOR_SHIFT; - - DescPerBlock = ClustSize >> ext2_group_desc_lg2size; - InodePerBlock = ClustSize / sb.s_inode_size; - - SecPerClust = ClustSize >> SECTOR_SHIFT; - ClustMask = SecPerClust - 1; + + sbi = malloc(sizeof(*sbi)); + if (!sbi) { + malloc_error("ext2_sb_info structure"); + return -1; + } + fs->fs_info = sbi; - PtrsPerBlock1 = 1 << (ClustByteShift - 2); - PtrsPerBlock2 = 1 << ((ClustByteShift - 2) * 2); - PtrsPerBlock3 = 1 << ((ClustByteShift - 2) * 3); + if (sb.s_magic != EXT2_SUPER_MAGIC) { + printf("ext2 mount error: it's not a EXT2/3/4 file system!\n"); + return 0; + } + + fs->sector_shift = disk->sector_shift; + fs->block_shift = sb.s_log_block_size + 10; + + sbi->s_inodes_per_group = sb.s_inodes_per_group; + sbi->s_blocks_per_group = sb.s_blocks_per_group; + sbi->s_inodes_per_block = BLOCK_SIZE(fs) / sb.s_inode_size; + if (sb.s_desc_size < sizeof(struct ext2_group_desc)) + sb.s_desc_size = sizeof(struct ext2_group_desc); + sbi->s_desc_per_block = BLOCK_SIZE(fs) / sb.s_desc_size; + sbi->s_groups_count = (sb.s_blocks_count - sb.s_first_data_block + + EXT2_BLOCKS_PER_GROUP(fs) - 1) + / EXT2_BLOCKS_PER_GROUP(fs); + db_count = (sbi->s_groups_count + EXT2_DESC_PER_BLOCK(fs) - 1) / + EXT2_DESC_PER_BLOCK(fs); + sbi->s_inode_size = sb.s_inode_size; + + /* read the descpritors */ + desc_block = sb.s_first_data_block + 1; + desc_buffer = malloc(db_count * BLOCK_SIZE(fs)); + if (!desc_buffer) { + malloc_error("desc_buffer"); + return -1; + } + blk_bits = fs->block_shift - fs->sector_shift; + disk->rdwr_sectors(disk, desc_buffer, desc_block << blk_bits, + db_count << blk_bits, 0); + sbi->s_group_desc = malloc(sizeof(struct ext2_group_desc *) + * sbi->s_groups_count); + if (!sbi->s_group_desc) { + malloc_error("sbi->s_group_desc"); + return -1; + } + for (i = 0; i < (int)sbi->s_groups_count; i++) { + sbi->s_group_desc[i] = (struct ext2_group_desc *)desc_buffer; + desc_buffer += sb.s_desc_size; + } - return ClustByteShift; + return fs->block_shift; } const struct fs_ops ext2_fs_ops = { .fs_name = "ext2", - .fs_flags = 0, + .fs_flags = FS_USEMEM, .fs_init = ext2_fs_init, - .searchdir = ext2_searchdir, + .searchdir = NULL, .getfssec = ext2_getfssec, .close_file = ext2_close_file, .mangle_name = generic_mangle_name, .unmangle_name = generic_unmangle_name, - .load_config = ext2_load_config + .load_config = ext2_load_config, + .iget_root = ext2_iget_root, + .iget_current = ext2_iget_current, + .iget = ext2_iget, + .follow_symlink = ext2_follow_symlink, + .readdir = ext2_readdir }; diff --git a/core/fs/ext2/ext2_fs.h b/core/fs/ext2/ext2_fs.h index d579eade..0249ee16 100644 --- a/core/fs/ext2/ext2_fs.h +++ b/core/fs/ext2/ext2_fs.h @@ -105,8 +105,37 @@ struct ext2_super_block { uint32_t s_algorithm_usage_bitmap; /* For compression */ uint8_t s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ uint8_t s_prealloc_dir_blocks; - uint16_t s_padding1; - uint32_t s_reserved[204]; /* Padding to the end of the block */ + uint16_t s_reserved_gdt_blocks; /* Per group desc for online growth */ + /* + * Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL set. + */ + uint8_t s_journal_uuid[16]; /* uuid of journal superblock */ + uint32_t s_journal_inum; /* inode number of journal file */ + uint32_t s_journal_dev; /* device number of journal file */ + uint32_t s_last_orphan; /* start of list of inodes to delete */ + uint32_t s_hash_seed[4]; /* HTREE hash seed */ + uint8_t s_def_hash_version; /* Default hash version to use */ + uint8_t s_reserved_char_pad; + uint16_t s_desc_size; /* size of group descriptor */ + uint32_t s_default_mount_opts; + uint32_t s_first_meta_bg; /* First metablock block group */ + uint32_t s_mkfs_time; /* When the filesystem was created */ + uint32_t s_jnl_blocks[17]; /* Backup of the journal inode */ + /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */ + uint32_t s_blocks_count_hi; /* Blocks count */ + uint32_t s_r_blocks_count_hi; /* Reserved blocks count */ + uint32_t s_free_blocks_count_hi;/* Free blocks count */ + uint16_t s_min_extra_isize; /* All inodes have at least # bytes */ + uint16_t s_want_extra_isize; /* New inodes should reserve # bytes */ + uint32_t s_flags; /* Miscellaneous flags */ + uint16_t s_raid_stride; /* RAID stride */ + uint16_t s_mmp_interval; /* # seconds to wait in MMP checking */ + uint64_t s_mmp_block; /* Block for multi-mount protection */ + uint32_t s_raid_stripe_width; /* blocks on all data disks (N*stride)*/ + uint8_t s_log_groups_per_flex; /* FLEX_BG group size */ + uint8_t s_reserved_char_pad2; + uint16_t s_reserved_pad; + uint32_t s_reserved[162]; /* Padding to the end of the block */ }; /******************************************************************************* @@ -237,16 +266,40 @@ struct ext4_extent_header { #define EXT4_FIRST_INDEX(header) ( (struct ext4_extent_idx *) (header + 1) ) +/* + * The ext2 super block information in memory + */ +struct ext2_sb_info { + uint32_t s_inodes_per_block;/* Number of inodes per block */ + uint32_t s_inodes_per_group;/* Number of inodes in a group */ + uint32_t s_blocks_per_group;/* Number of blocks in a group */ + uint32_t s_desc_per_block; /* Number of group descriptors per block */ + uint32_t s_groups_count; /* Number of groups in the fs */ + int s_inode_size; + + /* + * Here did not like Linux Kernel did; the group descriptor cache + * here is based on ext2_group_desc structure, instead of buffer + * head structure in Linux Kernel, where cache one block data. + */ + struct ext2_group_desc ** s_group_desc; +}; +static inline struct ext2_sb_info *EXT2_SB(struct fs_info *fs) +{ + return fs->fs_info; +} +#define EXT2_BLOCKS_PER_GROUP(fs) (EXT2_SB(fs)->s_blocks_per_group) +#define EXT2_INODES_PER_GROUP(fs) (EXT2_SB(fs)->s_inodes_per_group) +#define EXT2_INODES_PER_BLOCK(fs) (EXT2_SB(fs)->s_inodes_per_block) +#define EXT2_DESC_PER_BLOCK(fs) (EXT2_SB(fs)->s_desc_per_block) - -/* function declartion */ -/******************************************************************************* -extern struct open_file_t * ext2_read(char *); -extern int ext2_read(struct open_file_t *, char *, int); -*******************************************************************************/ +/* + * functions + */ +block_t bmap(struct fs_info *, struct inode *, int); #endif /* ext2_fs.h */ diff --git a/core/fs/fat/fat.c b/core/fs/fat/fat.c index e7931b0f..d77fcb12 100644 --- a/core/fs/fat/fat.c +++ b/core/fs/fat/fat.c @@ -7,213 +7,153 @@ #include <fs.h> #include "fat_fs.h" -#define ROOT_DIR_WORD 0x002f -/* file structure. This holds the information for each currently open file */ -struct open_file_t { - sector_t file_sector; /* sector pointer (0 = structure free) */ - uint32_t file_bytesleft; /* number of bytes left */ - uint32_t file_left; /* number of sectors left */ -}; - -static struct open_file_t Files[MAX_OPEN]; - -extern uint8_t SecPerClust; - -/* the fat bpb data */ -static struct fat_bpb fat; -static int FATType = 0; - -/* generic information about FAT fs */ -static sector_t FAT; /* Location of (first) FAT */ -static sector_t RootDirArea; /* Location of root directory area */ -static sector_t RootDir; /* Location of root directory proper */ -static sector_t DataArea; /* Location of data area */ -static uint32_t TotalSectors; /* Total number of sectors */ -static uint32_t ClustSize; /* Bytes/cluster */ -static uint32_t ClustMask; /* Sector/cluster - 1 */ -static uint8_t ClustShift; /* Shift count for sectors/cluster */ -static uint8_t ClustByteShift; /* Shift count for bytes/cluster */ - -static int CurrentDir; -static int PrevDir; - -/* used for long name entry */ -static char MangleBuf[12]; -static char entry_name[14]; - -/* try with the biggest long name */ -static char long_name[0x40 * 13]; -static char *NameStart; -static int NameLen; - -/* - * Allocate a file structure, if successful return the file pointer, or NULL. - * - */ -static struct open_file_t *allocate_file(void) -{ - struct open_file_t *file = Files; - int i = 0; - - for (; i < MAX_OPEN; i ++) { - if (file->file_sector == 0) /* found it */ - return file; - file ++; - } - - return NULL; /* not found */ -} - - -/* - * Allocate then fill a file structure for a directory starting in - * sector SECTOR. if successful, return the pointer of filled file - * structure, or return NULL. - * - */ -static struct open_file_t *alloc_fill_dir(sector_t sector) +static struct inode * new_fat_inode(void) { - struct open_file_t *file; + struct inode *inode = malloc(sizeof(*inode)); + if (!inode) + malloc_error("inode structure"); + memset(inode, 0, sizeof(*inode)); - file = allocate_file(); - if (!file) - return NULL; + /* + * We just need allocate one uint32_t data to store the + * first cluster number. + */ + inode->data = malloc(sizeof(uint32_t)); + if (!inode->data) + malloc_error("inode->data"); - file->file_sector = sector; /* current sector */ - file->file_bytesleft = 0; /* current offset */ - file->file_left = sector; /* beginning sector */ - return file; + return inode; } -/* Deallocates a file structure */ -static inline void close_pvt(struct open_file_t *of) -{ - of->file_sector = 0; -} - static void vfat_close_file(struct file *file) { - close_pvt(file->open_file); + if (file->inode) { + file->offset = 0; + free_inode(file->inode); + } } /* - * check for a particular sector in the FAT cache. - * + * Check for a particular sector in the FAT cache */ -static struct cache_struct *getfatsector(struct fs_info *fs, sector_t sector) +static struct cache_struct * get_fat_sector(struct fs_info *fs, sector_t sector) { - return get_cache_block(fs->fs_dev, FAT + sector); + return get_cache_block(fs->fs_dev, FAT_SB(fs)->fat + sector); } - -/** - * Advance a cluster pointer in clust_num to the next cluster - * pointer at in the FAT tables. return the next cluster number - * if success, or return 0 if end of file. - * - */ -static uint32_t nextcluster(struct fs_info *fs, uint32_t clust_num) +static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num) { uint32_t next_cluster; sector_t fat_sector; uint32_t offset; int lo, hi; struct cache_struct *cs; - - switch(FATType) { + + switch(FAT_SB(fs)->fat_type) { case FAT12: - fat_sector = (clust_num + clust_num / 2) >> SECTOR_SHIFT; - cs = getfatsector(fs, fat_sector); - offset = (clust_num * 3 / 2) & (SECTOR_SIZE -1); - if (offset == 0x1ff) { - /* - * we got the end of the one fat sector, - * but we don't got we have(just one byte, we need two), - * so store the low part, then read the next fat - * sector, read the high part, then combine it. - */ - lo = *(uint8_t *)(cs->data + offset); - cs = getfatsector(fs, fat_sector + 1); - hi = *(uint8_t *)cs->data; - next_cluster = (hi << 8) + lo; - } else - next_cluster = *(uint16_t *)(cs->data + offset); - - if (clust_num & 0x0001) - next_cluster >>= 4; /* cluster number is ODD */ - else - next_cluster &= 0x0fff; /* cluster number is EVEN */ - if (next_cluster > 0x0ff0) - goto fail; - break; - + fat_sector = (clust_num + clust_num / 2) >> SECTOR_SHIFT; + cs = get_fat_sector(fs, fat_sector); + offset = (clust_num * 3 / 2) & ((1 << SECTOR_SHIFT) - 1); + if (offset == 0x1ff) { + /* + * we got the end of the one fat sector, + * but we don't got we have(just one byte, we need two), + * so store the low part, then read the next fat + * sector, read the high part, then combine it. + */ + lo = *(uint8_t *)(cs->data + offset); + cs = get_fat_sector(fs, fat_sector + 1); + hi = *(uint8_t *)cs->data; + next_cluster = (hi << 8) + lo; + } else { + next_cluster = *(uint16_t *)(cs->data + offset); + } + + if (clust_num & 0x0001) + next_cluster >>= 4; /* cluster number is ODD */ + else + next_cluster &= 0x0fff; /* cluster number is EVEN */ + if (next_cluster > 0x0ff0) + goto fail; + break; + case FAT16: - fat_sector = clust_num >> (SECTOR_SHIFT - 1); - offset = clust_num & ((1 << (SECTOR_SHIFT-1)) -1); - cs = getfatsector(fs, fat_sector); - next_cluster = ((uint16_t *)cs->data)[offset]; - if (next_cluster > 0xfff0) - goto fail; - break; - + fat_sector = clust_num >> (SECTOR_SHIFT - 1); + offset = clust_num & ((1 << (SECTOR_SHIFT-1)) -1); + cs = get_fat_sector(fs, fat_sector); + next_cluster = ((uint16_t *)cs->data)[offset]; + if (next_cluster > 0xfff0) + goto fail; + break; + case FAT32: - fat_sector = clust_num >> (SECTOR_SHIFT - 2); - offset = clust_num & ((1 << (SECTOR_SHIFT-2)) -1); - cs = getfatsector(fs, fat_sector); - next_cluster = ((uint32_t *)cs->data)[offset] & 0x0fffffff; - if (next_cluster > 0x0ffffff0) - goto fail; - break; + fat_sector = clust_num >> (SECTOR_SHIFT - 2); + offset = clust_num & ((1 << (SECTOR_SHIFT-2)) -1); + cs = get_fat_sector(fs, fat_sector); + next_cluster = ((uint32_t *)cs->data)[offset] & 0x0fffffff; + if (next_cluster > 0x0ffffff0) + goto fail; + break; } return next_cluster; - fail: +fail: /* got an unexcepted cluster number, so return ZERO */ return 0; } - -/* - * given a sector on input, return the next sector of the - * same filesystem object, which may be the root directory or a - * cluster chain. Returns EOF. - * - */ -static sector_t nextsector(struct fs_info *fs, sector_t sector) +static sector_t get_next_sector(struct fs_info* fs, uint32_t sector) { + sector_t data_area = FAT_SB(fs)->data; sector_t data_sector; uint32_t cluster; - if (sector < DataArea) { - sector ++; - /* if we reached the end of root area */ - if (sector == DataArea) - sector = 0; /* return 0 */ - return sector; + if (sector < data_area) { + sector++; + /* if we reached the end of root area */ + if (sector == data_area) + sector = 0; /* return 0 */ + return sector; } - data_sector = sector - DataArea; - if ((data_sector+1) & ClustMask) /* in a cluster */ - return (++sector); + data_sector = sector - data_area; + if ((data_sector + 1) & FAT_SB(fs)->clust_mask) /* in a cluster */ + return ++sector; - /* got a new cluster */ - cluster = nextcluster(fs, (data_sector >> ClustShift) + 2); + /* get a new cluster */ + cluster = get_next_cluster(fs, (data_sector >> FAT_SB(fs)->clust_shift) + 2); if (!cluster ) - return 0; + return 0; /* return the start of the new cluster */ - sector = ((cluster - 2) << ClustShift) + DataArea; + sector = ((cluster - 2) << FAT_SB(fs)->clust_shift) + data_area; return sector; } - - - +/* + * Here comes the place I don't like VFAT fs most; if we need seek + * the file to the right place, we need get the right sector address + * from begining everytime! Since it's a kind a signle link list, we + * need to traver from the head-node to find the right node in that list. + * + * What a waste of time! + */ +static sector_t get_the_right_sector(struct file *file) +{ + int i = 0; + int sector_pos = file->offset >> SECTOR_SHIFT; + sector_t sector = *file->inode->data; + + for (; i < sector_pos; i++) + sector = get_next_sector(file->fs, sector); + + return sector; +} /** * __getfssec: @@ -224,15 +164,11 @@ static sector_t nextsector(struct fs_info *fs, sector_t sector) * and will correct the situation if it does, UNLESS *sectos* cross * 64K boundaries. * - * @param: buf - * @param: file structure - * @param: sectors - * */ static void __getfssec(struct fs_info *fs, char *buf, - struct open_file_t *file, uint32_t sectors) + struct file *file, uint32_t sectors) { - sector_t curr_sector = file->file_sector; + sector_t curr_sector = get_the_right_sector(file); sector_t frag_start , next_sector; uint32_t con_sec_cnt; struct disk *disk = fs->fs_dev->disk; @@ -249,7 +185,7 @@ static void __getfssec(struct fs_info *fs, char *buf, if (sectors == 0) break; - next_sector = nextsector(fs, curr_sector); + next_sector = get_next_sector(fs, curr_sector); if (!next_sector) break; }while(next_sector == (++curr_sector)); @@ -261,16 +197,14 @@ static void __getfssec(struct fs_info *fs, char *buf, /* do read */ disk->rdwr_sectors(disk, buf, frag_start, con_sec_cnt, 0); - buf += con_sec_cnt << 9;/* adjust buffer pointer */ + buf += con_sec_cnt << SECTOR_SHIFT;/* adjust buffer pointer */ if (!sectors) break; - //curr_sector --; /* this is the last sector actually read */ + curr_sector = next_sector; } - /* update the file_sector filed for the next read */ - file->file_sector = nextsector(fs, curr_sector); } @@ -279,32 +213,34 @@ static void __getfssec(struct fs_info *fs, char *buf, * get multiple sectors from a file * * @param: buf, the buffer to store the read data - * @param: gfile, the file structure pointer + * @param: file, the file structure pointer * @param: sectors, number of sectors wanna read * @param: have_more, set one if has more * * @return: number of bytes read * */ -static uint32_t vfat_getfssec(struct file *gfile, char *buf, int sectors, +static uint32_t vfat_getfssec(struct file *file, char *buf, int sectors, bool *have_more) { - uint32_t bytes_read = sectors << SECTOR_SHIFT; - struct open_file_t *file = gfile->open_file; - struct fs_info *fs = gfile->fs; - - if (sectors > file->file_left) - sectors = file->file_left; + struct fs_info *fs = file->fs; + uint32_t bytes_left = file->inode->size - file->offset; + uint32_t bytes_read = sectors << fs->sector_shift; + int sector_left; + + sector_left = (bytes_left + SECTOR_SIZE(fs) - 1) >> fs->sector_shift; + if (sectors > sector_left) + sectors = sector_left; __getfssec(fs, buf, file, sectors); - if (bytes_read >= file->file_bytesleft) { - bytes_read = file->file_bytesleft; + if (bytes_read >= bytes_left) { + bytes_read = bytes_left; *have_more = 0; - } else - *have_more = 1; - file->file_bytesleft -= bytes_read; - file->file_left -= sectors; + } else { + *have_more = 1; + } + file->offset += bytes_read; return bytes_read; } @@ -356,72 +292,56 @@ static void vfat_mangle_name(char *dst, const char *src) for (; i > 0; i --) *dst++ = '\0'; } - + /* - * Mangle a dos filename component pointed to by FILENAME - * into MangleBuf; ends on encountering any whitespace or - * slash. - * - * WARNING: saves pointers into the buffer for longname matchs! - */ -/** - * for now, it can't handle this case: - * xyxzxyxjfdkfjdjf.txt as it will just output the first 11 chars - * but not care the dot char at the later, so I think we need do - * this, but it seems that the SYSLINUX doesn't do it, so I will - * make it stay as what it was orignal. - * + * Mangle a normal style string to DOS style string. */ -static void mangle_dos_name(char *MangleBuf, char *filename) -{ - - char *dst = MangleBuf; - char *src = filename; +static void mangle_dos_name(char *mangle_buf, char *src) +{ + char *dst = mangle_buf; int i = 0; unsigned char c; - NameStart = filename; - for (; i < 11; i ++) - MangleBuf[i] = ' '; + mangle_buf[i] = ' '; for (i = 0; i < 11; i++) { - c = *src ++; - - if ((c <= ' ') || (c == '/')) - break; - - if (c == '.') { - dst = &MangleBuf[8]; - i = 7; - continue; - } - - if (c >= 'a' && c <= 'z') - c -= 32; - if ((c == 0xe5) && (i == 11)) - c = 0x05; - - *dst++ = c; + c = *src ++; + + if ((c <= ' ') || (c == '/')) + break; + + if (c == '.') { + dst = &mangle_buf[8]; + i = 7; + continue; + } + + if (c >= 'a' && c <= 'z') + c -= 32; + if ((c == 0xe5) && (i == 11)) + c = 0x05; + + *dst++ = c; } - MangleBuf[12] = '\0'; - - while((*src != '/') && (*src > ' ')) - src ++; - - NameLen = src - filename; + mangle_buf[11] = '\0'; } + +/* try with the biggest long name */ +static char long_name[0x40 * 13]; +static char entry_name[14]; + static void unicode_to_ascii(char *entry_name, uint16_t *unicode_buf) { int i = 0; for (; i < 13; i++) { - if (unicode_buf[i] == 0xffff) { - entry_name[i] = '\0'; - return; - } - entry_name[i] = (char)unicode_buf[i]; + if (unicode_buf[i] == 0xffff) { + entry_name[i] = '\0'; + return; + } + entry_name[i] = (char)unicode_buf[i]; } } @@ -433,384 +353,322 @@ static void long_entry_name(struct fat_long_name_entry *dir) { uint16_t unicode_buf[13]; - memcpy(unicode_buf, dir->name1, 5 * 2); - memcpy(unicode_buf + 5, dir->name2, 6 * 2); - memcpy(unicode_buf + 11,dir->name3, 2 * 2); - - unicode_to_ascii(entry_name, unicode_buf); + memcpy(unicode_buf, dir->name1, 5 * 2); + memcpy(unicode_buf + 5, dir->name2, 6 * 2); + memcpy(unicode_buf + 11, dir->name3, 2 * 2); + unicode_to_ascii(entry_name, unicode_buf); } static uint8_t get_checksum(char *dir_name) { int i; - uint8_t sum=0; + uint8_t sum = 0; - for (i=11; i; i--) - sum = ((sum & 1) << 7) + (sum >> 1) + *dir_name++; + for (i = 11; i; i--) + sum = ((sum & 1) << 7) + (sum >> 1) + *dir_name++; return sum; } + /* compute the first sector number of one dir where the data stores */ static inline sector_t first_sector(struct fat_dir_entry *dir) { - uint32_t first_clust, sector; + struct fat_sb_info *sbi = FAT_SB(this_fs); + uint32_t first_clust; + sector_t sector; first_clust = (dir->first_cluster_high << 16) + dir->first_cluster_low; - sector = ((first_clust - 2) << ClustShift) + DataArea; + sector = ((first_clust - 2) << sbi->clust_shift) + sbi->data; return sector; } +static inline int get_inode_mode(uint8_t attr) +{ + if (attr == FAT_ATTR_DIRECTORY) + return I_DIR; + else + return I_FILE; +} -/** - * search a specific directory for a pre-mangled filename in - * MangleBuf, in the directory starting in sector SECTOR - * - * NOTE: This file considers finding a zero-length file an - * error. This is so we don't have to deal with that special - * case elsewhere in the program (most loops have the test - * at the end). - * - * @param: MangleBuf - * @param: dir_sector, directory sector - * - * @out: file pointer - * @out: file length (MAY BE ZERO!) - * @out: file attribute - * - */ -static struct open_file_t* -search_dos_dir(struct fs_info *fs, char *MangleBuf, - uint32_t dir_sector, uint32_t *file_len, uint8_t *attr) + +static struct inode *vfat_find_entry(char *dname, struct inode *dir) { - struct open_file_t* file; - struct cache_struct* cs; - struct fat_dir_entry *dir; - struct fat_long_name_entry *long_dir; - - uint8_t VFATInit, VFATNext, VFATCsum; - uint8_t id; - uint32_t slots; - uint32_t entries; + struct inode *inode = new_fat_inode(); + struct fat_dir_entry *de; + struct fat_long_name_entry *long_de; + struct cache_struct *cs; + + char mangled_name[12] = {0, }; + sector_t dir_sector = *dir->data; + + uint8_t vfat_init, vfat_next, vfat_csum = 0; + uint8_t id; + int slots; + int entries; int checksum; - - file = allocate_file(); - if (!file) - return NULL; + int long_match = 0; - /* - * Compute the value of a possible VFAT longname - * "last" entry (which, of coures, comes first ...) - */ - slots = (NameLen + 12) / 13; - slots |= 0x40; - VFATInit = slots; - VFATNext = slots; - - do { - cs = get_cache_block(fs->fs_dev, dir_sector); - dir = (struct fat_dir_entry *)cs->data; - entries = SECTOR_SIZE / 32; - - /* scan all the entries in a sector */ - do { - if (dir->name[0] == 0) - return NULL; /* Hit directory high water mark */ - - if (dir->attr == 0x0f) { - /* it's a long name entry */ - long_dir = (struct fat_long_name_entry *)dir; - id = long_dir->id; - if (id !=VFATNext) - goto not_match; - - if (id & 0x40) { - /*get the initial checksum value*/ - VFATCsum = long_dir->checksum; - } else { - if (long_dir->checksum != VFATCsum) - goto not_match; - } - - id &= 0x3f; - VFATNext = --id; - - /* got the long entry name */ - long_entry_name(long_dir); - memcpy(long_name + id * 13, entry_name, 13); - - /* - * if we got the last entry? - * if so, check it, or go on with the next entry - */ - if (id == 0) { - if (strcmp(long_name, NameStart)) - goto not_match; - } - - goto next_entry; - - } else { - /* it's a short entry */ - if (dir->attr & 0x08) /* ingore volume labels */ - goto not_match; - - - /* If we have a long name match, then VFATNext must be 0 */ - if (!VFATNext) { - /* - * we already have a VFAT long name match, however, - * the match is only valid if the checksum matchs. - */ - checksum = get_checksum(dir->name); - if (checksum == VFATCsum) - goto found; /* got a match on long name */ - - } else { - if (strncmp(MangleBuf, dir->name, 11) == 0) - goto found; - } - } - - not_match:/* find it again */ - VFATNext = VFATInit; - - next_entry: - dir ++; - - }while (--entries); - - dir_sector = nextsector(fs, dir_sector); - - }while (dir_sector); /* scan another secotr */ - - found: - *file_len = file->file_bytesleft = dir->file_size; - file->file_sector = first_sector(dir); - *attr = dir->attr; + slots = (strlen(dname) + 12) / 13 ; + slots |= 0x40; + vfat_init = vfat_next = slots; + + while (1) { + cs = get_cache_block(this_fs->fs_dev, dir_sector); + de = (struct fat_dir_entry *)cs->data; + entries = 1 << (this_fs->sector_shift - 5); + + while(entries--) { + if (de->name[0] == 0) + return NULL; + + if (de->attr == 0x0f) { + /* + * It's a long name entry. + */ + long_de = (struct fat_long_name_entry *)de; + id = long_de->id; + if (id != vfat_next) + goto not_match; + + if (id & 0x40) { + /* get the initial checksum value */ + vfat_csum = long_de->checksum; + id &= 0x3f; + + /* ZERO the long_name buffer */ + memset(long_name, 0, sizeof long_name); + } else { + if (long_de->checksum != vfat_csum) + goto not_match; + } + + vfat_next = --id; + + /* got the long entry name */ + long_entry_name(long_de); + memcpy(long_name + id * 13, entry_name, 13); + + /* + * If we got the last entry, check it. + * Or, go on with the next entry. + */ + if (id == 0) { + if (strcmp(long_name, dname)) + goto not_match; + long_match = 1; + } + + de++; + continue; /* Try the next entry */ + } else { + /* + * It's a short entry + */ + if (de->attr & 0x08) /* ignore volume labels */ + goto not_match; + + if (long_match == 1) { + /* + * We already have a VFAT long name match. However, the + * match is only valid if the checksum matches. + * + * Well, let's trun the long_match flag off first. + */ + long_match = 0; + checksum = get_checksum(de->name); + if (checksum == vfat_csum) + goto found; /* Got it */ + } else { + if (mangled_name[0] == 0) { + /* We haven't mangled it, mangle it first. */ + mangle_dos_name(mangled_name, dname); + } + + if (!strncmp(mangled_name, de->name, 11)) + goto found; + } + } + + not_match: + vfat_next = vfat_init; + + de++; + } + + /* Try with the next sector */ + dir_sector = get_next_sector(this_fs, dir_sector); + if (!dir_sector) + return NULL; + } - return file; +found: + inode->size = de->file_size; + *inode->data = first_sector(de); + inode->mode = get_inode_mode(de->attr); + + return inode; } - - -/** - * open a file - * - * @param: filename, the file we wanna open - * @param: file_len, to return the file length - * - * @return: return the file structure on successful, or NULL. - * - */ -static void vfat_searchdir(char *filename, struct file *file) +static struct inode *vfat_iget_root(void) { - sector_t dir_sector; - uint32_t file_len = 0; - uint8_t attr = 0; - char *p; - struct open_file_t *open_file = NULL; - - dir_sector = CurrentDir; - if (*filename == '/') { - dir_sector = RootDir; - if (*(filename + 1) == 0) /* root dir is what we need */ - goto found_dir; - } - - while (*filename) { - if (*filename == '/') - filename++; /* skip '/' */ - p = filename; - if (*p == 0) - break; - PrevDir = dir_sector; - - /* try to find the end */ - while ((*p > ' ') && (*p != '/')) - p ++; - - if (filename == p) { - /* found a dir */ - dir_sector = PrevDir; - goto found_dir; - } - - mangle_dos_name(MangleBuf, filename); - /* close it before open a new dir file */ - if (open_file) - close_pvt(open_file); - open_file = search_dos_dir(file->fs, MangleBuf, dir_sector, &file_len, &attr); - if (!open_file) - goto fail; - - dir_sector = open_file->file_sector; - filename = p; - } + struct inode *inode = new_fat_inode(); + int root_size = FAT_SB(this_fs)->root_size; - if (attr & 0x10) { - found_dir: - open_file = alloc_fill_dir(dir_sector); - } else if ((attr & 0x18) || (file_len == 0)) { - fail: - file_len = 0; - open_file = NULL; - } else { - open_file->file_bytesleft = file_len; - open_file->file_left = (file_len + SECTOR_SIZE -1) >> SECTOR_SHIFT; - } - - file->file_len = file_len; - file->open_file = open_file; + inode->size = root_size << this_fs->sector_shift; + *inode->data = FAT_SB(this_fs)->root; + inode->mode = I_DIR; + + return inode; } -/* - * The open dir function, just call the searchdir function directly. - * I don't think we need call the mangle_name function first - */ -void vfat_opendir(com32sys_t *regs) +static struct inode *vfat_iget(char *dname, struct inode *parent) { - char *src = MK_PTR(regs->es, regs->esi.w[0]); - char *dst = MK_PTR(regs->ds, regs->edi.w[0]); - strcpy(dst, src); - searchdir(regs); + return vfat_find_entry(dname, parent); } -/* - * read one file from a directory; return the newly read de structure - */ -struct dirent* vfat_readdir(struct file *dir) +static struct dirent * vfat_readdir(struct file *file) { - uint32_t sector, sec_off; - /* make it to be 1 to check if we have met a long name entry before */ - uint8_t id = 1; - uint8_t init_id, next_id; - uint8_t checksum = 0; - uint8_t entries_left; - int i; - static struct dirent de; - char *de_name = de.d_name; - struct cache_struct *cs; - struct fat_dir_entry *fat_dir; - struct fat_long_name_entry *long_dir; - struct open_file_t *file = dir->open_file; - struct fs_info *fs = dir->fs; - - sector = file->file_sector; - sec_off = file->file_bytesleft; - if (!sector) - return NULL; - entries_left = (SECTOR_SIZE - sec_off) >> 5; + struct fs_info *fs = file->fs; + struct dirent *dirent; + struct fat_dir_entry *de; + struct fat_long_name_entry *long_de; + struct cache_struct *cs; + + sector_t sector = get_the_right_sector(file); + + uint8_t vfat_init, vfat_next, vfat_csum; + uint8_t id; + int entries_left; + int checksum; + int long_entry = 0; + int sec_off = file->offset & ((1 << fs->sector_shift) - 1); + cs = get_cache_block(fs->fs_dev, sector); - fat_dir = (struct fat_dir_entry *)(cs->data + sec_off);/* resume last position in sector */ + de = (struct fat_dir_entry *)(cs->data + sec_off); + entries_left = ((1 << fs->sector_shift) - sec_off) >> 5; while (1) { - if (!entries_left) { - sector = nextsector(fs, sector); - if (!sector) - return NULL; - cs = get_cache_block(fs->fs_dev, sector); - fat_dir = (struct fat_dir_entry *)cs->data; - } + while(entries_left--) { + if (de->name[0] == 0) + return NULL; + if ((uint8_t)de->name[0] == 0xe5) + goto invalid; + + if (de->attr == 0x0f) { + /* + * It's a long name entry. + */ + long_de = (struct fat_long_name_entry *)de; + id = long_de->id; - if (fat_dir->name[0] == 0) - return NULL; - if (fat_dir->attr == FAT_ATTR_LONG_NAME) { - /* it's a long name */ - long_dir = (struct fat_long_name_entry *)fat_dir; - - if (long_dir->id & 0x40) { - checksum = long_dir->checksum; - init_id = id = long_dir->id & 0x3f; - id--; - } else { - next_id = (long_dir->id & 0x3f) - 1; - id--; - if (id != next_id || long_dir->checksum != checksum) - goto next_entry; - } - - long_entry_name(long_dir); - memcpy(de_name + id * 13, entry_name, 13); - - /* - * we need go on with the next entry - * and we will fall through to next entry - */ - - } else { - /* it's a short entry */ - - if (!id) { - /* Got a long name match */ - if (get_checksum(fat_dir->name) != checksum) - goto next_entry; + if (id & 0x40) { + /* init vfat_csum and vfat_init */ + vfat_csum = long_de->checksum; + id &= 0x3f; + vfat_init = id; + + /* ZERO the long_name buffer */ + memset(long_name, 0, sizeof long_name); + } else { + if (long_de->checksum != vfat_csum || + id != vfat_next) + goto invalid; + } - break; + vfat_next = --id; + + /* got the long entry name */ + long_entry_name(long_de); + memcpy(long_name + id * 13, entry_name, 13); + + if (id == 0) + long_entry = 1; + + de++; + file->offset += sizeof(struct fat_dir_entry); + continue; /* Try the next entry */ + } else { + /* + * It's a short entry + */ + if (de->attr & 0x08) /* ignore volume labels */ + goto invalid; + + if (long_entry == 1) { + /* Got a long entry */ + checksum = get_checksum(de->name); + if (checksum == vfat_csum) + goto got; + } else { + /* Use the long_name buffer to store a short one. */ + int i; + char *p = long_name; + + for (i = 0; i < 8; i++) { + if (de->name[i] == ' ') + break; + *p++ = de->name[i]; + } + *p++ = '.'; + if (de->name[8] == ' ') { + *--p = '\0'; + } else { + for (i = 8; i < 11; i++) { + if (de->name[i] == ' ') + break; + *p++ = de->name[i]; + } + *p = '\0'; + } + + goto got; + } } - - if (fat_dir->attr & FAT_ATTR_VOLUME_ID || - get_checksum(fat_dir->name) != checksum ) - goto next_entry; - - for(i = 0; i < 8; i ++) { - if (fat_dir->name[i] == ' ') - break; - *de_name++ = fat_dir->name[i]; - } - *de_name++ = '.'; - for (i = 8; i < 11; i ++) { - if (fat_dir->name[i] == ' ') - break; - *de_name ++ = fat_dir->name[i]; - } - /* check if we have got an extention */ - if (*(de_name - 1) == '.') - *(de_name - 1) = '\0'; - else - *de_name = '\0'; - break; - } - - next_entry: - entries_left --; - fat_dir ++; + invalid: + de++; + file->offset += sizeof(struct fat_dir_entry); + } + + /* Try with the next sector */ + sector = get_next_sector(fs, sector); + if (!sector) + return NULL; + cs = get_cache_block(fs->fs_dev, sector); + de = (struct fat_dir_entry *)cs->data; + entries_left = 1 << (fs->sector_shift - 5); } - /* found what we want, fill the de structure */ - de.d_reclen = DIR_REC_LEN(de.d_name); - de.d_type = fat_dir->attr; - - /* update the DIR structure */ - entries_left--; - if (!entries_left) { - sector = nextsector(fs, sector); - file->file_bytesleft = 0; - } else { - file->file_bytesleft = SECTOR_SIZE - (entries_left << 5); +got: + if (!(dirent = malloc(sizeof(*dirent)))) { + malloc_error("dirent structure in vfat_readdir"); + return NULL; } - file->file_sector = sector; + dirent->d_ino = 0; /* Inode number is invalid to FAT fs */ + dirent->d_off = file->offset; + dirent->d_reclen = 0; + dirent->d_type = get_inode_mode(de->attr); + strcpy(dirent->d_name, long_name); - return &de; + file->offset += sizeof(*de); /* Update for next reading */ + + return dirent; } /* Load the config file, return 1 if failed, or 0 */ static int vfat_load_config(void) { - static const char syslinux_cfg1[] = "/boot/syslinux/syslinux.cfg"; - static const char syslinux_cfg2[] = "/syslinux/syslinux.cfg"; - static const char syslinux_cfg3[] = "/syslinux.cfg"; - static const char config_name[] = "syslinux.cfg"; - const char * const syslinux_cfg[] = - { syslinux_cfg1, syslinux_cfg2, syslinux_cfg3 }; + const char * const syslinux_cfg[] = { + "/boot/syslinux/syslinux.cfg", + "/syslinux/syslinux.cfg", + "/syslinux.cfg" + }; com32sys_t regs; + char *p; int i = 0; - - *(uint16_t *)CurrentDirName = ROOT_DIR_WORD; - CurrentDir = RootDir; /* * we use the ConfigName to pass the config path because @@ -830,11 +688,12 @@ static int vfat_load_config(void) printf("no config file found\n"); return 1; /* no config file */ } - - strcpy(ConfigName, config_name); + + strcpy(ConfigName, "syslinux.cfg"); strcpy(CurrentDirName, syslinux_cfg[i]); - CurrentDirName[strlen(syslinux_cfg[i])-strlen(config_name)] = '\0'; - CurrentDir = PrevDir; + p = strrchr(CurrentDirName, '/'); + *(p + 1) = '\0'; /* In case we met '/syslinux.cfg' */ + return 0; } @@ -847,49 +706,59 @@ static inline __constfunc uint32_t bsr(uint32_t num) /* init. the fs meta data, return the block size in bits */ static int vfat_fs_init(struct fs_info *fs) { - int sectors_per_fat; - uint32_t clust_num; - int RootDirSize; + struct fat_bpb fat; + struct fat_sb_info *sbi; struct disk *disk = fs->fs_dev->disk; + int sectors_per_fat; + uint32_t clust_num; + sector_t total_sectors; - /* get the fat bpb information */ + fs->sector_shift = fs->block_shift = disk->sector_shift; disk->rdwr_sectors(disk, &fat, 0, 1, 0); - TotalSectors = fat.bxSectors ? : fat.bsHugeSectors; - FAT = fat.bxResSectors; + sbi = malloc(sizeof(*sbi)); + if (!sbi) + malloc_error("fat_sb_info structure"); + fs->fs_info = sbi; + this_fs = fs; sectors_per_fat = fat.bxFATsecs ? : fat.u.fat32.bxFATsecs_32; - RootDir = RootDirArea = FAT + sectors_per_fat * fat.bxFATs; - RootDirSize = (fat.bxRootDirEnts+SECTOR_SIZE/32-1) >> (SECTOR_SHIFT-5); - DataArea = RootDirArea + RootDirSize; - - ClustShift = bsr(fat.bxSecPerClust); - ClustByteShift = ClustShift + SECTOR_SHIFT; - ClustMask = fat.bxSecPerClust - 1; - ClustSize = fat.bxSecPerClust << SECTOR_SHIFT; - - clust_num = (TotalSectors - DataArea) >> ClustShift; + total_sectors = fat.bxSectors ? : fat.bsHugeSectors; + + sbi->fat = fat.bxResSectors; + sbi->root = sbi->fat + sectors_per_fat * fat.bxFATs; + sbi->root_size = root_dir_size(&fat); + sbi->data = sbi->root + sbi->root_size; + + sbi->clust_shift = bsr(fat.bxSecPerClust); + sbi->clust_byte_shift = sbi->clust_shift + fs->sector_shift; + sbi->clust_mask = fat.bxSecPerClust - 1; + sbi->clust_size = fat.bxSecPerClust << fs->sector_shift; + + clust_num = (total_sectors - sbi->data) >> sbi->clust_shift; if (clust_num < 4085) - FATType = FAT12; + sbi->fat_type = FAT12; else if (clust_num < 65525) - FATType = FAT16; - else - FATType = FAT32; + sbi->fat_type = FAT16; + else + sbi->fat_type = FAT32; /* for SYSLINUX, the cache is based on sector size */ - return SECTOR_SHIFT; + return fs->sector_shift; } const struct fs_ops vfat_fs_ops = { .fs_name = "vfat", - .fs_flags = 0, + .fs_flags = FS_USEMEM | FS_THISIND, .fs_init = vfat_fs_init, - .searchdir = vfat_searchdir, + .searchdir = NULL, .getfssec = vfat_getfssec, .close_file = vfat_close_file, .mangle_name = vfat_mangle_name, .unmangle_name = generic_unmangle_name, .load_config = vfat_load_config, - .opendir = vfat_opendir, - .readdir = vfat_readdir + .readdir = vfat_readdir, + .iget_root = vfat_iget_root, + .iget_current = NULL, + .iget = vfat_iget, }; diff --git a/core/fs/fat/fat_fs.h b/core/fs/fat/fat_fs.h index 71c1d9a7..9453a67f 100644 --- a/core/fs/fat/fat_fs.h +++ b/core/fs/fat/fat_fs.h @@ -76,9 +76,26 @@ struct fat_bpb { } __attribute__ ((packed)) u; -} __attribute__ ((packed)); + uint8_t pad[422]; /* padding to 512 Bytes (one sector) */ +} __attribute__ ((packed)); +/* + * The fat file system info in memory + */ +struct fat_sb_info { + sector_t fat; /* The FAT region */ + sector_t root; /* The root dir region */ + int root_size; /* The root dir size in sectores */ + sector_t data; /* The data region */ + + int clust_shift; /* based on sectors */ + int clust_byte_shift; /* based on bytes */ + int clust_mask; + int clust_size; + + int fat_type; +} __attribute__ ((packed)); struct fat_dir_entry { char name[11]; @@ -108,7 +125,21 @@ struct fat_long_name_entry { uint16_t name3[2]; } __attribute__ ((packed)); +static inline struct fat_sb_info *FAT_SB(struct fs_info *fs) +{ + return fs->fs_info; +} +/* + * Count the root dir size in sectors + */ +static inline int root_dir_size(struct fat_bpb *fat) +{ + int sector_size = 1 << SECTOR_SHIFT; + + return (fat->bxRootDirEnts + sector_size / sizeof(struct fat_dir_entry) + - 1) >> (SECTOR_SHIFT - 5); +} #endif /* fat_fs.h */ diff --git a/core/fs/iso9660/iso9660.c b/core/fs/iso9660/iso9660.c index e060b13d..1670e607 100644 --- a/core/fs/iso9660/iso9660.c +++ b/core/fs/iso9660/iso9660.c @@ -1,77 +1,44 @@ #include <stdio.h> #include <string.h> +#include <sys/dirent.h> #include <core.h> +#include <cache.h> #include <disk.h> #include <fs.h> #include "iso9660_fs.h" -#define DEBUG 1 - -#define ISO_SECTOR_SHIFT 11 -#define ISO_SECTOR_SIZE (1 << ISO_SECTOR_SHIFT) -#define ROOT_DIR_WORD 0x002f -#define TRACKBUF_SIZE 8192 - -struct open_file_t { - sector_t file_sector; - uint32_t file_bytesleft; - uint32_t file_left; -}; -static struct open_file_t Files[MAX_OPEN]; - -struct dir_t { - uint32_t dir_lba; /* Directory start (LBA) */ - uint32_t dir_len; /* Length in bytes */ - uint32_t dir_clust; /* Length in clusters */ -}; -static struct dir_t RootDir; -static struct dir_t CurrentDir; - -static uint16_t BufSafe = TRACKBUF_SIZE >> ISO_SECTOR_SHIFT; - -static char ISOFileName[64]; /* ISO filename canonicalizatin buffer */ -static char *ISOFileNameEnd = &ISOFileName[64]; - -/* - * use to store the block shift, since we treat the hd-mode as 512 bytes - * sector size, 2048 bytes block size. we still treat the cdrom as 2048 - * bytes sector size and also the block size. - */ -static int block_shift; - -/* - * allocate a file structure - * - */ -static struct open_file_t *allocate_file(void) +static struct inode *new_iso_inode(void) { - struct open_file_t *file = Files; - int i; - - for (i = 0; i < MAX_OPEN; i++) { - if ( file->file_sector == 0 ) /* found it */ - return file; - file++; + struct inode *inode = malloc(sizeof(*inode)); + + if (!inode) { + malloc_error("inode structure in new_iso_inode"); + return NULL; } - - return NULL; /* not found */ + memset(inode, 0, sizeof(*inode)); + + inode->data = malloc(sizeof(uint32_t)); + if (!inode) { + malloc_error("inode->data in new_iso_inode"); + free(inode); + return NULL; + } + + return inode; } -/** - * close_file: - * - * Deallocates a file structure - * - */ -static inline void close_pvt(struct open_file_t *file) +static void iso_close_file(struct file *file) { - file->file_sector = 0; + if (file->inode) { + file->offset = 0; + free_inode(file->inode); + } } -static void iso_close_file(struct file *file) +static inline struct iso_sb_info * ISO_SB(struct fs_info *fs) { - close_pvt(file->open_file); + return fs->fs_info; } /* @@ -120,417 +87,375 @@ static void iso_mangle_name(char *dst, const char *src) *dst++ = '\0'; } -/** - * compare the names de_name and file_name and report if they are - * equal from an ISO 9600 perspective. - * - * @param: de_name, the name from the file system. - * @param: len, the length of de_name, and will return the real name of the de_name - * ';' and other terminates excluded. - * @param: file_name, the name we want to check, is expected to end with a null - * - * @return: 1 on match, or 0. - * - */ -static int iso_compare_names(char *de_name, int *len, char *file_name) +static int iso_convert_name(char *dst, char *src, int len) { - char *p = ISOFileName; - char c1, c2; - int i = 0; - - while ( (i < *len) && *de_name && (*de_name != ';') && (p < ISOFileNameEnd - 1) ) { - *p++ = *de_name++; - i++; + char c; + + for (; i < len; i++) { + c = src[i]; + if (!c) + break; + + /* remove ';1' in the end */ + if (c == ';' && i == len - 2 && src[i + 1] == '1') + break; + /* convert others ';' to '.' */ + if (c == ';') + c = '.'; + *dst++ = c; } - - /* Remove terminal dots */ - while ( *(p-1) == '.' ) { - if ( *len <= 2 ) - break; - - if ( p <= ISOFileName ) - break; - p --; - i--; + + /* Then remove the terminal dots */ + while (*(dst - 1) == '.') { + if (i <= 2) + break; + dst--; + i--; } + *dst = 0; + + return i; +} - if ( i <= 0 ) - return 0; - - *p = '\0'; - - /* return the 'real' length of de_name */ - *len = i; - - p = ISOFileName; - - /* i is the 'real' name length of file_name */ - while ( i ) { - c1 = *p++; - c2 = *file_name++; - - if ( (c1 == 0) && (c2 == 0) ) - return 1; /* success */ - - else if ( (c1 == 0) || ( c2 == 0 ) ) - return 0; - - c1 |= 0x20; - c2 |= 0x20; /* convert to lower case */ - if ( c1 != c2 ) - return 0; - i --; +/* + * Unlike strcmp, it does return 1 on match, or reutrn 0 if not match. + */ +static int iso_compare_name(char *de_name, int len, char *file_name) +{ + char iso_file_name[256]; + char *p = iso_file_name; + char c1, c2; + int i; + + i = iso_convert_name(iso_file_name, de_name, len); + + if (i != (int)strlen(file_name)) + return 0; + + while (i--) { + c1 = *p++; + c2 = *file_name++; + + /* convert to lower case */ + c1 |= 0x20; + c2 |= 0x20; + if (c1 != c2) + return 0; } - + return 1; } -static inline int cdrom_read_sectors(struct disk *disk, void *buf, int block, int count) +static inline int cdrom_read_blocks(struct disk *disk, void *buf, + int block, int blocks) { - /* changed those to _sector_ */ - block <<= block_shift; - count <<= block_shift; - return disk->rdwr_sectors(disk, buf, block, count, 0); + return disk->rdwr_sectors(disk, buf, block, blocks, 0); } -/** +/* * Get multiple clusters from a file, given the file pointer. - * - * @param: buf - * @param: file, the address of the open file structure - * @param: sectors, how many we want to read at once - * @param: have_more, to indicate if we have reach the end of the file - * */ -static uint32_t iso_getfssec(struct file *gfile, char *buf, - int sectors, bool *have_more) +static uint32_t iso_getfssec(struct file *file, char *buf, + int blocks, bool *have_more) { - uint32_t bytes_read = sectors << ISO_SECTOR_SHIFT; - struct open_file_t *file = gfile->open_file; - struct disk *disk = gfile->fs->fs_dev->disk; - - if ( sectors > file->file_left ) - sectors = file->file_left; - - cdrom_read_sectors(disk, buf, file->file_sector, sectors); - - file->file_sector += sectors; - file->file_left -= sectors; + struct fs_info *fs = file->fs; + struct disk *disk = fs->fs_dev->disk; + uint32_t bytes_read = blocks << fs->block_shift; + uint32_t bytes_left = file->inode->size - file->offset; + uint32_t blocks_left = (bytes_left + BLOCK_SIZE(file->fs) - 1) + >> file->fs->block_shift; + block_t block = *file->inode->data + (file->offset >> fs->block_shift); + + if (blocks > blocks_left) + blocks = blocks_left; + cdrom_read_blocks(disk, buf, block, blocks); - if ( bytes_read >= file->file_bytesleft ) { - bytes_read = file->file_bytesleft; + if (bytes_read >= bytes_left) { + bytes_read = bytes_left; *have_more = 0; - } else + } else { *have_more = 1; - file->file_bytesleft -= bytes_read; - + } + + file->offset += bytes_read; return bytes_read; } - - /* - * find a file or directory with name within the _dir_ directory. - * - * the return value will tell us what we find, it's a file or dir? - * on 1 be dir, 2 be file, 0 be error. res will return the result. - * + * Find a entry in the specified dir with name _dname_. */ -static int do_search_dir(struct fs_info *fs, struct dir_t *dir, - char *name, uint32_t *file_len, void **res) +static struct iso_dir_entry *iso_find_entry(char *dname, struct inode *inode) { - struct open_file_t *file; + block_t dir_block = *inode->data; + int i = 0, offset = 0; + char *de_name; + int de_name_len, de_len; struct iso_dir_entry *de; struct iso_dir_entry tmpde; - struct file xfile; - - uint32_t offset = 0; /* let's start it with the start */ - uint32_t file_pos = 0; - char *de_name; - int de_len; - int de_name_len; - bool have_more; - - file = allocate_file(); - if ( !file ) - return 0; - - file->file_left = dir->dir_clust; - file->file_sector = dir->dir_lba; - - xfile.fs = fs; - xfile.open_file = file; - - iso_getfssec(&xfile, trackbuf, BufSafe, &have_more); - de = (struct iso_dir_entry *)trackbuf; - - while ( file_pos < dir->dir_len ) { - int found = 0; - - if ( (char *)de >= (char *)(trackbuf + TRACKBUF_SIZE) ) { - if ( !have_more ) - return 0; - - iso_getfssec(&xfile, trackbuf, BufSafe, &have_more); - offset = 0; - } - - de = (struct iso_dir_entry *) (trackbuf + offset); - - de_len = de->length; - - if ( de_len == 0) { - offset = file_pos = (file_pos+ISO_SECTOR_SIZE) & ~(ISO_SECTOR_SIZE-1); - continue; - } - - - offset += de_len; - - /* Make sure we have a full directory entry */ - if ( offset >= TRACKBUF_SIZE ) { - int slop = TRACKBUF_SIZE - offset + de_len; - memcpy(&tmpde, de, slop); - offset &= TRACKBUF_SIZE - 1; - file->file_sector++; - if ( offset ) { - if ( !have_more ) - return 0; - iso_getfssec(&xfile, trackbuf, BufSafe, &have_more); - memcpy((void*)&tmpde + slop, trackbuf, offset); - } - de = &tmpde; - } - - if ( de_len < 33 ) { - printf("Corrutped directory entry in sector %d\n", file->file_sector); - return 0; - } - - de_name_len = de->name_len; - de_name = (char *)((void *)de + 0x21); - - - if ( (de_name_len == 1) && (*de_name == 0) ) { - found = iso_compare_names(".", &de_name_len, name); - - } else if ( (de_name_len == 1) && (*de_name == 1) ) { - de_name_len = 2; - found = iso_compare_names("..", &de_name_len, name); - - } else - found = iso_compare_names(de_name, &de_name_len, name); - - if (found) - break; - - file_pos += de_len; - } - - if ( file_pos >= dir->dir_len ) - return 0; /* not found */ - - - if ( *(name+de_name_len) && (*(name+de_name_len) != '/' ) ) { - printf("Something wrong happened during searching file %s\n", name); - - *res = NULL; - return 0; - } - - if ( de->flags & 0x02 ) { - /* it's a directory */ - dir = &CurrentDir; - dir->dir_lba = *(uint32_t *)de->extent; - dir->dir_len = *(uint32_t *)de->size; - dir->dir_clust = (dir->dir_len + ISO_SECTOR_SIZE - 1) >> ISO_SECTOR_SHIFT; - - *file_len = dir->dir_len; - *res = dir; - - /* we can close it now */ - close_pvt(file); - - /* Mark we got a directory */ - return 1; - } else { - /* it's a file */ - file->file_sector = *(uint32_t *)de->extent; - file->file_bytesleft = *(uint32_t *)de->size; - file->file_left = (file->file_bytesleft + ISO_SECTOR_SIZE - 1) >> ISO_SECTOR_SHIFT; - - *file_len = file->file_bytesleft; - *res = file; - - /* Mark we got a file */ - return 2; + struct cache_struct *cs = NULL; + + while (1) { + if (!cs) { + if (++i > inode->blocks) + return NULL; + cs = get_cache_block(this_fs->fs_dev, dir_block++); + de = (struct iso_dir_entry *)cs->data; + offset = 0; + } + de = (struct iso_dir_entry *)(cs->data + offset); + + de_len = de->length; + if (de_len == 0) { /* move on to the next block */ + cs = NULL; + continue; + } + offset += de_len; + + /* Make sure we have a full directory entry */ + if (offset >= BLOCK_SIZE(this_fs)) { + int slop = de_len + BLOCK_SIZE(this_fs) - offset; + + memcpy(&tmpde, de, slop); + offset &= BLOCK_SIZE(this_fs) - 1; + if (offset) { + if (++i > inode->blocks) + return NULL; + cs = get_cache_block(this_fs->fs_dev, dir_block++); + memcpy((void *)&tmpde + slop, cs->data, offset); + } + de = &tmpde; + } + + if (de_len < 33) { + printf("Corrupted directory entry in sector %u\n", + (uint32_t)(dir_block - 1)); + return NULL; + } + + de_name_len = de->name_len; + de_name = de->name; + /* Handling the special case ".' and '..' here */ + if((de_name_len == 1) && (*de_name == 0)) { + de_name = "."; + } else if ((de_name_len == 1) && (*de_name == 1)) { + de_name =".."; + de_name_len = 2; + } + if (iso_compare_name(de_name, de_name_len, dname)) + return de; } } - -/* - * open a file - * - * searchdir_iso is a special entry point for ISOLINUX only. In addition - * to the above, searchdir_iso passes a file flag mask in AL. This is - * useful for searching for directories. - * - * well, it's not like the searchidr function in EXT fs or FAT fs; it also - * can read a diretory.(Just thought of mine, liu) - * - */ -static void iso_searchdir(char *filename, struct file *file) +static inline int get_inode_mode(uint8_t flags) { - struct open_file_t *open_file = NULL; - struct dir_t *dir; - uint32_t file_len = 0; - int ret; - void *res; + if (flags & 0x02) + return I_DIR; + else + return I_FILE; +} - dir = &CurrentDir; - if ( *filename == '/' ) { - dir = &RootDir; - filename ++; - } +static struct inode *iso_get_inode(struct iso_dir_entry *de) +{ + struct inode *inode = new_iso_inode(); + + if (!inode) + return NULL; + inode->mode = get_inode_mode(de->flags); + inode->size = *(uint32_t *)de->size; + *inode->data = *(uint32_t *)de->extent; + inode->blocks = (inode->size + BLOCK_SIZE(this_fs) - 1) + >> this_fs->block_shift; + + return inode; +} - while ( *filename ) { - ret = do_search_dir(file->fs, dir, filename, &file_len, &res); - if ( ret == 1 ) - dir = (struct dir_t *)res; - else if ( ret == 2 ) - break; - else - goto err; - /* find the end */ - while ( *filename && (*filename != '/') ) - filename ++; +static struct inode *iso_iget_root(void) +{ + struct inode *inode = new_iso_inode(); + struct iso_dir_entry *root = &ISO_SB(this_fs)->root; + + if (!inode) + return NULL; + + inode->mode = I_DIR; + inode->size = *(uint32_t *)root->size; + *inode->data = *(uint32_t *)root->extent; + inode->blocks = (inode->size + BLOCK_SIZE(this_fs) - 1) + >> this_fs->block_shift; + + return inode; +} - /* skip the slash */ - while ( *filename && (*filename == '/') ) - filename++; - } +static struct inode *iso_iget(char *dname, struct inode *parent) +{ + struct iso_dir_entry *de; + + de = iso_find_entry(dname, parent); + if (!de) + return NULL; + + return iso_get_inode(de); +} - /* well , we need recheck it , becuase it can be a directory */ - if ( ret == 2 ) { - open_file = (struct open_file_t *)res; - goto found; - } else { - open_file = allocate_file(); - if ( !open_file ) - goto err; +/* Convert to lower case string */ +static void tolower_str(char *str) +{ + while (*str) { + if (*str >= 'A' && *str <= 'Z') + *str = *str + 0x20; + str++; + } +} - open_file->file_sector = dir->dir_lba; - open_file->file_bytesleft = dir->dir_len; - open_file->file_left = (dir->dir_len + ISO_SECTOR_SIZE - 1) >> ISO_SECTOR_SHIFT; - goto found; +static struct dirent *iso_readdir(struct file *file) +{ + struct fs_info *fs = file->fs; + struct inode *inode = file->inode; + struct iso_dir_entry *de, tmpde; + struct dirent *dirent; + struct cache_struct *cs = NULL; + block_t block = *file->inode->data + (file->offset >> fs->block_shift); + int offset = file->offset & (BLOCK_SIZE(fs) - 1); + int i = 0; + int de_len, de_name_len; + char *de_name; + + while (1) { + if (!cs) { + if (++i > inode->blocks) + return NULL; + cs = get_cache_block(fs->fs_dev, block++); + } + de = (struct iso_dir_entry *)(cs->data + offset); + + de_len = de->length; + if (de_len == 0) { /* move on to the next block */ + cs = NULL; + file->offset = (file->offset + BLOCK_SIZE(fs) - 1) + >> fs->block_shift; + continue; + } + offset += de_len; + + /* Make sure we have a full directory entry */ + if (offset >= BLOCK_SIZE(fs)) { + int slop = de_len + BLOCK_SIZE(fs) - offset; + + memcpy(&tmpde, de, slop); + offset &= BLOCK_SIZE(fs) - 1; + if (offset) { + if (++i > inode->blocks) + return NULL; + cs = get_cache_block(fs->fs_dev, block++); + memcpy((void *)&tmpde + slop, cs->data, offset); + } + de = &tmpde; + } + + if (de_len < 33) { + printf("Corrupted directory entry in sector %u\n", + (uint32_t)(block - 1)); + return NULL; + } + + de_name_len = de->name_len; + de_name = de->name; + /* Handling the special case ".' and '..' here */ + if((de_name_len == 1) && (*de_name == 0)) { + de_name = "."; + } else if ((de_name_len == 1) && (*de_name == 1)) { + de_name =".."; + de_name_len = 2; + } + + break; } - err: - close_pvt(open_file); - file_len = 0; - open_file = NULL; - - found: - file->file_len = file_len; - file->open_file = (void*)open_file; - -#if 0 - if (open_file) { - printf("file bytesleft: %d\n", open_file->file_bytesleft); - printf("file sector : %d\n", open_file->file_sector); - printf("file in sector: %d\n", open_file->file_in_sec); - printf("file offsector: %d\n", open_file->file_in_off); + + if (!(dirent = malloc(sizeof(*dirent)))) { + malloc_error("dirent structure in iso_readdir"); + return NULL; } -#endif + + dirent->d_ino = 0; /* Inode number is invalid to ISO fs */ + dirent->d_off = file->offset; + dirent->d_reclen = de_len; + dirent->d_type = get_inode_mode(de->flags); + iso_convert_name(dirent->d_name, de_name, de_name_len); + tolower_str(dirent->d_name); + + file->offset += de_len; /* Update for next reading */ + + return dirent; } /* Load the config file, return 1 if failed, or 0 */ static int iso_load_config(void) { - char *config_name = "isolinux.cfg"; + const char *config_file[] = { + "/boot/isolinux/isolinux.cfg", + "/isolinux/isolinux.cfg" + }; com32sys_t regs; + int i = 0; + char *p; - memset(®s, 0, sizeof regs); - strcpy(ConfigName, config_name); - regs.edi.w[0] = OFFS_WRT(ConfigName, 0); - call16(core_open, ®s, ®s); - - return !!(regs.eflags.l & EFLAGS_ZF); + for (; i < 2; i++) { + memset(®s, 0, sizeof regs); + strcpy(ConfigName, config_file[i]); + regs.edi.w[0] = OFFS_WRT(ConfigName, 0); + call16(core_open, ®s, ®s); + if (!(regs.eflags.l & EFLAGS_ZF)) + break; + } + if (i == 2) { + printf("No config file found\n"); + return 1; + } + + strcpy(ConfigName, "isolinux.cfg"); + strcpy(CurrentDirName, config_file[i]); + p = strrchr(CurrentDirName, '/'); + *p = '\0'; + + return 0; } static int iso_fs_init(struct fs_info *fs) { - char *iso_dir; - char *boot_dir = "/boot/isolinux"; - char *isolinux_dir = "/isolinux"; - int len; - int bi_pvd = 16; - struct file file; - struct open_file_t *open_file; - struct disk *disk = fs->fs_dev->disk; - - block_shift = ISO_SECTOR_SHIFT - disk->sector_shift; - cdrom_read_sectors(disk, trackbuf, bi_pvd, 1); - CurrentDir.dir_lba = RootDir.dir_lba = *(uint32_t *)(trackbuf + 156 + 2); - -#ifdef DEBUG - printf("Root directory at LBA = 0x%x\n", RootDir.dir_lba); -#endif - - CurrentDir.dir_len = RootDir.dir_len = *(uint32_t*)(trackbuf + 156 + 10); - CurrentDir.dir_clust = RootDir.dir_clust = (RootDir.dir_len + ISO_SECTOR_SIZE - 1) >> ISO_SECTOR_SHIFT; - - /* - * Look for an isolinux directory, and if found, - * make it the current directory instead of the - * root directory. - * - * Also copy the name of the directory to CurrrentDirName - */ - *(uint16_t *)CurrentDirName = ROOT_DIR_WORD; - - iso_dir = boot_dir; - file.fs = fs; - iso_searchdir(boot_dir, &file); /* search for /boot/isolinux */ - if ( !file.file_len ) { - iso_dir = isolinux_dir; - iso_searchdir(isolinux_dir, &file); /* search for /isolinux */ - if ( !file.file_len ) { - printf("No isolinux directory found!\n"); - return 0; - } + struct iso_sb_info *sbi; + + this_fs = fs; + + sbi = malloc(sizeof(*sbi)); + if (!sbi) { + malloc_error("iso_sb_info structure"); + return 1; } - - strcpy(CurrentDirName, iso_dir); - len = strlen(CurrentDirName); - CurrentDirName[len] = '/'; - CurrentDirName[len+1] = '\0'; - - open_file = (struct open_file_t *)file.open_file; - CurrentDir.dir_len = open_file->file_bytesleft; - CurrentDir.dir_clust = open_file->file_left; - CurrentDir.dir_lba = open_file->file_sector; - close_pvt(open_file); - -#ifdef DEBUG - printf("isolinux directory at LBA = 0x%x\n", CurrentDir.dir_lba); -#endif - - /* we do not use cache for now, so we can just return 0 */ - return 0; + fs->fs_info = sbi; + + cdrom_read_blocks(fs->fs_dev->disk, trackbuf, 16, 1); + memcpy(&sbi->root, trackbuf + ROOT_DIR_OFFSET, sizeof(sbi->root)); + + fs->block_shift = 11; + return fs->block_shift; } const struct fs_ops iso_fs_ops = { .fs_name = "iso", - .fs_flags = 0, + .fs_flags = FS_USEMEM | FS_THISIND, .fs_init = iso_fs_init, - .searchdir = iso_searchdir, + .searchdir = NULL, .getfssec = iso_getfssec, .close_file = iso_close_file, .mangle_name = iso_mangle_name, .unmangle_name = generic_unmangle_name, - .load_config = iso_load_config + .load_config = iso_load_config, + .iget_root = iso_iget_root, + .iget_current = NULL, + .iget = iso_iget, + .readdir = iso_readdir }; diff --git a/core/fs/iso9660/iso9660_fs.h b/core/fs/iso9660/iso9660_fs.h index ca123b16..e77ae109 100644 --- a/core/fs/iso9660/iso9660_fs.h +++ b/core/fs/iso9660/iso9660_fs.h @@ -3,6 +3,9 @@ #include <stdint.h> +/* The root dir entry offset in the primary volume descriptor */ +#define ROOT_DIR_OFFSET 156 + struct iso_dir_entry { uint8_t length; /* 00 */ uint8_t ext_attr_length; /* 01 */ @@ -14,8 +17,11 @@ struct iso_dir_entry { uint8_t interleave; /* 1b */ uint8_t volume_sequence_number[4]; /* 1c */ uint8_t name_len; /* 20 */ - //uint8_t name[]; /* 21 */ + char name[0]; /* 21 */ }; +struct iso_sb_info { + struct iso_dir_entry root; +}; #endif /* iso9660_fs.h */ diff --git a/core/fs/pxe/dhcp_option.c b/core/fs/pxe/dhcp_option.c index 7935f8ba..0787078a 100644 --- a/core/fs/pxe/dhcp_option.c +++ b/core/fs/pxe/dhcp_option.c @@ -224,28 +224,26 @@ void parse_dhcp_options(void *option, int size, int filter) } /* + * parse_dhcp * - ; - ; parse_dhcp - ; - ; Parse a DHCP packet. This includes dealing with "overloaded" - ; option fields (see RFC 2132, section 9.3) - ; - ; This should fill in the following global variables, if the - ; information is present: - ; - ; MyIP - client IP address - ; server_ip - boot server IP address - ; net_mask - network mask - ; gate_way - default gateway router IP - ; boot_file - boot file name - ; DNSServers - DNS server IPs - ; LocalDomain - Local domain name - ; MAC_len, MAC - Client identifier, if MAC_len == 0 - ; - ; This assumes the DHCP packet is in "trackbuf". - ; -*/ + * Parse a DHCP packet. This includes dealing with "overloaded" + * option fields (see RFC 2132, section 9.3) + * + * This should fill in the following global variables, if the + * information is present: + * + * MyIP - client IP address + * server_ip - boot server IP address + * net_mask - network mask + * gate_way - default gateway router IP + * boot_file - boot file name + * DNSServers - DNS server IPs + * LocalDomain - Local domain name + * MAC_len, MAC - Client identifier, if MAC_len == 0 + * + * This assumes the DHCP packet is in "trackbuf". + * + */ void parse_dhcp(int pkt_len) { struct bootp_t *dhcp = (struct bootp_t *)trackbuf; diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 0e6e2271..1bbb6b0a 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -1571,5 +1571,6 @@ const struct fs_ops pxe_fs_ops = { .close_file = pxe_close_file, .mangle_name = pxe_mangle_name, .unmangle_name = pxe_unmangle_name, - .load_config = pxe_load_config + .load_config = pxe_load_config, + .iget_current = NULL }; diff --git a/core/include/core.h b/core/include/core.h index dbcbff1c..1a9e1b96 100644 --- a/core/include/core.h +++ b/core/include/core.h @@ -23,6 +23,11 @@ extern void (*idle_hook_func)(void); /* hello.c */ extern void myputs(const char*); +/* malloc.c */ +extern void *malloc(int); +extern void free(void *); +extern void mem_init(void); + void __cdecl core_intcall(uint8_t, const com32sys_t *, com32sys_t *); void __cdecl core_farcall(uint32_t, const com32sys_t *, com32sys_t *); int __cdecl core_cfarcall(uint32_t, const void *, uint32_t); diff --git a/core/include/disk.h b/core/include/disk.h index 91f7a57a..55d24fbc 100644 --- a/core/include/disk.h +++ b/core/include/disk.h @@ -6,7 +6,6 @@ #include <stdbool.h> #define SECTOR_SHIFT 9 -#define SECTOR_SIZE (1 << SECTOR_SHIFT) typedef uint64_t sector_t; typedef uint64_t block_t; diff --git a/core/include/fs.h b/core/include/fs.h index df103023..f0fe5347 100644 --- a/core/include/fs.h +++ b/core/include/fs.h @@ -5,6 +5,7 @@ #include <stdbool.h> #include <string.h> #include <com32.h> +#include <stdio.h> #include "core.h" #include "disk.h" @@ -20,22 +21,30 @@ #define FILENAME_MAX_LG2 8 #define FILENAME_MAX (1 << FILENAME_MAX_LG2) +#define BLOCK_SIZE(fs) (1 << fs->block_shift) +#define SECTOR_SIZE(fs) (1 << fs->sector_shift) + struct fs_info { const struct fs_ops *fs_ops; struct device *fs_dev; + void *fs_info; /* The fs-specific information */ + int sector_shift; + int block_shift; }; -struct open_file_t; /* Filesystem private structure */ -struct dirent; /* Directory entry structure */ - -struct file { - struct open_file_t *open_file; /* Filesystem private data */ - struct fs_info *fs; - uint32_t file_len; -}; +extern struct fs_info *this_fs; +struct dirent; /* Directory entry structure */ +struct file; enum fs_flags { - FS_NODEV = 1, + FS_NODEV = 1 << 0, + FS_USEMEM = 1 << 1, /* If we need a malloc routine, set it */ + + /* + * Update the this_inode pointer at each part of path searching. This + * flag is just used for FAT and ISO fs for now. + */ + FS_THISIND = 1 << 2, }; struct fs_ops { @@ -51,11 +60,56 @@ struct fs_ops { char * (*unmangle_name)(char *, const char *); int (*load_config)(); + struct inode * (*iget_root)(void); + struct inode * (*iget_current)(void); + struct inode * (*iget)(char *, struct inode *); + char * (*follow_symlink)(struct inode *, const char *); + /* the _dir_ stuff */ - void (*opendir)(com32sys_t *); struct dirent * (*readdir)(struct file *); }; +enum inode_mode {I_FILE, I_DIR, I_SYMLINK}; + +/* + * The inode structure, including the detail file information + */ +struct inode { + int mode; /* FILE , DIR or SYMLINK */ + uint32_t size; + uint32_t ino; /* Inode number */ + uint32_t atime; /* Access time */ + uint32_t mtime; /* Modify time */ + uint32_t ctime; /* Create time */ + uint32_t dtime; /* Delete time */ + int blocks; /* How many blocks the file take */ + uint32_t * data; /* The block address array where the file stored */ + uint32_t flags; + uint32_t file_acl; +}; + +extern struct inode *this_inode; + +struct open_file_t; + +struct file { + struct fs_info *fs; + union { + /* For the new universal-path_lookup */ + struct { + struct inode *inode; /* The file-specific information */ + uint32_t offset; /* for next read */ + }; + + /* For the old searhdir method */ + struct { + struct open_file_t *open_file;/* The fs-specific open file struct */ + uint32_t file_len; + }; + }; +}; + + enum dev_type {CHS, EDD}; /* @@ -88,6 +142,21 @@ static inline bool not_whitespace(char c) return (unsigned char)c > ' '; } +static inline void free_inode(struct inode * inode) +{ + if (inode) { + if (inode->data) + free(inode->data); + free(inode); + } +} + +static inline void malloc_error(char *obj) +{ + printf("Out of memory: can't allocate memory for %s\n", obj); + kaboom(); +} + /* * functions */ diff --git a/core/malloc.c b/core/malloc.c new file mode 100644 index 00000000..a033ff03 --- /dev/null +++ b/core/malloc.c @@ -0,0 +1,216 @@ +/* + * A simple temp malloc for Sysliux project from fstk. For now, just used + * in fsc branch, which it's would be easy to remove it when we have a + * powerful one, as hpa said this would happen when elflink branch do the + * work. + * + * Copyright (C) 2009 Liu Aleaxander -- All rights reserved. This file + * may be redistributed under the terms of the GNU Public License. + */ + + +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +/* The memory managemant structure */ +struct mem_struct { + struct mem_struct *prev; + int size; + int free; +}; + + +/* First, assume we just need 64K memory */ +static char memory[0x10000]; + +/* Next free memory address */ +static struct mem_struct *next_start = (struct mem_struct *)memory; +static uint32_t mem_end = (uint32_t)(memory + 0x10000); + + +static inline struct mem_struct *get_next(struct mem_struct *mm) +{ + uint32_t next = (uint32_t)mm + mm->size; + + if (next >= mem_end) + return NULL; + else + return (struct mem_struct *)next; +} + +/* + * Here are the _merge_ functions, that merges a adjacent memory region, + * from front, or from back, or even merges both. It returns the headest + * region mem_struct. + * + */ + +static struct mem_struct * merge_front(struct mem_struct *mm, + struct mem_struct *prev) +{ + struct mem_struct *next = get_next(mm); + + prev->size += mm->size; + if (next) + next->prev = prev; + return prev; +} + +static struct mem_struct * merge_back(struct mem_struct *mm, + struct mem_struct *next) +{ + mm->free = 1; /* Mark it free first */ + mm->size += next->size; + + next = get_next(next); + if (next) + next->prev = mm; + return mm; +} + +static struct mem_struct * merge_both(struct mem_struct *mm, + struct mem_struct *prev, + struct mem_struct *next) +{ + prev->size += mm->size + next->size; + + next = get_next(next); + if (next) + next->prev = prev; + return prev; +} + +static inline struct mem_struct * try_merge_front(struct mem_struct *mm) +{ + mm->free = 1; + if (mm->prev->free) + mm = merge_front(mm, mm->prev); + return mm; +} + +static inline struct mem_struct * try_merge_back(struct mem_struct *mm) +{ + struct mem_struct *next = get_next(mm); + + mm->free = 1; + if (next->free) + merge_back(mm, next); + return mm; +} + +/* + * Here's the main function, malloc, which allocates a memory rigon + * of size _size_. Returns NULL if failed, or the address newly allocated. + * + */ +void *malloc(int size) +{ + struct mem_struct *next = next_start; + struct mem_struct *good = next, *prev; + int size_needed = (size + sizeof(struct mem_struct) + 3) & ~3; + + while(next) { + if (next->free && next->size >= size_needed) { + good = next; + break; + } + next = get_next(next); + } + if (good->size < size_needed) { + printf("Out of memory, maybe we need append it\n"); + return NULL; + } else if (good->size == size_needed) { + /* + * We just found a right memory that with the exact + * size we want. So we just Mark it _not_free_ here, + * and move on the _next_start_ pointer, even though + * the next may not be a right next start. + */ + good->free = 0; + next_start = get_next(good); + goto out; + } else + size = good->size; /* save the total size */ + + /* + * Note: allocate a new memory region will not change + * it's prev memory, so we don't need change it here. + */ + good->free = 0; /* Mark it not free any more */ + good->size = size_needed; + + next = get_next(good); + if (next) { + next->size = size - size_needed; + /* check if it can contain 1 byte allocation at least */ + if (next->size <= (int)sizeof(struct mem_struct)) { + good->size = size; /* restore the original size */ + next_start = get_next(good); + goto out; + } + + next->prev = good; + next->free = 1; + next_start = next; /* Update next_start */ + + prev = next; + next = get_next(next); + if (next) + next->prev = prev; + } else + next_start = (struct mem_struct *)memory; +out: + return (void *)((uint32_t)good + sizeof(struct mem_struct)); +} + +void free(void *ptr) +{ + struct mem_struct *mm = ptr - sizeof(*mm); + struct mem_struct *prev = mm->prev; + struct mem_struct *next = get_next(mm); + + if (!prev) + mm = try_merge_back(mm); + else if (!next) + mm = try_merge_front(mm); + else if (prev->free && !next->free) + merge_front(mm, prev); + else if (!prev->free && next->free) + merge_back(mm, next); + else if (prev->free && next->free) + merge_both(mm, prev, next); + else + mm->free = 1; + + if (mm < next_start) + next_start = mm; +} + +/* + * The debug function + */ +void check_mem(void) +{ + struct mem_struct *next = (struct mem_struct *)memory; + + printf("____________\n"); + while (next) { + printf("%-6d %s\n", next->size, next->free ? "Free" : "Notf"); + next = get_next(next); + } + printf("\n"); +} + + +void mem_init(void) +{ + + struct mem_struct *first = (struct mem_struct *)memory; + + first->prev = NULL; + first->size = 0x10000; + first->free = 1; + + next_start = first; +} |