[syslinux] [PATCH 1/1] COMBOOT API: Add calls for directory functions; Implement for FAT
Gene Cumm
gene.cumm at gmail.com
Thu Dec 4 10:47:45 PST 2008
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.
More information about the Syslinux
mailing list