summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--bootsect.inc2
-rw-r--r--comboot.inc2
-rw-r--r--dos/syslinux.asm (renamed from syslinux.asm)6
-rwxr-xr-xfindpatch.pl28
-rw-r--r--ldlinux.asm926
-rw-r--r--libfat/cache.c70
-rw-r--r--libfat/fat.h112
-rw-r--r--libfat/fatchain.c136
-rw-r--r--libfat/libfat.h81
-rw-r--r--libfat/libfatint.h56
-rw-r--r--libfat/open.c118
-rw-r--r--libfat/searchdir.c61
-rw-r--r--libfat/ulint.h115
-rw-r--r--mtools/Makefile39
-rw-r--r--mtools/syslinux.c (renamed from syslinux.c)57
-rw-r--r--syslinux.h5
-rw-r--r--syslxmod.c82
-rw-r--r--unix/Makefile39
-rw-r--r--unix/syslinux.c487
-rw-r--r--version2
21 files changed, 1952 insertions, 474 deletions
diff --git a/Makefile b/Makefile
index 6706c11c..afc30a4c 100644
--- 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)
#
diff --git a/bootsect.inc b/bootsect.inc
index 654ab6b8..f901cf5e 100644
--- a/bootsect.inc
+++ b/bootsect.inc
@@ -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
diff --git a/comboot.inc b/comboot.inc
index af2ea0cc..f3aec3b0 100644
--- a/comboot.inc
+++ b/comboot.inc
@@ -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
diff --git a/syslinux.asm b/dos/syslinux.asm
index 25d73394..9b61f242 100644
--- a/syslinux.asm
+++ b/dos/syslinux.asm
@@ -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+3 ; 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
index 551d8741..00000000
--- a/findpatch.pl
+++ /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";
diff --git a/ldlinux.asm b/ldlinux.asm
index 7dcfb574..2e6503e2 100644
--- a/ldlinux.asm
+++ b/ldlinux.asm
@@ -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
index 00000000..cc6c57fe
--- /dev/null
+++ b/libfat/cache.c
@@ -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
index 00000000..921f9f4a
--- /dev/null
+++ b/libfat/fat.h
@@ -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
index 00000000..6c640bed
--- /dev/null
+++ b/libfat/fatchain.c
@@ -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
index 00000000..b0682279
--- /dev/null
+++ b/libfat/libfat.h
@@ -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
index 00000000..6da47f18
--- /dev/null
+++ b/libfat/libfatint.h
@@ -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
index 00000000..835c336d
--- /dev/null
+++ b/libfat/open.c
@@ -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
index 00000000..7c07165b
--- /dev/null
+++ b/libfat/searchdir.c
@@ -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
index 00000000..0b200ea8
--- /dev/null
+++ b/libfat/ulint.h
@@ -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
index 00000000..a13642cc
--- /dev/null
+++ b/mtools/Makefile
@@ -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
+
+
+
diff --git a/syslinux.c b/mtools/syslinux.c
index bb1de8ad..69d7a3dd 100644
--- a/syslinux.c
+++ b/mtools/syslinux.c
@@ -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
*/
diff --git a/syslinux.h b/syslinux.h
index f20172cd..da6eeccf 100644
--- a/syslinux.h
+++ b/syslinux.h
@@ -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
diff --git a/syslxmod.c b/syslxmod.c
index 96e60888..1f837439 100644
--- a/syslxmod.c
+++ b/syslxmod.c
@@ -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
index 00000000..9ba5e554
--- /dev/null
+++ b/unix/Makefile
@@ -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
index 00000000..182e1bf9
--- /dev/null
+++ b/unix/syslinux.c
@@ -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 ae656d47..a4b5a6f4 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-2.13
+2.20