1 /* ----------------------------------------------------------------------- *
3 * Copyright 1998-2008 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 * ----------------------------------------------------------------------- */
14 * syslinux.c - Linux installer program for SYSLINUX
16 * This program now requires mtools. It turned out to be a lot
17 * easier to deal with than dealing with needing mount privileges.
18 * We need device write permission anyway.
21 #define _XOPEN_SOURCE 500 /* Required on glibc 2.x */
34 #include <sys/types.h>
41 char *program
; /* Name of program */
42 char *device
; /* Device to install to */
44 off_t filesystem_offset
= 0; /* Offset of filesystem */
46 void __attribute__ ((noreturn
)) usage(void)
48 fprintf(stderr
, "Usage: %s [-sfr][-d directory][-o offset] device\n",
53 void __attribute__ ((noreturn
)) die(const char *msg
)
55 fprintf(stderr
, "%s: %s\n", program
, msg
);
60 * read/write wrapper functions
62 ssize_t
xpread(int fd
, void *buf
, size_t count
, off_t offset
)
64 char *bufp
= (char *)buf
;
69 rv
= pread(fd
, bufp
, count
, offset
);
72 } else if (rv
== -1) {
89 ssize_t
xpwrite(int fd
, const void *buf
, size_t count
, off_t offset
)
91 const char *bufp
= (const char *)buf
;
96 rv
= pwrite(fd
, bufp
, count
, offset
);
99 } else if (rv
== -1) {
100 if (errno
== EINTR
) {
103 die(strerror(errno
));
117 * Version of the read function suitable for libfat
119 int libfat_xpread(intptr_t pp
, void *buf
, size_t secsize
,
120 libfat_sector_t sector
)
122 off_t offset
= (off_t
) sector
* secsize
+ filesystem_offset
;
123 return xpread(pp
, buf
, secsize
, offset
);
126 int main(int argc
, char *argv
[])
128 static unsigned char sectbuf
[SECTOR_SIZE
];
133 char mtools_conf
[] = "/tmp/syslinux-mtools-XXXXXX";
134 const char *subdir
= NULL
;
137 struct libfat_filesystem
*fs
;
138 libfat_sector_t s
, *secp
;
139 libfat_sector_t
*sectors
;
140 int32_t ldlinux_cluster
;
143 int ldlinux_sectors
, patch_sectors
;
146 int force
= 0; /* -f (force) option */
147 int stupid
= 0; /* -s (stupid) option */
148 int raid_mode
= 0; /* -r (RAID) option */
150 (void)argc
; /* Unused */
157 for (argp
= argv
+ 1; *argp
; argp
++) {
166 } else if (*opt
== 'r') {
168 } else if (*opt
== 'f') {
169 force
= 1; /* Force install */
170 } else if (*opt
== 'd' && argp
[1]) {
172 } else if (*opt
== 'o' && argp
[1]) {
173 filesystem_offset
= (off_t
) strtoull(*++argp
, NULL
, 0); /* Byte offset */
190 * First make sure we can open the device at all, and that we have
191 * read/write permission.
193 dev_fd
= open(device
, O_RDWR
);
194 if (dev_fd
< 0 || fstat(dev_fd
, &st
) < 0) {
199 if (!force
&& !S_ISBLK(st
.st_mode
) && !S_ISREG(st
.st_mode
)) {
201 "%s: not a block device or regular file (use -f to override)\n",
206 xpread(dev_fd
, sectbuf
, SECTOR_SIZE
, filesystem_offset
);
209 * Check to see that what we got was indeed an MS-DOS boot sector/superblock
211 if ((errmsg
= syslinux_check_bootsect(sectbuf
))) {
216 * Create an mtools configuration file
218 mtc_fd
= mkstemp(mtools_conf
);
219 if (mtc_fd
< 0 || !(mtc
= fdopen(mtc_fd
, "w"))) {
224 /* These are needed for some flash memories */
225 "MTOOLS_SKIP_CHECK=1\n"
226 "MTOOLS_FAT_COMPATIBILITY=1\n"
228 " file=\"/proc/%lu/fd/%d\"\n"
230 (unsigned long)mypid
,
231 dev_fd
, (unsigned long long)filesystem_offset
);
235 * Run mtools to create the LDLINUX.SYS file
237 if (setenv("MTOOLSRC", mtools_conf
, 1)) {
242 /* This command may fail legitimately */
243 system("mattrib -h -r -s s:/ldlinux.sys 2>/dev/null");
245 mtp
= popen("mcopy -D o -D O -o - s:/ldlinux.sys", "w");
246 if (!mtp
|| (fwrite(syslinux_ldlinux
, 1, syslinux_ldlinux_len
, mtp
)
247 != syslinux_ldlinux_len
) ||
248 (status
= pclose(mtp
), !WIFEXITED(status
) || WEXITSTATUS(status
))) {
249 die("failed to create ldlinux.sys");
253 * Now, use libfat to create a block map
255 ldlinux_sectors
= (syslinux_ldlinux_len
+ SECTOR_SIZE
- 1) >> SECTOR_SHIFT
;
256 sectors
= calloc(ldlinux_sectors
, sizeof *sectors
);
257 fs
= libfat_open(libfat_xpread
, dev_fd
);
258 ldlinux_cluster
= libfat_searchdir(fs
, 0, "LDLINUX SYS", NULL
);
261 s
= libfat_clustertosector(fs
, ldlinux_cluster
);
262 while (s
&& nsectors
< ldlinux_sectors
) {
265 s
= libfat_nextsector(fs
, s
);
269 /* Patch ldlinux.sys and the boot sector */
270 i
= syslinux_patch(sectors
, nsectors
, stupid
, raid_mode
, subdir
, NULL
);
271 patch_sectors
= (i
+ SECTOR_SIZE
- 1) >> SECTOR_SHIFT
;
273 /* Write the now-patched first sectors of ldlinux.sys */
274 for (i
= 0; i
< patch_sectors
; i
++) {
275 xpwrite(dev_fd
, syslinux_ldlinux
+ i
* SECTOR_SIZE
, SECTOR_SIZE
,
276 filesystem_offset
+ ((off_t
) sectors
[i
] << SECTOR_SHIFT
));
279 /* Move ldlinux.sys to the desired location */
281 char target_file
[4096], command
[5120];
282 char *cp
= target_file
, *ep
= target_file
+ sizeof target_file
- 16;
286 cp
+= sprintf(cp
, "'s:/");
287 for (sd
= subdir
; *sd
; sd
++) {
288 if (*sd
== '/' || *sd
== '\\') {
290 continue; /* Remove duplicated slashes */
292 } else if (*sd
== '\'' || *sd
== '!') {
312 strcpy(cp
, "ldlinux.sys'");
314 /* This command may fail legitimately */
315 sprintf(command
, "mattrib -h -r -s %s 2>/dev/null", target_file
);
318 sprintf(command
, "mmove -D o -D O s:/ldlinux.sys %s", target_file
);
319 status
= system(command
);
321 if (!WIFEXITED(status
) || WEXITSTATUS(status
)) {
323 "%s: warning: unable to move ldlinux.sys\n", program
);
325 status
= system("mattrib +r +h +s s:/ldlinux.sys");
327 sprintf(command
, "mattrib +r +h +s %s", target_file
);
328 status
= system(command
);
331 status
= system("mattrib +r +h +s s:/ldlinux.sys");
334 if (!WIFEXITED(status
) || WEXITSTATUS(status
)) {
336 "%s: warning: failed to set system bit on ldlinux.sys\n",
346 * To finish up, write the boot sector
349 /* Read the superblock again since it might have changed while mounted */
350 xpread(dev_fd
, sectbuf
, SECTOR_SIZE
, filesystem_offset
);
352 /* Copy the syslinux code into the boot sector */
353 syslinux_make_bootsect(sectbuf
);
355 /* Write new boot sector */
356 xpwrite(dev_fd
, sectbuf
, SECTOR_SIZE
, filesystem_offset
);