From 1be29e2cdba4e406e3a7ca78e3959caacb925e75 Mon Sep 17 00:00:00 2001 From: Liu Aleaxander Date: Fri, 5 Jun 2009 09:29:13 +0800 Subject: [PATCH] Core: Convert the searchdir function to C for now, almost all the stuff related to EXT have been converted to C, and fow my (limit) test, it works well. The getfssec function haven't converted to C, because it also be called from asm file, and I find it's a bit hard to convert it to C. But however, it's my next plan. --- core/comboot.inc | 6 +- core/diskstart.inc | 1 + core/ext2.c | 317 ++++++++++++++++++++++++++++++++++++++++++++++++--- core/extern.inc | 2 +- core/extlinux.asm | 223 +----------------------------------- core/getc.inc | 2 +- core/include/core.h | 3 + core/parseconfig.inc | 2 +- core/runkernel.inc | 2 +- core/ui.inc | 2 +- 10 files changed, 315 insertions(+), 245 deletions(-) diff --git a/core/comboot.inc b/core/comboot.inc index 0710b96..1cb5ac6 100644 --- a/core/comboot.inc +++ b/core/comboot.inc @@ -519,7 +519,7 @@ comapi_open: mov di,InitRD call mangle_name pop ds - call searchdir + pm_call searchdir jz comapi_err mov P_EAX,eax mov P_CX,SECTOR_SIZE @@ -750,7 +750,7 @@ comapi_runkernel: mov di,KernelName call mangle_name pop ds - call searchdir + pm_call searchdir jz comapi_err ; The kernel image was found, so we can load it... @@ -906,7 +906,7 @@ comapi_opendir: mov di,InitRD call mangle_name pop ds - call searchdir + pm_call searchdir jnz comapi_err ; Didn't find a directory cmp eax,0 jz comapi_err ; Found nothing diff --git a/core/diskstart.inc b/core/diskstart.inc index 1b4d4d6..38222e0 100644 --- a/core/diskstart.inc +++ b/core/diskstart.inc @@ -489,6 +489,7 @@ ADVSectors dw 0 ; Additional sectors for ADVs LDLDwords dd 0 ; Total dwords starting at ldlinux_sys, CheckSum dd 0 ; Checksum starting at ldlinux_sys ; value = LDLINUX_MAGIC - [sum of dwords] + global CurrentDir CurrentDir dd 2 ; "Current" directory inode number (EXTLINUX) SecPtrOffset dw SectorPtrs - ldlinux_sys SecPtrCnt dw (SectorPtrsEnd - SectorPtrs) >> 2 diff --git a/core/ext2.c b/core/ext2.c index 28606ee..b7f6141 100644 --- a/core/ext2.c +++ b/core/ext2.c @@ -33,9 +33,37 @@ uint32_t PtrsPerBlock3; int DescPerBlock, InodePerBlock; -struct ext2_super_block *sb; +struct ext2_super_block *sb; +extern char trackbuf[8192]; + +#define MAX_SYMLINKS 64 +#define SYMLINK_SECTORS 2 +extern char SymlinkBuf[SYMLINK_SECTORS * SECTOR_SIZE + 64]; + + + +/** + * strecpy: + * + * just like the function strcpy(), except it returns non-zero if overflow. + * + * well, in Syslinux, strcpy() will advance both the dst and src string pointer. + * + */ +int strecpy(char *dst, char *src, char *end) +{ + while ( *src != '\0' ) + *dst++ = *src++; + *dst = '\0'; + + if ( dst > end ) + return 1; + else + return 0; +} + /** * allocate_file: @@ -61,6 +89,21 @@ struct open_file_t *allocate_file() /** + * close_file: + * + * Deallocates a file structure point by FILE + * + * @param: file, the file structure we want deallocate + * + */ +void close_file(struct open_file_t *file) +{ + if (file) + file->file_bytesleft = 0; +} + + +/** * get_group_desc: * * get the group's descriptor of group_num @@ -127,17 +170,14 @@ void read_inode(uint32_t inode_offset, * * open a file indicated by an inode number in INR * - * @param : regs, regs->eax stores the inode number - * @return: a open_file_t structure pointer, stores in regs->esi - * file length in bytes, stores in regs->eax + * @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 * */ -void open_inode(com32sys_t *regs) +struct open_file_t * open_inode(uint32_t inr, uint32_t *file_len) { - uint32_t inr = regs->eax.l; - uint32_t file_len; - struct open_file_t *file; struct ext2_group_desc *desc; @@ -147,7 +187,7 @@ void open_inode(com32sys_t *regs) file = allocate_file(); if (!file) - goto err; + return NULL; file->file_sector = 0; @@ -164,17 +204,12 @@ void open_inode(com32sys_t *regs) file->file_in_sec = (block_num<>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) - goto err; + *file_len = file->file_bytesleft = this_inode->i_size; - regs->esi.w[0] = file; - regs->eax.l = file_len; - return; + if (*file_len == 0) + return NULL; - err: - regs->eax.l = 0; + return file; } @@ -345,6 +380,252 @@ void linsector(com32sys_t *regs) } +/* + * 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); +} + + +/* + * p is at least 6 bytes before the end of page + */ +inline struct ext2_dir_entry *ext2_next_entry(struct ext2_dir_entry *p) +{ + return (struct ext2_dir_entry *)((char*)p + p->d_rec_len); +} + + +void getfssec_ext(char *buf, struct open_file_t *file, + int sectors, int *have_more) +{ + com32sys_t regs, out_regs; + + memset(®s, 0, sizeof regs); + memset(&out_regs, 0, sizeof out_regs); + + /* + * for now, the buf and file structure are stored at low + * address, so we can reference it safely by SEG() and + * OFFS() operation. + * + * !find we can't use SEG stuff here, say the address of buf + * is 0x800(found in debug), the addres would be broken like + * this: es = 0x80, bx = 0, while that's not the getfssec + * function need, the one that still be asm code now. + * + * so we just do like: + * regs.ebx.w[0] = buf; + */ + regs.ebx.w[0] = buf; + regs.esi.w[0] = file; + regs.ecx.w[0] = sectors; + + call16(getfssec, ®s, &out_regs); + + *have_more = 1; + + /* the file is closed ? */ + if( !out_regs.esi.w[0] ) + *have_more = 0; +} + + +/** + * find_dir_entry: + * + * find a dir entry, if find return it or return NULL + * + */ +struct ext2_dir_entry* find_dir_entry(struct open_file_t *file, char *filename) +{ + int have_more; + char *EndBlock = trackbuf + (SecPerClust << SECTOR_SHIFT);; + struct ext2_dir_entry *de; + + /* read a clust at a time */ + getfssec_ext(trackbuf, file, SecPerClust, &have_more); + de = (struct ext2_dir_entry *)trackbuf; + + while ( 1 ) { + if ( (char *)de >= (char *)EndBlock ) { + if (have_more) { + getfssec_ext(trackbuf, file,SecPerClust,&have_more); + de = (struct ext2_dir_entry *)trackbuf; + } else + return NULL; + } + + /* 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); + } +} + + +char* do_symlink(struct open_file_t *file, uint32_t file_len, char *filename) +{ + int flag, have_more; + + char *SymlinkTmpBuf = trackbuf; + char *lnk_end; + char *SymlinkTmpBufEnd = trackbuf + SYMLINK_SECTORS * SECTOR_SIZE+64; + + flag = this_inode->i_file_acl ? SecPerClust : 0; + + if ( this_inode->i_blocks == flag ) { + /* fast symlink */ + close_file(file); /* we've got all we need */ + memcpy(SymlinkTmpBuf, this_inode->i_block, file_len); + lnk_end = SymlinkTmpBuf + file_len; + + } else { + /* slow symlink */ + getfssec_ext(SymlinkTmpBuf,file,SYMLINK_SECTORS,&have_more); + lnk_end = SymlinkTmpBuf + file_len; + } + + /* + * well, this happens like: + * "/boot/xxx/y/z.abc" where xxx is a symlink to "/other/here" + * so, we should get a new path name like: + * "/other/here/y/z.abc" + */ + if ( *filename != 0 ) + *lnk_end++ = '/'; + + if ( strecpy(lnk_end, filename, SymlinkTmpBufEnd) ) + return NULL; /* buffer overflow */ + + /* + * 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); + + /* return the new path */ + return SymlinkBuf; +} + + + + +/** + * searchdir: + * + * Search the root directory for a pre-mangle filename in FILENAME. + * + * @param: filename, the filename we want to search. + * + * @out : a file pointer + * @out : file lenght in bytes + * + */ +void searchdir(com32sys_t * regs) +{ + extern int CurrentDir; + + struct open_file_t *file; + struct ext2_dir_entry *de; + uint8_t file_mode; + uint8_t SymlinkCtr = MAX_SYMLINKS; + uint32_t inr = CurrentDir; + uint32_t ThisDir; + uint32_t file_len; + char *filename = (char *)MK_PTR(regs->ds, regs->edi.w[0]); + + begin_path: + while ( *filename == '/' ) { /* Absolute filename */ + inr = EXT2_ROOT_INO; + filename ++; + } + open: + if ( (file = open_inode(inr, &file_len) ) == NULL ) + goto err_noclose; + + file_mode = file->file_mode >> S_IFSHIFT; + + /* It's a file */ + if ( file_mode == T_IFREG ) { + if ( *filename == '\0' ) + goto done; + else + goto err; + } + + + /* It's a directory */ + if ( file_mode == T_IFDIR ) { + ThisDir = inr; + + if ( *filename == 0 ) + goto err; + while ( *filename == '/' ) + filename ++; + + de = find_dir_entry(file, filename); + if ( !de ) + goto err; + + inr = de->d_inode; + filename += de->d_name_len; + close_file(file); + goto open; + } + + + /* + * 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, file_len, filename); + if ( !filename ) + goto err_noclose;/* buffer overflow */ + + inr = ThisDir; + goto begin_path; /* we got a new path, so search it again */ + } + + /* Otherwise, something bad ... */ + err: + close_file(file); + err_noclose: + file_len = 0; + file = NULL; + done: + + regs->eax.l = file_len; + regs->esi.w[0] = file; +} + /** * init. the fs meta data, return the block size in eax diff --git a/core/extern.inc b/core/extern.inc index c8288fe..e8e0a69 100644 --- a/core/extern.inc +++ b/core/extern.inc @@ -19,6 +19,6 @@ extern load_config ; ext2.c - extern init_fs, linsector, open_inode + extern init_fs, linsector, searchdir %endif ; EXTERN_INC diff --git a/core/extlinux.asm b/core/extlinux.asm index e4e0fed..9e9b652 100644 --- a/core/extlinux.asm +++ b/core/extlinux.asm @@ -88,6 +88,7 @@ file_mode resw 1 ; Memory below this point is reserved for the BIOS and the MBR ; section .earlybss + global trackbuf trackbufsize equ 8192 trackbuf resb trackbufsize ; Track buffer goes here ; ends at 2800h @@ -215,229 +216,12 @@ close_file: xor si,si .closed: ret -; -; searchdir: -; Search the root directory for a pre-mangled filename in DS:DI. -; -; 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). -; -; If successful: -; ZF clear -; SI = file pointer -; DX:AX = EAX = file length in bytes -; If unsuccessful -; ZF set -; -; Assumes CS == DS == ES; *** IS THIS CORRECT ***? -; -searchdir: - push bx - push cx - push bp - mov byte [SymlinkCtr],MAX_SYMLINKS - - mov eax,[CurrentDir] -.begin_path: -.leadingslash: - cmp byte [di],'/' ; Absolute filename? - jne .gotdir - mov eax,EXT2_ROOT_INO - inc di ; Skip slash - jmp .leadingslash -.gotdir: - - ; At this point, EAX contains the directory inode, - ; and DS:DI contains a pathname tail. -.open: - push eax ; Save directory inode - - pm_call open_inode - jz .missing ; If error, done - - mov cx,[si+file_mode] - shr cx,S_IFSHIFT ; Get file type - - cmp cx,T_IFDIR - je .directory - - add sp,4 ; Drop directory inode - - cmp cx,T_IFREG - je .file - cmp cx,T_IFLNK - je .symlink - - ; Otherwise, something bad... -.err: - call close_file -.err_noclose: - xor eax,eax - xor si,si - cwd ; DX <- 0 - -.done: - and eax,eax ; Set/clear ZF - pop bp - pop cx - pop bx - ret - -.missing: - add sp,4 ; Drop directory inode - jmp .done - - ; - ; It's a file. - ; -.file: - cmp byte [di],0 ; End of path? - je .done ; If so, done - jmp .err ; Otherwise, error - - ; - ; It's a directory. - ; -.directory: - pop dword [ThisDir] ; Remember what directory we're searching - - cmp byte [di],0 ; More path? - je .err ; If not, bad - -.skipslash: ; Skip redundant slashes - cmp byte [di],'/' - jne .readdir - inc di - jmp .skipslash - -.readdir: - mov cx,[SecPerClust] - push cx - shl cx,SECTOR_SHIFT - mov bx,trackbuf - add cx,bx - mov [EndBlock],cx - pop cx - push bx - call getfssec - pop bx - pushf ; Save EOF flag - push si ; Save filesystem pointer -.getent: - cmp bx,[EndBlock] - jae .endblock - - push di - cmp dword [bx+d_inode],0 ; Zero inode = void entry - je .nope - - movzx cx,byte [bx+d_name_len] - lea si,[bx+d_name] - repe cmpsb - je .maybe -.nope: - pop di - add bx,[bx+d_rec_len] - jmp .getent - -.endblock: - pop si - popf - jnc .readdir ; There is more - jmp .err ; Otherwise badness... - -.maybe: - mov eax,[bx+d_inode] - - ; Does this match the end of the requested filename? - cmp byte [di],0 - je .finish - cmp byte [di],'/' - jne .nope - - ; We found something; now we need to open the file -.finish: - pop bx ; Adjust stack (di) - pop si - call close_file ; Close directory - pop bx ; Adjust stack (flags) - jmp .open - - ; - ; 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. - ; -.symlink: - dec byte [SymlinkCtr] - jz .err ; Too many symlink references - - cmp eax,SYMLINK_SECTORS*SECTOR_SIZE - jae .err ; Symlink too long - - ; Computation for fast symlink, as defined by ext2/3 spec - xor ecx,ecx - cmp [ThisInode+i_file_acl],ecx - setne cl ; ECX <- i_file_acl ? 1 : 0 - cmp [ThisInode+i_blocks],ecx - jne .slow_symlink - - ; It's a fast symlink -.fast_symlink: - call close_file ; We've got all we need - mov si,ThisInode+i_block - - push di - mov di,SymlinkTmpBuf - mov ecx,eax - rep movsb - pop si - -.symlink_finish: - cmp byte [si],0 - je .no_slash - mov al,'/' - stosb -.no_slash: - mov bp,SymlinkTmpBufEnd - call strecpy - jc .err_noclose ; Buffer overflow - - ; Now copy it to the "real" buffer; we need to have - ; two buffers so we avoid overwriting the tail on the - ; next copy - mov si,SymlinkTmpBuf - mov di,SymlinkBuf - push di - call strcpy - pop di - mov eax,[ThisDir] ; Resume searching previous directory - jmp .begin_path - -.slow_symlink: - mov bx,SymlinkTmpBuf - mov cx,SYMLINK_SECTORS - call getfssec - ; The EOF closed the file - - mov si,di ; SI = filename tail - mov di,SymlinkTmpBuf - add di,ax ; AX = file length - jmp .symlink_finish - - section .bss16 alignb 4 + global SymlinkBuf SymlinkBuf resb SYMLINK_SECTORS*SECTOR_SIZE+64 -SymlinkTmpBuf equ trackbuf -SymlinkTmpBufEnd equ trackbuf+SYMLINK_SECTORS*SECTOR_SIZE+64 ThisDir resd 1 -EndBlock resw 1 -SymlinkCtr resb 1 + section .text16 ; @@ -538,6 +322,7 @@ kaboom2: ; On return ECX = number of bytes read ; All arguments are advanced to reflect data read. ; + global getfssec getfssec: push ebp push eax diff --git a/core/getc.inc b/core/getc.inc index e74fa12..b40a420 100644 --- a/core/getc.inc +++ b/core/getc.inc @@ -62,7 +62,7 @@ getc_file_lg2 equ 4 ; Size of getc_file as a power of 2 ; global core_open core_open: - call searchdir + pm_call searchdir jz openfd.ret openfd: push bx diff --git a/core/include/core.h b/core/include/core.h index 574fd7a..a57dac2 100644 --- a/core/include/core.h +++ b/core/include/core.h @@ -14,6 +14,9 @@ extern char ConfigName[]; /* diskstart.inc */ extern void getlinsec(void); +/* extlinux.asm */ +extern void getfssec(void); + /* getc.inc */ extern void core_open(void); diff --git a/core/parseconfig.inc b/core/parseconfig.inc index aecf463..f8cead0 100644 --- a/core/parseconfig.inc +++ b/core/parseconfig.inc @@ -148,7 +148,7 @@ pc_filecmd: push ax ; Function to tailcall call pc_getline mov di,MNameBuf call mangle_name - call searchdir + pm_call searchdir jnz .ok pop ax ; Drop the successor function .ok: ret ; Tailcall if OK, error return diff --git a/core/runkernel.inc b/core/runkernel.inc index 8d0f296..7130210 100644 --- a/core/runkernel.inc +++ b/core/runkernel.inc @@ -589,7 +589,7 @@ loadinitrd: sub di,InitRDCName mov [InitRDCNameLen],di mov di,InitRD - call searchdir ; Look for it in directory + pm_call searchdir ; Look for it in directory pop edi jz .notthere diff --git a/core/ui.inc b/core/ui.inc index 5b05536..15ae449 100644 --- a/core/ui.inc +++ b/core/ui.inc @@ -448,7 +448,7 @@ get_kernel: mov byte [KernelName+FILENAME_MAX],0 ; Zero-terminate filename/e mov bx,exten_table .search_loop: push bx mov di,KernelName ; Search on disk - call searchdir + pm_call searchdir pop bx jnz kernel_good mov eax,[bx] ; Try a different extension -- 2.7.4