Adding upstream version 4.00~pre53+dfsg.
[syslinux-debian/hramrach.git] / mtools / syslinux.c
blob0f4ccf9cbd80af55b5c252a1f84267c354598eb9
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",
49 program);
50 exit(1);
53 void __attribute__ ((noreturn)) die(const char *msg)
55 fprintf(stderr, "%s: %s\n", program, msg);
56 exit(1);
60 * read/write wrapper functions
62 ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
64 char *bufp = (char *)buf;
65 ssize_t rv;
66 ssize_t done = 0;
68 while (count) {
69 rv = pread(fd, bufp, count, offset);
70 if (rv == 0) {
71 die("short read");
72 } else if (rv == -1) {
73 if (errno == EINTR) {
74 continue;
75 } else {
76 die(strerror(errno));
78 } else {
79 bufp += rv;
80 offset += rv;
81 done += rv;
82 count -= rv;
86 return done;
89 ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
91 const char *bufp = (const char *)buf;
92 ssize_t rv;
93 ssize_t done = 0;
95 while (count) {
96 rv = pwrite(fd, bufp, count, offset);
97 if (rv == 0) {
98 die("short write");
99 } else if (rv == -1) {
100 if (errno == EINTR) {
101 continue;
102 } else {
103 die(strerror(errno));
105 } else {
106 bufp += rv;
107 offset += rv;
108 done += rv;
109 count -= rv;
113 return done;
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];
129 int dev_fd;
130 struct stat st;
131 int status;
132 char **argp, *opt;
133 char mtools_conf[] = "/tmp/syslinux-mtools-XXXXXX";
134 const char *subdir = NULL;
135 int mtc_fd;
136 FILE *mtc, *mtp;
137 struct libfat_filesystem *fs;
138 libfat_sector_t s, *secp;
139 libfat_sector_t *sectors;
140 int32_t ldlinux_cluster;
141 int nsectors;
142 const char *errmsg;
143 int ldlinux_sectors, patch_sectors;
144 int i;
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 */
152 mypid = getpid();
153 program = argv[0];
155 device = NULL;
157 for (argp = argv + 1; *argp; argp++) {
158 if (**argp == '-') {
159 opt = *argp + 1;
160 if (!*opt)
161 usage();
163 while (*opt) {
164 if (*opt == 's') {
165 stupid = 1;
166 } else if (*opt == 'r') {
167 raid_mode = 1;
168 } else if (*opt == 'f') {
169 force = 1; /* Force install */
170 } else if (*opt == 'd' && argp[1]) {
171 subdir = *++argp;
172 } else if (*opt == 'o' && argp[1]) {
173 filesystem_offset = (off_t) strtoull(*++argp, NULL, 0); /* Byte offset */
174 } else {
175 usage();
177 opt++;
179 } else {
180 if (device)
181 usage();
182 device = *argp;
186 if (!device)
187 usage();
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) {
195 perror(device);
196 exit(1);
199 if (!force && !S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode)) {
200 fprintf(stderr,
201 "%s: not a block device or regular file (use -f to override)\n",
202 device);
203 exit(1);
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))) {
212 die(errmsg);
216 * Create an mtools configuration file
218 mtc_fd = mkstemp(mtools_conf);
219 if (mtc_fd < 0 || !(mtc = fdopen(mtc_fd, "w"))) {
220 perror(program);
221 exit(1);
223 fprintf(mtc,
224 /* These are needed for some flash memories */
225 "MTOOLS_SKIP_CHECK=1\n"
226 "MTOOLS_FAT_COMPATIBILITY=1\n"
227 "drive s:\n"
228 " file=\"/proc/%lu/fd/%d\"\n"
229 " offset=%llu\n",
230 (unsigned long)mypid,
231 dev_fd, (unsigned long long)filesystem_offset);
232 fclose(mtc);
235 * Run mtools to create the LDLINUX.SYS file
237 if (setenv("MTOOLSRC", mtools_conf, 1)) {
238 perror(program);
239 exit(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);
259 secp = sectors;
260 nsectors = 0;
261 s = libfat_clustertosector(fs, ldlinux_cluster);
262 while (s && nsectors < ldlinux_sectors) {
263 *secp++ = s;
264 nsectors++;
265 s = libfat_nextsector(fs, s);
267 libfat_close(fs);
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 */
280 if (subdir) {
281 char target_file[4096], command[5120];
282 char *cp = target_file, *ep = target_file + sizeof target_file - 16;
283 const char *sd;
284 int slash = 1;
286 cp += sprintf(cp, "'s:/");
287 for (sd = subdir; *sd; sd++) {
288 if (*sd == '/' || *sd == '\\') {
289 if (slash)
290 continue; /* Remove duplicated slashes */
291 slash = 1;
292 } else if (*sd == '\'' || *sd == '!') {
293 slash = 0;
294 if (cp < ep)
295 *cp++ = '\'';
296 if (cp < ep)
297 *cp++ = '\\';
298 if (cp < ep)
299 *cp++ = *sd;
300 if (cp < ep)
301 *cp++ = '\'';
302 continue;
303 } else {
304 slash = 0;
307 if (cp < ep)
308 *cp++ = *sd;
310 if (!slash)
311 *cp++ = '/';
312 strcpy(cp, "ldlinux.sys'");
314 /* This command may fail legitimately */
315 sprintf(command, "mattrib -h -r -s %s 2>/dev/null", target_file);
316 system(command);
318 sprintf(command, "mmove -D o -D O s:/ldlinux.sys %s", target_file);
319 status = system(command);
321 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
322 fprintf(stderr,
323 "%s: warning: unable to move ldlinux.sys\n", program);
325 status = system("mattrib +r +h +s s:/ldlinux.sys");
326 } else {
327 sprintf(command, "mattrib +r +h +s %s", target_file);
328 status = system(command);
330 } else {
331 status = system("mattrib +r +h +s s:/ldlinux.sys");
334 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
335 fprintf(stderr,
336 "%s: warning: failed to set system bit on ldlinux.sys\n",
337 program);
341 * Cleanup
343 unlink(mtools_conf);
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);
358 close(dev_fd);
359 sync();
361 /* Done! */
363 return 0;