MEMDISK: Use aligning memcpy/memset, compile with -mregparm=3
[syslinux.git] / mtools / syslinux.c
blob46b33f4e6d9e5ab2792d0b15448f0c7279b1d61d
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 */
22 #define _BSD_SOURCE
23 #include <alloca.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <inttypes.h>
27 #include <mntent.h>
28 #include <paths.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <syslog.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/wait.h>
38 #include "syslinux.h"
39 #include "libfat.h"
41 char *program; /* Name of program */
42 char *device; /* Device to install to */
43 pid_t mypid;
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", program);
49 exit(1);
52 void __attribute__((noreturn)) die(const char *msg)
54 fprintf(stderr, "%s: %s\n", program, msg);
55 exit(1);
59 * read/write wrapper functions
61 ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
63 char *bufp = (char *)buf;
64 ssize_t rv;
65 ssize_t done = 0;
67 while ( count ) {
68 rv = pread(fd, bufp, count, offset);
69 if ( rv == 0 ) {
70 die("short read");
71 } else if ( rv == -1 ) {
72 if ( errno == EINTR ) {
73 continue;
74 } else {
75 die(strerror(errno));
77 } else {
78 bufp += rv;
79 offset += rv;
80 done += rv;
81 count -= rv;
85 return done;
88 ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
90 const char *bufp = (const char *)buf;
91 ssize_t rv;
92 ssize_t done = 0;
94 while ( count ) {
95 rv = pwrite(fd, bufp, count, offset);
96 if ( rv == 0 ) {
97 die("short write");
98 } else if ( rv == -1 ) {
99 if ( errno == EINTR ) {
100 continue;
101 } else {
102 die(strerror(errno));
104 } else {
105 bufp += rv;
106 offset += rv;
107 done += rv;
108 count -= rv;
112 return done;
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];
128 int dev_fd;
129 struct stat st;
130 int status;
131 char **argp, *opt;
132 char mtools_conf[] = "/tmp/syslinux-mtools-XXXXXX";
133 const char *subdir = NULL;
134 int mtc_fd;
135 FILE *mtc, *mtp;
136 struct libfat_filesystem *fs;
137 libfat_sector_t s, *secp, sectors[65]; /* 65 is maximum possible */
138 int32_t ldlinux_cluster;
139 int nsectors;
140 const char *errmsg;
142 int force = 0; /* -f (force) option */
143 int stupid = 0; /* -s (stupid) option */
144 int raid_mode = 0; /* -r (RAID) option */
146 (void)argc; /* Unused */
148 mypid = getpid();
149 program = argv[0];
151 device = NULL;
153 for ( argp = argv+1 ; *argp ; argp++ ) {
154 if ( **argp == '-' ) {
155 opt = *argp + 1;
156 if ( !*opt )
157 usage();
159 while ( *opt ) {
160 if ( *opt == 's' ) {
161 stupid = 1;
162 } else if ( *opt == 'r' ) {
163 raid_mode = 1;
164 } else if ( *opt == 'f' ) {
165 force = 1; /* Force install */
166 } else if ( *opt == 'd' && argp[1] ) {
167 subdir = *++argp;
168 } else if ( *opt == 'o' && argp[1] ) {
169 filesystem_offset = (off_t)strtoull(*++argp, NULL, 0); /* Byte offset */
170 } else {
171 usage();
173 opt++;
175 } else {
176 if ( device )
177 usage();
178 device = *argp;
182 if ( !device )
183 usage();
186 * First make sure we can open the device at all, and that we have
187 * read/write permission.
189 dev_fd = open(device, O_RDWR);
190 if ( dev_fd < 0 || fstat(dev_fd, &st) < 0 ) {
191 perror(device);
192 exit(1);
195 if ( !force && !S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode) ) {
196 fprintf(stderr, "%s: not a block device or regular file (use -f to override)\n", device);
197 exit(1);
200 xpread(dev_fd, sectbuf, 512, filesystem_offset);
203 * Check to see that what we got was indeed an MS-DOS boot sector/superblock
205 if( (errmsg = syslinux_check_bootsect(sectbuf)) ) {
206 die(errmsg);
210 * Create an mtools configuration file
212 mtc_fd = mkstemp(mtools_conf);
213 if ( mtc_fd < 0 || !(mtc = fdopen(mtc_fd, "w")) ) {
214 perror(program);
215 exit(1);
217 fprintf(mtc,
218 /* "MTOOLS_NO_VFAT=1\n" */
219 "MTOOLS_SKIP_CHECK=1\n" /* Needed for some flash memories */
220 "drive s:\n"
221 " file=\"/proc/%lu/fd/%d\"\n"
222 " offset=%llu\n",
223 (unsigned long)mypid,
224 dev_fd,
225 (unsigned long long)filesystem_offset);
226 fclose(mtc);
229 * Run mtools to create the LDLINUX.SYS file
231 if ( setenv("MTOOLSRC", mtools_conf, 1) ) {
232 perror(program);
233 exit(1);
236 /* This command may fail legitimately */
237 system("mattrib -h -r -s s:/ldlinux.sys 2>/dev/null");
239 mtp = popen("mcopy -D o -D O -o - s:/ldlinux.sys", "w");
240 if ( !mtp ||
241 (fwrite(syslinux_ldlinux, 1, syslinux_ldlinux_len, mtp)
242 != syslinux_ldlinux_len) ||
243 (status = pclose(mtp), !WIFEXITED(status) || WEXITSTATUS(status)) ) {
244 die("failed to create ldlinux.sys");
248 * Now, use libfat to create a block map
250 fs = libfat_open(libfat_xpread, dev_fd);
251 ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL);
252 secp = sectors;
253 nsectors = 0;
254 s = libfat_clustertosector(fs, ldlinux_cluster);
255 while ( s && nsectors < 65 ) {
256 *secp++ = s;
257 nsectors++;
258 s = libfat_nextsector(fs, s);
260 libfat_close(fs);
262 /* Patch ldlinux.sys and the boot sector */
263 syslinux_patch(sectors, nsectors, stupid, raid_mode);
265 /* Write the now-patched first sector of ldlinux.sys */
266 xpwrite(dev_fd, syslinux_ldlinux, 512,
267 filesystem_offset + ((off_t)sectors[0] << 9));
269 /* Move ldlinux.sys to the desired location */
270 if (subdir) {
271 char target_file[4096], command[5120];
272 char *cp = target_file, *ep = target_file+sizeof target_file-16;
273 const char *sd;
274 int slash = 1;
276 cp += sprintf(cp, "'s:/");
277 for (sd = subdir; *sd; sd++) {
278 if (*sd == '/' || *sd == '\\') {
279 if (slash)
280 continue; /* Remove duplicated slashes */
281 slash = 1;
282 } else if (*sd == '\'' || *sd == '!') {
283 slash = 0;
284 if (cp < ep) *cp++ = '\'';
285 if (cp < ep) *cp++ = '\\';
286 if (cp < ep) *cp++ = *sd;
287 if (cp < ep) *cp++ = '\'';
288 continue;
289 } else {
290 slash = 0;
293 if (cp < ep)
294 *cp++ = *sd;
296 if (!slash)
297 *cp++ = '/';
298 strcpy(cp, "ldlinux.sys'");
300 /* This command may fail legitimately */
301 sprintf(command, "mattrib -h -r -s %s 2>/dev/null", target_file);
302 system(command);
304 sprintf(command, "mmove -D o -D O s:/ldlinux.sys %s", target_file);
305 status = system(command);
307 if ( !WIFEXITED(status) || WEXITSTATUS(status) ) {
308 fprintf(stderr,
309 "%s: warning: unable to move ldlinux.sys\n",
310 program);
312 status = system("mattrib +r +h +s s:/ldlinux.sys");
313 } else {
314 sprintf(command, "mattrib +r +h +s %s", target_file);
315 status = system(command);
317 } else {
318 status = system("mattrib +r +h +s s:/ldlinux.sys");
321 if ( !WIFEXITED(status) || WEXITSTATUS(status) ) {
322 fprintf(stderr,
323 "%s: warning: failed to set system bit on ldlinux.sys\n",
324 program);
329 * Cleanup
331 unlink(mtools_conf);
334 * To finish up, write the boot sector
337 /* Read the superblock again since it might have changed while mounted */
338 xpread(dev_fd, sectbuf, 512, filesystem_offset);
340 /* Copy the syslinux code into the boot sector */
341 syslinux_make_bootsect(sectbuf);
343 /* Write new boot sector */
344 xpwrite(dev_fd, sectbuf, 512, filesystem_offset);
346 close(dev_fd);
347 sync();
349 /* Done! */
351 return 0;