summaryrefslogtreecommitdiff
path: root/extlinux/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'extlinux/main.c')
-rw-r--r--extlinux/main.c54
1 files changed, 44 insertions, 10 deletions
diff --git a/extlinux/main.c b/extlinux/main.c
index 7eb59dae..05b390f3 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");