1 /* ----------------------------------------------------------------------- *
3 * Copyright 1998-2005 H. Peter Anvin - All Rights Reserved
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 * Boston MA 02111-1307, USA; either version 2 of the License, or
9 * (at your option) any later version; incorporated herein by reference.
11 * ----------------------------------------------------------------------- */
16 * Install the extlinux boot block on an ext2/3 filesystem
19 #define _GNU_SOURCE /* Enable everything */
21 /* This is needed to deal with the kernel headers imported into glibc 3.3.3. */
35 #include <sys/ioctl.h>
37 #include <sys/types.h>
38 #include <sys/mount.h>
40 #include <linux/fd.h> /* Floppy geometry */
41 #include <linux/hdreg.h> /* Hard disk geometry */
42 #include <linux/fs.h> /* FIGETBSZ, FIBMAP */
45 #include "../version.h"
48 # define dprintf printf
50 # define dprintf(...) ((void)0)
53 /* Global option handling */
57 /* These are the options we can set and their values */
68 static void __attribute__((noreturn
)) usage(int rv
)
71 "Usage: %s [options] directory\n"
72 " --install -i Install over the current bootsector\n"
73 " --update -U Update a previous EXTLINUX installation\n"
74 " --zip -z Force zipdrive geometry (-H 64 -S 32)\n"
75 " --sectors=# -S Force the number of sectors per track\n"
76 " --heads=# -H Force number of heads\n"
77 " --raid -r Fall back to the next device on boot failure\n"
79 " Note: geometry is determined at boot time for devices which\n"
80 " are considered hard disks by the BIOS. Unfortunately, this is\n"
81 " not possible for devices which are considered floppy disks,\n"
82 " which includes zipdisks and LS-120 superfloppies.\n"
84 " The -z option is useful for USB devices which are considered\n"
85 " hard disks by some BIOSes and zipdrives by other BIOSes.\n",
91 static const struct option long_options
[] = {
92 { "install", 0, NULL
, 'i' },
93 { "update", 0, NULL
, 'U' },
94 { "zipdrive", 0, NULL
, 'z' },
95 { "sectors", 1, NULL
, 'S' },
96 { "heads", 1, NULL
, 'H' },
97 { "raid-mode", 0, NULL
, 'r' },
98 { "version", 0, NULL
, 'v' },
99 { "help", 0, NULL
, 'h' },
103 static const char short_options
[] = "iUuzS:H:rvh";
105 #if defined(__linux__) && !defined(BLKGETSIZE64)
106 /* This takes a u64, but the size field says size_t. Someone screwed big. */
107 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
110 #define LDLINUX_MAGIC 0x3eb202fe
115 bsBytesPerSec
= 0x0b,
116 bsSecPerClust
= 0x0d,
119 bsRootDirEnts
= 0x11,
123 bsSecPerTrack
= 0x18,
126 bsHugeSectors
= 0x20,
129 bs16DriveNumber
= 0x24,
130 bs16Reserved1
= 0x25,
131 bs16BootSignature
= 0x26,
133 bs16VolumeLabel
= 0x2b,
134 bs16FileSysType
= 0x36,
145 bs32DriveNumber
= 64,
147 bs32BootSignature
= 66,
149 bs32VolumeLabel
= 71,
150 bs32FileSysType
= 82,
156 #define bsHead bsJump
157 #define bsHeadLen (bsOemName-bsHead)
158 #define bsCode bs32Code /* The common safe choice */
159 #define bsCodeLen (bsSignature-bs32Code)
162 * Access functions for littleendian numbers, possibly misaligned.
164 static inline uint8_t get_8(const unsigned char *p
)
166 return *(const uint8_t *)p
;
169 static inline uint16_t get_16(const unsigned char *p
)
171 #if defined(__i386__) || defined(__x86_64__)
172 /* Littleendian and unaligned-capable */
173 return *(const uint16_t *)p
;
175 return (uint16_t)p
[0] + ((uint16_t)p
[1] << 8);
179 static inline uint32_t get_32(const unsigned char *p
)
181 #if defined(__i386__) || defined(__x86_64__)
182 /* Littleendian and unaligned-capable */
183 return *(const uint32_t *)p
;
185 return (uint32_t)p
[0] + ((uint32_t)p
[1] << 8) +
186 ((uint32_t)p
[2] << 16) + ((uint32_t)p
[3] << 24);
190 static inline void set_16(unsigned char *p
, uint16_t v
)
192 #if defined(__i386__) || defined(__x86_64__)
193 /* Littleendian and unaligned-capable */
197 p
[1] = ((v
>> 8) & 0xff);
201 static inline void set_32(unsigned char *p
, uint32_t v
)
203 #if defined(__i386__) || defined(__x86_64__)
204 /* Littleendian and unaligned-capable */
208 p
[1] = ((v
>> 8) & 0xff);
209 p
[2] = ((v
>> 16) & 0xff);
210 p
[3] = ((v
>> 24) & 0xff);
214 #ifndef EXT2_SUPER_OFFSET
215 #define EXT2_SUPER_OFFSET 1024
218 #define SECTOR_SHIFT 9 /* 512-byte sectors */
219 #define SECTOR_SIZE (1 << SECTOR_SHIFT)
226 extern unsigned char extlinux_bootsect
[];
227 extern unsigned int extlinux_bootsect_len
;
228 #define boot_block extlinux_bootsect
229 #define boot_block_len extlinux_bootsect_len
234 extern unsigned char extlinux_image
[];
235 extern unsigned int extlinux_image_len
;
236 #define boot_image extlinux_image
237 #define boot_image_len extlinux_image_len
240 * Common abort function
242 void __attribute__((noreturn
)) die(const char *msg
)
249 * read/write wrapper functions
251 ssize_t
xpread(int fd
, void *buf
, size_t count
, off_t offset
)
253 char *bufp
= (char *)buf
;
258 rv
= pread(fd
, bufp
, count
, offset
);
261 } else if ( rv
== -1 ) {
262 if ( errno
== EINTR
) {
265 die(strerror(errno
));
278 ssize_t
xpwrite(int fd
, const void *buf
, size_t count
, off_t offset
)
280 const char *bufp
= (const char *)buf
;
285 rv
= pwrite(fd
, bufp
, count
, offset
);
288 } else if ( rv
== -1 ) {
289 if ( errno
== EINTR
) {
292 die(strerror(errno
));
309 sectmap(int fd
, uint32_t *sectors
, int nsectors
)
311 unsigned int blksize
, blk
, nblk
;
315 if ( ioctl(fd
, FIGETBSZ
, &blksize
) )
318 /* Number of sectors per block */
319 blksize
>>= SECTOR_SHIFT
;
325 dprintf("querying block %u\n", blk
);
326 if ( ioctl(fd
, FIBMAP
, &blk
) )
330 for ( i
= 0 ; i
< blksize
; i
++ ) {
334 dprintf("Sector: %10u\n", blk
);
344 * Get the size of a block device
346 uint64_t get_size(int devfd
)
353 if ( !ioctl(devfd
, BLKGETSIZE64
, &bytes
) )
356 if ( !ioctl(devfd
, BLKGETSIZE
, §s
) )
357 return (uint64_t)sects
<< 9;
358 else if ( !fstat(devfd
, &st
) && st
.st_size
)
366 * Get device geometry and partition offset
368 struct geometry_table
{
370 struct hd_geometry g
;
373 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
374 (x/64/32) is the final fallback. I don't know what LS-240 has
375 as its geometry, since I don't have one and don't know anyone that does,
376 and Google wasn't helpful... */
377 static const struct geometry_table standard_geometries
[] = {
378 { 360*1024, { 2, 9, 40, 0 } },
379 { 720*1024, { 2, 9, 80, 0 } },
380 { 1200*1024, { 2, 15, 80, 0 } },
381 { 1440*1024, { 2, 18, 80, 0 } },
382 { 1680*1024, { 2, 21, 80, 0 } },
383 { 1722*1024, { 2, 21, 80, 0 } },
384 { 2880*1024, { 2, 36, 80, 0 } },
385 { 3840*1024, { 2, 48, 80, 0 } },
386 { 123264*1024, { 8, 32, 963, 0 } }, /* LS120 */
391 get_geometry(int devfd
, uint64_t totalbytes
, struct hd_geometry
*geo
)
393 struct floppy_struct fd_str
;
394 const struct geometry_table
*gp
;
396 memset(geo
, 0, sizeof *geo
);
398 if ( !ioctl(devfd
, HDIO_GETGEO
, &geo
) ) {
400 } else if ( !ioctl(devfd
, FDGETPRM
, &fd_str
) ) {
401 geo
->heads
= fd_str
.head
;
402 geo
->sectors
= fd_str
.sect
;
403 geo
->cylinders
= fd_str
.track
;
408 /* Didn't work. Let's see if this is one of the standard geometries */
409 for ( gp
= standard_geometries
; gp
->bytes
; gp
++ ) {
410 if ( gp
->bytes
== totalbytes
) {
411 memcpy(geo
, &gp
->g
, sizeof *geo
);
416 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
417 what zipdisks use, so this would help if someone has a USB key that
418 they're booting in USB-ZIP mode. */
420 geo
->heads
= opt
.heads
?: 64;
421 geo
->sectors
= opt
.sectors
?: 32;
422 geo
->cylinders
= totalbytes
/(geo
->heads
*geo
->sectors
<< SECTOR_SHIFT
);
425 if ( !opt
.sectors
&& !opt
.heads
)
426 fprintf(stderr
, "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
427 " (on hard disks, this is usually harmless.)\n",
428 geo
->heads
, geo
->sectors
);
434 * Query the device geometry and put it into the boot sector.
435 * Map the file and put the map in the boot sector and file.
436 * Stick the "current directory" inode number into the file.
439 patch_file_and_bootblock(int fd
, int dirfd
, int devfd
)
442 struct hd_geometry geo
;
444 uint64_t totalbytes
, totalsectors
;
446 unsigned char *p
, *patcharea
;
450 if ( fstat(dirfd
, &dirst
) ) {
451 perror("fstat dirfd");
452 exit(255); /* This should never happen */
455 totalbytes
= get_size(devfd
);
456 get_geometry(devfd
, totalbytes
, &geo
);
459 geo
.heads
= opt
.heads
;
461 geo
.sectors
= opt
.sectors
;
463 /* Patch this into a fake FAT superblock. This isn't because
464 FAT is a good format in any way, it's because it lets the
465 early bootstrap share code with the FAT version. */
466 dprintf("heads = %u, sect = %u\n", geo
.heads
, geo
.sectors
);
468 totalsectors
= totalbytes
>> SECTOR_SHIFT
;
469 if ( totalsectors
>= 65536 ) {
470 set_16(boot_block
+bsSectors
, 0);
472 set_16(boot_block
+bsSectors
, totalsectors
);
474 set_32(boot_block
+bsHugeSectors
, totalsectors
);
476 set_16(boot_block
+bsBytesPerSec
, SECTOR_SIZE
);
477 set_16(boot_block
+bsSecPerTrack
, geo
.sectors
);
478 set_16(boot_block
+bsHeads
, geo
.heads
);
479 set_32(boot_block
+bsHiddenSecs
, geo
.start
);
481 /* If we're in RAID mode then patch the appropriate instruction;
482 either way write the proper boot signature */
483 i
= get_16(boot_block
+0x1FE);
485 set_16(boot_block
+i
, 0x18CD); /* INT 18h */
487 set_16(boot_block
+0x1FE, 0xAA55);
489 /* Construct the boot file */
491 dprintf("directory inode = %lu\n", (unsigned long) dirst
.st_ino
);
492 nsect
= (boot_image_len
+SECTOR_SIZE
-1) >> SECTOR_SHIFT
;
493 sectp
= alloca(sizeof(uint32_t)*nsect
);
494 if ( sectmap(fd
, sectp
, nsect
) ) {
499 /* First sector need pointer in boot sector */
500 set_32(boot_block
+0x1F8, *sectp
++);
503 /* Search for LDLINUX_MAGIC to find the patch area */
504 for ( p
= boot_image
; get_32(p
) != LDLINUX_MAGIC
; p
+= 4 );
507 /* Set up the totals */
508 dw
= boot_image_len
>> 2; /* COMPLETE dwords! */
509 set_16(patcharea
, dw
);
510 set_16(patcharea
+2, nsect
); /* Does not include the first sector! */
511 set_32(patcharea
+8, dirst
.st_ino
); /* "Current" directory */
513 /* Set the sector pointers */
522 /* Now produce a checksum */
523 set_32(patcharea
+4, 0);
525 csum
= LDLINUX_MAGIC
;
526 for ( i
= 0, p
= boot_image
; i
< dw
; i
++, p
+= 4 )
527 csum
-= get_32(p
); /* Negative checksum */
529 set_32(patcharea
+4, csum
);
533 * Install the boot block on the specified device.
534 * Must be run AFTER install_file()!
537 install_bootblock(int fd
, const char *device
)
539 struct ext2_super_block sb
;
541 if ( xpread(fd
, &sb
, sizeof sb
, EXT2_SUPER_OFFSET
) != sizeof sb
) {
542 perror("reading superblock");
546 if ( sb
.s_magic
!= EXT2_SUPER_MAGIC
) {
547 fprintf(stderr
, "no ext2/ext3 superblock found on %s\n", device
);
551 if ( xpwrite(fd
, boot_block
, boot_block_len
, 0) != boot_block_len
) {
552 perror("writing bootblock");
560 install_file(const char *path
, int devfd
, struct stat
*rst
)
563 int fd
= -1, dirfd
= -1, flags
;
566 asprintf(&file
, "%s%sextlinux.sys",
568 path
[0] && path
[strlen(path
)-1] == '/' ? "" : "/");
574 dirfd
= open(path
, O_RDONLY
|O_DIRECTORY
);
580 fd
= open(file
, O_RDONLY
);
582 if ( errno
!= ENOENT
) {
587 /* If file exist, remove the immutable flag and set u+w mode */
588 if ( !ioctl(fd
, EXT2_IOC_GETFLAGS
, &flags
) ) {
589 flags
&= ~EXT2_IMMUTABLE_FL
;
590 ioctl(fd
, EXT2_IOC_SETFLAGS
, &flags
);
592 if ( !fstat(fd
, &st
) ) {
593 fchmod(fd
, st
.st_mode
| S_IWUSR
);
598 fd
= open(file
, O_WRONLY
|O_TRUNC
|O_CREAT
, S_IRUSR
|S_IRGRP
|S_IROTH
);
604 /* Write it the first time */
605 if ( xpwrite(fd
, boot_image
, boot_image_len
, 0) != boot_image_len
) {
606 fprintf(stderr
, "%s: write failure on %s\n", program
, file
);
610 /* Map the file, and patch the initial sector accordingly */
611 patch_file_and_bootblock(fd
, dirfd
, devfd
);
613 /* Write it again - this relies on the file being overwritten in place! */
614 if ( xpwrite(fd
, boot_image
, boot_image_len
, 0) != boot_image_len
) {
615 fprintf(stderr
, "%s: write failure on %s\n", program
, file
);
619 /* Attempt to set immutable flag and remove all write access */
620 /* Only set immutable flag if file is owned by root */
621 if ( !fstat(fd
, &st
) ) {
622 fchmod(fd
, st
.st_mode
& (S_IRUSR
|S_IRGRP
|S_IROTH
));
623 if ( st
.st_uid
== 0 && !ioctl(fd
, EXT2_IOC_GETFLAGS
, &flags
) ) {
624 flags
|= EXT2_IMMUTABLE_FL
;
625 ioctl(fd
, EXT2_IOC_SETFLAGS
, &flags
);
629 if ( fstat(fd
, rst
) ) {
647 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
648 sector; this is consistent with FAT filesystems. */
650 already_installed(int devfd
)
654 xpread(devfd
, buffer
, 8, 3);
655 return !memcmp(buffer
, "EXTLINUX", 8);
660 static char devname_buf
[64];
662 static void device_cleanup(void)
670 install_loader(const char *path
, int update_only
)
674 const char *devname
= NULL
;
676 struct mntent
*mnt
= NULL
;
681 if ( stat(path
, &st
) || !S_ISDIR(st
.st_mode
) ) {
682 fprintf(stderr
, "%s: Not a directory: %s\n", program
, path
);
690 /* klibc doesn't have getmntent and friends; instead, just create
691 a new device with the appropriate device type */
692 snprintf(devname_buf
, sizeof devname_buf
, "/tmp/dev-%u:%u",
693 major(st
.st_dev
), minor(st
.st_dev
));
695 if (mknod(devname_buf
, S_IFBLK
|0600, st
.st_dev
)) {
696 fprintf(stderr
, "%s: cannot create device %s\n", program
, devname
);
700 atexit(device_cleanup
); /* unlink the device node on exit */
701 devname
= devname_buf
;
705 if ( (mtab
= setmntent("/proc/mounts", "r")) ) {
706 while ( (mnt
= getmntent(mtab
)) ) {
707 if ( (!strcmp(mnt
->mnt_type
, "ext2") ||
708 !strcmp(mnt
->mnt_type
, "ext3")) &&
709 !stat(mnt
->mnt_fsname
, &dst
) &&
710 dst
.st_rdev
== st
.st_dev
) {
711 devname
= mnt
->mnt_fsname
;
718 /* Didn't find it in /proc/mounts, try /etc/mtab */
719 if ( (mtab
= setmntent("/etc/mtab", "r")) ) {
720 while ( (mnt
= getmntent(mtab
)) ) {
721 devname
= mnt
->mnt_fsname
;
728 fprintf(stderr
, "%s: cannot find device for path %s\n", program
, path
);
732 fprintf(stderr
, "%s is device %s\n", path
, devname
);
736 if ( (devfd
= open(devname
, O_RDWR
|O_SYNC
)) < 0 ) {
737 fprintf(stderr
, "%s: cannot open device %s\n", program
, devname
);
741 if ( update_only
&& !already_installed(devfd
) ) {
742 fprintf(stderr
, "%s: no previous extlinux boot sector found\n", program
);
746 install_file(path
, devfd
, &fst
);
748 if ( fst
.st_dev
!= st
.st_dev
) {
749 fprintf(stderr
, "%s: file system changed under us - aborting!\n",
755 rv
= install_bootblock(devfd
, devname
);
763 main(int argc
, char *argv
[])
766 const char *directory
;
767 int update_only
= -1;
771 while ( (o
= getopt_long(argc
, argv
, short_options
,
772 long_options
, NULL
)) != EOF
) {
779 opt
.sectors
= strtoul(optarg
, NULL
, 0);
780 if ( opt
.sectors
< 1 || opt
.sectors
> 63 ) {
781 fprintf(stderr
, "%s: invalid number of sectors: %u (must be 1-63)\n",
782 program
, opt
.sectors
);
787 opt
.heads
= strtoul(optarg
, NULL
, 0);
788 if ( opt
.heads
< 1 || opt
.heads
> 256 ) {
789 fprintf(stderr
, "%s: invalid number of heads: %u (must be 1-256)\n",
808 fputs("extlinux " VERSION
"\n", stderr
);
815 directory
= argv
[optind
];
820 if ( update_only
== -1 ) {
821 fprintf(stderr
, "%s: warning: a future version will require "
822 "--install or --update\n", program
);
826 return install_loader(directory
, update_only
);