Merge branch 'pathbased' of ssh://terminus.zytor.com/pub/git/syslinux/syslinux into...
[profile/ivi/syslinux.git] / extlinux / main.c
index 8a0ac63..6ce3b60 100644 (file)
@@ -41,18 +41,8 @@ typedef uint64_t u64;
 #include <sys/mount.h>
 #include <sys/vfs.h>
 
-#include <linux/fd.h>          /* Floppy geometry */
-#include <linux/hdreg.h>       /* Hard disk geometry */
-#define statfs _kernel_statfs  /* HACK to deal with broken 2.4 distros */
-#include <linux/fs.h>          /* FIGETBSZ, FIBMAP */
-#include <linux/msdos_fs.h>    /* FAT_IOCTL_SET_ATTRIBUTES */
-#ifndef FAT_IOCTL_SET_ATTRIBUTES
-# define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, uint32_t)
-#endif
-#undef statfs
-#undef SECTOR_SIZE             /* Garbage from <linux/msdos_fs.h> */
+#include "linuxioctl.h"
 
-#include "ext2_fs.h"
 #include "btrfs.h"
 #include "fat.h"
 #include "../version.h"
@@ -82,11 +72,14 @@ typedef uint64_t u64;
 #define BTRFS_SUBVOL_OPT "subvol="
 #define BTRFS_SUBVOL_MAX 256   /* By btrfs specification */
 static char subvol[BTRFS_SUBVOL_MAX];
+
 /*
  * Boot block
  */
 extern unsigned char extlinux_bootsect[];
 extern unsigned int extlinux_bootsect_len;
+#undef  boot_block
+#undef  boot_block_len
 #define boot_block     extlinux_bootsect
 #define boot_block_len  extlinux_bootsect_len
 
@@ -95,6 +88,8 @@ extern unsigned int extlinux_bootsect_len;
  */
 extern unsigned char extlinux_image[];
 extern unsigned int extlinux_image_len;
+#undef  boot_image
+#undef  boot_image_len
 #define boot_image     extlinux_image
 #define boot_image_len  extlinux_image_len
 
@@ -190,6 +185,53 @@ int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
 }
 
 /*
+ * Generate sector extents
+ */
+static void generate_extents(struct syslinux_extent *ex, int nptrs,
+                            const sector_t *sectp, int nsect)
+{
+    uint32_t addr = 0x7c00 + 2*SECTOR_SIZE;
+    uint32_t base;
+    sector_t sect, lba;
+    unsigned int len;
+
+    len = lba = base = 0;
+
+    memset(ex, 0, nptrs * sizeof *ex);
+
+    while (nsect) {
+       sect = *sectp++;
+
+       if (len && sect == lba + len &&
+           ((addr ^ (base + len * SECTOR_SIZE)) & 0xffff0000) == 0) {
+           /* We can add to the current extent */
+           len++;
+           goto next;
+       }
+
+       if (len) {
+           set_64(&ex->lba, lba);
+           set_16(&ex->len, len);
+           ex++;
+       }
+
+       base = addr;
+       lba  = sect;
+       len  = 1;
+
+    next:
+       addr += SECTOR_SIZE;
+       nsect--;
+    }
+
+    if (len) {
+       set_64(&ex->lba, lba);
+       set_16(&ex->len, len);
+       ex++;
+    }
+}
+
+/*
  * Query the device geometry and put it into the boot sector.
  * Map the file and put the map in the boot sector and file.
  * Stick the "current directory" inode number into the file.
@@ -200,16 +242,18 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
 {
     struct stat dirst, xdst;
     struct hd_geometry geo;
-    uint32_t *sectp;
+    sector_t *sectp;
     uint64_t totalbytes, totalsectors;
     int nsect;
     uint32_t *wp;
     struct boot_sector *bs;
     struct patch_area *patcharea;
+    struct syslinux_extent *ex;
     int i, dw, nptrs;
     uint32_t csum;
     int secptroffset, diroffset, dirlen, subvoloffset, subvollen;
     char *dirpath, *subpath, *xdirpath, *xsubpath;
+    uint64_t *advptrs;
 
     dirpath = realpath(dir, NULL);
     if (!dirpath || stat(dir, &dirst)) {
@@ -294,7 +338,7 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
     dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
     nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
     nsect += 2;                        /* Two sectors for the ADV */
-    sectp = alloca(sizeof(uint32_t) * nsect);
+    sectp = alloca(sizeof(sector_t) * nsect);
     if (fs_type == EXT2 || fs_type == VFAT) {
        if (sectmap(fd, sectp, nsect)) {
                perror("bmap");
@@ -308,10 +352,7 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
     }
 
     /* First sector need pointer in boot sector */
-    set_32(&bs->NextSector, *sectp++);
-    /* Stupid mode? */
-    if (opt.stupid_mode)
-       set_16(&bs->MaxTransfer, 1);
+    set_64(&bs->NextSector, *sectp++);
 
     /* Search for LDLINUX_MAGIC to find the patch area */
     for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
@@ -323,14 +364,29 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
     set_16(&patcharea->adv_sectors, 2);
     set_32(&patcharea->dwords, dw);
 
-    /* Set the sector pointers */
+    /* Stupid mode? */
+    if (opt.stupid_mode)
+       set_16(&patcharea->maxtransfer, 1);
+
+    /* Set the sector extents */
     secptroffset = get_16(&patcharea->secptroffset);
-    wp = (uint32_t *) ((char *)boot_image + secptroffset);
+    ex = (struct syslinux_extent *) ((char *)boot_image + secptroffset);
     nptrs = get_16(&patcharea->secptrcnt);
 
-    memset(wp, 0, nptrs * 4);
-    while (--nsect) /* the first sector in bs->NextSector */
-       set_32(wp++, *sectp++);
+    if (nsect > nptrs) {
+       /* Not necessarily an error in this case, but a general problem */
+       fprintf(stderr, "Insufficient extent space, build error!\n");
+       exit(1);
+    }
+
+    /* -1 for the pointer in the boot sector, -2 for the two ADVs */
+    generate_extents(ex, nptrs, sectp, nsect-1-2);
+
+    /* ADV pointers */
+    advptrs = (uint64_t *)((char *)boot_image +
+                          get_16(&patcharea->advptroffset));
+    set_64(&advptrs[0], sectp[nsect-1-2]);
+    set_64(&advptrs[1], sectp[nsect-1-1]);
 
     /* Poke in the base directory path */
     diroffset = get_16(&patcharea->diroffset);
@@ -878,7 +934,7 @@ int modify_existing_adv(const char *path)
 
 int main(int argc, char *argv[])
 {
-    parse_options(argc, argv, 0);
+    parse_options(argc, argv, MODE_EXTLINUX);
 
     if (!opt.directory)
        usage(EX_USAGE, 0);
@@ -887,7 +943,7 @@ int main(int argc, char *argv[])
        if (opt.reset_adv || opt.set_once || opt.menu_save)
            return modify_existing_adv(opt.directory);
        else
-           usage(EX_USAGE, 0);
+           usage(EX_USAGE, MODE_EXTLINUX);
     }
 
     return install_loader(opt.directory, opt.update_only);