• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
4  *   Copyright 2010 Intel Corporation; author: H. Peter Anvin
5  *
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., 53 Temple Place Ste 330,
9  *   Boston MA 02111-1307, USA; either version 2 of the License, or
10  *   (at your option) any later version; incorporated herein by reference.
11  *
12  * ----------------------------------------------------------------------- */
13 
14 /*
15  * syslinux.c - Linux installer program for SYSLINUX
16  *
17  * This program now requires mtools.  It turned out to be a lot
18  * easier to deal with than dealing with needing mount privileges.
19  * We need device write permission anyway.
20  */
21 
22 #define _GNU_SOURCE
23 #include <alloca.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <getopt.h>
27 #include <inttypes.h>
28 #include <mntent.h>
29 #include <paths.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <sysexits.h>
34 #include <syslog.h>
35 #include <unistd.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/wait.h>
39 
40 #include "syslinux.h"
41 #include "libfat.h"
42 #include "setadv.h"
43 #include "syslxopt.h"
44 #include "syslxfs.h"
45 
46 char *program;			/* Name of program */
47 pid_t mypid;
48 
die(const char * msg)49 void __attribute__ ((noreturn)) die(const char *msg)
50 {
51     fprintf(stderr, "%s: %s\n", program, msg);
52     exit(1);
53 }
54 
die_err(const char * msg)55 void __attribute__ ((noreturn)) die_err(const char *msg)
56 {
57     fprintf(stderr, "%s: %s: %s\n", program, msg, strerror(errno));
58     exit(1);
59 }
60 
61 /*
62  * read/write wrapper functions
63  */
xpread(int fd,void * buf,size_t count,off_t offset)64 ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
65 {
66     char *bufp = (char *)buf;
67     ssize_t rv;
68     ssize_t done = 0;
69 
70     while (count) {
71 	rv = pread(fd, bufp, count, offset);
72 	if (rv == 0) {
73 	    die("short read");
74 	} else if (rv == -1) {
75 	    if (errno == EINTR) {
76 		continue;
77 	    } else {
78 		die(strerror(errno));
79 	    }
80 	} else {
81 	    bufp += rv;
82 	    offset += rv;
83 	    done += rv;
84 	    count -= rv;
85 	}
86     }
87 
88     return done;
89 }
90 
xpwrite(int fd,const void * buf,size_t count,off_t offset)91 ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
92 {
93     const char *bufp = (const char *)buf;
94     ssize_t rv;
95     ssize_t done = 0;
96 
97     while (count) {
98 	rv = pwrite(fd, bufp, count, offset);
99 	if (rv == 0) {
100 	    die("short write");
101 	} else if (rv == -1) {
102 	    if (errno == EINTR) {
103 		continue;
104 	    } else {
105 		die(strerror(errno));
106 	    }
107 	} else {
108 	    bufp += rv;
109 	    offset += rv;
110 	    done += rv;
111 	    count -= rv;
112 	}
113     }
114 
115     return done;
116 }
117 
118 /*
119  * Version of the read function suitable for libfat
120  */
libfat_xpread(intptr_t pp,void * buf,size_t secsize,libfat_sector_t sector)121 int libfat_xpread(intptr_t pp, void *buf, size_t secsize,
122 		  libfat_sector_t sector)
123 {
124     off_t offset = (off_t) sector * secsize + opt.offset;
125     return xpread(pp, buf, secsize, offset);
126 }
127 
move_file(char * filename)128 static int move_file(char *filename)
129 {
130     char target_file[4096], command[5120];
131     char *cp = target_file, *ep = target_file + sizeof target_file - 16;
132     const char *sd;
133     int slash = 1;
134     int status;
135 
136     cp += sprintf(cp, "'s:/");
137     for (sd = opt.directory; *sd; sd++) {
138 	if (*sd == '/' || *sd == '\\') {
139 	    if (slash)
140 		continue;	/* Remove duplicated slashes */
141 	    slash = 1;
142 	} else if (*sd == '\'' || *sd == '!') {
143 	    slash = 0;
144 	    if (cp < ep)
145 		*cp++ = '\'';
146 	    if (cp < ep)
147 		*cp++ = '\\';
148 	    if (cp < ep)
149 		*cp++ = *sd;
150 	    if (cp < ep)
151 		*cp++ = '\'';
152 	    continue;
153 	} else {
154 	    slash = 0;
155 	}
156 
157 	if (cp < ep)
158 	    *cp++ = *sd;
159     }
160     if (!slash)
161 	*cp++ = '/';
162     sprintf(cp, "%s'", filename);
163 
164     /* This command may fail legitimately */
165     sprintf(command, "mattrib -h -r -s %s 2>/dev/null", target_file);
166     status = system(command);
167     (void)status;		/* Keep _FORTIFY_SOURCE happy */
168 
169     sprintf(command, "mmove -D o -D O s:/%s %s", filename, target_file);
170     status = system(command);
171 
172     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
173 	fprintf(stderr,
174 		"%s: warning: unable to move %s\n", program, filename);
175 
176 	sprintf(command, "mattrib +r +h +s s:/%s", filename);
177 	status = system(command);
178     } else {
179 	sprintf(command, "mattrib +r +h +s %s", target_file);
180 	status = system(command);
181     }
182 
183     return status;
184 }
185 
main(int argc,char * argv[])186 int main(int argc, char *argv[])
187 {
188     static unsigned char sectbuf[SECTOR_SIZE];
189     int dev_fd;
190     struct stat st;
191     int status;
192     const char *tmpdir;
193     char *mtools_conf;
194     int mtc_fd;
195     FILE *mtc, *mtp;
196     struct libfat_filesystem *fs;
197     libfat_sector_t s, *secp;
198     libfat_sector_t *sectors;
199     int32_t ldlinux_cluster;
200     int nsectors;
201     const char *errmsg;
202     int ldlinux_sectors, patch_sectors;
203     int i;
204 
205     (void)argc;			/* Unused */
206 
207     mypid = getpid();
208     program = argv[0];
209 
210     parse_options(argc, argv, MODE_SYSLINUX);
211 
212     if (!opt.device)
213 	usage(EX_USAGE, MODE_SYSLINUX);
214 
215     if (opt.sectors || opt.heads || opt.reset_adv || opt.set_once
216 	|| (opt.update_only > 0) || opt.menu_save) {
217 	fprintf(stderr,
218 		"At least one specified option not yet implemented"
219 		" for this installer.\n");
220 	exit(1);
221     }
222 
223     /*
224      * Temp directory of choice...
225      */
226     tmpdir = getenv("TMPDIR");
227     if (!tmpdir) {
228 #ifdef P_tmpdir
229 	tmpdir = P_tmpdir;
230 #elif defined(_PATH_TMP)
231 	tmpdir = _PATH_TMP;
232 #else
233 	tmpdir = "/tmp";
234 #endif
235     }
236 
237     /*
238      * First make sure we can open the device at all, and that we have
239      * read/write permission.
240      */
241     dev_fd = open(opt.device, O_RDWR);
242     if (dev_fd < 0 || fstat(dev_fd, &st) < 0) {
243 	die_err(opt.device);
244 	exit(1);
245     }
246 
247     if (!opt.force && !S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode)) {
248 	fprintf(stderr,
249 		"%s: not a block device or regular file (use -f to override)\n",
250 		opt.device);
251 	exit(1);
252     }
253 
254     xpread(dev_fd, sectbuf, SECTOR_SIZE, opt.offset);
255 
256     /*
257      * Check to see that what we got was indeed an MS-DOS boot sector/superblock
258      */
259     if ((errmsg = syslinux_check_bootsect(sectbuf, NULL))) {
260 	die(errmsg);
261     }
262 
263     /*
264      * Create an mtools configuration file
265      */
266     if (asprintf(&mtools_conf, "%s//syslinux-mtools-XXXXXX", tmpdir) < 0 ||
267 	!mtools_conf)
268 	die_err(tmpdir);
269 
270     mtc_fd = mkstemp(mtools_conf);
271     if (mtc_fd < 0 || !(mtc = fdopen(mtc_fd, "w")))
272 	die_err(mtools_conf);
273 
274     fprintf(mtc,
275 	    /* These are needed for some flash memories */
276 	    "MTOOLS_SKIP_CHECK=1\n"
277 	    "MTOOLS_FAT_COMPATIBILITY=1\n"
278 	    "drive s:\n"
279 	    "  file=\"/proc/%lu/fd/%d\"\n"
280 	    "  offset=%llu\n",
281 	    (unsigned long)mypid,
282 	    dev_fd, (unsigned long long)opt.offset);
283 
284     if (ferror(mtc) || fclose(mtc))
285 	die_err(mtools_conf);
286 
287     /*
288      * Run mtools to create the LDLINUX.SYS file
289      */
290     if (setenv("MTOOLSRC", mtools_conf, 1)) {
291 	perror(program);
292 	exit(1);
293     }
294 
295     /*
296      * Create a vacuous ADV in memory.  This should be smarter.
297      */
298     syslinux_reset_adv(syslinux_adv);
299 
300     /* This command may fail legitimately */
301     status = system("mattrib -h -r -s s:/ldlinux.sys 2>/dev/null");
302     (void)status;		/* Keep _FORTIFY_SOURCE happy */
303 
304     mtp = popen("mcopy -D o -D O -o - s:/ldlinux.sys", "w");
305     if (!mtp ||
306 	fwrite((const void _force *)syslinux_ldlinux,
307 	       1, syslinux_ldlinux_len, mtp)
308 		!= syslinux_ldlinux_len ||
309 	fwrite((const void _force *)syslinux_adv,
310 	       1, 2 * ADV_SIZE, mtp)
311 		!= 2 * ADV_SIZE ||
312 	(status = pclose(mtp), !WIFEXITED(status) || WEXITSTATUS(status))) {
313 	die("failed to create ldlinux.sys");
314     }
315 
316     /*
317      * Now, use libfat to create a block map
318      */
319     ldlinux_sectors = (syslinux_ldlinux_len + 2 * ADV_SIZE
320 		       + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
321     sectors = calloc(ldlinux_sectors, sizeof *sectors);
322     fs = libfat_open(libfat_xpread, dev_fd);
323     ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL);
324     secp = sectors;
325     nsectors = 0;
326     s = libfat_clustertosector(fs, ldlinux_cluster);
327     while (s && nsectors < ldlinux_sectors) {
328 	*secp++ = s;
329 	nsectors++;
330 	s = libfat_nextsector(fs, s);
331     }
332     libfat_close(fs);
333 
334     /* Patch ldlinux.sys and the boot sector */
335     i = syslinux_patch(sectors, nsectors, opt.stupid_mode, opt.raid_mode,
336 		       opt.directory, NULL);
337     patch_sectors = (i + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
338 
339     /* Write the now-patched first sectors of ldlinux.sys */
340     for (i = 0; i < patch_sectors; i++) {
341 	xpwrite(dev_fd, (const char _force *)syslinux_ldlinux
342 		+ i * SECTOR_SIZE, SECTOR_SIZE,
343 		opt.offset + ((off_t) sectors[i] << SECTOR_SHIFT));
344     }
345 
346     /* Move ldlinux.sys to the desired location */
347     if (opt.directory) {
348 	status = move_file("ldlinux.sys");
349     } else {
350 	status = system("mattrib +r +h +s s:/ldlinux.sys");
351     }
352 
353     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
354 	fprintf(stderr,
355 		"%s: warning: failed to set system bit on ldlinux.sys\n",
356 		program);
357     }
358 
359     /* This command may fail legitimately */
360     status = system("mattrib -h -r -s s:/ldlinux.c32 2>/dev/null");
361     (void)status;		/* Keep _FORTIFY_SOURCE happy */
362 
363     mtp = popen("mcopy -D o -D O -o - s:/ldlinux.c32", "w");
364     if (!mtp ||	fwrite((const char _force *)syslinux_ldlinuxc32,
365 		       1, syslinux_ldlinuxc32_len, mtp)
366 	!= syslinux_ldlinuxc32_len ||
367 	(status = pclose(mtp), !WIFEXITED(status) || WEXITSTATUS(status))) {
368 	die("failed to create ldlinux.c32");
369     }
370 
371     /* Move ldlinux.c32 to the desired location */
372     if (opt.directory) {
373 	status = move_file("ldlinux.c32");
374     } else {
375 	status = system("mattrib +r +h +s s:/ldlinux.c32");
376     }
377 
378     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
379 	fprintf(stderr,
380 		"%s: warning: failed to set system bit on ldlinux.c32\n",
381 		program);
382     }
383 
384     /*
385      * Cleanup
386      */
387     unlink(mtools_conf);
388 
389     /*
390      * To finish up, write the boot sector
391      */
392 
393     /* Read the superblock again since it might have changed while mounted */
394     xpread(dev_fd, sectbuf, SECTOR_SIZE, opt.offset);
395 
396     /* Copy the syslinux code into the boot sector */
397     syslinux_make_bootsect(sectbuf, VFAT);
398 
399     /* Write new boot sector */
400     xpwrite(dev_fd, sectbuf, SECTOR_SIZE, opt.offset);
401 
402     close(dev_fd);
403     sync();
404 
405     /* Done! */
406 
407     return 0;
408 }
409