[syslinux] [PATCH 1/1] COMBOOT API: Add calls for directory functions; Implement for FAT; Try 2

Gene Cumm gene.cumm at gmail.com
Sun Feb 8 06:36:59 PST 2009


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= 001Fh, 0020h, 0021h and 0022h to prepare for the
COM32 C functions getcwd(), opendir(), readdir(), and closedir(),
respectively.  INT22h, AX=001Fh will return a valid value for all
variants.  INT22h, AX= 0020h, 0021h, and 0022h 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>

---

This is the second try at implementing these calls after feedback on the first.

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.  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, this version should work effectively,
given the patch and feedback from HPA.  (Thank you again).

I've fixed the special cases of the directories named "." and "..",
based on the assumption that no short name shall begin with a '.'
unless it is one of these two directories.  The assumption is based on
some material stating that '.' can not be in a short filename.

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 plus the patch that HPA sent on Mon, Jan 12,
2009 at 2:59 PM EST, subject "Re: [syslinux] COMBOOT API: Add calls
for directory functions; Implement for FAT.".  I just tested and his
patch does still apply to head without any modification needed.

As before, 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 7210b8b..2ff5f33 100644
--- a/core/comboot.inc
+++ b/core/comboot.inc
@@ -1047,6 +1047,72 @@ comapi_kbdtable:
 		stc
 		ret

+;
+; INT 22h AX=001Fh	Get current working directory
+;
+comapi_getcwd:
+		mov P_ES,cs
+		mov P_BX,CurrentDirName
+		clc
+		ret
+
+;
+; INT 22h AX=0020h	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=0021h	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=0022h	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
@@ -1100,6 +1166,10 @@ int22_table:
 		dw comapi_getadv	; 001C get pointer to ADV
 		dw comapi_writeadv	; 001D write ADV to disk
 		dw comapi_kbdtable	; 001E keyboard remapping table
+		dw comapi_getcwd	; 001F get current working directory
+		dw comapi_opendir	; 0020 open directory
+		dw comapi_readdir	; 0021 read directory
+		dw comapi_closedir	; 0022 close directory
 int22_count	equ ($-int22_table)/2

 APIKeyWait	db 0
@@ -1124,3 +1194,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..32a9d2d 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 +906,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 +972,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 +1275,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 +1320,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 +1367,257 @@ 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
+;	DS	Must be the SYSLINUX Data Segment
+;
+;	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 bp		; Using bp to transfer between segment registers
+		push si
+		push es
+		push fs		; Using fs to store the current es (from COMBOOT)
+		push gs
+		mov bp,es
+		mov fs,bp
+		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_ck_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?
+		mov bl,[gs:si+13]
+		cmp bl,[VFATCsum]
+		jne .vfat_abort
+		jmp .vfat_cp_ln
+
+.vfat_ck_ln:		; Load this line's VFAT CheckSum
+		mov bl,[gs:si+13]
+		mov [VFATCsum],bl
+.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 lodsw	; Unicode here!!
+		mov bp,ds
+		mov es,bp
+		call ucs2_to_cp	; Convert to local codepage
+		mov bp,fs
+		mov es,bp
+		jc .vfat_abort	;-; Use short name if character not on codepage
+		stosb		; CAN NOT OVERRIDE es
+		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_dot
+.short_file_loop:
+		cmp al,' '
+		jz .short_skip_bs
+		stosb
+		loop .short_file_loop
+		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
+		jmp .short_done
+.short_dot:
+		stosb
+		gs lodsb
+		cmp al,' '
+		jz .short_done
+		stosb
+.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 fs
+		pop es
+		pop si
+		mov [ds:si+file_sector],eax
+		mov eax,0	; Now at beginning of new sector
+		jmp .success
+
+.store_offset:
+		pop gs
+		pop fs
+		pop es
+		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 fs
+		pop es
+		pop si
+		call close_dir
 		xor eax,eax
+		stc
+.done:
+		pop bp
+		pop ecx
+.end:
 		ret

 		section .bss
diff --git a/core/pxelinux.asm b/core/pxelinux.asm
index aac1ec2..94f23af 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 5329502..387303d 100644
--- a/doc/comboot.txt
+++ b/doc/comboot.txt
@@ -921,3 +921,46 @@ AX=001Eh [3.74] Keyboard remapping table
 	version, the format code is always 1 and the length is always
 	256.  This version can be updated simply by overwriting the version
 	in memory; this may not be true in the future.
+
+
+AX=001Fh [BETA-3.74+]	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=0020h [BETA-3.74+] 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=0021h [BETA-3.74+] 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=0022h [BETA-3.74+] Close directory
+	Input:	AX	001Fh
+		SI	directory handle
+	Output	SI	0
+
+	Closes a directory.




More information about the Syslinux mailing list