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 } 409