From 44bad3a849a5f30186365eecf69722133775cee8 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 6 Jan 2010 18:40:52 -0800 Subject: [PATCH] core: initial work on path-based cwd selection Work on picking the initial cwd by storing a path instead of by storing an inode number. This should be both more general (in the sense of supporting filesystems in a generic way) as well as conceptually cleaner. The code doesn't work yet, but this at least provides support for the extlinux installer to store its subpath into the installed image. Signed-off-by: H. Peter Anvin --- core/comboot.inc | 5 ++++- core/diskstart.inc | 19 +++++++++++++---- core/fs/ext2/ext2.c | 6 +++--- extlinux/main.c | 54 ++++++++++++++++++++++++++++++++++++++++--------- libinstaller/syslxint.h | 3 ++- libinstaller/syslxmod.c | 1 - 6 files changed, 68 insertions(+), 20 deletions(-) diff --git a/core/comboot.inc b/core/comboot.inc index 2540959..03507c8 100644 --- a/core/comboot.inc +++ b/core/comboot.inc @@ -1038,6 +1038,9 @@ err_comlarge db 'COMBOOT image too large.', CR, LF, 0 alignb 4 DOSErrTramp resd 33 ; Error trampolines - global ConfigName, CurrentDirName + global ConfigName ConfigName resb FILENAME_MAX +%ifndef HAVE_CURRENTDIRNAME + global CurrentDirName CurrentDirName resb FILENAME_MAX +%endif diff --git a/core/diskstart.inc b/core/diskstart.inc index 8bb4f78..3bffc89 100644 --- a/core/diskstart.inc +++ b/core/diskstart.inc @@ -1,7 +1,7 @@ ; ----------------------------------------------------------------------- ; ; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved -; Copyright 2009 Intel Corporation; author: H. Peter Anvin +; Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin ; ; 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 @@ -471,6 +471,7 @@ bootsignature dw kaboom.again-bootsec ; Start of LDLINUX.SYS ; =========================================================================== +LDLINUX_SYS equ 0x7e00 ldlinux_sys: syslinux_banner db 0Dh, 0Ah @@ -491,11 +492,21 @@ ADVSectors dw 0 ; Additional sectors for ADVs LDLDwords dd 0 ; Total dwords starting at ldlinux_sys, CheckSum dd 0 ; Checksum starting at ldlinux_sys ; value = LDLINUX_MAGIC - [sum of dwords] - global CurrentDir -CurrentDir dd 2 ; "Current" directory inode number (EXTLINUX) -SecPtrOffset dw SectorPtrs - ldlinux_sys +CurrentDirPtr dw CurrentDirName-LDLINUX_SYS ; Current directory name string +CurrentDirLen dw FILENAME_MAX +SecPtrOffset dw SectorPtrs - LDLINUX_SYS SecPtrCnt dw (SectorPtrsEnd - SectorPtrs) >> 2 +; +; Installer pokes the base directory here, needs to be in .text16 so the pointer +; address can be calculated by the assembler. +; +%define HAVE_CURRENTDIRNAME + section .data16 + global CurrentDirName +CurrentDirName times FILENAME_MAX db 0 + + section .text16 ldlinux_ent: ; ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000 diff --git a/core/fs/ext2/ext2.c b/core/fs/ext2/ext2.c index 9d4d9ec..a978677 100644 --- a/core/fs/ext2/ext2.c +++ b/core/fs/ext2/ext2.c @@ -294,14 +294,14 @@ static struct inode *ext2_iget_by_inr(uint32_t inr) return inode; } -static struct inode *ext2_iget_root() +static struct inode *ext2_iget_root(void) { return ext2_iget_by_inr(EXT2_ROOT_INO); } -static struct inode *ext2_iget_current() +static struct inode *ext2_iget_current(void) { - extern int CurrentDir; + static int CurrentDir = 2; return ext2_iget_by_inr(CurrentDir); } diff --git a/extlinux/main.c b/extlinux/main.c index 7eb59da..05b390f 100644 --- a/extlinux/main.c +++ b/extlinux/main.c @@ -354,9 +354,9 @@ int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo) * * Returns the number of modified bytes in the boot file. */ -int patch_file_and_bootblock(int fd, int dirfd, int devfd) +int patch_file_and_bootblock(int fd, const char *dir, int devfd) { - struct stat dirst; + struct stat dirst, xdst; struct hd_geometry geo; uint32_t *sectp; uint64_t totalbytes, totalsectors; @@ -366,13 +366,38 @@ int patch_file_and_bootblock(int fd, int dirfd, int devfd) struct patch_area *patcharea; int i, dw, nptrs; uint32_t csum; - int secptroffset; + int secptroffset, diroffset, dirlen; + char *dirpath, *subpath; - if (fs_type == EXT2) - if (fstat(dirfd, &dirst)) { - perror("fstat dirfd"); - exit(255); /* This should never happen */ + dirpath = realpath(dir, NULL); + if (!dirpath || stat(dir, &dirst)) { + perror("accessing install directory"); + exit(255); /* This should never happen */ + } + + if (lstat(dirpath, &xdst) || + dirst.st_ino != xdst.st_ino || + dirst.st_dev != xdst.st_dev) { + perror("realpath returned nonsense"); + exit(255); + } + + subpath = strchr(dirpath, '\0'); + while (--subpath > dirpath) { + if (*subpath == '/') { + *subpath = '\0'; + if (lstat(dirpath, &xdst) || dirst.st_dev != xdst.st_dev) { + subpath = strchr(subpath+1, '/'); + if (!subpath) + subpath = "/"; /* It's the root of the filesystem */ + break; + } + *subpath = '/'; } + } + + /* Now subpath should contain the path relative to the fs base */ + dprintf("subpath = %s\n", subpath); totalbytes = get_size(devfd); get_geometry(devfd, totalbytes, &geo); @@ -443,7 +468,6 @@ int patch_file_and_bootblock(int fd, int dirfd, int devfd) set_16(&patcharea->data_sectors, nsect - 2); /* -2 for the ADVs */ set_16(&patcharea->adv_sectors, 2); set_32(&patcharea->dwords, dw); - set_32(&patcharea->currentdir, dirst.st_ino); /* Set the sector pointers */ secptroffset = get_16(&patcharea->secptroffset); @@ -454,6 +478,16 @@ int patch_file_and_bootblock(int fd, int dirfd, int devfd) while (nsect--) set_32(wp++, *sectp++); + /* Poke in the base directory path */ + diroffset = get_16(&patcharea->diroffset); + dirlen = get_16(&patcharea->dirlen); + if (dirlen <= strlen(subpath)) { + fprintf(stderr, "Subdirectory path too long... aborting install!\n"); + exit(1); + } + strncpy((char *)boot_image + diroffset, subpath, dirlen); + free(dirpath); + /* Now produce a checksum */ set_32(&patcharea->checksum, 0); @@ -729,7 +763,7 @@ int ext2_install_file(const char *path, int devfd, struct stat *rst) } /* Map the file, and patch the initial sector accordingly */ - modbytes = patch_file_and_bootblock(fd, dirfd, devfd); + modbytes = patch_file_and_bootblock(fd, path, devfd); /* Write the patch area again - this relies on the file being overwritten in place! */ @@ -771,7 +805,7 @@ bail: since the cow feature of btrfs will move the extlinux.sys every where */ int btrfs_install_file(const char *path, int devfd, struct stat *rst) { - patch_file_and_bootblock(-1, -1, devfd); + patch_file_and_bootblock(-1, path, devfd); if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET) != boot_image_len) { perror("writing bootblock"); diff --git a/libinstaller/syslxint.h b/libinstaller/syslxint.h index e2a8072..ba3e501 100644 --- a/libinstaller/syslxint.h +++ b/libinstaller/syslxint.h @@ -87,7 +87,8 @@ struct patch_area { uint16_t adv_sectors; uint32_t dwords; uint32_t checksum; - uint32_t currentdir; + uint16_t diroffset; + uint16_t dirlen; uint16_t secptroffset; uint16_t secptrcnt; }; diff --git a/libinstaller/syslxmod.c b/libinstaller/syslxmod.c index 9e1da44..4216037 100644 --- a/libinstaller/syslxmod.c +++ b/libinstaller/syslxmod.c @@ -255,7 +255,6 @@ int syslinux_patch(const uint32_t * sectors, int nsectors, set_16_sl(&patcharea->data_sectors, nsect); /* Not including ADVs */ set_16_sl(&patcharea->adv_sectors, 0); /* ADVs not supported yet */ set_32_sl(&patcharea->dwords, dw); - set_32_sl(&patcharea->currentdir, 0); /* Set the sector pointers */ wp = (uint32_t *) ((char *)syslinux_ldlinux + -- 2.7.4