Support subdirectories in the FAT version of SYSLINUX
authorH. Peter Anvin <hpa@zytor.com>
Wed, 24 Jan 2007 06:26:06 +0000 (22:26 -0800)
committerH. Peter Anvin <hpa@zytor.com>
Wed, 24 Jan 2007 06:26:06 +0000 (22:26 -0800)
ldlinux.asm

index b5a9ebf..733f2ec 100644 (file)
@@ -11,7 +11,7 @@
 ;  from MS-LOSS, and can be especially useful in conjunction with the
 ;  umsdos filesystem.
 ;
-;   Copyright (C) 1994-2005  H. Peter Anvin
+;   Copyright (C) 1994-200  H. Peter Anvin
 ;
 ;  This program is free software; you can redistribute it and/or modify
 ;  it under the terms of the GNU General Public License as published by
@@ -30,9 +30,9 @@
 ; Some semi-configurable constants... change on your own risk.
 ;
 my_id          equ syslinux_id
-FILENAME_MAX_LG2 equ 4                 ; log2(Max filename size Including final null)
-FILENAME_MAX   equ 11                  ; Max mangled filename size
-NULLFILE       equ ' '                 ; First char space == null filename
+FILENAME_MAX_LG2 equ 6                 ; log2(Max filename size Including final null)
+FILENAME_MAX   equ (1<<FILENAME_MAX_LG2) ; Max mangled filename size
+NULLFILE       equ 0                   ; First char space == null filename
 NULLOFFSET     equ 0                   ; Position in which to look
 retry_count    equ 16                  ; How patient are we with the disk?
 %assign HIGHMEM_SLOP 0                 ; Avoid this much memory near the top
@@ -899,9 +899,18 @@ getfattype:
 ;
 ; Load configuration file
 ;
-               mov di,syslinux_cfg
+               mov di,syslinux_cfg1
+               call open
+               jnz .config_open
+               mov di,syslinux_cfg2
+               call open
+               jnz .config_open
+               mov di,syslinux_cfg3
                call open
                jz no_config_file
+.config_open:
+               mov eax,[PrevDir]       ; Make the directory with syslinux.cfg ...
+               mov [CurrentDir],eax    ; ... the current directory
 
 ;
 ; Now we have the config file open.  Parse the config file and
@@ -954,23 +963,27 @@ allocate_file:
                ret
 
 ;
-; searchdir:
-;           Search the root directory for a pre-mangled filename in DS:DI.
+; search_dos_dir:
+;           Search a specific directory for a pre-mangled filename in
+;            MangledBuf, in the directory starting in sector EAX.
 ;
 ;           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).
 ;
+;           Assumes DS == ES == CS.
+;
 ;           If successful:
 ;              ZF clear
 ;              SI      = file pointer
-;              DX:AX   = file length in bytes
+;              EAX     = file length (MAY BE ZERO!)
+;              DL      = file attributes
 ;           If unsuccessful
 ;              ZF set
 ;
 
-searchdir:
+search_dos_dir:
                push bx
                call allocate_file
                jnz .alloc_failure
@@ -981,9 +994,8 @@ searchdir:
                push ds
                pop es                          ; ES = DS
 
-               mov eax,[RootDir]               ; First root directory sector
-
 .scansector:
+               ; EAX <- directory sector to scan
                call getcachesector
                ; GS:SI now points to this sector
 
@@ -991,15 +1003,20 @@ searchdir:
 .scanentry:
                cmp byte [gs:si],0
                jz .failure                     ; Hit directory high water mark
+               test byte [gs:si+11],8          ; Ignore volume labels and
+                                               ; VFAT long filename entries
+               jnz .nomatch
                push cx
                push si
                push di
+               mov di,MangledBuf
                mov cx,11
                gs repe cmpsb
                pop di
                pop si
                pop cx
                jz .found
+.nomatch:
                add si,32
                loop .scanentry
 
@@ -1019,8 +1036,7 @@ searchdir:
                mov eax,[gs:si+28]              ; File size
                add eax,SECTOR_SIZE-1
                shr eax,SECTOR_SHIFT
-               jz .failure                     ; Zero-length file
-               mov [bx+4],eax
+               mov [bx+4],eax                  ; Sector count
 
                mov cl,[ClustShift]
                mov dx,[gs:si+20]               ; High cluster word
@@ -1032,9 +1048,9 @@ searchdir:
                mov [bx],edx                    ; Starting sector
 
                mov eax,[gs:si+28]              ; File length again
-               mov dx,[gs:si+30]               ; 16-bitism, sigh
-               mov si,bx
-               and eax,eax                     ; ZF <- 0
+               mov dl,[gs:si+11]               ; File attribute
+               mov si,bx                       ; File pointer...
+               and si,si                       ; ZF <- 0
 
                pop es
                pop gs
@@ -1043,6 +1059,91 @@ searchdir:
                ret
 
 ;
+; searchdir:
+;
+;      Open a file
+;
+;           On entry:
+;              DS:DI   = filename
+;           If successful:
+;              ZF clear
+;              SI              = file pointer
+;              DX:AX or EAX    = file length in bytes
+;           If unsuccessful
+;              ZF set
+;
+; Assumes CS == DS == ES, and trashes BX and CX.
+;
+searchdir:
+               mov eax,[CurrentDir]
+               cmp byte [di],'/'       ; Root directory?
+               jne .notroot
+               mov eax,[RootDir]
+               inc di
+.notroot:
+
+.pathwalk:
+               push eax                ; <A> Current directory sector
+               mov si,di
+.findend:
+               lodsb
+               cmp al,' '
+               jbe .endpath
+               cmp al,'/'
+               jne .findend
+.endpath:
+               xchg si,di
+               pop eax                 ; <A> Current directory sector
+
+               mov [PrevDir],eax       ; Remember last directory searched
+
+               push di
+               call mangle_dos_name    ; MangledBuf <- component
+               call search_dos_dir
+               pop di
+               jz .notfound            ; Pathname component missing
+
+               cmp byte [di-1],'/'     ; Do we expect a directory
+               je .isdir
+
+               ; Otherwise, it should be a file
+.isfile:
+               test dl,18h             ; Subdirectory|Volume Label
+               jnz .badfile            ; If not a file, it's a bad thing
+
+               ; SI and EAX are already set
+               mov edx,eax
+               shr edx,16              ; Old 16-bit remnant...
+               and eax,eax             ; EAX != 0
+               jz .badfile
+               ret                     ; Done!
+
+               ; If we expected a directory, it better be one...
+.isdir:
+               test dl,10h             ; Subdirectory
+               jz .badfile
+
+               xor eax,eax
+               xchg eax,[si+file_sector] ; Get sector number and free file structure
+               jmp .pathwalk           ; Walk the next bit of the path
+
+.badfile:
+               xor eax,eax
+               mov [si],eax            ; Free file structure
+
+.notfound:
+               xor eax,eax
+               xor dx,dx
+               ret
+
+               section .bss
+               alignb 4
+CurrentDir     resd 1                  ; Current directory
+PrevDir                resd 1                  ; Last scanned directory
+
+               section .text
+
+;
 ;
 ; kaboom2: once everything is loaded, replace the part of kaboom
 ;         starting with "kaboom.patch" with this part
@@ -1056,115 +1157,138 @@ kaboom2:
 .norge:                jmp short .norge        ; If int 19h returned; this is the end
 
 ;
-; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed
-;             to by ES:DI; ends on encountering any whitespace
+; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
+;             to by ES:DI; ends on encountering any whitespace.
+;
+;             This verifies that a filename is < FILENAME_MAX characters,
+;             doesn't contain whitespace, zero-pads the output buffer,
+;             and removes trailing dots and redundant slashes, plus changes
+;              backslashes to forward slashes,
+;             so "repe cmpsb" can do a compare, and the path-searching routine
+;              gets a bit of an easier job.
+;
 ;
-
 mangle_name:
+               push bx
+               xor ax,ax
+               mov cx,FILENAME_MAX-1
+               mov bx,di
+
+.mn_loop:
+               lodsb
+               cmp al,' '                      ; If control or space, end
+               jna .mn_end
+               cmp al,'\'                      ; Backslash?
+               jne .mn_not_bs
+               mov al,'/'                      ; Change to forward slash
+.mn_not_bs:
+               cmp al,ah                       ; Repeated slash?
+               je .mn_skip
+               xor ah,ah
+               cmp al,'/'
+               jne .mn_ok
+               mov ah,al
+.mn_ok         stosb
+.mn_skip:      loop .mn_loop
+.mn_end:
+               cmp bx,di                       ; At the beginning of the buffer?
+               jbe .mn_zero
+               cmp byte [es:di-1],'.'          ; Terminal dot?
+               je .mn_kill
+               cmp byte [es:di-1],'/'          ; Terminal slash?
+               jne .mn_zero
+.mn_kill:      dec di                          ; If so, remove it
+               inc cx
+               jmp short .mn_end
+.mn_zero:
+               inc cx                          ; At least one null byte
+               xor ax,ax                       ; Zero-fill name
+               rep stosb
+               pop bx
+               ret                             ; Done
+
+;
+; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
+;                filename to the conventional representation.  This is needed
+;                for the BOOT_IMAGE= parameter for the kernel.
+;                NOTE: A 13-byte buffer is mandatory, even if the string is
+;                known to be shorter.
+;
+;                DS:SI -> input mangled file name
+;                ES:DI -> output buffer
+;
+;                On return, DI points to the first byte after the output name,
+;                which is set to a null byte.
+;
+unmangle_name: call strcpy
+               dec di                          ; Point to final null byte
+               ret
+
+;
+; mangle_dos_name:
+;              Mangle a DOS filename component pointed to by DS:SI
+;              into [MangledBuf]; ends on encountering any whitespace or slash.
+;              Assumes CS == DS == ES.
+;
+
+mangle_dos_name:
+               pusha
+               mov di,MangledBuf
+
                mov cx,11                       ; # of bytes to write
-mn_loop:
+.loop:
                lodsb
                cmp al,' '                      ; If control or space, end
-               jna mn_end
+               jna .end
+               cmp al,'/'                      ; Slash, too
+               je .end
                cmp al,'.'                      ; Period -> space-fill
-               je mn_is_period
+               je .is_period
                cmp al,'a'
-               jb mn_not_lower
+               jb .not_lower
                cmp al,'z'
-               ja mn_not_uslower
+               ja .not_uslower
                sub al,020h
-               jmp short mn_not_lower
-mn_is_period:  mov al,' '                      ; We need to space-fill
-mn_period_loop: cmp cx,3                       ; If <= 3 characters left
-               jbe mn_loop                     ; Just ignore it
+               jmp short .not_lower
+.is_period:    mov al,' '                      ; We need to space-fill
+.period_loop:  cmp cx,3                        ; If <= 3 characters left
+               jbe .loop                       ; Just ignore it
                stosb                           ; Otherwise, write a period
-               loop mn_period_loop             ; Dec CX and (always) jump
-mn_not_uslower: cmp al,ucase_low
-               jb mn_not_lower
+               loop .period_loop               ; Dec CX and (always) jump
+.not_uslower:  cmp al,ucase_low
+               jb .not_lower
                cmp al,ucase_high
-               ja mn_not_lower
+               ja .not_lower
                mov bx,ucase_tab-ucase_low
-                cs xlatb
-mn_not_lower:  stosb
-               loop mn_loop                    ; Don't continue if too long
-mn_end:
+                xlatb
+.not_lower:    stosb
+               loop .loop                      ; Don't continue if too long
+.end:
                mov al,' '                      ; Space-fill name
                rep stosb                       ; Doesn't do anything if CX=0
+               popa
                ret                             ; Done
 
+               section .bss
+MangledBuf     resb 11
+
+               section .text
 ;
-; Upper-case table for extended characters; this is technically code page 865,
+; Case tables for extended characters; this is technically code page 865,
 ; but code page 437 users will probably not miss not being able to use the
 ; cent sign in kernel images too much :-)
 ;
 ; The table only covers the range 129 to 164; the rest we can deal with.
 ;
+               section .data
+
 ucase_low      equ 129
 ucase_high     equ 164
 ucase_tab      db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
                db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
                db 157, 156, 157, 158, 159, 'AIOU', 165
 
-;
-; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
-;                filename to the conventional representation.  This is needed
-;                for the BOOT_IMAGE= parameter for the kernel.
-;                NOTE: A 13-byte buffer is mandatory, even if the string is
-;                known to be shorter.
-;
-;                DS:SI -> input mangled file name
-;                ES:DI -> output buffer
-;
-;                On return, DI points to the first byte after the output name,
-;                which is set to a null byte.
-;
-unmangle_name:
-                push si                 ; Save pointer to original name
-                mov cx,8
-                mov bp,di
-un_copy_body:   lodsb
-                call lower_case
-                stosb
-                cmp al,' '
-                jbe un_cb_space
-                mov bp,di               ; Position of last nonblank+1
-un_cb_space:    loop un_copy_body
-                mov di,bp
-                mov al,'.'              ; Don't save
-                stosb
-                mov cx,3
-un_copy_ext:    lodsb
-                call lower_case
-                stosb
-                cmp al,' '
-                jbe un_ce_space
-                mov bp,di
-un_ce_space:    loop un_copy_ext
-                mov di,bp
-                mov byte [es:di], 0
-                pop si
-                ret
-
-;
-; lower_case: Lower case a character in AL
-;
-lower_case:
-                cmp al,'A'
-                jb lc_ret
-                cmp al,'Z'
-                ja lc_1
-                or al,20h
-                ret
-lc_1:           cmp al,lcase_low
-                jb lc_ret
-                cmp al,lcase_high
-                ja lc_ret
-                push bx
-                mov bx,lcase_tab-lcase_low
-                       cs xlatb
-                pop bx
-lc_ret:         ret
-
+               section .text
 ;
 ; getfssec_edx: Get multiple sectors from a file
 ;
@@ -1423,16 +1547,6 @@ getfatsector:
 ; -----------------------------------------------------------------------------
 
                section .data
-;
-; Lower-case table for codepage 865
-;
-lcase_low       equ 128
-lcase_high      equ 165
-lcase_tab       db 135, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138
-                db 139, 140, 141, 132, 134, 130, 145, 145, 147, 148, 149
-                db 150, 151, 152, 148, 129, 155, 156, 155, 158, 159, 160
-                db 161, 162, 163, 164, 164
-
 copyright_str   db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
                db CR, LF, 0
 boot_prompt    db 'boot: ', 0
@@ -1469,11 +1583,11 @@ aborted_msg     db ' aborted.'                  ; Fall through to crlf_msg!
 crlf_msg       db CR, LF
 null_msg       db 0
 crff_msg       db CR, FF, 0
-syslinux_cfg   db 'SYSLINUXCFG'                ; Mangled form
-ConfigName     db 'syslinux.cfg',0             ; Unmangled form
-%if IS_MDSLINUX
-manifest       db 'MANIFEST   '
-%endif
+syslinux_cfg1  db '/boot'                      ; /boot/syslinux/syslinux.cfg
+syslinux_cfg2  db '/syslinux'                  ; /syslinux/syslinux.cfg
+syslinux_cfg3  db '/'                          ; /syslinux.cfg
+ConfigName     db 'syslinux.cfg', 0            ; syslinux.cfg
+
 ;
 ; Command line options we'd like to take a look at
 ;