Symlink support for EXTLINUX
authorhpa <hpa>
Mon, 22 Aug 2005 15:42:22 +0000 (15:42 +0000)
committerhpa <hpa>
Mon, 22 Aug 2005 15:42:22 +0000 (15:42 +0000)
NEWS
comboot.inc
ext2_fs.inc
extlinux.asm
extlinux.doc
strecpy.inc [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 2969e89..0f7cfbd 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,7 @@ Changes in 3.10:
          problem has not yet been debugged.
        * MEMDISK: the handling of DOSEMU-headered images was broken;
          fix it.
+       * EXTLINUX: symlinks are now supported.
 
 Changes in 3.09:
        * gcc4 compilation fix.
index e41b1d5..4ed6fa6 100644 (file)
@@ -583,6 +583,8 @@ numIPAppends        equ ($-IPAppends)/2
 comapi_ipappend equ comapi_err
 %endif
 
+               section .text
+
 ;
 ; INT 22h AX=0010h     Resolve hostname
 ;
@@ -597,12 +599,7 @@ comapi_dnsresolv:
 comapi_dnsresolv equ comapi_err
 %endif
                
-               section .data
-%macro                 int21 2
-               db %1
-               dw %2
-%endmacro
-
+               section .text
 
 ;
 ; INT 22h AX=0011h     Maximum number of shuffle descriptors
@@ -744,6 +741,12 @@ comapi_runkernel:
                jmp kernel_good_saved
 
                section .data
+
+%macro                 int21 2
+               db %1
+               dw %2
+%endmacro
+
 int21_table:
                int21   00h, comboot_return
                int21   01h, comboot_getkey
index c4c8bf7..3041616 100644 (file)
@@ -1,7 +1,7 @@
 ; $Id$
 ; -----------------------------------------------------------------------
 ;   
-;   Copyright 1998-1999 H. Peter Anvin - All Rights Reserved
+;   Copyright 1998-2005 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
 %define        EXT2_N_BLOCKS           (EXT2_TIND_BLOCK+1)
 
 ;
+; File types and file modes
+;
+%define S_IFDIR                0040000o        ; Directory
+%define S_IFCHR                0020000o        ; Character device
+%define S_IFBLK                0060000o        ; Block device
+%define S_IFREG                0100000o        ; Regular file
+%define S_IFIFO                0010000o        ; FIFO
+%define S_IFLNK                0120000o        ; Symbolic link
+%define S_IFSOCK       0140000o        ; Socket
+
+%define S_IFSHIFT      12
+
+%define T_IFDIR                (S_IFDIR >> S_IFSHIFT)
+%define T_IFCHR                (S_IFCHR >> S_IFSHIFT)
+%define T_IFBLK                (S_IFBLK >> S_IFSHIFT)
+%define T_IFREG                (S_IFREG >> S_IFSHIFT)
+%define T_IFIFO                (S_IFIFO >> S_IFSHIFT)
+%define T_IFLNK                (S_IFLNK >> S_IFSHIFT)
+%define T_IFSOCK       (S_IFSOCK >> S_IFSHIFT)
+
+;
 ; Structure definition for the ext2 superblock
 ;
                        struc ext2_super_block
index f4770f1..d291c2c 100644 (file)
@@ -45,6 +45,10 @@ MAX_OPEN     equ (1 << MAX_OPEN_LG2)
 SECTOR_SHIFT   equ 9
 SECTOR_SIZE    equ (1 << SECTOR_SHIFT)
 
+MAX_SYMLINKS   equ 64                  ; Maximum number of symlinks per lookup
+SYMLINK_SECTORS        equ 2                   ; Max number of sectors in a symlink
+                                       ; (should be >= FILENAME_MAX)
+
 ;
 ; This is what we need to do when idle
 ;
@@ -939,17 +943,25 @@ allocate_file:
 ;              ZF clear
 ;              SI          = file pointer
 ;              DX:AX = EAX = file length in bytes
+;              ThisInode   = the first 128 bytes of the inode
 ;           If unsuccessful
 ;              ZF set
 ;
+;           Assumes CS == DS == ES.
+;
 open_inode.allocate_failure:
                xor eax,eax
+               pop bx
+               pop di
                ret
 
 open_inode:
+               push di
+               push bx
                call allocate_file
                jnz .allocate_failure
 
+               push cx
                push gs
                ; First, get the appropriate inode group and index
                dec eax                         ; There is no inode 0
@@ -996,9 +1008,13 @@ open_inode:
 
                call getcachesector
                add si,dx
-               mov ax,[gs:si+i_mode]
+               mov cx,EXT2_GOOD_OLD_INODE_SIZE >> 2
+               mov di,ThisInode
+               gs rep movsd
+
+               mov ax,[ThisInode+i_mode]
                mov [bx+file_mode],ax
-               mov eax,[gs:si+i_size]
+               mov eax,[ThisInode+i_size]
                push eax
                add eax,SECTOR_SIZE-1
                shr eax,SECTOR_SHIFT
@@ -1009,8 +1025,16 @@ open_inode:
                shr edx,16                      ; 16-bitism, sigh
                and eax,eax                     ; ZF clear unless zero-length file
                pop gs
+               pop cx
+               pop bx
+               pop di
                ret
 
+               section .latebss
+               alignb 4
+ThisInode      resb EXT2_GOOD_OLD_INODE_SIZE   ; The most recently opened inode
+
+               section .text
 ;
 ; close:
 ;           Deallocates a file structure (pointer in SI)
@@ -1042,17 +1066,78 @@ searchdir:
                push bx
                push cx
                push di
+               push bp
+               mov byte [SymlinkCtr],MAX_SYMLINKS
+
                mov eax,[CurrentDir]
+.begin_path:
 .leadingslash:
                cmp byte [di],'/'       ; Absolute filename?
-               jne .searchloop
+               jne .gotdir
                mov eax,EXT2_ROOT_INO
                inc di                  ; Skip slash
+               jmp .leadingslash
+.gotdir:
 
-.searchloop:
                ; At this point, EAX contains the directory inode,
                ; and DS:DI contains a pathname tail.
+.open:
+               push eax                ; Save directory inode
+
                call open_inode
+               jz .done                ; If error, done
+
+               mov cx,[si+file_mode]
+               shr cx,S_IFSHIFT        ; Get file type
+
+               cmp cx,T_IFDIR
+               je .directory
+
+               add sp,4                ; Drop directory inode
+
+               cmp cx,T_IFREG
+               je .file
+               cmp cx,T_IFLNK
+               je .symlink
+
+               ; Otherwise, something bad...
+.err:
+               call close
+.err_noclose:
+               xor eax,eax
+               xor si,si
+               cwd                     ; DX <- 0
+
+.done:
+               and eax,eax             ; Set/clear ZF
+               pop bp
+               pop di
+               pop cx
+               pop bx
+               ret
+
+               ;
+               ; It's a file.
+               ;
+.file:
+               cmp byte [di],0         ; End of path?
+               je .done                ; If so, done
+               jmp .err                ; Otherwise, error
+
+               ;
+               ; It's a directory.
+               ;
+.directory:
+               pop dword [ThisDir]     ; Remember what directory we're searching
+
+               cmp byte [di],0         ; More path?
+               je .err                 ; If not, bad
+               
+.skipslash:                            ; Skip redundant slashes
+               cmp byte [di],'/'
+               jne .readdir
+               inc di
+               jmp .skipslash
 
 .readdir:
                mov bx,trackbuf
@@ -1081,38 +1166,99 @@ searchdir:
                pop si
                popf
                jnc .readdir            ; There is more
-.failure:
-               xor eax,eax
-               jmp .done
+               jmp .err                ; Otherwise badness...
+
 .maybe:
                mov eax,[bx+d_inode]
                
+               ; Does this match the end of the requested filename?
                cmp byte [di],0
-               je .finish              ; It's a real file; done
+               je .finish
                cmp byte [di],'/'
-               jne .nope               ; False alarm
-               
-               ; It's a match, but it's a directory.
-               ; Repeat operation.
-               pop bx                  ; Adjust stack (di)
-               pop si
-               call close
-               pop bx                  ; Adjust stack (flags)
-               inc di                  ; Skip slash
-               jmp .searchloop
-               
+               jne .nope
 
-.finish:       ; We found it; now we need to open the file
+               ; We found something; now we need to open the file
+.finish:
                pop bx                  ; Adjust stack (di)
                pop si
                call close              ; Close directory
                pop bx                  ; Adjust stack (flags)
-               call open_inode
-.done:
+               jmp .open
+
+               ;
+               ; It's a symlink.  We have to determine if it's a fast symlink
+               ; (data stored in the inode) or not (data stored as a regular
+               ; file.)  Either which way, we start from the directory
+               ; which we just visited if relative, or from the root directory
+               ; if absolute, and append any remaining part of the path.
+               ;
+.symlink:
+               dec byte [SymlinkCtr]
+               jz .err                 ; Too many symlink references
+
+               cmp eax,SYMLINK_SECTORS*SECTOR_SIZE
+               jae .err                ; Symlink too long
+
+               ; Computation for fast symlink, as defined by ext2/3 spec
+               xor ecx,ecx
+               cmp [ThisInode+i_file_acl],ecx
+               setne cl                ; ECX <- i_file_acl ? 1 : 0
+               cmp [ThisInode+i_blocks],ecx
+               jne .slow_symlink
+
+               ; It's a fast symlink
+.fast_symlink:
+               call close              ; We've got all we need
+               mov si,ThisInode+i_block
+
+               push di
+               mov di,SymlinkTmpBuf
+               mov ecx,eax
+               rep movsb
+               pop si
+
+.symlink_finish:
+               cmp byte [si],0
+               je .no_slash
+               mov al,'/'
+               stosb
+.no_slash:
+               mov bp,SymlinkTmpBufEnd
+               call strecpy
+               jc .err_noclose         ; Buffer overflow
+
+               ; Now copy it to the "real" buffer; we need to have
+               ; two buffers so we avoid overwriting the tail on the
+               ; next copy
+               mov si,SymlinkTmpBuf
+               mov di,SymlinkBuf
+               push di
+               call strcpy
                pop di
-               pop cx
-               pop bx
-               ret
+               mov eax,[ThisDir]       ; Resume searching previous directory
+               jmp .begin_path
+
+.slow_symlink:
+               mov bx,SymlinkTmpBuf
+               mov cx,SYMLINK_SECTORS
+               call getfssec
+               ; The EOF closed the file
+
+               mov si,di               ; SI = filename tail
+               mov di,SymlinkTmpBuf
+               add ax,di               ; AX = file length
+               jmp .symlink_finish
+
+
+               section .bss
+               alignb  4
+SymlinkBuf     resb    SYMLINK_SECTORS*SECTOR_SIZE+64
+SymlinkTmpBuf   equ    trackbuf
+SymlinkTmpBufEnd equ   trackbuf+SYMLINK_SECTORS*SECTOR_SIZE+64
+ThisDir                resd    1
+SymlinkCtr     resb    1
+
+               section .text
 ;
 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
 ;             to by ES:DI; ends on encountering any whitespace.
@@ -1405,6 +1551,7 @@ getfssec:
 %include "graphics.inc"                ; VGA graphics
 %include "highmem.inc"         ; High memory sizing
 %include "strcpy.inc"           ; strcpy()
+%include "strecpy.inc"          ; strcpy with end pointer check
 %include "cache.inc"
 
 ; -----------------------------------------------------------------------------
index 0bf7487..79ca7a3 100644 (file)
@@ -30,9 +30,13 @@ It works the same way as SYSLINUX, with a few slight modifications.
    limited to 255 characters.
 
 
-4. EXTLINUX currently doesn't support symlinks it does, however,
-   support hard links.  This will be fixed in a future version.
-
+4. EXTLINUX now supports symbolic links.  However, extremely long
+   symbolic links might hit the pathname limit.  Also, please note
+   that absolute symbolic links are interpreted from the root *of the
+   filesystem*, which might be different from now the running system
+   would interpret it (e.g. in the case of a separate /boot
+   partition.)  Therefore, use relative symbolic links if at all
+   possible.
 
 
 Note that EXTLINUX installs in the filesystem partition like a
diff --git a/strecpy.inc b/strecpy.inc
new file mode 100644 (file)
index 0000000..b4013ec
--- /dev/null
@@ -0,0 +1,29 @@
+;
+; strecpy: Copy DS:SI -> ES:DI up to and including a null byte;
+;         on exit SI and DI point to the byte *after* the null byte.
+;         BP holds a pointer to the first byte beyond the end of the
+;         target buffer; return with CF=1 if target buffer overflows;
+;         the output is still zero-terminated.
+;        
+               section .text
+
+strecpy:
+               push ax
+               push bp
+               dec bp
+               dec bp
+.loop:         lodsb
+               stosb
+               and al,al       ; CF=0
+               jz .done
+               cmp bp,di       ; CF set if BP < DI
+               jnc .loop
+
+               ; Zero-terminate overflow string
+               mov al,0        ; Avoid changing flags
+               stosb
+.done:
+               pop bp
+               pop ax
+               ret
+