From 21f060a5c6211233b91a48b18a79bda32f1dd5c6 Mon Sep 17 00:00:00 2001 From: hpa Date: Mon, 22 Aug 2005 15:42:22 +0000 Subject: [PATCH] Symlink support for EXTLINUX --- NEWS | 1 + comboot.inc | 15 +++-- ext2_fs.inc | 23 ++++++- extlinux.asm | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- extlinux.doc | 10 ++- strecpy.inc | 29 +++++++++ 6 files changed, 240 insertions(+), 35 deletions(-) create mode 100644 strecpy.inc diff --git a/NEWS b/NEWS index 2969e89..0f7cfbd 100644 --- 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. diff --git a/comboot.inc b/comboot.inc index e41b1d5..4ed6fa6 100644 --- a/comboot.inc +++ b/comboot.inc @@ -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 diff --git a/ext2_fs.inc b/ext2_fs.inc index c4c8bf7..3041616 100644 --- a/ext2_fs.inc +++ b/ext2_fs.inc @@ -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 @@ -46,6 +46,27 @@ %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 diff --git a/extlinux.asm b/extlinux.asm index f4770f1..d291c2c 100644 --- a/extlinux.asm +++ b/extlinux.asm @@ -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" ; ----------------------------------------------------------------------------- diff --git a/extlinux.doc b/extlinux.doc index 0bf7487..79ca7a3 100644 --- a/extlinux.doc +++ b/extlinux.doc @@ -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 index 0000000..b4013ec --- /dev/null +++ b/strecpy.inc @@ -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 + -- 2.7.4