1 /* ----------------------------------------------------------------------- *
3 * Copyright 1998-2007 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 [-sf][-d directory][-o offset] device\n", program
);
52 void __attribute__((noreturn
)) die(const char *msg
)
54 fprintf(stderr
, "%s: %s\n", program
, msg
);
59 * read/write wrapper functions
61 ssize_t
xpread(int fd
, void *buf
, size_t count
, off_t offset
)
63 char *bufp
= (char *)buf
;
68 rv
= pread(fd
, bufp
, count
, offset
);
71 } else if ( rv
== -1 ) {
72 if ( errno
== EINTR
) {
88 ssize_t
xpwrite(int fd
, const void *buf
, size_t count
, off_t offset
)
90 const char *bufp
= (const char *)buf
;
95 rv
= pwrite(fd
, bufp
, count
, offset
);
98 } else if ( rv
== -1 ) {
99 if ( errno
== EINTR
) {
102 die(strerror(errno
));
116 * Version of the read function suitable for libfat
118 int libfat_xpread(intptr_t pp
, void *buf
, size_t secsize
, libfat_sector_t sector
)
120 off_t offset
= (off_t
)sector
* secsize
+ filesystem_offset
;
121 return xpread(pp
, buf
, secsize
, offset
);
125 int main(int argc
, char *argv
[])
127 static unsigned char sectbuf
[512];
132 int force
= 0; /* -f (force) option */
133 char mtools_conf
[] = "/tmp/syslinux-mtools-XXXXXX";
134 const char *subdir
= NULL
;
137 struct libfat_filesystem
*fs
;
138 libfat_sector_t s
, *secp
, sectors
[65]; /* 65 is maximum possible */
139 int32_t ldlinux_cluster
;
143 (void)argc
; /* Unused */
150 for ( argp
= argv
+1 ; *argp
; argp
++ ) {
151 if ( **argp
== '-' ) {
158 syslinux_make_stupid(); /* Use "safe, slow and stupid" code */
159 } else if ( *opt
== 'f' ) {
160 force
= 1; /* Force install */
161 } else if ( *opt
== 'd' && argp
[1] ) {
163 } else if ( *opt
== 'o' && argp
[1] ) {
164 filesystem_offset
= (off_t
)strtoull(*++argp
, NULL
, 0); /* Byte offset */
181 * First make sure we can open the device at all, and that we have
182 * read/write permission.
184 dev_fd
= open(device
, O_RDWR
);
185 if ( dev_fd
< 0 || fstat(dev_fd
, &st
) < 0 ) {
190 if ( !force
&& !S_ISBLK(st
.st_mode
) && !S_ISREG(st
.st_mode
) ) {
191 fprintf(stderr
, "%s: not a block device or regular file (use -f to override)\n", device
);
195 xpread(dev_fd
, sectbuf
, 512, filesystem_offset
);
198 * Check to see that what we got was indeed an MS-DOS boot sector/superblock
200 if( (errmsg
= syslinux_check_bootsect(sectbuf
)) ) {
205 * Create an mtools configuration file
207 mtc_fd
= mkstemp(mtools_conf
);
208 if ( mtc_fd
< 0 || !(mtc
= fdopen(mtc_fd
, "w")) ) {
214 "MTOOLS_SKIP_CHECK=1\n" /* Needed for some flash memories */
216 " file=\"/proc/%lu/fd/%d\"\n"
218 (unsigned long)mypid
,
220 (unsigned long long)filesystem_offset
);
224 * Run mtools to create the LDLINUX.SYS file
226 if ( setenv("MTOOLSRC", mtools_conf
, 1) ) {
231 /* This command may fail legitimately */
232 system("mattrib -h -r -s s:/ldlinux.sys 2>/dev/null");
234 mtp
= popen("mcopy -D o -D O -o - s:/ldlinux.sys", "w");
236 (fwrite(syslinux_ldlinux
, 1, syslinux_ldlinux_len
, mtp
)
237 != syslinux_ldlinux_len
) ||
238 (status
= pclose(mtp
), !WIFEXITED(status
) || WEXITSTATUS(status
)) ) {
239 die("failed to create ldlinux.sys");
243 * Now, use libfat to create a block map
245 fs
= libfat_open(libfat_xpread
, dev_fd
);
246 ldlinux_cluster
= libfat_searchdir(fs
, 0, "LDLINUX SYS", NULL
);
249 s
= libfat_clustertosector(fs
, ldlinux_cluster
);
250 while ( s
&& nsectors
< 65 ) {
253 s
= libfat_nextsector(fs
, s
);
257 /* Patch ldlinux.sys and the boot sector */
258 syslinux_patch(sectors
, nsectors
);
260 /* Write the now-patched first sector of ldlinux.sys */
261 xpwrite(dev_fd
, syslinux_ldlinux
, 512,
262 filesystem_offset
+ ((off_t
)sectors
[0] << 9));
264 /* Move ldlinux.sys to the desired location */
266 char target_file
[4096], command
[5120];
267 char *cp
= target_file
, *ep
= target_file
+sizeof target_file
-16;
271 cp
+= sprintf(cp
, "'s:/");
272 for (sd
= subdir
; *sd
; sd
++) {
273 if (*sd
== '/' || *sd
== '\\') {
275 continue; /* Remove duplicated slashes */
277 } else if (*sd
== '\'' || *sd
== '!') {
279 if (cp
< ep
) *cp
++ = '\'';
280 if (cp
< ep
) *cp
++ = '\\';
281 if (cp
< ep
) *cp
++ = *sd
;
282 if (cp
< ep
) *cp
++ = '\'';
293 strcpy(cp
, "ldlinux.sys'");
295 /* This command may fail legitimately */
296 sprintf(command
, "mattrib -h -r -s %s 2>/dev/null", target_file
);
299 sprintf(command
, "mmove -D o -D O s:/ldlinux.sys %s", target_file
);
300 status
= system(command
);
302 if ( !WIFEXITED(status
) || WEXITSTATUS(status
) ) {
304 "%s: warning: unable to move ldlinux.sys\n",
307 status
= system("mattrib +r +h +s s:/ldlinux.sys");
309 sprintf(command
, "mattrib +r +h +s %s", target_file
);
310 status
= system(command
);
313 status
= system("mattrib +r +h +s s:/ldlinux.sys");
316 if ( !WIFEXITED(status
) || WEXITSTATUS(status
) ) {
318 "%s: warning: failed to set system bit on ldlinux.sys\n",
329 * To finish up, write the boot sector
332 /* Read the superblock again since it might have changed while mounted */
333 xpread(dev_fd
, sectbuf
, 512, filesystem_offset
);
335 /* Copy the syslinux code into the boot sector */
336 syslinux_make_bootsect(sectbuf
);
338 /* Write new boot sector */
339 xpwrite(dev_fd
, sectbuf
, 512, filesystem_offset
);