[e16e8f2] | 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 | |
---|
| 49 | void __attribute__ ((noreturn)) die(const char *msg) |
---|
| 50 | { |
---|
| 51 | fprintf(stderr, "%s: %s\n", program, msg); |
---|
| 52 | exit(1); |
---|
| 53 | } |
---|
| 54 | |
---|
| 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 | */ |
---|
| 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 | |
---|
| 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 | */ |
---|
| 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 | |
---|
| 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 | |
---|
| 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 | } |
---|