2 /* ----------------------------------------------------------------------- *
4 * Copyright 1998 H. Peter Anvin - All Rights Reserved
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
9 * USA; either version 2 of the License, or (at your option) any later
10 * version; incorporated herein by reference.
12 * ----------------------------------------------------------------------- */
15 * syslinux.c - Linux installer program for SYSLINUX
17 * This program ought to be portable. I hope so, at least.
19 * HPA note: this program needs too much privilege. We should probably
20 * access the filesystem directly like mtools does so we don't have to
21 * mount the disk. Either that or if Linux gets an fmount() system call
22 * we probably could do the mounting ourselves, and make this program
25 * Also, sync() between accessing the raw device and mount/umount seems
26 * to be necessary; I get data corruption otherwise.
38 #include <sys/types.h>
42 #define _PATH_MOUNT "/bin/mount"
46 #define _PATH_UMOUNT "/bin/umount"
49 extern unsigned char bootsect[];
50 extern unsigned int bootsect_len;
52 extern unsigned char ldlinux[];
53 extern unsigned int ldlinux_len;
55 char *program; /* Name of program */
56 char *device; /* Device to install to */
57 uid_t euid; /* Our current euid */
76 bsBootSignature = 0x26,
84 #define bsCopyStart bsBytesPerSec
85 #define bsCopyLen (bsCode-bsBytesPerSec)
88 * Access functions for littleendian numbers, possibly misaligned.
90 static u_int16_t get_16(unsigned char *p)
92 return (u_int16_t)p[0] + ((u_int16_t)p[1] << 8);
95 static u_int32_t get_32(unsigned char *p)
97 return (u_int32_t)p[0] + ((u_int32_t)p[1] << 8) +
98 ((u_int32_t)p[2] << 16) + ((u_int32_t)p[3] << 24);
101 int main(int argc, char *argv[])
103 static unsigned char sectbuf[512];
105 const unsigned char *cdp;
108 int nb, left, veryold;
109 unsigned int sectors, clusters;
113 char *mntpath = NULL, mntname[64];
119 if ( argc != 2 || argv[1][0] == '-' ) {
120 fprintf(stderr, "Usage: %s device\n", program);
127 * First make sure we can open the device at all, and that we have
128 * read/write permission.
130 dev_fd = open(device, O_RDWR);
131 if ( dev_fd < 0 || fstat(dev_fd, &st) < 0 ) {
136 if ( !S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode) ) {
137 fprintf(stderr, "%s: not a block device or regular file\n", device);
144 nb = read(dev_fd, dp, left);
145 if ( nb == -1 && errno == EINTR )
150 } else if ( nb == 0 ) {
151 fprintf(stderr, "%s: no boot sector\n", device);
162 * Check to see that what we got was indeed an MS-DOS boot sector/superblock
165 if ( sectbuf[bsBootSignature] == 0x29 ) {
166 /* It's DOS, and it has all the new nice fields */
170 sectors = get_16(sectbuf+bsSectors);
171 sectors = sectors ? sectors : get_32(sectbuf+bsHugeSectors);
172 clusters = sectors / sectbuf[bsSecPerClust];
174 if ( !memcmp(sectbuf+bsFileSysType, "FAT12 ", 8) ) {
175 if ( clusters > 4086 ) {
176 fprintf(stderr, "%s: ERROR: FAT12 but claims more than 4086 clusters\n",
180 } else if ( !memcmp(sectbuf+bsFileSysType, "FAT16 ", 8) ) {
181 if ( clusters <= 4086 ) {
182 fprintf(stderr, "%s: ERROR: FAT16 but claims less than 4086 clusters\n",
187 fprintf(stderr, "%s: filesystem type \"%8.8s\" not supported\n",
188 device, sectbuf+bsFileSysType);
194 if ( sectbuf[bsSecPerClust] & (sectbuf[bsSecPerClust] - 1) ||
195 sectbuf[bsSecPerClust] == 0 ) {
196 fprintf(stderr, "%s: This doesn't look like a FAT filesystem\n",
200 sectors = get_16(sectbuf+bsSectors);
201 sectors = sectors ? sectors : get_32(sectbuf+bsHugeSectors);
202 clusters = sectors / sectbuf[bsSecPerClust];
204 if ( clusters > 4086 ) {
205 fprintf(stderr, "%s: Only FAT12 filesystems supported\n", device);
210 if ( get_16(sectbuf+bsBytesPerSec) != 512 ) {
211 fprintf(stderr, "%s: Sector sizes other than 512 not supported\n",
215 if ( sectbuf[bsSecPerClust] > 32 ) {
216 fprintf(stderr, "%s: Cluster sizes larger than 16K not supported\n",
221 * Now mount the device. If we are non-root we need to find an fstab
222 * entry for this device which has the user flag and the appropriate
225 if ( (euid = geteuid()) ) {
229 if ( !(fstab = setmntent(MNTTAB, "r")) ) {
230 fprintf(stderr, "%s: cannot open " MNTTAB "\n", program);
233 while ( (mnt = getmntent(fstab)) ) {
234 if ( !strcmp(device, mnt->mnt_fsname) &&
235 ( !strcmp(mnt->mnt_type, "msdos") ||
236 !strcmp(mnt->mnt_type, "umsdos") ||
237 !strcmp(mnt->mnt_type, "vfat") ||
238 !strcmp(mnt->mnt_type, "uvfat") ||
239 !strcmp(mnt->mnt_type, "auto") ) &&
240 hasmntopt(mnt, "user") &&
241 !hasmntopt(mnt, "ro") &&
242 mnt->mnt_dir[0] == '/' &&
243 !!hasmntopt(mnt, "loop") == !!S_ISREG(st.st_mode)) {
244 /* Okay, this is an fstab entry we should be able to live with. */
246 mntpath = mnt->mnt_dir;
253 fprintf(stderr, "%s: not root and no appropriate entry for %s in "
254 MNTTAB "\n", program, device);
262 } else if ( f == 0 ) {
263 execl(_PATH_MOUNT, _PATH_MOUNT, mntpath, NULL);
264 _exit(255); /* If execl failed, trouble... */
269 /* We're root. Make a temp dir and pass all the gunky options to mount. */
272 sprintf(mntname, "/tmp/syslinux.mnt.%lu.%d", (unsigned long)getpid(), i++);
273 } while ( (errno = 0, mkdir(mntname, 0700) == -1) &&
274 (errno == EEXIST || errno == EINTR) );
287 } else if ( f == 0 ) {
288 if ( S_ISREG(st.st_mode) )
289 execl(_PATH_MOUNT, _PATH_MOUNT, "-t", "msdos", "-o", "loop", "-w",
290 device, mntpath, NULL);
292 execl(_PATH_MOUNT, _PATH_MOUNT, "-t", "msdos", "-w", device, mntpath,
294 _exit(255); /* execl failed */
298 w = waitpid(f, &status, 0);
299 if ( w != f || status ) {
302 exit(1); /* Mount failed */
305 ldlinux_name = alloca(strlen(mntpath)+13);
306 if ( !ldlinux_name ) {
311 sprintf(ldlinux_name, "%s/ldlinux.sys", mntpath);
313 unlink(ldlinux_name);
314 fd = open(ldlinux_name, O_WRONLY|O_CREAT|O_TRUNC, 0444);
324 nb = write(fd, cdp, left);
325 if ( nb == -1 && errno == EINTR )
327 else if ( nb <= 0 ) {
338 * I don't understand why I need this. Does the DOS filesystems
339 * not honour the mode passed to open()?
341 my_umask = umask(0777);
343 fchmod(fd, 0444 & ~my_umask);
352 } else if ( f == 0 ) {
353 execl(_PATH_UMOUNT, _PATH_UMOUNT, mntpath, NULL);
356 w = waitpid(f, &status, 0);
357 if ( w != f || status ) {
370 * To finish up, write the boot sector
373 dev_fd = open(device, O_RDWR);
379 /* Copy the old superblock into the new boot sector */
380 memcpy(bootsect+bsCopyStart, sectbuf+bsCopyStart, bsCopyLen);
385 nb = write(dev_fd, dp, left);
386 if ( nb == -1 && errno == EINTR )
388 else if ( nb <= 0 ) {