1 /* ----------------------------------------------------------------------- * 2 * 3 * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved 4 * Copyright 2009-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 is Linux-specific by now. 18 * 19 * This is an alternate version of the installer which doesn't require 20 * mtools, but requires root privilege. 21 */ 22 23 /* 24 * If DO_DIRECT_MOUNT is 0, call mount(8) 25 * If DO_DIRECT_MOUNT is 1, call mount(2) 26 */ 27 #ifdef __KLIBC__ 28 # define DO_DIRECT_MOUNT 1 29 #else 30 # define DO_DIRECT_MOUNT 0 /* glibc has broken losetup ioctls */ 31 #endif 32 33 #define _GNU_SOURCE 34 #define _XOPEN_SOURCE 500 /* For pread() pwrite() */ 35 #define _FILE_OFFSET_BITS 64 36 #include <alloca.h> 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <paths.h> 40 #include <stdio.h> 41 #include <string.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 #include <inttypes.h> 45 #include <sys/stat.h> 46 #include <sys/types.h> 47 #include <sys/wait.h> 48 #include <sys/mount.h> 49 50 #include "linuxioctl.h" 51 52 #include <paths.h> 53 #ifndef _PATH_MOUNT 54 # define _PATH_MOUNT "/bin/mount" 55 #endif 56 #ifndef _PATH_UMOUNT 57 # define _PATH_UMOUNT "/bin/umount" 58 #endif 59 #ifndef _PATH_TMP 60 # define _PATH_TMP "/tmp/" 61 #endif 62 63 #include "syslinux.h" 64 65 #if DO_DIRECT_MOUNT 66 # include <linux/loop.h> 67 #endif 68 69 #include <getopt.h> 70 #include <sysexits.h> 71 #include "syslxcom.h" 72 #include "syslxfs.h" 73 #include "setadv.h" 74 #include "syslxopt.h" /* unified options */ 75 76 extern const char *program; /* Name of program */ 77 78 pid_t mypid; 79 char *mntpath = NULL; /* Path on which to mount */ 80 81 #if DO_DIRECT_MOUNT 82 int loop_fd = -1; /* Loop device */ 83 #endif 84 85 void __attribute__ ((noreturn)) die(const char *msg) 86 { 87 fprintf(stderr, "%s: %s\n", program, msg); 88 89 #if DO_DIRECT_MOUNT 90 if (loop_fd != -1) { 91 ioctl(loop_fd, LOOP_CLR_FD, 0); /* Free loop device */ 92 close(loop_fd); 93 loop_fd = -1; 94 } 95 #endif 96 97 if (mntpath) 98 unlink(mntpath); 99 100 exit(1); 101 } 102 103 /* 104 * Mount routine 105 */ 106 int do_mount(int dev_fd, int *cookie, const char *mntpath, const char *fstype) 107 { 108 struct stat st; 109 110 (void)cookie; 111 112 if (fstat(dev_fd, &st) < 0) 113 return errno; 114 115 #if DO_DIRECT_MOUNT 116 { 117 if (!S_ISBLK(st.st_mode)) { 118 /* It's file, need to mount it loopback */ 119 unsigned int n = 0; 120 struct loop_info64 loopinfo; 121 int loop_fd; 122 123 for (n = 0; loop_fd < 0; n++) { 124 snprintf(devfdname, sizeof devfdname, "/dev/loop%u", n); 125 loop_fd = open(devfdname, O_RDWR); 126 if (loop_fd < 0 && errno == ENOENT) { 127 die("no available loopback device!"); 128 } 129 if (ioctl(loop_fd, LOOP_SET_FD, (void *)dev_fd)) { 130 close(loop_fd); 131 loop_fd = -1; 132 if (errno != EBUSY) 133 die("cannot set up loopback device"); 134 else 135 continue; 136 } 137 138 if (ioctl(loop_fd, LOOP_GET_STATUS64, &loopinfo) || 139 (loopinfo.lo_offset = opt.offset, 140 ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo))) 141 die("cannot set up loopback device"); 142 } 143 144 *cookie = loop_fd; 145 } else { 146 snprintf(devfdname, sizeof devfdname, "/proc/%lu/fd/%d", 147 (unsigned long)mypid, dev_fd); 148 *cookie = -1; 149 } 150 151 return mount(devfdname, mntpath, fstype, 152 MS_NOEXEC | MS_NOSUID, "umask=077,quiet"); 153 } 154 #else 155 { 156 char devfdname[128], mnt_opts[128]; 157 pid_t f, w; 158 int status; 159 160 snprintf(devfdname, sizeof devfdname, "/proc/%lu/fd/%d", 161 (unsigned long)mypid, dev_fd); 162 163 f = fork(); 164 if (f < 0) { 165 return -1; 166 } else if (f == 0) { 167 if (!S_ISBLK(st.st_mode)) { 168 snprintf(mnt_opts, sizeof mnt_opts, 169 "rw,nodev,noexec,loop,offset=%llu,umask=077,quiet", 170 (unsigned long long)opt.offset); 171 } else { 172 snprintf(mnt_opts, sizeof mnt_opts, 173 "rw,nodev,noexec,umask=077,quiet"); 174 } 175 execl(_PATH_MOUNT, _PATH_MOUNT, "-t", fstype, "-o", mnt_opts, 176 devfdname, mntpath, NULL); 177 _exit(255); /* execl failed */ 178 } 179 180 w = waitpid(f, &status, 0); 181 return (w != f || status) ? -1 : 0; 182 } 183 #endif 184 } 185 186 /* 187 * umount routine 188 */ 189 void do_umount(const char *mntpath, int cookie) 190 { 191 #if DO_DIRECT_MOUNT 192 int loop_fd = cookie; 193 194 if (umount2(mntpath, 0)) 195 die("could not umount path"); 196 197 if (loop_fd != -1) { 198 ioctl(loop_fd, LOOP_CLR_FD, 0); /* Free loop device */ 199 close(loop_fd); 200 loop_fd = -1; 201 } 202 #else 203 pid_t f = fork(); 204 pid_t w; 205 int status; 206 (void)cookie; 207 208 if (f < 0) { 209 perror("fork"); 210 exit(1); 211 } else if (f == 0) { 212 execl(_PATH_UMOUNT, _PATH_UMOUNT, mntpath, NULL); 213 } 214 215 w = waitpid(f, &status, 0); 216 if (w != f || status) { 217 exit(1); 218 } 219 #endif 220 } 221 222 /* 223 * Modify the ADV of an existing installation 224 */ 225 int modify_existing_adv(const char *path) 226 { 227 if (opt.reset_adv) 228 syslinux_reset_adv(syslinux_adv); 229 else if (read_adv(path, "ldlinux.sys") < 0) 230 return 1; 231 232 if (modify_adv() < 0) 233 return 1; 234 235 if (write_adv(path, "ldlinux.sys") < 0) 236 return 1; 237 238 return 0; 239 } 240 241 int do_open_file(char *name) 242 { 243 int fd; 244 245 if ((fd = open(name, O_RDONLY)) >= 0) { 246 uint32_t zero_attr = 0; 247 ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &zero_attr); 248 close(fd); 249 } 250 251 unlink(name); 252 fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0444); 253 if (fd < 0) 254 perror(name); 255 256 return fd; 257 } 258 259 int main(int argc, char *argv[]) 260 { 261 static unsigned char sectbuf[SECTOR_SIZE]; 262 int dev_fd, fd; 263 struct stat st; 264 int err = 0; 265 char mntname[128]; 266 char *ldlinux_name; 267 char *ldlinux_path; 268 char *subdir; 269 sector_t *sectors = NULL; 270 int ldlinux_sectors = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT; 271 const char *errmsg; 272 int mnt_cookie; 273 int patch_sectors; 274 int i, rv; 275 276 mypid = getpid(); 277 umask(077); 278 parse_options(argc, argv, MODE_SYSLINUX); 279 280 /* Note: subdir is guaranteed to start and end in / */ 281 if (opt.directory && opt.directory[0]) { 282 int len = strlen(opt.directory); 283 int rv = asprintf(&subdir, "%s%s%s", 284 opt.directory[0] == '/' ? "" : "/", 285 opt.directory, 286 opt.directory[len-1] == '/' ? "" : "/"); 287 if (rv < 0 || !subdir) { 288 perror(program); 289 exit(1); 290 } 291 } else { 292 subdir = "/"; 293 } 294 295 if (!opt.device || opt.install_mbr || opt.activate_partition) 296 usage(EX_USAGE, MODE_SYSLINUX); 297 298 /* 299 * First make sure we can open the device at all, and that we have 300 * read/write permission. 301 */ 302 dev_fd = open(opt.device, O_RDWR); 303 if (dev_fd < 0 || fstat(dev_fd, &st) < 0) { 304 perror(opt.device); 305 exit(1); 306 } 307 308 if (!S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode)) { 309 die("not a device or regular file"); 310 } 311 312 if (opt.offset && S_ISBLK(st.st_mode)) { 313 die("can't combine an offset with a block device"); 314 } 315 316 xpread(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); 317 fsync(dev_fd); 318 319 /* 320 * Check to see that what we got was indeed an FAT/NTFS 321 * boot sector/superblock 322 */ 323 if ((errmsg = syslinux_check_bootsect(sectbuf, &fs_type))) { 324 fprintf(stderr, "%s: %s\n", opt.device, errmsg); 325 exit(1); 326 } 327 328 /* 329 * Now mount the device. 330 */ 331 if (geteuid()) { 332 die("This program needs root privilege"); 333 } else { 334 int i = 0; 335 struct stat dst; 336 int rv; 337 338 /* We're root or at least setuid. 339 Make a temp dir and pass all the gunky options to mount. */ 340 341 if (chdir(_PATH_TMP)) { 342 fprintf(stderr, "%s: Cannot access the %s directory.\n", 343 program, _PATH_TMP); 344 exit(1); 345 } 346 #define TMP_MODE (S_IXUSR|S_IWUSR|S_IXGRP|S_IWGRP|S_IWOTH|S_IXOTH|S_ISVTX) 347 348 if (stat(".", &dst) || !S_ISDIR(dst.st_mode) || 349 (dst.st_mode & TMP_MODE) != TMP_MODE) { 350 die("possibly unsafe " _PATH_TMP " permissions"); 351 } 352 353 for (i = 0;; i++) { 354 snprintf(mntname, sizeof mntname, "syslinux.mnt.%lu.%d", 355 (unsigned long)mypid, i); 356 357 if (lstat(mntname, &dst) != -1 || errno != ENOENT) 358 continue; 359 360 rv = mkdir(mntname, 0000); 361 362 if (rv == -1) { 363 if (errno == EEXIST || errno == EINTR) 364 continue; 365 perror(program); 366 exit(1); 367 } 368 369 if (lstat(mntname, &dst) || dst.st_mode != (S_IFDIR | 0000) || 370 dst.st_uid != 0) { 371 die("someone is trying to symlink race us!"); 372 } 373 break; /* OK, got something... */ 374 } 375 376 mntpath = mntname; 377 } 378 379 if (fs_type == VFAT) { 380 if (do_mount(dev_fd, &mnt_cookie, mntpath, "vfat") && 381 do_mount(dev_fd, &mnt_cookie, mntpath, "msdos")) { 382 rmdir(mntpath); 383 die("failed on mounting fat volume"); 384 } 385 } else if (fs_type == NTFS) { 386 if (do_mount(dev_fd, &mnt_cookie, mntpath, "ntfs-3g")) { 387 rmdir(mntpath); 388 die("failed on mounting ntfs volume"); 389 } 390 } 391 392 ldlinux_path = alloca(strlen(mntpath) + strlen(subdir) + 1); 393 sprintf(ldlinux_path, "%s%s", mntpath, subdir); 394 395 ldlinux_name = alloca(strlen(ldlinux_path) + 14); 396 if (!ldlinux_name) { 397 perror(program); 398 err = 1; 399 goto umount; 400 } 401 sprintf(ldlinux_name, "%sldlinux.sys", ldlinux_path); 402 403 /* update ADV only ? */ 404 if (opt.update_only == -1) { 405 if (opt.reset_adv || opt.set_once) { 406 modify_existing_adv(ldlinux_path); 407 do_umount(mntpath, mnt_cookie); 408 sync(); 409 rmdir(mntpath); 410 exit(0); 411 } else if (opt.update_only && !syslinux_already_installed(dev_fd)) { 412 fprintf(stderr, "%s: no previous syslinux boot sector found\n", 413 argv[0]); 414 exit(1); 415 } else { 416 fprintf(stderr, "%s: please specify --install or --update for the future\n", argv[0]); 417 opt.update_only = 0; 418 } 419 } 420 421 /* Read a pre-existing ADV, if already installed */ 422 if (opt.reset_adv) 423 syslinux_reset_adv(syslinux_adv); 424 else if (read_adv(ldlinux_path, "ldlinux.sys") < 0) 425 syslinux_reset_adv(syslinux_adv); 426 if (modify_adv() < 0) 427 exit(1); 428 429 fd = do_open_file(ldlinux_name); 430 if (fd < 0) { 431 err = 1; 432 goto umount; 433 } 434 435 /* Write it the first time */ 436 if (xpwrite(fd, (const char _force *)boot_image, boot_image_len, 0) 437 != (int)boot_image_len || 438 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE, 439 boot_image_len) != 2 * ADV_SIZE) { 440 fprintf(stderr, "%s: write failure on %s\n", program, ldlinux_name); 441 exit(1); 442 } 443 444 fsync(fd); 445 /* 446 * Set the attributes 447 */ 448 { 449 uint32_t attr = 0x07; /* Hidden+System+Readonly */ 450 ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr); 451 } 452 453 /* 454 * Create a block map. 455 */ 456 ldlinux_sectors += 2; /* 2 ADV sectors */ 457 sectors = calloc(ldlinux_sectors, sizeof *sectors); 458 if (sectmap(fd, sectors, ldlinux_sectors)) { 459 perror("bmap"); 460 exit(1); 461 } 462 close(fd); 463 sync(); 464 465 sprintf(ldlinux_name, "%sldlinux.c32", ldlinux_path); 466 fd = do_open_file(ldlinux_name); 467 if (fd < 0) { 468 err = 1; 469 goto umount; 470 } 471 472 rv = xpwrite(fd, (const char _force *)syslinux_ldlinuxc32, 473 syslinux_ldlinuxc32_len, 0); 474 if (rv != (int)syslinux_ldlinuxc32_len) { 475 fprintf(stderr, "%s: write failure on %s\n", program, ldlinux_name); 476 exit(1); 477 } 478 479 fsync(fd); 480 /* 481 * Set the attributes 482 */ 483 { 484 uint32_t attr = 0x07; /* Hidden+System+Readonly */ 485 ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr); 486 } 487 488 close(fd); 489 sync(); 490 491 umount: 492 do_umount(mntpath, mnt_cookie); 493 sync(); 494 rmdir(mntpath); 495 496 if (err) 497 exit(err); 498 499 /* 500 * Patch ldlinux.sys and the boot sector 501 */ 502 i = syslinux_patch(sectors, ldlinux_sectors, opt.stupid_mode, 503 opt.raid_mode, subdir, NULL); 504 patch_sectors = (i + SECTOR_SIZE - 1) >> SECTOR_SHIFT; 505 506 /* 507 * Write the now-patched first sectors of ldlinux.sys 508 */ 509 for (i = 0; i < patch_sectors; i++) { 510 xpwrite(dev_fd, 511 (const char _force *)boot_image + i * SECTOR_SIZE, 512 SECTOR_SIZE, 513 opt.offset + ((off_t) sectors[i] << SECTOR_SHIFT)); 514 } 515 516 /* 517 * To finish up, write the boot sector 518 */ 519 520 /* Read the superblock again since it might have changed while mounted */ 521 xpread(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); 522 523 /* Copy the syslinux code into the boot sector */ 524 syslinux_make_bootsect(sectbuf, fs_type); 525 526 /* Write new boot sector */ 527 xpwrite(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); 528 529 close(dev_fd); 530 sync(); 531 532 /* Done! */ 533 534 return 0; 535 } 536