[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