Prepping for new 2.20 version: rewrite main syslinux program to support
authorhpa <hpa>
Wed, 15 Dec 2004 10:14:39 +0000 (10:14 +0000)
committerhpa <hpa>
Wed, 15 Dec 2004 10:14:39 +0000 (10:14 +0000)
FAT32 and EDD, and a new cleaner installer infrastructure.

21 files changed:
Makefile
bootsect.inc
comboot.inc
dos/syslinux.asm [moved from syslinux.asm with 98% similarity]
findpatch.pl [deleted file]
ldlinux.asm
libfat/cache.c [new file with mode: 0644]
libfat/fat.h [new file with mode: 0644]
libfat/fatchain.c [new file with mode: 0644]
libfat/libfat.h [new file with mode: 0644]
libfat/libfatint.h [new file with mode: 0644]
libfat/open.c [new file with mode: 0644]
libfat/searchdir.c [new file with mode: 0644]
libfat/ulint.h [new file with mode: 0644]
mtools/Makefile [new file with mode: 0644]
mtools/syslinux.c [moved from syslinux.c with 80% similarity]
syslinux.h
syslxmod.c
unix/Makefile [new file with mode: 0644]
unix/syslinux.c [new file with mode: 0644]
version

index 6706c11..afc30a4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -39,7 +39,7 @@ VERSION  = $(shell cat version)
        $(CC) $(INCLUDE) $(CFLAGS) -c $<
 
 # libsyslinux.so
-LIB_SONAME = libsyslinux.so.2
+LIB_SONAME = libsyslinux.so.2.2
 LIB_SO  = libsyslinux.so.$(VERSION)
 
 #
index 654ab6b..f901cf5 100644 (file)
@@ -65,7 +65,7 @@ load_bootsec:
                mov eax,[OrigFDCTabPtr]
                mov [fdctab],eax
 
-               mov dl,[bsDriveNumber]
+               mov dl,[DriveNumber]
                mov si,PartInfo         ; Partition info buffer
                mov di,800h-18          ; Put partition info here
                push di
index af2ea0c..f3aec3b 100644 (file)
@@ -475,7 +475,7 @@ comapi_pxecall      equ comapi_err                  ; Not available
 comapi_derinfo:
                mov P_AL,my_id
 %if IS_SYSLINUX || IS_MDSLINUX
-               mov al,[bsDriveNumber]
+               mov al,[DriveNumber]
                mov P_DL,al
                mov P_ES,cs
                mov P_BX,PartInfo
similarity index 98%
rename from syslinux.asm
rename to dos/syslinux.asm
index 25d7339..9b61f24 100644 (file)
@@ -328,9 +328,9 @@ read_bootsect:
                pop ax                          ; Remove flags from stack
                jc disk_read_error
 
-               mov si,SectorBuffer+11          ; Offset of superblock
-               mov di,BootSector+11
-               mov cx,51                       ; Superblock = 51 bytes
+               mov si,SectorBuffer+          ; Offset of superblock
+               mov di,BootSector+3
+               mov cx,59                       ; Superblock = 59 bytes
                rep movsb                       ; Copy the superblock
                jmp short write_bootsect
 disk_read_error:
diff --git a/findpatch.pl b/findpatch.pl
deleted file mode 100755 (executable)
index 551d874..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/perl
-#
-# Script to find the "patch area" of ldlinux.sys
-#
-
-eval { use bytes; };
-
-open(SYS, "< ldlinux.sys") or die "$0: Cannot open ldlinux.sys\n";
-eval { binmode SYS; };
-if ( read(SYS,$sec1,512) != 512 ) {
-    die "$0: ldlinux.sys: short read\n";
-}
-close(SYS);
-
-for ( $i = 0 ; $i < 512; $i++ ) {
-    $scan = substr($sec1,$i,12);
-
-    if ( $scan eq "\032LDLINUX SYS" &&
-        substr($sec1,$i+16,2) eq "\x55\xAA" ) {
-       last;
-    }
-}
-
-
-die "$0: Did not find patch area signature\n" unless ( $i < 512 );
-
-# Past signature, plus align to the subsequent dword.
-print ((($i+18)+3) & ~3); print "\n";
index 7dcfb57..2e6503e 100644 (file)
@@ -44,6 +44,10 @@ FILENAME_MAX equ 11                  ; Max mangled filename size
 NULLFILE       equ ' '                 ; First char space == null filename
 retry_count    equ 6                   ; How patient are we with the disk?
 %assign HIGHMEM_SLOP 0                 ; Avoid this much memory near the top
+LDLINUX_MAGIC  equ 0x3eb202fe          ; A random number to identify ourselves with
+
+SECTOR_SHIFT   equ 9
+SECTOR_SIZE    equ (1 << SECTOR_SHIFT)
 
 ;
 ; This is what we need to do when idle
@@ -91,8 +95,8 @@ vk_end:               equ $                   ; Should be <= vk_size
 ;
 ; 0000h - main code/data segment (and BIOS segment)
 ;
-real_mode_seg  equ 5000h
-fat_seg                equ 3000h               ; 128K area for FAT (2x64K)
+real_mode_seg  equ 4000h
+cache_seg      equ 3000h               ; 64K area for metadata cache
 vk_seg          equ 2000h              ; Virtual kernels
 xfer_buf_seg   equ 1000h               ; Bounce buffer for I/O to high mem
 comboot_seg    equ real_mode_seg       ; COMBOOT image loading zone
@@ -187,6 +191,8 @@ VGAPos              resw 1                  ; Pointer into VGA memory
 VGACluster     resw 1                  ; Cluster pointer for VGA image file
 VGAFilePtr     resw 1                  ; Pointer into VGAFileBuf
 Com32SysSP     resw 1                  ; SP saved during COM32 syscall
+CachePtrs      times (65536/SECTOR_SIZE) resw 1
+NextCacheSlot  resw 1
 CursorDX        equ $
 CursorCol       resb 1                 ; Cursor column for message file
 CursorRow       resb 1                 ; Cursor row for message file
@@ -206,6 +212,7 @@ A20Tries    resb 1                  ; Times until giving up on A20
 FuncFlag       resb 1                  ; Escape sequences received from keyboard
 DisplayMask    resb 1                  ; Display modes mask
 CopySuper      resb 1                  ; Distinguish .bs versus .bss
+DriveNumber    resb 1                  ; BIOS drive number
 MNameBuf        resb 11                ; Generic mangled file name buffer
 InitRD          resb 11                 ; initrd= mangled name
 KernelCName     resb 13                 ; Unmangled kernel name
@@ -278,12 +285,11 @@ superblock        equ $
 superinfo_size equ ($-superblock)-1    ; How much to expand
                superd Hidden
                superd HugeSectors
-               superb DriveNumber
-               superb Reserved1
-               superb BootSignature    ; 29h if the following fields exist
-               superd VolumeID
-bsVolumeLabel  zb 11
-bsFileSysType  zb 8                    ; Must be FAT12 or FAT16 for this version
+               ;
+               ; This is as far as FAT12/16 and FAT32 are consistent
+               ;
+               zb 54                   ; FAT12/16 need 26 more bytes,
+                                       ; FAT32 need 54 more bytes
 superblock_len equ $-superblock
 
 SecPerClust    equ bxSecPerClust
@@ -313,7 +319,6 @@ start:
 
                mov ds,ax               ; Now we can initialize DS...
 
-               mov [di+bsDriveNumber-FloppyTable],dl
 ;
 ; Now sautee the BIOS floppy info block to that it will support decent-
 ; size transfers; the floppy block is 11 bytes and is stored in the
@@ -329,6 +334,7 @@ start:
 
                ; Save the old fdctab even if hard disk so the stack layout
                ; is the same.  The instructions above do not change the flags
+               mov [DriveNumber],dl    ; Save drive number in DL
                and dl,dl               ; If floppy disk (00-7F), assume no
                                        ; partition table
                js harddisk
@@ -384,86 +390,45 @@ not_harddisk:
 ; Ready to enable interrupts, captain
 ;
                sti
-;
-; Insane hack to expand the superblock to dwords
-;
-expand_super:
-               xor eax,eax
-               mov es,ax                       ; INT 13:08 destroys ES
-               mov si,superblock
-               mov di,SuperInfo
-               mov cl,superinfo_size           ; CH == 0
-.loop:
-               lodsw
-               dec si
-               stosd                           ; Store expanded word
-               xor ah,ah
-               stosd                           ; Store expanded byte
-               loop .loop
+
 
 ;
-; Now we have to do some arithmetric to figure out where things are located.
-; If Micro$oft had had brains they would already have done this for us,
-; and stored it in the superblock at format time, but here we go,
-; wasting precious boot sector space again...
+; Do we have EBIOS (EDD)?
 ;
-%define                Z di-superinfo_size*8-SuperInfo
-debugentrypt:
-               mov ax,[bxFATs]         ; Number of FATs (eax<31:16> == 0)
-               mov edx,[Z+bxFATsecs]   ; Sectors/FAT
-               mul edx                 ; Get the size of the FAT area
-               ; edx <- 0
-               add eax,[bxHidden]              ; Add hidden sectors
-               add eax,[Z+bxResSectors]        ; And reserved sectors
-
-               mov [RootDir],eax       ; Location of root directory
-               mov [DataArea],eax      ; First data sector
-               push eax
-
-               mov eax,[Z+bxRootDirEnts]
-               shl ax,5                ; Size of a directory entry
-               mov bx,[Z+bxBytesPerSec]
-               add ax,bx               ; Round up, not down
-               dec ax
-               div bx                  ; Now we have the size of the root dir
-               mov [RootDirSize],ax
-               mov [DirScanCtr],ax
-               add bx,trackbuf-31
-               mov [Z+EndofDirSec],bx  ; End of a single directory sector
-               add [Z+DataArea],eax
-               pop eax                 ; Reload root directory starting point
+eddcheck:
+               mov bx,55AAh
+               mov ah,41h              ; EDD existence query
+               mov dl,[DriveNumber]
+               int 13h
+               jc .noedd
+               cmp bx,0AA55h
+               jne .noedd
+               test cl,1               ; Extended disk access functionality set
+               jz .noedd
+               ;
+               ; We have EDD support...
+               ;
+               mov byte [getlinsec+1],getlinsec_ebios-(getlinsec+2)
+.noedd:
 
 ;
-; Now the fun begins.  We have to search the root directory for
-; LDLINUX.SYS and load the first sector, so we have a little more
-; space to have fun with.  Then we can go chasing through the FAT.
-; Joy!!
+; Load the first sector of LDLINUX.SYS; this used to be all proper
+; with parsing the superblock and root directory; it doesn't fit
+; together with EBIOS support, unfortunately.
 ;
-sd_nextsec:    push eax
-               mov bx,trackbuf
-               push bx
+               mov eax,[FirstSector]   ; Sector start
+               mov bx,ldlinux_sys      ; Where to load it
                call getonesec
-               pop si
-sd_nextentry:  mov cx,11
-               cmp [si],ch             ; Directory high water mark
-               je kaboom
-; This no longer fits... since we'd be dead anyway if there
-; was a nonfile named LDLINUX.SYS on the disk, it shouldn't
-; matter...
-;              test byte [si+11],18h   ; Must be a file
-;              jnz sd_not_file
-               mov di,ldlinux_name
-               push si
-               repe cmpsb
-               pop si
-               je found_it
-sd_not_file:   add si,byte 32          ; Distance to next
-               cmp si,[EndofDirSec]
-               jb sd_nextentry
-               pop eax
-               inc eax
-               dec word [DirScanCtr]
-               jnz sd_nextsec
+               
+               ; Some modicum of integrity checking
+               cmp dword [ldlinux_magic],LDLINUX_MAGIC
+               jne kaboom
+               cmp dword [ldlinux_magic+4],HEXDATE
+               jne kaboom
+
+               ; Go for it...
+               jmp ldlinux_ent
+
 ;
 ; kaboom: write a message and bail out.
 ;
@@ -481,31 +446,6 @@ kaboom:
 .norge:                jmp short .norge        ; If int 19h returned; this is the end
 
 ;
-; found_it: now we compute the location of the first sector, then
-;          load it and JUMP (since we're almost out of space)
-;
-found_it:      ; Note: we actually leave two words on the stack here
-               ; (who cares?)
-               mov eax,[bxSecPerClust]
-               mov bp,ax               ; Load an entire cluster
-               movzx ebx,word [si+26]  ; First cluster
-               mov [RunLinClust],bx    ; Save for later use
-               dec bx                  ; First cluster is "cluster 2"
-               dec bx
-               mul ebx
-               add eax,[DataArea]
-               mov bx,ldlinux_sys
-               call getlinsec
-               mov si,bs_magic
-               mov di,ldlinux_magic
-               mov cx,magic_len
-               repe cmpsb              ; Make sure that the bootsector
-               jne kaboom              ; matches LDLINUX.SYS
-;
-; Done! Jump to the entry point!
-;
-               jmp ldlinux_ent
-;
 ;
 ; writestr: write a null-terminated string to the console
 ;          This assumes we're on page 0.  This is only used for early
@@ -522,21 +462,28 @@ writestr:
 .return:       ret
 
 ;
-; disk_error: decrement the retry count and bail if zero.
-;            This gets patched once we have more space to try to
-;             optimize transfer sizes on broken machines.
+; xint13: wrapper for int 13h which will retry 6 times and then die,
+;        AND save all registers except BP
 ;
-disk_error:    dec si                  ; SI holds the disk retry counter
-               jz kaboom
-               ; End of patched "call" instruction!
-               jmp short disk_try_again
+xint13:
+.again:
+                mov bp,retry_count
+.loop:          pushad
+                int 13h
+                popad
+                jnc writestr.return
+                dec bp
+                jnz .loop
+.disk_error:
+               jmp strict near kaboom  ; Patched
+
 
 ;
-; getonesec: like getlinsec, but pre-sets the count to 1
+; getonesec: get one disk sector
 ;
 getonesec:
-               mov bp,1
-               ; Fall through to getlinsec
+               mov bp,1                ; One sector
+               ; Fall through
 
 ;
 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
@@ -548,18 +495,58 @@ getonesec:
 ;           On return, BX points to the first byte after the transferred
 ;           block.
 ;
-;           The "stupid patch area" gets replaced by the code
-;           mov bp,1 ; nop ... (BD 01 00 90 90...) when installing with
-;           the -s option.
-;
-;            This routine assumes CS == DS.
+;            This routine assumes CS == DS, and trashes most registers.
 ;
 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
 ; that is dead from that point; this saves space.  However, please keep
 ; the order to dst,src to keep things sane.
 ;
 getlinsec:
-               mov esi,[bxSecPerTrack]
+               jmp strict short getlinsec_cbios        ; This is patched
+
+;
+; getlinsec_ebios:
+;
+; getlinsec implementation for EBIOS (EDD)
+;
+getlinsec_ebios:
+                mov si,dapa                     ; Load up the DAPA
+                mov [si+4],bx
+                mov [si+6],es
+                mov [si+8],eax
+.loop:
+                push bp                         ; Sectors left
+               call maxtrans                   ; Enforce maximum transfer size
+.bp_ok:
+                mov [si+2],bp
+                mov dl,[DriveNumber]
+                mov ah,42h                      ; Extended Read
+                call xint13
+                pop bp
+                movzx eax,word [si+2]           ; Sectors we read
+                add [si+8],eax                  ; Advance sector pointer
+                sub bp,ax                       ; Sectors left
+                shl ax,9                        ; 512-byte sectors
+                add [si+4],ax                   ; Advance buffer pointer
+                and bp,bp
+                jnz .loop
+                mov eax,[si+8]                  ; Next sector
+                mov bx,[si+4]                   ; Buffer pointer
+                ret
+
+;
+; getlinsec_cbios:
+;
+; getlinsec implementation for legacy CBIOS
+;
+getlinsec_cbios:
+.loop:
+               push eax
+               push bp
+               push bx
+
+               movzx esi,word [bsSecPerTrack]
+               movzx edi,word [bsHeads]
                ;
                ; Dividing by sectors to get (track,sector): we may have
                ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
@@ -570,95 +557,84 @@ getlinsec:
                xchg cx,dx              ; CX <- sector index (0-based)
                                        ; EDX <- 0
                ; eax = track #
-               div dword [bxHeads]     ; Convert track to head/cyl
+               div edi                 ; Convert track to head/cyl
                ;
                ; Now we have AX = cyl, DX = head, CX = sector (0-based),
                ; BP = sectors to transfer, SI = bsSecPerTrack,
                ; ES:BX = data target
                ;
-gls_nextchunk: push si                 ; <A> bsSecPerTrack
-               push bp                 ; <B> Sectors to transfer
-
-               ; Important - this gets patched with a call.  The call
-               ; assumes cx, si and bp are set up, and can modify bp
-               ; and destroy si.  Until we have the space to do so,
-               ; transfer one sector at a time.
-gls_set_size:
-__BEGIN_STUPID_PATCH_AREA:
-               mov bp,1                ; 3 bytes, same as a call insn
-__END_STUPID_PATCH_AREA:
-
-               push ax                 ; <C> Cylinder #
-               push dx                 ; <D> Head #
-
-               push cx                 ; <E> Sector #
+
+               call maxtrans                   ; Enforce maximum transfer size
+
+               ; Must not cross track boundaries, so BP <= SI-CX
+               sub si,cx
+               cmp bp,si
+               jna .bp_ok
+               mov bp,si
+.bp_ok:        
+
                shl ah,6                ; Because IBM was STOOPID
                                        ; and thought 8 bits were enough
                                        ; then thought 10 bits were enough...
-               pop cx                  ; <E> Sector #
-               push cx                 ; <E> Sector #
                inc cx                  ; Sector numbers are 1-based, sigh
                or cl,ah
                mov ch,al
                mov dh,dl
-               mov dl,[bsDriveNumber]
+               mov dl,[DriveNumber]
                xchg ax,bp              ; Sector to transfer count
-                                       ; (xchg shorter than mov)
-               mov si,retry_count      ; # of times to retry a disk access
+               mov ah,02h              ; Read sectors
+               call xint13
+               movzx ecx,al
+               shl ax,9                ; Convert sectors in AL to bytes in AX
+               pop bx
+               add bx,ax
+               pop bp
+               pop eax
+               add eax,ecx
+               sub bp,cx
+               jnz .loop
+               ret
+
 ;
-; Do the disk transfer... save the registers in case we fail :(
+; Truncate BP to MaxTransfer
 ;
-disk_try_again: 
-               pusha                   ; <F>
-               mov ah,02h              ; READ DISK
-               int 13h
-               popa                    ; <F>
-               jc disk_error
-;
-; Disk access successful
-;
-               pop cx                  ; <E> Sector #
-               mov di,ax               ; Reduce sector left count
-               mul word [bsBytesPerSec] ; Figure out how much to advance ptr
-               add bx,ax               ; Update buffer location
-               pop dx                  ; <D> Head #
-               pop ax                  ; <C> Cyl #
-               pop bp                  ; <B> Sectors left to transfer
-               pop si                  ; <A> Number of sectors/track
-               sub bp,di               ; Reduce with # of sectors just read
-               jz writestr.return      ; Done!
-               add cx,di
-               cmp cx,si
-               jb gls_nextchunk
-               inc dx                  ; Next track on cyl
-               cmp dx,[bsHeads]        ; Was this the last one?
-               jb gls_nonewcyl
-               inc ax                  ; If so, new cylinder
-               xor dx,dx               ; First head on new cylinder
-gls_nonewcyl:  sub cx,si               ; First sector on new track
-               jmp short gls_nextchunk
+maxtrans:
+               cmp bp,[MaxTransfer]
+               jna .ok
+               mov bp,[MaxTransfer]
+.ok:           ret
 
+;
+; Error message on failure
+;
 bailmsg:       db 'Boot failed', 0Dh, 0Ah, 0
 
-bs_checkpt     equ $                   ; Must be <= 7DEFh
+;
+; EBIOS disk address packet
+;
+               align 4, db 0
+dapa:
+                dw 16                           ; Packet size
+.count:         dw 0                            ; Block count
+.off:           dw 0                            ; Offset of buffer
+.seg:           dw 0                            ; Segment of buffer
+.lba:           dd 0                            ; LBA (LSW)
+                dd 0                            ; LBA (MSW)
+
 
 %if 1
 bs_checkpt_off equ ($-$$)
 %ifndef DEPEND
-%if bs_checkpt_off > 1EFh
+%if bs_checkpt_off > 1F8h
 %error "Boot sector overflow"
 %endif
 %endif
 
-               zb 1EFh-($-$$)
+               zb 1F8h-($-$$)
 %endif
-bs_magic       equ $                   ; From here to the magic_len equ
-                                       ; must match ldlinux_magic
-ldlinux_name:  db 'LDLINUX SYS'        ; Looks like this in the root dir
-               dd HEXDATE              ; Hopefully unique between compiles
-
+FirstSector    dd 0xDEADBEEF                   ; Location of sector 1
+MaxTransfer    dw 0x007F                       ; Max transfer size
 bootsignature  dw 0AA55h
-magic_len      equ $-bs_magic
 
 ;
 ; ===========================================================================
@@ -678,19 +654,23 @@ syslinux_banner   db 0Dh, 0Ah
                db version_str, ' ', date, ' ', 0
                db 0Dh, 0Ah, 1Ah        ; EOF if we "type" this in DOS
 
-ldlinux_magic  db 'LDLINUX SYS'
+               align 8, db 0
+ldlinux_magic  dd LDLINUX_MAGIC
                dd HEXDATE
-               dw 0AA55h
 
 ;
-; This area is possibly patched by the installer.  It is located
-; immediately after the EOF + LDLINUX SYS + 4 bytes + 55 AA + alignment,
-; so we can find it algorithmically.
+; This area is patched by the installer.  It is found by looking for
+; LDLINUX_MAGIC, plus 4 bytes.
 ;
-               alignb 4, db 0
-MaxTransfer    dw 00FFh                ; Absolutely maximum transfer size
+patch_area:
+TotalDwords    dw 0                    ; Total dwords starting at ldlinux_sys
+TotalSectors   dw 0                    ; Number of sectors minus bootsec, this sec
+CheckSum       dd 0                    ; Checksum starting at ldlinux_sys
+                                       ; value = LDLINUX_MAGIC - [sum of dwords]
+
+; Space for up to 64 sectors, the theoretical maximum
+SectorPtrs     times 64 dd 0
 
-               align 4
 ldlinux_ent:
 ; 
 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
@@ -707,175 +687,72 @@ ldlinux_ent:
 ;
                mov si,syslinux_banner
                call writestr
-;
-; Remember, the boot sector loaded only the first cluster of LDLINUX.SYS.
-; We can really only rely on a single sector having been loaded.  Hence
-; we should load the FAT into RAM and start chasing pointers...
-;
-               xor ax,ax
-               cwd
-               inc dx                          ; DX:AX <- 64K
-               div word [bxBytesPerSec]        ; sectors/64K
-               mov si,ax
 
-               push es
-               mov bx,fat_seg                  ; Load into fat_seg:0000
-               mov es,bx
-               
-               mov eax,[bsHidden]              ; Hidden sectors
-               add edx,[bxResSectors]
-               add eax,edx
-               mov ecx,[bxFATsecs]             ; Sectors/FAT
-fat_load_loop: 
-               mov ebp,ecx                     ; Make sure high EBP = 0
-               cmp bp,si
-               jna fat_load
-               mov bp,si                       ; A full 64K moby
-fat_load:      
-               xor bx,bx                       ; Offset 0 in the current ES
-               call getlinsecsr
-               sub cx,bp
-               jz fat_load_done                ; Last moby?
-               add eax,ebp                     ; Advance sector count
-               mov bx,es                       ; Next 64K moby
-               add bx,1000h
-               mov es,bx
-               jmp short fat_load_loop
-fat_load_done:
-               pop es
 ;
-; Fine, now we have the FAT in memory. How big is a cluster, really?
-; Also figure out how many clusters will fit in an 8K buffer, and how
-; many sectors and bytes that is
+; Patch disk error handling
 ;
-               mov edi,[bxBytesPerSec]         ; Used a lot below
-               mov eax,[SecPerClust]
-               mov si,ax                       ; Also used a lot
-               mul di
-               mov [ClustSize],eax             ; Bytes/cluster
-               mov bx,ax
-               mov ax,trackbufsize             ; High bit 0
-               cwd
-               div bx
-               mov [BufSafe],ax                ; # of cluster in trackbuf
-               mul si
-               mov [BufSafeSec],ax
-               mul di
-               mov [BufSafeBytes],ax
-               add ax,getcbuf                  ; Size of getcbuf is the same
-               mov [EndOfGetCBuf],ax           ; as for trackbuf
-;
-; FAT12 or FAT16?  This computation is fscking ridiculous...
-;
-               mov eax,[bxSectors]
-               and ax,ax
-               jnz have_secs
-               mov eax,[bsHugeSectors]
-have_secs:     add eax,[bsHidden]              ; These are not included
-               sub eax,[RootDir]               ; Start of root directory
-               movzx ebx,word [RootDirSize]
-               sub eax,ebx                     ; Subtract root directory size
-               xor edx,edx
-               div esi                         ; Convert to clusters
-               cmp ax,4086                     ; FAT12 limit
-               jna is_fat12
-               ; Patch the jump
-               mov byte [nextcluster+1],nextcluster_fat16-(nextcluster+2)
-is_fat12:
+               mov word [xint13.disk_error+1],do_disk_error-(xint13.disk_error+3)
 
 ;
-; Patch gls_set_size so we can transfer more than one sector at a time.
+; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
+; sector again, though.
 ;
-               mov byte [gls_set_size],0xe8    ; E8 = CALL NEAR
-               mov word [gls_set_size+1],do_gls_set_size-(gls_set_size+3)
-               mov byte [disk_error],0xe8
-               mov word [disk_error+1],do_disk_error-(disk_error+3)
+load_rest:
+               mov si,SectorPtrs
+               mov bx,7C00h+2*SECTOR_SIZE      ; Where we start loading
+               mov cx,[TotalSectors]
+
+.get_chunk:
+               jcxz .done
+               xor bp,bp
+               lodsd                           ; First sector of this chunk
+
+               mov edx,eax
+
+.make_chunk:
+               inc bp
+               dec cx
+               jz .chunk_ready
+               inc edx                         ; Next linear sector
+               cmp [esi],edx                   ; Does it match
+               jnz .chunk_ready                ; If not, this is it
+               inc esi                         ; If so, add sector to chunk
+               jmp short .make_chunk
+
+.chunk_ready:
+               call getlinsecsr
+               jmp .get_chunk
+
+.done:
 
 ;
-; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
-; cluster again, though.
+; All loaded up, verify that we got what we needed.
+; Note: the checksum field is embedded in the checksum region, so
+; by the time we get to the end it should all cancel out.
 ;
-load_rest:
-               mov cx,[ClustSize]
-               mov bx,ldlinux_sys
-               add bx,cx
-               mov si,[RunLinClust]
-               call nextcluster
-               xor dx,dx
-               mov ax,ldlinux_len-1            ; To be on the safe side
-               add ax,cx
-               div cx                          ; the number of clusters
-               dec ax                          ; We've already read one
-               jz all_read_jmp
-               mov cx,ax
-               call getfssec
+verify_checksum:
+               mov si,ldlinux_sys
+               mov cx,[TotalDwords]
+               mov edx,-LDLINUX_MAGIC
+.checksum:
+               lodsd
+               sub edx,eax
+               loop .checksum
+
+               and edx,edx                     ; Should be zero
+               jz all_read                     ; We're cool, go for it!
+
 ;
-; All loaded up
+; Uh-oh, something went bad...
 ;
-all_read_jmp:
-               jmp all_read
+               mov si,checksumerr_msg
+               call writestr
+               jmp kaboom
+
 ;
 ; -----------------------------------------------------------------------------
 ; Subroutines that have to be in the first sector
 ; -----------------------------------------------------------------------------
-;
-; getfssec: Get multiple clusters from a file, given the starting cluster.
-;
-;      This routine makes sure the subtransfers do not cross a 64K boundary,
-;      and will correct the situation if it does, UNLESS *sectors* cross
-;      64K boundaries.
-;
-;      ES:BX   -> Buffer
-;      SI      -> Starting cluster number (2-based)
-;      CX      -> Cluster count (0FFFFh = until end of file)
-;
-;      Returns CF=1 on EOF
-;
-getfssec:
-.getfragment:  xor ebp,ebp                     ; Fragment sector count
-               lea eax,[si-2]                  ; Get 0-based sector address
-               mul dword [SecPerClust]
-               add eax,[DataArea]
-.getseccnt:                                    ; See if we can read > 1 clust
-               add bp,[SecPerClust]
-               dec cx                          ; Reduce clusters left to find
-               lea di,[si+1]
-               call nextcluster
-               cmc
-               jc .eof                         ; At EOF?
-               jcxz .endfragment               ; Or was it the last we wanted?
-               cmp si,di                       ; Is file continuous?
-               je .getseccnt                   ; Yes, we can get
-.endfragment:  clc                             ; Not at EOF
-.eof:          pushf                           ; Remember EOF or not
-               push si
-               push cx
-.getchunk:
-               push eax
-               mov ax,es                       ; Check for 64K boundaries.
-               shl ax,4
-               add ax,bx
-               xor dx,dx
-               neg ax
-               setz dl                         ; DX <- 1 if full 64K segment
-               div word [bsBytesPerSec]        ; How many sectors fit?
-               mov si,bp
-               sub si,ax                       ; Compute remaining sectors
-               jbe .lastchunk
-               mov bp,ax
-               pop eax
-               call getlinsecsr
-               add eax,ebp                     ; EBP<31:16> == 0
-               mov bp,si                       ; Remaining sector count
-               jmp short .getchunk
-.lastchunk:    pop eax
-               call getlinsec
-               pop cx
-               pop si
-               popf
-               jcxz .return                    ; If we hit the count limit
-               jnc .getfragment                ; If we didn't hit EOF
-.return:       ret
 
 ;
 ; getlinsecsr: save registers, call getlinsec, restore registers
@@ -886,83 +763,29 @@ getlinsecsr:      pushad
                ret
 
 ;
-; nextcluster: Advance a cluster pointer in SI to the next cluster
-;             pointed at in the FAT tables.  CF=0 on return if end of file.
-;
-nextcluster:
-               jmp short nextcluster_fat12     ; This gets patched
-
-nextcluster_fat12:
-               push bx
-               push ds
-               mov bx,fat_seg
-               mov ds,bx
-               mov bx,si                       ; Multiply by 3/2
-               shr bx,1                        ; CF now set if odd
-               mov si,[si+bx]
-               jnc nc_even
-               shr si,4                        ; Needed for odd only
-nc_even:
-               and si,0FFFh
-               cmp si,0FF0h                    ; Clears CF if at end of file
-               pop ds
-               pop bx
-nc_return:     ret
-
-;
-; FAT16 decoding routine.  Note that a 16-bit FAT can be up to 128K,
-; so we have to decide if we're in the "low" or the "high" 64K-segment...
+; This routine captures disk errors, and tries to decide if it is
+; time to reduce the transfer size.
 ;
-nextcluster_fat16:
+do_disk_error:
+               cmp ah,42h
+               je .ebios
+               shr al,1                ; Try reducing the transfer size
+               mov [MaxTransfer],al    
+               jz kaboom               ; If we can't, we're dead...
+               jmp xint13              ; Try again
+.ebios:
                push ax
-               push ds
-               mov ax,fat_seg
-               shl si,1
-               jnc .seg0
-               mov ax,fat_seg+1000h
-.seg0:         mov ds,ax
-               mov si,[si]
-               cmp si,0FFF0h
-               pop ds
+               mov ax,[si+2]
+               shr ax,1
+               mov [MaxTransfer],ax
+               mov [si+2],ax
                pop ax
-               ret
+               jmp xint13
 
 ;
-; Routine that controls how much we can transfer in one chunk.  Called
-; from gls_set_size in getlinsec.
+; Checksum error message
 ;
-do_gls_set_size:
-               sub si,cx               ; Sectors left on track
-               cmp bp,si
-               jna .lastchunk
-               mov bp,si               ; No more than a trackful, please!
-.lastchunk:
-               cmp bp,[MaxTransfer]    ; Absolute maximum transfer size
-               jna .oktransfer
-               mov bp,[MaxTransfer]
-.oktransfer:   
-               ret
-
-;
-; This routine captures disk errors, and tries to decide if it is
-; time to reduce the transfer size.
-;
-do_disk_error:
-               dec si                  ; Decrement the retry counter
-               jz kaboom               ; If expired, croak
-               cmp si,2                ; If only 2 attempts left
-               ja .nodanger
-               mov al,1                ; Drop transfer size to 1
-               jmp short .setsize
-.nodanger:
-               cmp si,retry_count-2
-               ja .again               ; First time, just try again
-               shr al,1                ; Otherwise, try to reduce
-               adc al,0                ; the max transfer size, but not to 0
-.setsize:
-               mov [MaxTransfer],al
-.again:
-               ret
+checksumerr_msg        db 'Load error - ', 0   ; Boot failed appended
 
 ;
 ; Debug routine
@@ -977,7 +800,7 @@ safedumpregs:
 rl_checkpt     equ $                           ; Must be <= 8000h
 
 rl_checkpt_off equ ($-$$)
-%ifndef DEPEND
+%if 0 ; ndef DEPEND
 %if rl_checkpt_off > 400h
 %error "Sector 1 overflow"
 %endif
@@ -995,6 +818,70 @@ all_read:
                mov si,copyright_str
                call writestr
 
+
+;
+; Insane hack to expand the superblock to dwords
+;
+expand_super:
+               xor eax,eax
+               mov es,ax                       ; INT 13:08 destroys ES
+               mov si,superblock
+               mov di,SuperInfo
+               mov cx,superinfo_size
+.loop:
+               lodsw
+               dec si
+               stosd                           ; Store expanded word
+               xor ah,ah
+               stosd                           ; Store expanded byte
+
+;
+; How big is a cluster, really?  Also figure out how many clusters
+; will fit in an 8K buffer, and how many sectors and bytes that is
+;
+               mov edi,[bxBytesPerSec]         ; Used a lot below
+               mov eax,[SecPerClust]
+               mov si,ax                       ; Also used a lot
+               mul di
+               mov [ClustSize],eax             ; Bytes/cluster
+               mov bx,ax
+               mov ax,trackbufsize             ; High bit 0
+               cwd
+               div bx
+               mov [BufSafe],ax                ; # of cluster in trackbuf
+               mul si
+               mov [BufSafeSec],ax
+               mul di
+               mov [BufSafeBytes],ax
+               add ax,getcbuf                  ; Size of getcbuf is the same
+               mov [EndOfGetCBuf],ax           ; as for trackbuf
+;
+; FAT12, FAT16 or FAT28^H^H32?  This computation is fscking ridiculous...
+;
+getfattype:
+               mov eax,[bxSectors]
+               and ax,ax
+               jnz .have_secs
+               mov eax,[bsHugeSectors]
+.have_secs:    add eax,[bsHidden]              ; These are not included
+               sub eax,[RootDir]               ; Start of root directory
+               movzx ebx,word [RootDirSize]
+               sub eax,ebx                     ; Subtract root directory size
+               xor edx,edx
+               div esi                         ; Convert to clusters
+               mov cl,nextcluster_fat12-(nextcluster+2)
+               cmp eax,4086                    ; FAT12 limit
+               jna .setsize
+               mov cl,nextcluster_fat16-(nextcluster+2)
+               cmp eax,65526                   ; FAT16 limit
+               jna .setsize
+               mov cl,nextcluster_fat28-(nextcluster+2)
+.setsize:
+               mov byte [nextcluster+1],cl
+
+
+
+
 ;
 ; Common initialization code
 ;
@@ -1342,6 +1229,209 @@ lc_1:           cmp al,lcase_low
                 pop bx
 lc_ret:         ret
 
+;
+; getfssec: Get multiple sectors from a file
+;
+;      This routine makes sure the subtransfers do not cross a 64K boundary,
+;      and will correct the situation if it does, UNLESS *sectors* cross
+;      64K boundaries.
+;
+;      ES:BX   -> Buffer
+;      SI      -> Pointer to structure:
+;                 0 - dword - Starting cluster number (2-based)
+;                 4 - word  - Sector number within cluster
+;                 8 - dword - Absolute sector number
+;      CX      -> Sector count (0FFFFh = until end of file)
+;                  Must not exceed the ES segment
+;      Returns CF=1 on EOF
+;
+getfssec:
+.getfragment:  xor ebp,ebp                     ; Fragment sector count
+               mov edi,[si]
+               lea eax,[edi-2]
+               ; mov eax,ebx
+               ; sub eax,2
+               ; jc .isrootdir                 ; Use cluster 1 for the root directory
+               mul dword [SecPerClust]
+               add eax,[DataArea]
+               sub bp,[si+4]                   ; Sectors already read
+.getseccnt:                                    ; See if we can read > 1 clust
+               add bp,[SecPerClust]
+               cmp cx,bp
+               jna .endfragment                ; Done?
+               lea eax,[edi+1]
+               call nextcluster
+               jc .eof                         ; At EOF?
+               cmp eax,edi                     ; Is file continuous?
+               je .getseccnt                   ; Yes, we can get
+.endfragment:  clc                             ; Not at EOF
+.eof:          pushf                           ; Remember EOF or not
+               push si
+               push cx
+.getchunk:
+               push eax
+               mov ax,es                       ; Check for 64K boundaries.
+               shl ax,4
+               add ax,bx
+               xor dx,dx
+               neg ax
+               setz dl                         ; DX <- 1 if full 64K segment
+               div word [bsBytesPerSec]        ; How many sectors fit?
+               mov si,bp
+               sub si,ax                       ; Compute remaining sectors
+               jbe .lastchunk
+               mov bp,ax
+               pop eax
+               call getlinsecsr
+               add eax,ebp                     ; EBP<31:16> == 0
+               mov bp,si                       ; Remaining sector count
+               jmp short .getchunk
+.lastchunk:    pop eax
+               call getlinsec
+               pop cx
+               pop si
+               popf
+               jcxz .return                    ; If we hit the count limit
+               jnc .getfragment                ; If we didn't hit EOF
+.return:       ret
+
+;
+; nextcluster: Advance a cluster pointer in EDI to the next cluster
+;             pointed at in the FAT tables.  CF=0 on return if end of file.
+;
+nextcluster:
+               jmp strict short nextcluster_fat28      ; This gets patched
+
+nextcluster_fat12:
+               push eax
+               push edx
+               push bx
+               push cx
+               push si
+               mov edx,edi
+               shr edi,1
+               add edx,edi
+               mov eax,edx
+               shr eax,9
+               call getfatsector
+               mov bx,dx
+               and bx,1FFh
+               mov cl,[gs:si+bx]
+               inc edx
+               mov eax,edx
+               shr eax,9
+               call getfatsector
+               mov bx,dx
+               and bx,1FFh
+               mov ch,[gs:si+bx]
+               test di,1
+               jz .even
+               shr cx,4
+.even:         and cx,0FFFh
+               movzx edi,cx
+               cmp di,0FF0h
+               pop si
+               pop cx
+               pop bx
+               pop edx
+               pop eax
+               ret
+
+;
+; FAT16 decoding routine.
+;
+nextcluster_fat16:
+               push eax
+               push si
+               push bx
+               mov eax,edi
+               shr eax,SECTOR_SHIFT-1
+               call getfatsector
+               mov bx,di
+               add bx,bx
+               and bx,1FEh
+               movzx edi,word [gs:si+bx]
+               cmp di,0FFF0h
+               pop bx
+               pop si
+               pop eax
+               ret
+;
+; FAT28 ("FAT32") decoding routine.
+;
+nextcluster_fat28:
+               push eax
+               push si
+               push bx
+               mov eax,edi
+               shr eax,SECTOR_SHIFT-2
+               call getfatsector
+               mov bx,di
+               add bx,bx
+               add bx,bx
+               and bx,1FCh
+               mov edi,dword [gs:si+bx]
+               and edi,0FFFFFFFh       ; 28 bits only
+               cmp edi,0FFFFFF0h
+               pop bx
+               pop si
+               pop eax
+               ret
+
+;
+; getfatsector: Check for a particular sector (in EAX) in the FAT cache,
+;              and return a pointer in GS:SI, loading it if needed.
+;
+;              Assumes CS == DS.
+;
+getfatsector:
+               add eax,[bsHidden]      ; Hidden sectors
+               add eax,[bxResSectors]  ; Reserved sectors
+               ; Fall through
+
+;
+; getcachesector: Check for a particular sector (EAX) in the sector cache,
+;                and if it is already there, return a pointer in GS:SI
+;                otherwise load it and return said pointer.
+;
+;              Assumes CS == DS.
+;
+getcachesector:
+               push cx
+               mov si,cache_seg
+               mov gs,si
+               mov si,CachePtrs        ; Sector cache pointers
+               mov cx,65536/SECTOR_SIZE
+               repne scasd             ; Do we have it?
+               jne .miss
+               ; We have it; get the pointer
+               sub si,CachePtrs+4
+               shl si,SECTOR_SHIFT-2
+               pop cx
+               ret
+.miss:
+               ; Need to load it.  Highly inefficient cache replacement
+               ; algorithm: Least Recently Written (LRW)
+               push bx
+               push es
+               push gs
+               pop es
+               mov bx,[NextCacheSlot]
+               inc bx
+               and bx,(1 << (16-SECTOR_SHIFT))-1
+               mov [NextCacheSlot],bx
+               shl bx,2
+               mov [CachePtrs+bx],eax
+               shl bx,SECTOR_SHIFT-2
+               mov si,bx
+               pushad
+               call getonesec
+               popad
+               pop es
+               pop bx
+               pop cx
+               ret
+
 ; -----------------------------------------------------------------------------
 ;  Common modules
 ; -----------------------------------------------------------------------------
diff --git a/libfat/cache.c b/libfat/cache.c
new file mode 100644 (file)
index 0000000..cc6c57f
--- /dev/null
@@ -0,0 +1,70 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2004 H. Peter Anvin - All Rights Reserved
+ *
+ *   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
+ *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ *   Boston MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * cache.c
+ *
+ * Simple sector cache
+ */
+
+#include <stdlib.h>
+#include "libfatint.h"
+
+void * libfat_get_sector(struct libfat_filesystem *fs, libfat_sector_t n)
+{
+  struct libfat_sector *ls;
+
+  for ( ls = fs->sectors ; ls ; ls = ls->next ) {
+    if ( ls->n == n )
+      return ls->data;         /* Found in cache */
+  }
+
+  /* Not found in cache */
+  ls = malloc(sizeof(struct libfat_sector));
+  if ( !ls ) {
+    libfat_flush(fs);
+    ls = malloc(sizeof(struct libfat_sector));
+  
+    if ( !ls )
+      return NULL;             /* Can't allocate memory */
+  }
+
+  if ( fs->read(fs->readptr, ls->data, LIBFAT_SECTOR_SIZE, n) 
+       != LIBFAT_SECTOR_SIZE ) {
+    free(ls);
+    return NULL;               /* I/O error */
+  }
+
+  ls->n = n;
+  ls->next = fs->sectors;
+  fs->sectors = ls;
+
+  return ls->data;
+}
+
+void libfat_flush(struct libfat_filesystem *fs)
+{
+  struct libfat_sector *ls, *lsnext;
+
+  lsnext = fs->sectors;
+  fs->sectors = NULL;
+
+  for ( ls = lsnext ; ls ; ls = lsnext ) {
+    lsnext = ls->next;
+    free(ls);
+  }
+}
+
+
+    
+  
diff --git a/libfat/fat.h b/libfat/fat.h
new file mode 100644 (file)
index 0000000..921f9f4
--- /dev/null
@@ -0,0 +1,112 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2001-2004 H. Peter Anvin - All Rights Reserved
+ *
+ *   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
+ *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ *   Boston MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * fat.h
+ *
+ * Basic data structures for a FAT filesystem
+ */
+
+#ifndef FAT_H
+#define FAT_H
+
+#include "ulint.h"
+
+/* The poor excuse FAT has for a superblock -- in the boot sector */
+struct fat_bootsect {
+  le8_t bsJump[3];             /* Jump to code */
+  char bsOemName[8];           /* Formatting program */
+  le16_t bsBytesPerSec;                /* Bytes/sector */
+  le8_t bsSecPerClust;         /* Sectors/cluster */
+  le16_t bsResSectors;         /* Reserved sectors */
+  le8_t bsFATs;                        /* Number of FATs */
+  le16_t bsRootDirEnts;                /* Number of entries/root directory */
+  le16_t bsSectors;            /* Number of sectors [1] */
+  le8_t bsMedia;               /* Magic media type byte */
+  le16_t bsFATsecs;            /* Sectors/FAT */
+  le16_t bsSecPerTrack;                /* Sectors/track */
+  le16_t bsHeads;              /* Number of heads */
+  le32_t bsHiddenSecs;         /* Number of hidden sectors */
+  le32_t bsHugeSectors;                /* Number of sectors [2] */
+  union {
+    /* FAT12/16 */
+    struct {
+      le8_t bsDriveNumber;     /* Drive number */
+      le8_t bsReserved1;       /* Reserved */
+      le8_t bsBootSignature;   /* 0x29 */
+      le32_t bsVolumeID;       /* Volume serial number */
+      char bsVolumeLabel[11];  /* Volume name */
+      char bsFileSysType[8];   /* File system type */
+  
+      le8_t bsCode[448];       /* Boot sector code */
+    } fat16;
+    
+    /* FAT32 */
+    struct {
+      le32_t bpb_fatsz32;      /* Sectors/FAT */
+      le16_t bpb_extflags;     /* Extended flags */
+      le16_t bpb_fsver;                /* Filesystem version */
+      le32_t bpb_rootclus;     /* Root directory cluster */
+      le16_t bpb_fsinfo;       /* FSINFO sector number */
+      le16_t bpb_bkbootsec;    /* Backup boot sector (superblock) */
+      char bpb_reserved[12];
+
+      /* Same shit, different offset! */
+      le8_t bsDriveNumber;     /* Drive number */
+      le8_t bsReserved1;       /* Reserved */
+      le8_t bsBootSignature;   /* 0x29 */
+      le32_t bsVolumeID;       /* Volume serial number */
+      char bsVolumeLabel[11];  /* Volume name */
+      char bsFileSysType[8];   /* File system type */
+
+      le8_t bsCode[420];       /* Boot sector code */
+    } fat32;
+  } u;
+
+  le16_t bsSignature;          /* 0xAA55 */
+};
+
+#define BS_BOOTSIGNATURE       0x29
+#define BS_SIGNATURE           0xAA55
+
+/* A FAT filesystem directory entry */
+
+struct fat_dirent
+{
+  le8_t name[11];              /* Mangled filename */
+  le8_t attribute;             /* File type/attribute */
+  le8_t caseflags;             /* VFAT: case for basis and extension */
+  le8_t ctime_ms;              /* ms of creation time */
+  le32_t ctime;                        /* Creation time */
+  le16_t atime;                        /* Date portion (high 16 bits) of atime */
+  le16_t clusthi;              /* FAT32: high 16 bits of cluster */
+  le32_t mtime;                        /* Modification time */
+  le16_t clustlo;              /* First cluster pointer */
+  le32_t size;                 /* File size (bytes) */
+};
+
+/* A VFAT filesystem continuation entry */
+struct fat_vfat_slot
+{
+  le8_t  id;                   /* Sequence number for slot */
+  le16_t name0[5];             /* 5 characters */
+  le8_t  attribute;            /* Attribute byte */
+  le8_t  reserved;             /* Reserved, MBZ */
+  le8_t  alias_csum;           /* Short name checksum */
+  le16_t name5[6];             /* 6 characters */
+  le16_t firstclust;           /* MBZ */
+  le16_t name11[2];            /* 2 characters */
+};
+
+#endif /* FAT_H */
+
diff --git a/libfat/fatchain.c b/libfat/fatchain.c
new file mode 100644 (file)
index 0000000..6c640be
--- /dev/null
@@ -0,0 +1,136 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2004 H. Peter Anvin - All Rights Reserved
+ *
+ *   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
+ *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ *   Boston MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * fatchain.c
+ *
+ * Follow a FAT chain
+ */
+
+#include "libfatint.h"
+#include "ulint.h"
+
+/*
+ * Convert a cluster number (or 0 for the root directory) to a
+ * sector number.  Return -1 on failure.
+ */
+libfat_sector_t libfat_clustertosector(const struct libfat_filesystem *fs,
+                                      int32_t cluster)
+{
+  if ( cluster == 0 )
+    cluster = fs->rootcluster;
+
+  if ( cluster == 0 )
+    return fs->rootdir;
+  else if ( cluster < 2 || cluster >= fs->endcluster )
+    return -1;
+  else
+    return fs->data + ((libfat_sector_t)(cluster-2) << fs->clustshift);
+}
+
+/*
+ * Get the next sector of either the root directory or a FAT chain.
+ * Returns 0 on end of file and -1 on error.
+ */
+
+libfat_sector_t libfat_nextsector(struct libfat_filesystem *fs,
+                                 libfat_sector_t s)
+{
+  int32_t cluster, nextcluster;
+  uint32_t fatoffset;
+  libfat_sector_t fatsect;
+  uint8_t *fsdata;
+  uint32_t clustmask = fs->clustsize - 1;
+  libfat_sector_t rs;
+
+  if ( s < fs->data ) {
+    if ( s < fs->rootdir )
+      return -1;
+
+    /* Root directory */
+    s++;
+    return ( s < fs->data ) ? s : 0;
+  }
+
+  rs = s - fs->data;
+
+  if ( ~rs & clustmask )
+    return s+1;                        /* Next sector in cluster */
+
+  cluster = 2 + (rs >> fs->clustshift);
+
+  if ( cluster >= fs->endcluster )
+    return -1;
+
+  switch ( fs->fat_type ) {
+  case FAT12:
+    /* Get first byte */
+    fatoffset = cluster + (cluster >> 1);
+    fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT);
+    fsdata = libfat_get_sector(fs, fatsect);
+    if ( !fsdata )
+      return -1;
+    nextcluster = fsdata[fatoffset & LIBFAT_SECTOR_MASK];
+
+    /* Get second byte */
+    fatoffset++;
+    fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT);
+    fsdata = libfat_get_sector(fs, fatsect);
+    if ( !fsdata )
+      return -1;
+    nextcluster |= fsdata[fatoffset & LIBFAT_SECTOR_MASK] << 8;
+
+    /* Extract the FAT entry */
+    if ( cluster & 1 )
+      nextcluster >>= 4;
+    else
+      nextcluster &= 0x0FFF;
+
+    if ( nextcluster >= 0x0FF8 )
+      return 0;
+    break;
+
+  case FAT16:
+    fatoffset = cluster << 1;
+    fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT);
+    fsdata = libfat_get_sector(fs, fatsect);
+    if ( !fsdata )
+      return -1;
+    nextcluster = read16((le16_t *)&fsdata[fatoffset & LIBFAT_SECTOR_MASK]);
+    
+    if ( nextcluster >= 0x0FFF8 )
+      return 0;
+    break;
+
+  case FAT28:
+    fatoffset = cluster << 2;
+    fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT);
+    fsdata = libfat_get_sector(fs, fatsect);
+    if ( !fsdata )
+      return -1;
+    nextcluster = read32((le32_t *)&fsdata[fatoffset & LIBFAT_SECTOR_MASK]);
+    nextcluster &= 0x0FFFFFFF;
+    
+    if ( nextcluster >= 0x0FFFFFF8 )
+      return 0;
+    break;
+    
+  default:
+    return -1;                 /* WTF? */
+  }
+
+  return libfat_clustertosector(fs, nextcluster);
+}
+
+    
+
diff --git a/libfat/libfat.h b/libfat/libfat.h
new file mode 100644 (file)
index 0000000..b068227
--- /dev/null
@@ -0,0 +1,81 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2004 H. Peter Anvin - All Rights Reserved
+ *
+ *   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
+ *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ *   Boston MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * libfat.h
+ *
+ * Headers for the libfat library
+ */
+
+#ifndef LIBFAT_H
+#define LIBFAT_H
+
+#include <stddef.h>
+#include <inttypes.h>
+
+#define LIBFAT_SECTOR_SHIFT    9
+#define LIBFAT_SECTOR_SIZE     512
+#define LIBFAT_SECTOR_MASK     511
+
+typedef uint32_t libfat_sector_t;
+struct libfat_filesystem;
+
+/*
+ * Open the filesystem.  The readfunc is the function to read
+ * sectors, in the format:
+ * int readfunc(void *readptr, void *buf, size_t secsize,
+ *              libfat_sector_t secno)
+ *
+ * ... where readptr is a private argument.
+ *
+ * A return value of != secsize is treated as error.
+ */
+struct libfat_filesystem *
+libfat_open(int (*readfunc)(void *, void *, size_t, libfat_sector_t),
+           void *readptr);
+
+void libfat_close(struct libfat_filesystem *);
+
+/*
+ * Convert a cluster number (or 0 for the root directory) to a
+ * sector number.  Return -1 on failure.
+ */
+libfat_sector_t libfat_clustertosector(const struct libfat_filesystem *fs,
+                                      int32_t cluster);
+
+/*
+ * Get the next sector of either the root directory or a FAT chain.
+ * Returns 0 on end of file and -1 on error.
+ */
+libfat_sector_t libfat_nextsector(struct libfat_filesystem *fs,
+                                  libfat_sector_t s);
+
+/*
+ * Flush all cached sectors for this filesystem.
+ */
+void libfat_flush(struct libfat_filesystem *fs);
+
+/*
+ * Get a pointer to a specific sector.
+ */
+void * libfat_get_sector(struct libfat_filesystem *fs, libfat_sector_t n);
+
+/*
+ * Search a FAT directory for a particular pre-mangled filename.
+ * Copies the directory entry into direntry and returns 0 if found.
+ */
+int32_t libfat_searchdir(struct libfat_filesystem *fs, int32_t dirclust,
+                        const void *name, void *direntry);
+
+#endif /* LIBFAT_H */
+
diff --git a/libfat/libfatint.h b/libfat/libfatint.h
new file mode 100644 (file)
index 0000000..6da47f1
--- /dev/null
@@ -0,0 +1,56 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2004 H. Peter Anvin - All Rights Reserved
+ *
+ *   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
+ *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ *   Boston MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * libfatint.h
+ *
+ * Internals for the libfat filesystem
+ */
+
+#ifndef LIBFATINT_H
+#define LIBFATINT_H
+
+#include "libfat.h"
+#include "fat.h"
+
+struct libfat_sector {
+  libfat_sector_t n;           /* Sector number */
+  struct libfat_sector *next;  /* Next in list */
+  char data[LIBFAT_SECTOR_SIZE];
+};
+
+enum fat_type {
+  FAT12,
+  FAT16,
+  FAT28
+};
+
+struct libfat_filesystem {
+  int (*read)(void *, void *, size_t, libfat_sector_t);
+  void *readptr;
+  enum fat_type fat_type;
+  unsigned int clustsize;
+  int clustshift;
+  int32_t endcluster;          /* Highest legal cluster number + 1 */
+  int32_t rootcluster;         /* Root directory cluster */
+
+  libfat_sector_t fat;         /* Start of FAT */
+  libfat_sector_t rootdir;     /* Start of root directory */
+  libfat_sector_t data;                /* Start of data area */
+  libfat_sector_t end;         /* End of filesystem */
+
+  struct libfat_sector *sectors;
+};
+
+#endif /* LIBFATINT_H */
diff --git a/libfat/open.c b/libfat/open.c
new file mode 100644 (file)
index 0000000..835c336
--- /dev/null
@@ -0,0 +1,118 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2004 H. Peter Anvin - All Rights Reserved
+ *
+ *   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
+ *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ *   Boston MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * open.c
+ *
+ * Open a FAT filesystem and compute some initial values; return NULL
+ * on failure.
+ */
+
+#include <stdlib.h>
+#include "libfatint.h"
+#include "ulint.h"
+
+struct libfat_filesystem *
+libfat_open(int (*readfunc)(void *, void *, size_t, libfat_sector_t),
+           void *readptr)
+{
+  struct libfat_filesystem *fs = NULL;
+  struct fat_bootsect *bs;
+  int i;
+  uint32_t sectors, fatsize, minfatsize, rootdirsize;
+  uint32_t nclusters;
+
+  fs = malloc(sizeof(struct libfat_filesystem));
+  if ( !fs )
+    goto barf;
+  
+  fs->sectors = NULL;
+  fs->read = readfunc;
+  fs->readptr = readptr;
+
+  bs = libfat_get_sector(fs, 0);
+  if ( !bs ) 
+    goto barf;
+
+  if ( read16(&bs->bsBytesPerSec) != LIBFAT_SECTOR_SIZE )
+    goto barf;
+
+  for ( i = 0 ; i <= 8 ; i++ ) {
+    if ( (uint8_t)(1 << i) == read8(&bs->bsSecPerClust) )
+      break;
+  }
+  if ( i > 8 )
+    goto barf;
+  fs->clustsize  = 1 << i;     /* Treat 0 as 2^8 = 64K */
+  fs->clustshift = i;
+  
+  sectors = read16(&bs->bsSectors);
+  if ( !sectors )
+    sectors = read32(&bs->bsHugeSectors);
+
+  fs->end = sectors;
+  
+  fs->fat     = read16(&bs->bsResSectors);
+  fatsize     = read16(&bs->bsFATsecs);
+  if ( !fatsize )
+    fatsize   = read32(&bs->u.fat32.bpb_fatsz32);
+  
+  fs->rootdir = fs->fat + fatsize * read8(&bs->bsFATs);
+  
+  rootdirsize = ((read16(&bs->bsRootDirEnts) << 5) + LIBFAT_SECTOR_MASK)
+                                           >> LIBFAT_SECTOR_SHIFT;
+  fs->data    = fs->rootdir + rootdirsize;
+
+  /* Sanity checking */
+  if ( fs->data >= fs->end )
+    goto barf;
+
+  /* Figure out how many clusters */
+  nclusters = (fs->end - fs->data) >> fs->clustshift;
+  fs->endcluster = nclusters + 2;
+
+  if ( nclusters <= 0xff4 ) {
+    fs->fat_type = FAT12;
+    minfatsize = fs->endcluster + (fs->endcluster >> 1);
+  } else if ( nclusters <= 0xfff4 ) {
+    fs->fat_type = FAT16;
+    minfatsize = fs->endcluster << 1;
+  }  else if ( nclusters <= 0xffffff4 ) {
+    fs->fat_type = FAT28;
+    minfatsize = fs->endcluster << 2;
+  } else
+    goto barf;                 /* Impossibly many clusters */
+  
+  minfatsize = (minfatsize + LIBFAT_SECTOR_SIZE-1) >> LIBFAT_SECTOR_SHIFT;
+
+  if ( minfatsize > fatsize )
+    goto barf;                 /* The FATs don't fit */
+
+  if ( fs->fat_type == FAT28 )
+    fs->rootcluster = read32(&bs->u.fat32.bpb_rootclus);
+  else
+    fs->rootcluster = 0;
+
+  return fs;                   /* All good */
+
+ barf:
+  if ( fs )
+    free(fs);
+  return NULL;
+}
+
+void libfat_close(struct libfat_filesystem *fs)
+{
+  libfat_flush(fs);
+  free(fs);
+}
diff --git a/libfat/searchdir.c b/libfat/searchdir.c
new file mode 100644 (file)
index 0000000..7c07165
--- /dev/null
@@ -0,0 +1,61 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2004 H. Peter Anvin - All Rights Reserved
+ *
+ *   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
+ *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ *   Boston MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * searchdir.c
+ *
+ * Search a FAT directory for a particular pre-mangled filename.
+ * Copies the directory entry into direntry and returns the starting cluster
+ * if found; returns -2 on not found, -1 on error, 0 on empty file.
+ */
+
+#include <string.h>
+#include "libfatint.h"
+
+int32_t libfat_searchdir(struct libfat_filesystem *fs, int32_t dirclust,
+                        const void *name, void *direntry)
+{
+  struct fat_dirent *dep;
+  int nent;
+  libfat_sector_t s = libfat_clustertosector(fs, dirclust);
+
+  while ( 1 ) {
+    if ( s == 0 )
+      return -2;               /* Not found */
+    else if ( s == (libfat_sector_t)-1 )
+      return -1;               /* Error */
+    
+    dep = libfat_get_sector(fs, s);
+    if ( !dep )
+      return -1;               /* Read error */
+
+    for ( nent = LIBFAT_SECTOR_SIZE/sizeof(struct fat_dirent) ;
+         nent ; nent-- ) {
+      if ( !memcmp(dep->name, name, 11) ) {
+       if ( direntry )
+         memcpy(direntry, dep, sizeof (*dep));
+       if ( read32(&dep->size) == 0 )
+         return 0;             /* An empty file has no clusters */
+       else
+         return read16(&dep->clustlo) + (read16(&dep->clusthi) << 16);
+      }
+      
+      if ( dep->name[0] == 0 )
+       return -2;              /* Hit high water mark */
+
+      dep++;
+    }   
+
+    s = libfat_nextsector(fs, s);
+  }
+}
diff --git a/libfat/ulint.h b/libfat/ulint.h
new file mode 100644 (file)
index 0000000..0b200ea
--- /dev/null
@@ -0,0 +1,115 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2001-2004 H. Peter Anvin - All Rights Reserved
+ *
+ *   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
+ *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ *   USA; either version 2 of the License, or (at your option) any later
+ *   version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * ulint.h
+ *
+ * Basic operations on unaligned, littleendian integers 
+ */
+
+#ifndef ULINT_H
+#define ULINT_H
+
+#include <inttypes.h>
+
+/* These are unaligned, littleendian integer types */
+
+typedef uint8_t le8_t;         /*  8-bit byte */
+typedef uint8_t le16_t[2];     /* 16-bit word */
+typedef uint8_t le32_t[4];     /* 32-bit dword */
+
+/* Read/write these quantities */
+
+static inline unsigned char
+read8(le8_t *_p)
+{
+  return *_p;
+}
+
+static inline void
+write8(le8_t *_p, unsigned char _v)
+{
+  *_p = _v;
+}
+
+#if defined(__i386__) || defined(__x86_64__)
+
+/* Littleendian architectures which support unaligned memory accesses */
+
+static inline unsigned short
+read16(le16_t *_p)
+{
+  return *((unsigned short *)_p);
+}
+
+static inline void
+write16(le16_t *_p, unsigned short _v)
+{
+  *((unsigned short *)_p) = _v;
+}
+
+static inline unsigned int
+read32(le32_t *_p)
+{
+  return *((unsigned int *)_p);
+}
+
+static inline void
+write32(le32_t *_p, unsigned int _v)
+{
+  *((unsigned int *)_p) = _v;
+}
+
+#else 
+
+/* Generic, mostly portable versions */
+
+static inline unsigned short
+read16(le16_t *_p)
+{
+  unsigned short _v;
+
+  _v  = p[0];
+  _v |= p[1] << 8;
+  return _v;
+}
+
+static inline void
+write16(le16_t *_p, unsigned short _v)
+{
+  _p[0] = _v & 0xFF;
+  _p[1] = (_v >> 8) & 0xFF;
+}
+
+static inline unsigned int
+read32(le32_t *_p)
+{
+  _v  = _p[0];
+  _v |= _p[1] << 8;
+  _v |= _p[2] << 16;
+  _v |= _p[3] << 24;
+  return _v;
+}
+
+static inline void
+write32(le32_t *_p, unsigned int _v)
+{
+  _p[0] = _v & 0xFF;
+  _p[1] = (_v >> 8) & 0xFF;
+  _p[2] = (_v >> 16) & 0xFF;
+  _p[3] = (_v >> 24) & 0xFF;
+}
+
+#endif
+
+#endif /* ULINT_H */
diff --git a/mtools/Makefile b/mtools/Makefile
new file mode 100644 (file)
index 0000000..a13642c
--- /dev/null
@@ -0,0 +1,39 @@
+CC      = gcc
+OPTFLAGS = -g -O -Dinline=
+INCLUDES = -I. -I.. -I../libfat
+CFLAGS  = -W -Wall -D_FILE_OFFSET_BITS=64 $(OPTFLAGS) $(INCLUDES)
+LDFLAGS         = 
+
+SRCS     = syslinux.c ../syslxmod.c ../bootsect_bin.c ../ldlinux_bin.c $(wildcard ../libfat/*.c)
+OBJS    = $(patsubst %.c,%.o,$(notdir $(SRCS)))
+
+.SUFFIXES: .c .o .i .s .S
+
+VPATH = .:..:../libfat
+
+all: installer
+
+tidy:
+       -rm -f *.o *.i *.s *.a .*.d
+
+clean: tidy
+       -rm -f syslinux
+
+spotless: clean
+
+installer: syslinux
+
+syslinux: $(OBJS)
+       $(CC) $(LDFLAGS) -o $@ $^
+
+%.o: %.c
+       $(CC) -Wp,-MT,$@,-MMD,.$@.d $(CFLAGS) -c -o $@ $<
+%.i: %.c
+       $(CC) $(CFLAGS) -E -o $@ $<
+%.s: %.c
+       $(CC) $(CFLAGS) -S -o $@ $<
+
+-include .*.d
+
+
+
similarity index 80%
rename from syslinux.c
rename to mtools/syslinux.c
index bb1de8a..69d7a3d 100644 (file)
@@ -37,6 +37,7 @@
 #include <sys/wait.h>
 
 #include "syslinux.h"
+#include "libfat.h"
 
 char *program;                 /* Name of program */
 char *device;                  /* Device to install to */
@@ -109,6 +110,16 @@ ssize_t xpwrite(int fd, void *buf, size_t count, off_t offset)
   return done;
 }
 
+/*
+ * Version of the read function suitable for libfat
+ */
+int libfat_xpread(void *pp, void *buf, size_t secsize, libfat_sector_t sector)
+{
+  off_t offset = (off_t)sector * secsize;
+  return xpread((int)pp, buf, secsize, offset);
+}
+
+
 int main(int argc, char *argv[])
 {
   static unsigned char sectbuf[512];
@@ -121,6 +132,12 @@ int main(int argc, char *argv[])
   char mtools_conf[] = "/tmp/syslinux-mtools-XXXXXX";
   int mtc_fd;
   FILE *mtc, *mtp;
+  struct libfat_filesystem *fs;
+  libfat_sector_t s, *secp, sectors[65]; /* 65 is maximum possible */
+  int32_t ldlinux_cluster;
+  int nsectors;
+
+  (void)argc;                  /* Unused */
 
   mypid = getpid();
   program = argv[0];
@@ -139,7 +156,7 @@ int main(int argc, char *argv[])
        } else if ( *opt == 'f' ) {
          force = 1;            /* Force install */
        } else if ( *opt == 'o' && argp[1] ) {
-         offset = strtoul(*++argp, NULL, 0); /* Byte offset */
+         offset = (off_t)strtoull(*++argp, NULL, 0); /* Byte offset */
        } else {
          usage();
        }
@@ -170,17 +187,12 @@ int main(int argc, char *argv[])
     exit(1);
   }
 
-  if ( !force && offset != 0 && !S_ISREG(st.st_mode) ) {
-    fprintf(stderr, "%s: not a regular file and an offset specified (use -f to override)\n", device);
-    exit(1);
-  }
-
   xpread(dev_fd, sectbuf, 512, offset);
   
   /*
    * Check to see that what we got was indeed an MS-DOS boot sector/superblock
    */
-  if(!syslinux_check_bootsect(sectbuf,device)) {
+  if( !syslinux_check_bootsect(sectbuf,device) ) {
     exit(1);
   }
 
@@ -197,7 +209,7 @@ int main(int argc, char *argv[])
          "MTOOLS_SKIP_CHECK=1\n" /* Needed for some flash memories */
          "drive s:\n"
          "  file=\"/proc/%lu/fd/%d\"\n"
-         "  offset=%lld\n",
+         "  offset=%llu\n",
          (unsigned long)mypid,
          dev_fd,
          (unsigned long long)offset);
@@ -223,17 +235,42 @@ int main(int argc, char *argv[])
     exit(1);
   }
 
-  status = system("mattrib +r s:ldlinux.sys");
+  status = system("mattrib +r +h +s s:ldlinux.sys");
 
   if ( !WIFEXITED(status) || WEXITSTATUS(status) ) {
     fprintf(stderr,
-           "%s: warning: failed to set readonly bit on ldlinux.sys\n",
+           "%s: warning: failed to set system bit on ldlinux.sys\n",
            program);
   }
 
   unlink(mtools_conf);
 
   /*
+   * Now, use libfat to create a block map
+   */
+  fs = libfat_open(libfat_xpread, (void *)dev_fd);
+  ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL);
+  secp = sectors;
+  nsectors = 0;
+  s = libfat_clustertosector(fs, ldlinux_cluster);
+  while ( s && nsectors < 65 ) {
+    *secp++ = s;
+    nsectors++;
+    s = libfat_nextsector(fs, s);
+  }
+  libfat_close(fs);
+
+  /*
+   * Patch ldlinux.sys and the boot sector
+   */
+  syslinux_patch(sectors, nsectors);
+
+  /*
+   * Write the now-patched first sector of ldlinux.sys
+   */
+  xpwrite(dev_fd, syslinux_ldlinux, 512, offset + ((off_t)sectors[0] << 9));
+
+  /*
    * To finish up, write the boot sector
    */
 
index f20172c..da6eecc 100644 (file)
@@ -23,7 +23,7 @@ extern unsigned char syslinux_ldlinux[];
 extern unsigned int  syslinux_ldlinux_len;
 extern int           syslinux_ldlinux_mtime;
 
-/* This switches the boot sector and ldlinux to "stupid mode" */
+/* This switches the boot sector to "stupid mode" */
 void syslinux_make_stupid(void);
 
 /* This takes a boot sector and merges in the syslinux fields */
@@ -32,4 +32,7 @@ void syslinux_make_bootsect(void *);
 /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */
 int syslinux_check_bootsect(const void *bs, const char *device);
 
+/* This patches the boot sector and ldlinux.sys based on a sector map */
+int syslinux_patch(const uint32_t *sectors, int nsectors);
+
 #endif
index 96e6088..1f83743 100644 (file)
@@ -23,6 +23,8 @@
 
 #include "syslinux.h"
 
+#define LDLINUX_MAGIC  0x3eb202fe
+
 enum bs_offsets {
   bsJump          = 0x00,
   bsOemName       = 0x03,
@@ -49,7 +51,7 @@ enum bs_offsets {
 };
 
 #define bsHead      bsJump
-#define bsHeadLen   (bsBytesPerSec-bsHead)
+#define bsHeadLen   (bsOemName-bsHead)
 #define bsCodeLen   (bsSignature-bsCode)
 
 /*
@@ -78,25 +80,33 @@ static inline uint32_t get_32(const unsigned char *p)
 
 static inline void set_16(unsigned char *p, uint16_t v)
 {
+#if defined(__i386__) || defined(__x86_64__)
+  /* Littleendian and unaligned-capable */
+  *(uint16_t *)p = v;
+#else
   p[0] = (v & 0xff);
   p[1] = ((v >> 8) & 0xff);
+#endif
 }
 
-#if 0                          /* Not needed */
 static inline void set_32(unsigned char *p, uint32_t v)
 {
+#if defined(__i386__) || defined(__x86_64__)
+  /* Littleendian and unaligned-capable */
+  *(uint32_t *)p = v;
+#else
   p[0] = (v & 0xff);
   p[1] = ((v >> 8) & 0xff);
   p[2] = ((v >> 16) & 0xff);
   p[3] = ((v >> 24) & 0xff);
-}
 #endif
+}
 
 /* Patch the code so that we're running in stupid mode */
 void syslinux_make_stupid(void)
 {
   /* Access only one sector at a time */
-  set_16(syslinux_ldlinux+PATCH_OFFSET, 1);
+  set_16(syslinux_bootsect+0x1FC, 1);
 }
   
 void syslinux_make_bootsect(void *bs)
@@ -108,7 +118,8 @@ void syslinux_make_bootsect(void *bs)
 }
 
 /*
- * Check to see that what we got was indeed an MS-DOS boot sector/superblock
+ * Check to see that what we got was indeed an MS-DOS boot sector/superblock;
+ * Return 0 if bad and 1 if OK.
  */
 int syslinux_check_bootsect(const void *bs, const char *device)
 {
@@ -116,6 +127,8 @@ int syslinux_check_bootsect(const void *bs, const char *device)
   unsigned int sectors, clusters;
   const unsigned char *sectbuf = bs;
 
+  /*** FIX: Handle FAT32 ***/
+
   if ( sectbuf[bsBootSignature] == 0x29 ) {
     /* It's DOS, and it has all the new nice fields */
 
@@ -164,11 +177,60 @@ int syslinux_check_bootsect(const void *bs, const char *device)
     return 0;
   }
 
-  if ( sectbuf[bsSecPerClust] > 32 ) {
-    fprintf(stderr, "%s: Cluster sizes larger than 16K not supported\n",
-           device);
-    return 0;
+  return 1;
+}
+
+/*
+ * This patches the boot sector and the first sector of ldlinux.sys
+ * based on an ldlinux.sys sector map passed in.  Typically this is
+ * handled by writing ldlinux.sys, mapping it, and then overwrite it
+ * with the patched version.  If this isn't safe to do because of
+ * an OS which does block reallocation, then overwrite it with
+ * direct access since the location is known.
+ *
+ * Return 0 if successful, otherwise -1.
+ */
+int syslinux_patch(const uint32_t *sectors, int nsectors)
+{
+  unsigned char *patcharea, *p;
+  int nsect = (syslinux_ldlinux_len+511) >> 9;
+  uint32_t csum;
+  int i, dw;
+
+  if ( nsectors < nsect )
+    return -1;
+
+  /* First sector need pointer in boot sector */
+  set_32(syslinux_bootsect+0x1F8, *sectors++);
+  nsect--;
+  
+  /* Search for LDLINUX_MAGIC to find the patch area */
+  for ( p = syslinux_ldlinux ; get_32(p) != LDLINUX_MAGIC ; p += 4 );
+  patcharea = p+4;
+
+  /* Set up the totals */
+  dw = syslinux_ldlinux_len >> 2; /* COMPLETE dwords! */
+  set_16(patcharea, dw);
+  set_16(patcharea+2, nsect);  /* Does not include the first sector! */
+
+  /* Set the sector pointers */
+  p = patcharea+8;
+
+  memset(p, 0, 64*4);
+  while ( nsect-- ) {
+    set_32(p, *sectors++);
+    p += 4;
   }
 
-  return 1;
+  /* Now produce a checksum */
+  set_32(patcharea+4, 0);
+
+  csum = LDLINUX_MAGIC;
+  for ( i = 0, p = syslinux_ldlinux ; i < dw ; i++, p += 4 )
+    csum -= get_32(p);         /* Negative checksum */
+
+  set_32(patcharea+4, csum);
+
+  return 0;
 }
+
diff --git a/unix/Makefile b/unix/Makefile
new file mode 100644 (file)
index 0000000..9ba5e55
--- /dev/null
@@ -0,0 +1,39 @@
+CC      = gcc
+OPTFLAGS = -g -O
+INCLUDES = -I. -I.. -I../libfat
+CFLAGS  = -W -Wall -D_FILE_OFFSET_BITS=64 $(OPTFLAGS) $(INCLUDES)
+LDFLAGS         = 
+
+SRCS     = syslinux.c ../syslxmod.c ../bootsect_bin.c ../ldlinux_bin.c $(wildcard ../libfat/*.c)
+OBJS    = $(patsubst %.c,%.o,$(notdir $(SRCS)))
+
+.SUFFIXES: .c .o .i .s .S
+
+VPATH = .:..:../libfat
+
+all: installer
+
+tidy:
+       -rm -f *.o *.i *.s *.a .*.d
+
+clean: tidy
+       -rm -f syslinux
+
+spotless: clean
+
+installer: syslinux
+
+syslinux: $(OBJS)
+       $(CC) $(LDFLAGS) -o $@ $^
+
+%.o: %.c
+       $(CC) -Wp,-MT,$@,-MMD,.$@.d $(CFLAGS) -c -o $@ $<
+%.i: %.c
+       $(CC) $(CFLAGS) -E -o $@ $<
+%.s: %.c
+       $(CC) $(CFLAGS) -S -o $@ $<
+
+-include .*.d
+
+
+
diff --git a/unix/syslinux.c b/unix/syslinux.c
new file mode 100644 (file)
index 0000000..182e1bf
--- /dev/null
@@ -0,0 +1,487 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 1998-2004 H. Peter Anvin - All Rights Reserved
+ *
+ *   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
+ *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ *   Boston MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * syslinux.c - Linux installer program for SYSLINUX
+ *
+ * This program ought to be portable.  I hope so, at least.
+ *
+ * This is an alternate version of the installer which doesn't require
+ * mtools, but requires root privilege.
+ */
+
+#ifdef __KLIBC__
+# define DO_DIRECT_MOUNT 1     /* Call mount(2) directly */
+#else
+# define DO_DIRECT_MOUNT 0     /* Call /bin/mount instead */
+#endif
+
+#define _GNU_SOURCE
+#define _XOPEN_SOURCE 500      /* For pread() pwrite() */
+#define _FILE_OFFSET_BITS 64
+#include <alloca.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#include "syslinux.h"
+#include "libfat.h"
+
+#if DO_DIRECT_MOUNT
+
+# include <linux/loop.h>
+
+#else
+
+# include <paths.h>
+# ifndef _PATH_MOUNT
+#  define _PATH_MOUNT "/bin/mount"
+# endif
+# ifndef _PATH_UMOUNT
+#  define _PATH_UMOUNT "/bin/umount"
+# endif
+
+#endif
+
+const char *program;           /* Name of program */
+const char *device;            /* Device to install to */
+pid_t mypid;
+char *mntpath = NULL;          /* Path on which to mount */
+#if DO_DIRECT_MOUNT
+int loop_fd = -1;              /* Loop device */
+#endif
+
+void __attribute__((noreturn)) usage(void)
+{
+  fprintf(stderr, "Usage: %s [-sf] [-o offset] device\n", program);
+  exit(1);
+}
+
+void __attribute__((noreturn)) die(const char *msg)
+{
+  fprintf(stderr, "%s: %s\n", program, msg);
+
+#if DO_DIRECT_MOUNT
+  if ( loop_fd != -1 ) {
+    ioctl(loop_fd, LOOP_CLR_FD, 0); /* Free loop device */
+    close(loop_fd);
+    loop_fd = -1;
+  }
+#endif
+
+  if ( mntpath )
+    unlink(mntpath);
+
+  exit(1);
+}
+
+/*
+ * read/write wrapper functions
+ */
+ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
+{
+  ssize_t rv;
+  ssize_t done = 0;
+
+  while ( count ) {
+    rv = pread(fd, buf, count, offset);
+    if ( rv == 0 ) {
+      die("short read");
+    } else if ( rv == -1 ) {
+      if ( errno == EINTR ) {
+       continue;
+      } else {
+       perror(program);
+       exit(1);
+      }
+    } else {
+      offset += rv;
+      done += rv;
+      count -= rv;
+    }
+  }
+
+  return done;
+}
+
+ssize_t xpwrite(int fd, void *buf, size_t count, off_t offset)
+{
+  ssize_t rv;
+  ssize_t done = 0;
+
+  while ( count ) {
+    rv = pwrite(fd, buf, count, offset);
+    if ( rv == 0 ) {
+      die("short write");
+    } else if ( rv == -1 ) {
+      if ( errno == EINTR ) {
+       continue;
+      } else {
+       perror(program);
+       exit(1);
+      }
+    } else {
+      offset += rv;
+      done += rv;
+      count -= rv;
+    }
+  }
+
+  return done;
+}
+
+/*
+ * Version of the read function suitable for libfat
+ */
+int libfat_xpread(void *pp, void *buf, size_t secsize, libfat_sector_t sector)
+{
+  off_t offset = (off_t)sector * secsize;
+  return xpread((int)pp, buf, secsize, offset);
+}
+
+int main(int argc, char *argv[])
+{
+  static unsigned char sectbuf[512];
+  unsigned char *dp;
+  const unsigned char *cdp;
+  int dev_fd, fd;
+  struct stat st;
+  int nb, left;
+  int err = 0;
+  pid_t f, w;
+  int status;
+  char mntname[64], devfdname[64];
+  char *ldlinux_name, **argp, *opt;
+  int force = 0;               /* -f (force) option */
+  off_t offset = 0;            /* -o (offset) option */
+  struct libfat_filesystem *fs;
+  libfat_sector_t s, *secp, sectors[65]; /* 65 is maximum possible */
+  int32_t ldlinux_cluster;
+  int nsectors;
+
+  (void)argc;                  /* Unused */
+
+  program = argv[0];
+  mypid = getpid();
+  
+  device = NULL;
+
+  umask(077);
+
+  for ( argp = argv+1 ; *argp ; argp++ ) {
+    if ( **argp == '-' ) {
+      opt = *argp + 1;
+      if ( !*opt )
+       usage();
+
+      while ( *opt ) {
+       if ( *opt == 's' ) {
+         syslinux_make_stupid();       /* Use "safe, slow and stupid" code */
+       } else if ( *opt == 'f' ) {
+         force = 1;            /* Force install */
+       } else if ( *opt == 'o' && argp[1] ) {
+         offset = (off_t)strtoull(*++argp, NULL, 0); /* Byte offset */
+       } else {
+         usage();
+       }
+       opt++;
+      }
+    } else {
+      if ( device )
+       usage();
+      device = *argp;
+    }
+  }
+
+  if ( !device )
+    usage();
+
+  /*
+   * First make sure we can open the device at all, and that we have
+   * read/write permission.
+   */
+  dev_fd = open(device, O_RDWR);
+  if ( dev_fd < 0 || fstat(dev_fd, &st) < 0 ) {
+    perror(device);
+    exit(1);
+  }
+
+  if ( !force && !S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode) ) {
+    die("not a block device or regular file (use -f to override)");
+  }
+
+  if ( !force && offset != 0 && !S_ISREG(st.st_mode) ) {
+    die("not a regular file and an offset specified (use -f to override)");
+  }
+
+  xpread(dev_fd, sectbuf, 512, offset);
+  fsync(dev_fd);
+
+  /*
+   * Check to see that what we got was indeed an MS-DOS boot sector/superblock
+   */
+  if(!syslinux_check_bootsect(sectbuf,device)) {
+    exit(1);
+  }
+
+  /*
+   * Now mount the device.
+   */
+  if ( geteuid() ) {
+    die("This program needs root privilege");
+  } else {
+    int i = 0;
+    struct stat dst;
+    int rv;
+
+    /* We're root or at least setuid.
+       Make a temp dir and pass all the gunky options to mount. */
+
+    if ( chdir("/tmp") ) {
+      perror(program);
+      exit(1);
+    }
+
+#define TMP_MODE (S_IXUSR|S_IWUSR|S_IXGRP|S_IWGRP|S_IWOTH|S_IXOTH|S_ISVTX)
+
+    if ( stat(".", &dst) || !S_ISDIR(dst.st_mode) ||
+        (dst.st_mode & TMP_MODE) != TMP_MODE ) {
+      die("possibly unsafe /tmp permissions");
+    }
+
+    for ( i = 0 ; ; i++ ) {
+      snprintf(mntname, sizeof mntname, "syslinux.mnt.%lu.%d",
+              (unsigned long)mypid, i);
+
+      if ( lstat(mntname, &dst) != -1 || errno != ENOENT )
+       continue;
+
+      rv = mkdir(mntname, 0000);
+
+      if ( rv == -1 ) {
+       if ( errno == EEXIST || errno == EINTR )
+         continue;
+       perror(program);
+       exit(1);
+      }
+
+      if ( lstat(mntname, &dst) || dst.st_mode != (S_IFDIR|0000) ||
+          dst.st_uid != 0 ) {
+       die("someone is trying to symlink race us!");
+      }
+      break;                   /* OK, got something... */
+    }
+
+    mntpath = mntname;
+
+#if DO_DIRECT_MOUNT
+    if ( S_ISREG(st.st_mode) ) {
+      /* It's file, need to mount it loopback */
+      unsigned int n = 0;
+      struct loop_info64 loopinfo;
+
+      for ( n = 0 ; loop_fd < 0 ; n++ ) {
+       snprintf(devfdname, sizeof devfdname, "/dev/loop%u", n);
+       loop_fd = open(devfdname, O_RDWR);
+       if ( loop_fd < 0 && errno == ENOENT ) {
+         die("no available loopback device!");
+       }
+       if ( ioctl(loop_fd, LOOP_SET_FD, (void *)dev_fd) ) {
+         close(loop_fd); loop_fd = -1;
+         if ( errno != EBUSY )
+           die("cannot set up loopback device");
+         else
+           continue;
+       }
+       
+       if ( ioctl(loop_fd, LOOP_GET_STATUS64, &loopinfo) ||
+            (loopinfo.lo_offset = offset,
+             ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo)) )
+         die("cannot set up loopback device");
+      }
+    } else {
+      snprintf(devfdname, sizeof devfdname, "/proc/%lu/fd/%d",
+              (unsigned long)mypid, dev_fd);
+    }
+
+    if ( mount(devfdname, mntpath, "msdos",
+              MS_NOEXEC|MS_NOSUID, "umask=077,quiet") )
+      die("could not mount filesystem");
+
+#else
+
+    snprintf(devfdname, sizeof devfdname, "/proc/%lu/fd/%d",
+            (unsigned long)mypid, dev_fd);
+
+    f = fork();
+    if ( f < 0 ) {
+      perror(program);
+      rmdir(mntpath);
+      exit(1);
+    } else if ( f == 0 ) {
+      char mnt_opts[128];
+      if ( S_ISREG(st.st_mode) ) {
+       snprintf(mnt_opts, sizeof mnt_opts, "rw,nodev,noexec,loop,offset=%llu,umask=077,quiet",
+                (unsigned long long)offset);
+      } else {
+       snprintf(mnt_opts, sizeof mnt_opts, "rw,nodev,noexec,umask=077,quiet");
+      }
+      execl(_PATH_MOUNT, _PATH_MOUNT, "-t", "msdos", "-o", mnt_opts,\
+           devfdname, mntpath, NULL);
+      _exit(255);              /* execl failed */
+    }
+
+    w = waitpid(f, &status, 0);
+    if ( w != f || status ) {
+      rmdir(mntpath);
+      exit(1);                 /* Mount failed */
+    }
+    
+#endif
+  }
+  
+  ldlinux_name = alloca(strlen(mntpath)+13);
+  if ( !ldlinux_name ) {
+    perror(program);
+    err = 1;
+    goto umount;
+  }
+  sprintf(ldlinux_name, "%s/ldlinux.sys", mntpath);
+
+  unlink(ldlinux_name);
+  fd = open(ldlinux_name, O_WRONLY|O_CREAT|O_TRUNC, 0444);
+  if ( fd < 0 ) {
+    perror(device);
+    err = 1;
+    goto umount;
+  }
+
+  cdp = syslinux_ldlinux;
+  left = syslinux_ldlinux_len;
+  while ( left ) {
+    nb = write(fd, cdp, left);
+    if ( nb == -1 && errno == EINTR )
+      continue;
+    else if ( nb <= 0 ) {
+      perror(device);
+      err = 1;
+      goto umount;
+    }
+
+    dp += nb;
+    left -= nb;
+  }
+
+  /*
+   * I don't understand why I need this.  Does the DOS filesystems
+   * not honour the mode passed to open()?
+   */
+  fchmod(fd, 0400);
+
+  close(fd);
+
+  sync();
+
+umount:
+#if DO_DIRECT_MOUNT
+
+  if ( umount2(mntpath, 0) )
+    die("could not umount path");
+
+  if ( loop_fd != -1 ) {
+    ioctl(loop_fd, LOOP_CLR_FD, 0); /* Free loop device */
+    close(loop_fd);
+    loop_fd = -1;
+  }
+
+#else
+
+  f = fork();
+  if ( f < 0 ) {
+    perror("fork");
+    exit(1);
+  } else if ( f == 0 ) {
+    execl(_PATH_UMOUNT, _PATH_UMOUNT, mntpath, NULL);
+  }
+
+  w = waitpid(f, &status, 0);
+  if ( w != f || status ) {
+    exit(1);
+  }
+
+#endif
+
+  sync();
+  rmdir(mntpath);
+
+  if ( err )
+    exit(err);
+
+  /*
+   * Now, use libfat to create a block map.  This probably
+   * should be changed to use ioctl(...,FIBMAP,...) since
+   * this is supposed to be a simple, privileged version
+   * of the installer.
+   */
+  fs = libfat_open(libfat_xpread, (void *)dev_fd);
+  ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL);
+  secp = sectors;
+  nsectors = 0;
+  s = libfat_clustertosector(fs, ldlinux_cluster);
+  while ( s && nsectors < 65 ) {
+    *secp++ = s;
+    nsectors++;
+    s = libfat_nextsector(fs, s);
+  }
+  libfat_close(fs);
+
+  /*
+   * Patch ldlinux.sys and the boot sector
+   */
+  syslinux_patch(sectors, nsectors);
+
+  /*
+   * Write the now-patched first sector of ldlinux.sys
+   */
+  xpwrite(dev_fd, syslinux_ldlinux, 512, offset + ((off_t)sectors[0] << 9));
+
+  /*
+   * To finish up, write the boot sector
+   */
+
+  /* Read the superblock again since it might have changed while mounted */
+  xpread(dev_fd, sectbuf, 512, offset);
+
+  /* Copy the syslinux code into the boot sector */
+  syslinux_make_bootsect(sectbuf);
+
+  /* Write new boot sector */
+  xpwrite(dev_fd, sectbuf, 512, offset);
+
+  close(dev_fd);
+  sync();
+
+  /* Done! */
+
+  return 0;
+}
+
diff --git a/version b/version
index ae656d4..a4b5a6f 100644 (file)
--- a/version
+++ b/version
@@ -1 +1 @@
-2.13
+2.20