FAT: Support VFAT long filenames syslinux-3.70-pre13
authorH. Peter Anvin <hpa@zytor.com>
Fri, 6 Jun 2008 07:08:13 +0000 (00:08 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Fri, 6 Jun 2008 07:09:58 +0000 (00:09 -0700)
Initial support for VFAT long filenames; currently hard-coded to
codepage 865, no support for other codepages or for accessing files
with names which aren't in the current codepage.  This hopefully
shouldn't be an issue for SYSLINUX applications.

NEWS
codepage/Makefile
core/Makefile
core/ldlinux.asm

diff --git a/NEWS b/NEWS
index bdda87c..085b5b8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -28,6 +28,7 @@ Changes in 3.70:
          all three.
        * Change default dir for auxiliary files from
          /usr/lib/syslinux to /usr/share/syslinux.
+       * SYSLINUX: VFAT long filename support.
 
 Changes in 3.64:
        * SYSLINUX/EXTLINUX: support "localboot" with the same feature
index ac0b234..d426eaa 100644 (file)
@@ -6,14 +6,14 @@ GENFILES      = $(patsubst %.txt,%.bin,$(CPSRC))
 
 all: $(GENFILES)
 
-tidy:
+%.bin: %.txt cptable.pl UnicodeData
+       $(PERL) cptable.pl UnicodeData $< $@
 
-clean:
+tidy:
        rm -f $(GENFILES)
 
+clean: tidy
+
 dist: tidy
 
 spotless: clean
-
-%.bin: %.txt cptable.pl UnicodeData
-       $(PERL) cptable.pl UnicodeData $< $@
index f763b0b..7289294 100644 (file)
@@ -45,6 +45,11 @@ PERL     = perl
 
 VERSION := $(shell cat ../version)
 
+# This is very similar to cp437; technically it's for Norway and Denmark,
+# but it's unlikely the characters that are different will be used in
+# filenames by other users.
+CODEPAGE = cp865
+
 # _bin.c files required by both BTARGET and ITARGET installers
 BINFILES = bootsect_bin.c ldlinux_bin.c \
           extlinux_bss_bin.c extlinux_sys_bin.c
@@ -140,6 +145,12 @@ extlinux_bss_bin.c: extlinux.bss ../bin2c.pl
 extlinux_sys_bin.c: extlinux.sys ../bin2c.pl
        $(PERL) ../bin2c.pl extlinux_image 512 < $< > $@
 
+# NASM prior to 2.03 wouldn't auto-generate this dependency...
+ldlinux.o: codepage.bin
+
+codepage.bin: ../codepage/$(CODEPAGE).bin
+       cp -f $< $@
+
 install: installer
 
 install-lib: installer
@@ -149,12 +160,12 @@ install-all: install install-lib
 netinstall: installer
 
 tidy dist:
-       rm -f *.o *.elf *_bin.c stupid.* patch.offset
+       rm -f codepage.bin *.o *.elf stupid.* patch.offset
        rm -f *.lsr *.lst *.map *.sec
        rm -f $(OBSOLETE)
 
 clean: tidy
-       rm -f $(ITARGET)
+       rm -f $(ITARGET) *_bin.c
 
 spotless: clean
        rm -f $(BTARGET) .depend
index 86de458..8243188 100644 (file)
@@ -957,11 +957,27 @@ search_dos_dir:
                jnz .alloc_failure
 
                push cx
+               push dx
                push gs
                push es
                push ds
                pop es                          ; ES = DS
 
+               ; Compute the value of a possible VFAT longname
+               ; "last" entry (which, of course, comes first...)
+               push ax
+               push dx
+               mov ax,[NameLen]
+               add ax,12
+               xor dx,dx
+               mov cx,13
+               div cx
+               or al,40h
+               mov [VFATInit],al
+               mov [VFATNext],al
+               pop dx
+               pop ax
+
 .scansector:
                ; EAX <- directory sector to scan
                call getcachesector
@@ -971,9 +987,108 @@ search_dos_dir:
 .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
+               cmp word [gs:si+11],0Fh         ; Long filename
+               jne .short_entry
+
+               ; Process a VFAT long entry
+               pusha
+               mov al,[gs:si]
+               cmp al,[VFATNext]
+               jne .not_us
+               mov bl,[gs:si+13]
+               test al,40h
+               jz .match_csum
+               ; Get the initial checksum value
+               mov [VFATCsum],bl
+               jmp .done_csum
+.match_csum:
+               cmp bl,[VFATCsum]
+               jne .not_us                     ; Checksum mismatch
+.done_csum:
+               and ax,03fh
+               jz .not_us                      ; Can't be zero...
+               dec ax
+               mov [VFATNext],al               ; Optimistically...
+               mov bx,ax
+               shl bx,2                        ; *4
+               add ax,bx                       ; *5
+               add bx,bx                       ; *8
+               add bx,ax                       ; *13
+               cmp bx,[NameLen]
+               jae .not_us
+               mov di,[NameStart]
+               inc si
+               mov cx,13
+.vfat_cmp:
+               gs lodsw
+               push bx
+               cmp bx,[NameLen]
+               jae .vfat_tail
+               movzx bx,byte [bx+di]
+               shl bx,2
+               cmp ax,[ucs_codepage+bx]        ; Primary case
+               je .ucs_ok
+               cmp ax,[ucs_codepage+bx+2]      ; Alternate case
+               je .ucs_ok
+               ; Mismatch...
+               jmp .not_us_pop
+.vfat_tail:
+               ; *AT* the end we should have 0x0000, *AFTER* the end
+               ; we should have 0xFFFF...
+               je .vfat_end
+               inc ax                  ; 0xFFFF -> 0x0000
+.vfat_end:
+               and ax,ax
+               jnz .not_us_pop
+.ucs_ok:
+               pop bx
+               inc bx
+               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_cmp
+               ; Okay, if we got here we had a match on this particular
+               ; entry... live to see another one.
+               popa
+               jmp .next_entry
+
+.not_us_pop:
+               pop bx
+.not_us:
+               popa
+               jmp .nomatch
+               
+.short_entry:
+               test byte [gs:si+11],8          ; Ignore volume labels
                jnz .nomatch
+
+               cmp byte [VFATNext],0           ; Do we have a longname match?
+               jne .no_long_match
+
+               ; We already have a VFAT longname match, however,
+               ; the match is only valid if the checksum matches
+               push cx
+               push si
+               push ax
+               xor ax,ax
+               mov cx,11
+.csum_loop:
+               gs lodsb
+               ror ah,1
+               add ah,al
+               loop .csum_loop
+               cmp ah,[VFATCsum]
+               pop ax
+               pop si
+               pop cx
+               je .found                       ; Got a match on longname
+
+.no_long_match:                                        ; Look for a shortname match
                push cx
                push si
                push di
@@ -983,10 +1098,15 @@ search_dos_dir:
                pop di
                pop si
                pop cx
-               jz .found
+               je .found
 .nomatch:
+               ; Reset the VFAT matching state machine
+               mov dh,[VFATInit]
+               mov [VFATNext],dh
+.next_entry:
                add si,32
-               loop .scanentry
+               dec cx
+               jnz .scanentry
 
                call nextsector
                jnc .scansector                 ; CF is set if we're at end
@@ -995,6 +1115,7 @@ search_dos_dir:
 .failure:
                pop es
                pop gs
+               pop dx
                pop cx
 .alloc_failure:
                pop bx
@@ -1022,10 +1143,22 @@ search_dos_dir:
 
                pop es
                pop gs
+               pop dx
                pop cx
                pop bx
                ret
 
+               section .data
+               alignb 4
+ucs_codepage:
+               incbin "codepage.bin"
+
+               section .bss
+VFATInit       resb 1
+VFATNext       resb 1
+VFATCsum       resb 1
+
+               section .text
 ;
 ; close_file:
 ;           Deallocates a file structure (pointer in SI)
@@ -1219,13 +1352,19 @@ unmangle_name:  call strcpy
 ;
 ; mangle_dos_name:
 ;              Mangle a DOS filename component pointed to by DS:SI
-;              into [MangledBuf]; ends on encountering any whitespace or slash.
+;              into [MangledBuf]; ends on encountering any whitespace or
+;              slash.
+;
+;              WARNING: saves pointers into the buffer for longname
+;              matches!
+;
 ;              Assumes CS == DS == ES.
 ;
 
 mangle_dos_name:
                pusha
                mov di,MangledBuf
+               mov [NameStart],si
 
                mov cx,11                       ; # of bytes to write
 .loop:
@@ -1255,13 +1394,26 @@ mangle_dos_name:
                 xlatb
 .not_lower:    stosb
                loop .loop                      ; Don't continue if too long
+               ; Find the end for the benefit of longname search
+.find_end:
+               lodsb
+               cmp al,' '
+               jna .end
+               cmp al,'/'
+               jne .find_end
 .end:
+               dec si
+               sub si,[NameStart]
+               mov [NameLen],si
                mov al,' '                      ; Space-fill name
                rep stosb                       ; Doesn't do anything if CX=0
                popa
                ret                             ; Done
 
                section .bss
+               alignb 2
+NameStart      resw 1
+NameLen                resw 1
 MangledBuf     resb 11
 
                section .text