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 */
66 static void __attribute__((noreturn
)) usage(int rv
)
69 "Usage: %s [options] directory\n"
70 " --install -i Install over the current bootsector\n"
71 " --update -U Update a previous EXTLINUX installation\n"
72 " --zip -z Force zipdrive geometry (-H 64 -S 32)\n"
73 " --sectors=# -S Force the number of sectors per track\n"
74 " --heads=# -H Force number of heads\n"
76 " Note: geometry is determined at boot time for devices which\n"
77 " are considered hard disks by the BIOS. Unfortunately, this is\n"
78 " not possible for devices which are considered floppy disks,\n"
79 " which includes zipdisks and LS-120 superfloppies.\n"
81 " The -z option is useful for USB devices which are considered\n"
82 " hard disks by some BIOSes and zipdrives by other BIOSes.\n",
88 static const struct option long_options
[] = {
89 { "install", 0, NULL
, 'i' },
90 { "update", 0, NULL
, 'U' },
91 { "zipdrive", 0, NULL
, 'z' },
92 { "sectors", 1, NULL
, 'S' },
93 { "heads", 1, NULL
, 'H' },
94 { "version", 0, NULL
, 'v' },
95 { "help", 0, NULL
, 'h' },
99 static const char short_options
[] = "iUuzS:H:vh";
103 #if defined(__linux__) && !defined(BLKGETSIZE64)
104 /* This takes a u64, but the size field says size_t. Someone screwed big. */
105 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
108 #define LDLINUX_MAGIC 0x3eb202fe
113 bsBytesPerSec
= 0x0b,
114 bsSecPerClust
= 0x0d,
117 bsRootDirEnts
= 0x11,
121 bsSecPerTrack
= 0x18,
124 bsHugeSectors
= 0x20,
127 bs16DriveNumber
= 0x24,
128 bs16Reserved1
= 0x25,
129 bs16BootSignature
= 0x26,
131 bs16VolumeLabel
= 0x2b,
132 bs16FileSysType
= 0x36,
143 bs32DriveNumber
= 64,
145 bs32BootSignature
= 66,
147 bs32VolumeLabel
= 71,
148 bs32FileSysType
= 82,
154 #define bsHead bsJump
155 #define bsHeadLen (bsOemName-bsHead)
156 #define bsCode bs32Code /* The common safe choice */
157 #define bsCodeLen (bsSignature-bs32Code)
160 * Access functions for littleendian numbers, possibly misaligned.
162 static inline uint8_t get_8(const unsigned char *p
)
164 return *(const uint8_t *)p
;
167 static inline uint16_t get_16(const unsigned char *p
)
169 #if defined(__i386__) || defined(__x86_64__)
170 /* Littleendian and unaligned-capable */
171 return *(const uint16_t *)p
;
173 return (uint16_t)p
[0] + ((uint16_t)p
[1] << 8);
177 static inline uint32_t get_32(const unsigned char *p
)
179 #if defined(__i386__) || defined(__x86_64__)
180 /* Littleendian and unaligned-capable */
181 return *(const uint32_t *)p
;
183 return (uint32_t)p
[0] + ((uint32_t)p
[1] << 8) +
184 ((uint32_t)p
[2] << 16) + ((uint32_t)p
[3] << 24);
188 static inline void set_16(unsigned char *p
, uint16_t v
)
190 #if defined(__i386__) || defined(__x86_64__)
191 /* Littleendian and unaligned-capable */
195 p
[1] = ((v
>> 8) & 0xff);
199 static inline void set_32(unsigned char *p
, uint32_t v
)
201 #if defined(__i386__) || defined(__x86_64__)
202 /* Littleendian and unaligned-capable */
206 p
[1] = ((v
>> 8) & 0xff);
207 p
[2] = ((v
>> 16) & 0xff);
208 p
[3] = ((v
>> 24) & 0xff);
212 #ifndef EXT2_SUPER_OFFSET
213 #define EXT2_SUPER_OFFSET 1024
216 #define SECTOR_SHIFT 9 /* 512-byte sectors */
217 #define SECTOR_SIZE (1 << SECTOR_SHIFT)
224 extern unsigned char extlinux_bootsect
[];
225 extern unsigned int extlinux_bootsect_len
;
226 #define boot_block extlinux_bootsect
227 #define boot_block_len extlinux_bootsect_len
232 extern unsigned char extlinux_image
[];
233 extern unsigned int extlinux_image_len
;
234 #define boot_image extlinux_image
235 #define boot_image_len extlinux_image_len
238 * Common abort function
240 void __attribute__((noreturn
)) die(const char *msg
)
247 * read/write wrapper functions
249 ssize_t
xpread(int fd
, void *buf
, size_t count
, off_t offset
)
251 char *bufp
= (char *)buf
;
256 rv
= pread(fd
, bufp
, count
, offset
);
259 } else if ( rv
== -1 ) {
260 if ( errno
== EINTR
) {
263 die(strerror(errno
));
276 ssize_t
xpwrite(int fd
, const void *buf
, size_t count
, off_t offset
)
278 const char *bufp
= (const char *)buf
;
283 rv
= pwrite(fd
, bufp
, count
, offset
);
286 } else if ( rv
== -1 ) {
287 if ( errno
== EINTR
) {
290 die(strerror(errno
));
307 sectmap(int fd
, uint32_t *sectors
, int nsectors
)
309 unsigned int blksize
, blk
, nblk
;
313 if ( ioctl(fd
, FIGETBSZ
, &blksize
) )
316 /* Number of sectors per block */
317 blksize
>>= SECTOR_SHIFT
;
323 dprintf("querying block %u\n", blk
);
324 if ( ioctl(fd
, FIBMAP
, &blk
) )
328 for ( i
= 0 ; i
< blksize
; i
++ ) {
332 dprintf("Sector: %10u\n", blk
);
342 * Get the size of a block device
344 uint64_t get_size(int devfd
)
351 if ( !ioctl(devfd
, BLKGETSIZE64
, &bytes
) )
354 if ( !ioctl(devfd
, BLKGETSIZE
, §s
) )
355 return (uint64_t)sects
<< 9;
356 else if ( !fstat(devfd
, &st
) && st
.st_size
)
364 * Get device geometry and partition offset
366 struct geometry_table
{
368 struct hd_geometry g
;
371 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
372 (x/64/32) is the final fallback. I don't know what LS-240 has
373 as its geometry, since I don't have one and don't know anyone that does,
374 and Google wasn't helpful... */
375 static const struct geometry_table standard_geometries
[] = {
376 { 360*1024, { 2, 9, 40, 0 } },
377 { 720*1024, { 2, 9, 80, 0 } },
378 { 1200*1024, { 2, 15, 80, 0 } },
379 { 1440*1024, { 2, 18, 80, 0 } },
380 { 1680*1024, { 2, 21, 80, 0 } },
381 { 1722*1024, { 2, 21, 80, 0 } },
382 { 2880*1024, { 2, 36, 80, 0 } },
383 { 3840*1024, { 2, 48, 80, 0 } },
384 { 123264*1024, { 8, 32, 963, 0 } }, /* LS120 */
389 get_geometry(int devfd
, uint64_t totalbytes
, struct hd_geometry
*geo
)
391 struct floppy_struct fd_str
;
392 const struct geometry_table
*gp
;
394 memset(geo
, 0, sizeof *geo
);
396 if ( !ioctl(devfd
, HDIO_GETGEO
, &geo
) ) {
398 } else if ( !ioctl(devfd
, FDGETPRM
, &fd_str
) ) {
399 geo
->heads
= fd_str
.head
;
400 geo
->sectors
= fd_str
.sect
;
401 geo
->cylinders
= fd_str
.track
;
406 /* Didn't work. Let's see if this is one of the standard geometries */
407 for ( gp
= standard_geometries
; gp
->bytes
; gp
++ ) {
408 if ( gp
->bytes
== totalbytes
) {
409 memcpy(geo
, &gp
->g
, sizeof *geo
);
414 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
415 what zipdisks use, so this would help if someone has a USB key that
416 they're booting in USB-ZIP mode. */
418 geo
->heads
= opt
.heads
?: 64;
419 geo
->sectors
= opt
.sectors
?: 32;
420 geo
->cylinders
= totalbytes
/(geo
->heads
*geo
->sectors
<< SECTOR_SHIFT
);
423 if ( !opt
.sectors
&& !opt
.heads
)
424 fprintf(stderr
, "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
425 " (on hard disks, this is usually harmless.)\n",
426 geo
->heads
, geo
->sectors
);
432 * Query the device geometry and put it into the boot sector.
433 * Map the file and put the map in the boot sector and file.
434 * Stick the "current directory" inode number into the file.
437 patch_file_and_bootblock(int fd
, int dirfd
, int devfd
)
440 struct hd_geometry geo
;
442 uint64_t totalbytes
, totalsectors
;
444 unsigned char *p
, *patcharea
;
448 if ( fstat(dirfd
, &dirst
) ) {
449 perror("fstat dirfd");
450 exit(255); /* This should never happen */
453 totalbytes
= get_size(devfd
);
454 get_geometry(devfd
, totalbytes
, &geo
);
457 geo
.heads
= opt
.heads
;
459 geo
.sectors
= opt
.sectors
;
461 /* Patch this into a fake FAT superblock. This isn't because
462 FAT is a good format in any way, it's because it lets the
463 early bootstrap share code with the FAT version. */
464 dprintf("heads = %u, sect = %u\n", geo
.heads
, geo
.sectors
);
466 totalsectors
= totalbytes
>> SECTOR_SHIFT
;
467 if ( totalsectors
>= 65536 ) {
468 set_16(boot_block
+bsSectors
, 0);
470 set_16(boot_block
+bsSectors
, totalsectors
);
472 set_32(boot_block
+bsHugeSectors
, totalsectors
);
474 set_16(boot_block
+bsBytesPerSec
, SECTOR_SIZE
);
475 set_16(boot_block
+bsSecPerTrack
, geo
.sectors
);
476 set_16(boot_block
+bsHeads
, geo
.heads
);
477 set_32(boot_block
+bsHiddenSecs
, geo
.start
);
479 /* Construct the boot file */
481 dprintf("directory inode = %lu\n", (unsigned long) dirst
.st_ino
);
482 nsect
= (boot_image_len
+SECTOR_SIZE
-1) >> SECTOR_SHIFT
;
483 sectp
= alloca(sizeof(uint32_t)*nsect
);
484 if ( sectmap(fd
, sectp
, nsect
) ) {
489 /* First sector need pointer in boot sector */
490 set_32(boot_block
+0x1F8, *sectp
++);
493 /* Search for LDLINUX_MAGIC to find the patch area */
494 for ( p
= boot_image
; get_32(p
) != LDLINUX_MAGIC
; p
+= 4 );
497 /* Set up the totals */
498 dw
= boot_image_len
>> 2; /* COMPLETE dwords! */
499 set_16(patcharea
, dw
);
500 set_16(patcharea
+2, nsect
); /* Does not include the first sector! */
501 set_32(patcharea
+8, dirst
.st_ino
); /* "Current" directory */
503 /* Set the sector pointers */
512 /* Now produce a checksum */
513 set_32(patcharea
+4, 0);
515 csum
= LDLINUX_MAGIC
;
516 for ( i
= 0, p
= boot_image
; i
< dw
; i
++, p
+= 4 )
517 csum
-= get_32(p
); /* Negative checksum */
519 set_32(patcharea
+4, csum
);
523 * Install the boot block on the specified device.
524 * Must be run AFTER install_file()!
527 install_bootblock(int fd
, const char *device
)
529 struct ext2_super_block sb
;
531 if ( xpread(fd
, &sb
, sizeof sb
, EXT2_SUPER_OFFSET
) != sizeof sb
) {
532 perror("reading superblock");
536 if ( sb
.s_magic
!= EXT2_SUPER_MAGIC
) {
537 fprintf(stderr
, "no ext2/ext3 superblock found on %s\n", device
);
541 if ( xpwrite(fd
, boot_block
, boot_block_len
, 0) != boot_block_len
) {
542 perror("writing bootblock");
550 install_file(const char *path
, int devfd
, struct stat
*rst
)
553 int fd
= -1, dirfd
= -1, flags
;
556 asprintf(&file
, "%s%sextlinux.sys",
558 path
[0] && path
[strlen(path
)-1] == '/' ? "" : "/");
564 dirfd
= open(path
, O_RDONLY
|O_DIRECTORY
);
570 fd
= open(file
, O_RDONLY
);
572 if ( errno
!= ENOENT
) {
577 /* If file exist, remove the immutable flag and set u+w mode */
578 if ( !ioctl(fd
, EXT2_IOC_GETFLAGS
, &flags
) ) {
579 flags
&= ~EXT2_IMMUTABLE_FL
;
580 ioctl(fd
, EXT2_IOC_SETFLAGS
, &flags
);
582 if ( !fstat(fd
, &st
) ) {
583 fchmod(fd
, st
.st_mode
| S_IWUSR
);
588 fd
= open(file
, O_WRONLY
|O_TRUNC
|O_CREAT
, S_IRUSR
|S_IRGRP
|S_IROTH
);
594 /* Write it the first time */
595 if ( xpwrite(fd
, boot_image
, boot_image_len
, 0) != boot_image_len
) {
596 fprintf(stderr
, "%s: write failure on %s\n", program
, file
);
600 /* Map the file, and patch the initial sector accordingly */
601 patch_file_and_bootblock(fd
, dirfd
, devfd
);
603 /* Write it again - this relies on the file being overwritten in place! */
604 if ( xpwrite(fd
, boot_image
, boot_image_len
, 0) != boot_image_len
) {
605 fprintf(stderr
, "%s: write failure on %s\n", program
, file
);
609 /* Attempt to set immutable flag and remove all write access */
610 /* Only set immutable flag if file is owned by root */
611 if ( !fstat(fd
, &st
) ) {
612 fchmod(fd
, st
.st_mode
& (S_IRUSR
|S_IRGRP
|S_IROTH
));
613 if ( st
.st_uid
== 0 && !ioctl(fd
, EXT2_IOC_GETFLAGS
, &flags
) ) {
614 flags
|= EXT2_IMMUTABLE_FL
;
615 ioctl(fd
, EXT2_IOC_SETFLAGS
, &flags
);
619 if ( fstat(fd
, rst
) ) {
637 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
638 sector; this is consistent with FAT filesystems. */
640 already_installed(int devfd
)
644 xpread(devfd
, buffer
, 8, 3);
645 return !memcmp(buffer
, "EXTLINUX", 8);
650 static char devname_buf
[64];
652 static void device_cleanup(void)
660 install_loader(const char *path
, int update_only
)
664 const char *devname
= NULL
;
666 struct mntent
*mnt
= NULL
;
671 if ( stat(path
, &st
) || !S_ISDIR(st
.st_mode
) ) {
672 fprintf(stderr
, "%s: Not a directory: %s\n", program
, path
);
680 /* klibc doesn't have getmntent and friends; instead, just create
681 a new device with the appropriate device type */
682 snprintf(devname_buf
, sizeof devname_buf
, "/tmp/dev-%u:%u",
683 major(st
.st_dev
), minor(st
.st_dev
));
685 if (mknod(devname_buf
, S_IFBLK
|0600, st
.st_dev
)) {
686 fprintf(stderr
, "%s: cannot create device %s\n", program
, devname
);
690 atexit(device_cleanup
); /* unlink the device node on exit */
691 devname
= devname_buf
;
695 if ( (mtab
= setmntent("/proc/mounts", "r")) ) {
696 while ( (mnt
= getmntent(mtab
)) ) {
697 if ( (!strcmp(mnt
->mnt_type
, "ext2") ||
698 !strcmp(mnt
->mnt_type
, "ext3")) &&
699 !stat(mnt
->mnt_fsname
, &dst
) &&
700 dst
.st_rdev
== st
.st_dev
) {
701 devname
= mnt
->mnt_fsname
;
708 /* Didn't find it in /proc/mounts, try /etc/mtab */
709 if ( (mtab
= setmntent("/etc/mtab", "r")) ) {
710 while ( (mnt
= getmntent(mtab
)) ) {
711 devname
= mnt
->mnt_fsname
;
718 fprintf(stderr
, "%s: cannot find device for path %s\n", program
, path
);
722 fprintf(stderr
, "%s is device %s\n", path
, devname
);
726 if ( (devfd
= open(devname
, O_RDWR
|O_SYNC
)) < 0 ) {
727 fprintf(stderr
, "%s: cannot open device %s\n", program
, devname
);
731 if ( update_only
&& !already_installed(devfd
) ) {
732 fprintf(stderr
, "%s: no previous extlinux boot sector found\n", program
);
736 install_file(path
, devfd
, &fst
);
738 if ( fst
.st_dev
!= st
.st_dev
) {
739 fprintf(stderr
, "%s: file system changed under us - aborting!\n",
745 rv
= install_bootblock(devfd
, devname
);
753 main(int argc
, char *argv
[])
756 const char *directory
;
757 int update_only
= -1;
761 while ( (o
= getopt_long(argc
, argv
, short_options
,
762 long_options
, NULL
)) != EOF
) {
769 opt
.sectors
= strtoul(optarg
, NULL
, 0);
770 if ( opt
.sectors
< 1 || opt
.sectors
> 63 ) {
771 fprintf(stderr
, "%s: invalid number of sectors: %u (must be 1-63)\n",
772 program
, opt
.sectors
);
777 opt
.heads
= strtoul(optarg
, NULL
, 0);
778 if ( opt
.heads
< 1 || opt
.heads
> 256 ) {
779 fprintf(stderr
, "%s: invalid number of heads: %u (must be 1-256)\n",
795 fputs("extlinux " VERSION
"\n", stderr
);
802 directory
= argv
[optind
];
807 if ( update_only
== -1 ) {
808 fprintf(stderr
, "%s: warning: a future version will require --install or --update\n",
813 return install_loader(directory
, update_only
);