Gene Cumm
2008-Dec-04 18:47 UTC
[syslinux] [PATCH 1/1] COMBOOT API: Add calls for directory functions; Implement for FAT
From: Gene Cumm <gene.cumm at gmail.com> COMBOOT API: Add calls for directory functions; Implement most only for FAT (SYSLINUX). Uses INT 22h AX= 001Eh, 001Fh, 0020h and 0021h to prepare for the COM32 C functions getcwd(), opendir(), readdir(), and closedir(), respectively. INT22h, AX=001Eh will return a valid value for all variants. INT22h, AX= 001Fh, 0020h, and 0021h are only implemented for SYSLINUX while other variants will call comapi_err for these 3. Signed-off-by: Gene Cumm <gene.cumm at gmail.com> --- Adds an API call to obtain the current working directory. EXTLINUX will not return the correct value yet however SYSLINUX, ISOLINUX, and PXELINUX will return the correct value. For the moment, EXTLINUX will ONLY return "./" (null-terminated of course). In SYSLINUX and ISOLINUX, it presets the CurrentDirName string to "/" for safety. PXELINUX copies TFTP Prefix to CurrentDirName. Also adds API calls for opening a directory, reading one filename and some of its info and closing the directory. These API calls are only implemented in SYSLINUX for now. ISOLINUX will probably be next on my list (after a codepage fix). It uses the existing array Files for now but I can easily tweak it to use its own array (I never saw any response to my request for comments). With regards to character sets, I will need to change some of how I'm coding this as I'm merely truncating the wide character to a 1-byte character for now. Quick but dirty. Peter, I know the codepage is usable for matching but does it provide for this sort of functionality (wide character to byte character conversion)? It doesn't look it (unless I'm thinking wrong). I also just found that the directories "." and ".." are returned as ".." and "..." respectively (makes sense with how it's coded. Time for a special case). Currently, I would classify this as BETA as it has had testing but only by me. I've been able to successfully handle short filenames, single entry long filenames, multi-entry filenames with an ending 0000h and multi-entry filenames ending on a directory entry line (32 bytes). Positive and negative testing results and constructive criticism are welcome. Based on current head. Conflicts with my patch for obtaining Full Config File Name. Includes my previous patch for getting current working directory. I've checked it against the checkpatch.pl and it returned no errors. Is there a better way? linux:Documentation/CodingStyle alludes to GCC's documentation on RTL for styling. diff --git a/core/comboot.inc b/core/comboot.inc index 810d825..24c69e2 100644 --- a/core/comboot.inc +++ b/core/comboot.inc @@ -1033,6 +1033,72 @@ comapi_getadv: ; comapi_writeadv equ adv_write +; +; INT 22h AX=001Eh Get current working directory +; +comapi_getcwd: + mov P_ES,cs + mov P_BX,CurrentDirName + clc + ret + +; +; INT 22h AX=001Fh Open directory +; +%if IS_SYSLINUX +comapi_opendir: + push ds + mov ds,P_ES + mov si,P_SI + mov di,InitRD + call mangle_name + pop ds + call searchdir + jnz comapi_err ; Didn't find a directory + cmp eax,0 + jz comapi_err ; Found nothing + ;ZF is unset + call alloc_fill_dir + mov P_EAX,eax + mov P_CX,SECTOR_SIZE + mov P_SI,si + clc + ret +%else +comapi_opendir equ comapi_err +%endif + +; +; INT 22h AX=0020h Read directory +; +%if IS_SYSLINUX +comapi_readdir: + mov es,P_ES + mov di,P_DI + mov si,P_SI + call readdir + mov P_EAX,eax + mov P_DL,dl + mov P_EBX,ebx + mov P_SI,si + ret +%else +comapi_readdir equ comapi_err +%endif + +; +; INT 22h AX=0021h Close directory +; +%if IS_SYSLINUX +comapi_closedir: + mov si,P_SI + call close_dir + clc + ret +%else +comapi_closedir equ comapi_err +%endif + section .data %macro int21 2 @@ -1085,6 +1151,10 @@ int22_table: dw comapi_shufflerm ; 001B cleanup, shuffle and boot to rm dw comapi_getadv ; 001C get pointer to ADV dw comapi_writeadv ; 001D write ADV to disk + dw comapi_getcwd ; 001E get current working directory + dw comapi_opendir ; 001F open directory + dw comapi_readdir ; 0020 read directory + dw comapi_closedir ; 0021 close directory int22_count equ ($-int22_table)/2 APIKeyWait db 0 @@ -1109,3 +1179,4 @@ err_comlarge db 'COMBOOT image too large.', CR, LF, 0 alignb 4 DOSErrTramp resd 33 ; Error trampolines ConfigName resb FILENAME_MAX +CurrentDirName resb FILENAME_MAX diff --git a/core/extlinux.asm b/core/extlinux.asm index 24d0d92..c7a51e9 100644 --- a/core/extlinux.asm +++ b/core/extlinux.asm @@ -42,6 +42,9 @@ MAX_SYMLINKS equ 64 ; Maximum number of symlinks per lookup SYMLINK_SECTORS equ 2 ; Max number of sectors in a symlink ; (should be >= FILENAME_MAX) +ROOT_DIR_WORD equ 0x002F +CUR_DIR_DWORD equ 0x00002F2E + ; ; This is what we need to do when idle ; @@ -843,6 +846,8 @@ load_config: mov si,config_name ; Save config file name mov di,ConfigName call strcpy + mov dword [CurrentDirName],CUR_DIR_DWORD ; Write './',0,0 to the CurrentDirName + call build_curdir_str mov di,ConfigName call open @@ -1515,6 +1520,9 @@ getfssec: pop ebp ret +build_curdir_str: + ret + ; ----------------------------------------------------------------------------- ; Common modules ; ----------------------------------------------------------------------------- diff --git a/core/isolinux.asm b/core/isolinux.asm index 3b97005..2c6d970 100644 --- a/core/isolinux.asm +++ b/core/isolinux.asm @@ -36,6 +36,8 @@ MAX_OPEN equ (1 << MAX_OPEN_LG2) SECTOR_SHIFT equ 11 ; 2048 bytes/sector (El Torito requirement) SECTOR_SIZE equ (1 << SECTOR_SHIFT) +ROOT_DIR_WORD equ 0x002F + ; ; This is what we need to do when idle ; @@ -1147,15 +1149,33 @@ get_fs_structures: ; 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 CurrentDirName + mov word [CurrentDirName],ROOT_DIR_WORD ; Write '/',0 to the CurrentDirName mov di,boot_dir ; Search for /boot/isolinux mov al,02h + push di call searchdir_iso + pop di jnz .found_dir mov di,isolinux_dir mov al,02h ; Search for /isolinux + push di call searchdir_iso + pop di jz .no_isolinux_dir .found_dir: + ; Copy current directory name to CurrentDirName + push si + push di + mov si,di + mov di,CurrentDirName + call strcpy + mov byte [di],0 ;done in case it's not word aligned + dec di + mov byte [di],'/' + pop di + pop si + mov [CurrentDir+dir_len],eax mov eax,[si+file_left] mov [CurrentDir+dir_clust],eax diff --git a/core/ldlinux.asm b/core/ldlinux.asm index c7f6577..e5983b5 100644 --- a/core/ldlinux.asm +++ b/core/ldlinux.asm @@ -44,6 +44,11 @@ MAX_OPEN equ (1 << MAX_OPEN_LG2) SECTOR_SHIFT equ 9 SECTOR_SIZE equ (1 << SECTOR_SHIFT) +DIRENT_SHIFT equ 5 +DIRENT_SIZE equ (1 << DIRENT_SHIFT) + +ROOT_DIR_WORD equ 0x002F + ; ; This is what we need to do when idle ; @@ -900,19 +905,40 @@ getfattype: mov si,config_name ; Save configuration file name mov di,ConfigName call strcpy + mov word [CurrentDirName],ROOT_DIR_WORD ; Write '/',0 to the CurrentDirName mov eax,[RootDir] ; Make the root directory ... mov [CurrentDir],eax ; ... the current directory mov di,syslinux_cfg1 + push di call open + pop di jnz .config_open mov di,syslinux_cfg2 + push di call open + pop di jnz .config_open mov di,syslinux_cfg3 + push di call open + pop di jz no_config_file .config_open: + push si + mov si,di + push si + mov di,CurrentDirName + ; This is inefficient as it will copy more than needed + ; but not by too much + call strcpy + mov ax,config_name ;Cut it down + pop si + sub ax,si + mov di,CurrentDirName + add di,ax + mov byte [di],0 + pop si mov eax,[PrevDir] ; Make the directory with syslinux.cfg ... mov [CurrentDir],eax ; ... the current directory @@ -945,6 +971,37 @@ allocate_file: ret ; +; alloc_fill_dir: +; Allocate then fill a file structure for a directory starting in +; sector EAX. +; +; Assumes DS == ES == CS. +; +; If successful: +; ZF clear +; SI = file pointer +; If unsuccessful +; ZF set +; EAX clobbered +; +alloc_fill_dir: + push bx + call allocate_file + jnz .alloc_failure +.found: + mov si,bx + mov [si+file_sector],eax ; Current sector + mov dword [si+file_bytesleft],0 ; Current offset + mov [si+file_left],eax ; Beginning sector + pop bx + ret + +.alloc_failure: + pop bx + xor eax,eax ; ZF <- 1 + ret + +; ; search_dos_dir: ; Search a specific directory for a pre-mangled filename in ; MangledBuf, in the directory starting in sector EAX. @@ -1191,6 +1248,18 @@ close_file: .closed: ret ; +; close_dir: +; Deallocates a directory structure (pointer in SI) +; Assumes CS == DS. +; +close_dir: + and si,si + jz .closed + mov dword [si],0 ; First dword == file_sector + xor si,si +.closed: ret + +; ; searchdir: ; ; Open a file @@ -1224,9 +1293,17 @@ searchdir: cmp al,'/' jne .findend .endpath: - xchg si,di + xchg si,di ; GRC: si begin; di end[ /]+1 pop eax ; <A> Current directory sector + ; GRC Here I need to check if di-1 = si which signifies + ; we have the desired directory in EAX + ; What about where the file name = "."; later + mov dx,di + dec dx + cmp dx,si + jz .founddir + mov [PrevDir],eax ; Remember last directory searched push di @@ -1263,12 +1340,221 @@ searchdir: xchg eax,[si+file_sector] ; Get sector number and free file structure jmp .pathwalk ; Walk the next bit of the path + ; Found the desired directory; ZF set but EAX not 0 +.founddir: + ret + .badfile: xor eax,eax mov [si],eax ; Free file structure .notfound: + xor eax,eax ; Zero out EAX + ret + +; +; readdir: Read one file from a directory +; +; ES:DI -> String buffer (filename) +; DS:SI -> Pointer to open_file_t +; +; Returns the file's name in the filename string buffer +; EAX returns the file size +; EBX returns the beginning sector (currently without offsetting) +; DL returns the file type +; The directory handle's data is incremented to reflect a name read. +; +readdir: + push ecx + push si + push gs + cmp si,0 + jz .fail +.load_handle: + mov eax,[ds:si+file_sector] ; Current sector + mov ebx,[ds:si+file_bytesleft] ; Current offset + cmp eax,0 + jz .fail +.fetch_cache: + call getcachesector +.move_current: + add si,bx ; Resume last position in sector + mov ecx,SECTOR_SIZE ; 0 out high part + sub cx,bx + shr cx,5 ; Number of entries left +.scanentry: + cmp byte [gs:si],0 + jz .fail + cmp word [gs:si+11],0Fh ; Long filename + jne .short_entry + +.vfat_entry: + push eax + push ecx + push si + push di +.vfat_ln_info: ; Get info about the line that we're on + mov al,[gs:si] + test al,40h + jz .vfat_tail_ln + and al,03Fh + mov ah,1 ; On beginning line + jmp .vfat_cp_ln + +.vfat_tail_ln: ; VFAT tail line processing (later in VFAT, head in name) + test al,80h ; Invalid data? + jnz .vfat_abort + mov ah,0 ; Not on beginning line + cmp dl,al + jne .vfat_abort ; Is this the entry we need? + +.vfat_cp_ln: ; Copy VFAT line + dec al ; Store the next line we need + mov dx,ax ; Use DX to store the progress + mov bx,13 + mov ah,0 + mul bl ; Offset for DI + add di,ax ; Increment DI + inc si ; Align to the real characters + mov cx,13 ; 13 characters per VFAT DIRENT +.vfat_cp_chr: + gs lodsb + inc si ; Unicode here!! + stosb + cmp al,0 + jz .vfat_find_next ; Null-terminated string; don't process more + cmp cx,3 + je .vfat_adj_add2 + cmp cx,9 + jne .vfat_adj_add0 +.vfat_adj_add3: inc si +.vfat_adj_add2: inc si +.vfat_adj_add1: inc si +.vfat_adj_add0: + loop .vfat_cp_chr + cmp dh,1 ; Is this the first round? + jnz .vfat_find_next +.vfat_null_term: ; Need to null-terminate if first line as we rolled over the end + mov al,0 + stosb + +.vfat_find_next: ;Find the next part of the name + pop di + pop si + pop ecx + pop eax + cmp dl,0 + jz .vfat_find_info ; We're done with the name + add si,DIRENT_SIZE + dec cx + jnz .vfat_entry + call nextsector + jnc .vfat_entry ; CF is set if we're at end + jmp .fail +.vfat_find_info: ; Fetch next entry for the size/"INode" + add si,DIRENT_SIZE + dec cx + jnz .get_info + call nextsector + jnc .get_info ; CF is set if we're at end + jmp .fail +.vfat_abort: ; Something went wrong, skip + pop di + pop si + pop ecx + pop eax + jmp .skip_entry + +.short_entry: + test byte [gs:si+11],8 ; Ignore volume labels //HERE + jnz .skip_entry + mov edx,eax ;Save current sector + push cx + push si + push di + mov cx,8 +.short_file: + gs lodsb + cmp al,' ' + jz .short_skip_bs + stosb + loop .short_file + jmp .short_period +.short_skip_bs: ; skip blank spaces in FILENAME (before EXT) + add si,cx + dec si +.short_period: + mov al,'.' + stosb + mov cx,3 +.short_ext: + gs lodsb + cmp al,' ' + jz .short_done + stosb + loop .short_ext +.short_done: + mov al,0 ; Null-terminate the short strings + stosb + pop di + pop si + pop cx + mov eax,edx +.get_info: + mov ebx,[gs:si+28] ; length + mov dl,[gs:si+11] ; type +.next_entry: + add si,DIRENT_SIZE + dec cx + jnz .store_offset + call nextsector + jnc .store_sect ; CF is set if we're at end + jmp .fail + +.skip_entry: + add si,DIRENT_SIZE + dec cx + jnz .scanentry + call nextsector + jnc .scanentry ; CF is set if we're at end + jmp .fail + +.store_sect: + pop gs + pop si + mov [ds:si+file_sector],eax + mov eax,0 ; Now at beginning of new sector + jmp .success + +.store_offset: + pop gs + pop si ; cx=num remain; SECTOR_SIZE-(cx*32)=cur pos + shl ecx,DIRENT_SHIFT + mov eax,SECTOR_SIZE + sub eax,ecx + and eax,0ffffh + +.success: + mov [ds:si+file_bytesleft],eax + ; "INode" number = ((CurSector-RootSector)*SECTOR_SIZE + Offset)/DIRENT_SIZE) + mov ecx,eax + mov eax,[ds:si+file_sector] + sub eax,[RootDir] + shl eax,SECTOR_SHIFT + add eax,ecx + shr eax,DIRENT_SHIFT + dec eax + xchg eax,ebx ; -> EBX=INode, EAX=FileSize + jmp .done + +.fail: + pop gs + pop si + call close_dir xor eax,eax + stc +.done: + pop ecx ret section .bss diff --git a/core/pxelinux.asm b/core/pxelinux.asm index 4398582..9836a1e 100644 --- a/core/pxelinux.asm +++ b/core/pxelinux.asm @@ -704,6 +704,13 @@ prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option call writestr_early call crlf + ; Set CurrentDirName + push di + mov si,PathPrefix + mov di,CurrentDirName + call strcpy + pop di + ; ; Load configuration file ; diff --git a/doc/comboot.txt b/doc/comboot.txt index ceee93a..135064c 100644 --- a/doc/comboot.txt +++ b/doc/comboot.txt @@ -908,3 +908,42 @@ AX=001Dh [3.60] Write auxilliary data vector In a future version, PXELINUX may end up attempting to save the ADV on the server via TFTP write. + +AX=001Eh [BETA] Get current working directory + Input: AX 0001Eh + Output: ES:BX null-terminated directory name string + + Returns the current working directory. For SYSLINUX, ISOLINUX, + and PXELINUX, this will be an absolute path. For EXTLINUX, it + currently returns "./". + +AX=001Fh [BETA] Open directory + Input: AX 001Fh + ES:SI /-null-terminated directory name + Output: SI directory handle + EAX clobbered + + Open a directory for reading. Directory name must have a trailing + "/" before the null (otherwise, you're looking for a file)(This + may change as this is a BETA call). + +AX=0020h [BETA] Read directory + Input: AX 0020h + SI directory handle + ES:DI buffer for file name + Output: DL Type of file + SI directory handle, or 0 if end of directory was reached + EAX Size of file + EBX Inode of file + + Read one filename from the directory, incrementing the directory + structure at SI as appropriate, storing the filename into the buffer + at ES:DI, and returning the type of the file in DL, the file length + in EAX, the INode/file number in EBX and the updated directory handle. + +AX=0021h [BETA] Close directory + Input: AX 001Fh + SI directory handle + Output SI 0 + + Closes a directory.
H. Peter Anvin
2008-Dec-04 22:37 UTC
[syslinux] [PATCH 1/1] COMBOOT API: Add calls for directory functions; Implement for FAT
Gene Cumm wrote:> > With regards to character sets, I will need to change some of how I'm > coding this as I'm merely truncating the wide character to a 1-byte > character for now. Quick but dirty. Peter, I know the codepage is > usable for matching but does it provide for this sort of functionality > (wide character to byte character conversion)? It doesn't look it > (unless I'm thinking wrong). >It doesn't, and -- this is the really sucky part -- in general it really can't. However, we can probably hack up something that is Good Enough[TM], basically by inverting the first field mapping in the codepage array and returning '?' or some such for anything we don't recognize -- I think there is actually a particular character specced in the FAT specification (probably '?', but it might be '_'). -hpa
H. Peter Anvin
2008-Dec-12 23:54 UTC
[syslinux] [PATCH 1/1] COMBOOT API: Add calls for directory functions; Implement for FAT
Gene Cumm wrote:> > Adds an API call to obtain the current working directory. EXTLINUX > will not return the correct value yet however SYSLINUX, ISOLINUX, and > PXELINUX will return the correct value. For the moment, EXTLINUX will > ONLY return "./" (null-terminated of course). In SYSLINUX and > ISOLINUX, it presets the CurrentDirName string to "/" for safety. > PXELINUX copies TFTP Prefix to CurrentDirName. >I was thinking about this again today, in particular how to handle it in EXTLINUX. There are a lot of people who really want some kind of chdir functionality, which means knowing the absolute path of the current configuration file at all times. It is actually possible to get the absolute path in EXTLINUX. It just requires a bit of new code. There are, in fact, two ways of doing it: one is simply to install a pathname to the install directory rather than the inode. We would then execute a chdir operation during startup, similar to how SYSLINUX and ISOLINUX already work. The other option is that we retain the current behaviour (stashed inode), but do a backwards path search by looking up the ".." entry in the current directory, and then search that directory for the name that corresponds to current directory; iterate until we reach the root. This is the traditional (all-userspace) implementation of getcwd() in Unix. -hpa
Possibly Parallel Threads
- [PATCH 1/1] COMBOOT API: Add calls for directory functions; Implement for FAT; Try 2
- [PATCH 1/1] COMBOOT API: Add get current working directory call to most
- [PATCH 1/1] SYSLINUX/COMBOOT: Abstract searchdir and fix the opendir call
- [PATCH 1/1] COMBOOT API: Add get current working directory call to most (revised)
- [PATCH 1/3] COMBOOT API: Improve readdir