secure-mount.c (24088B)
1 // $Id$ --*- c++ -*-- 2 3 // Copyright (C) 2003,2015 Enrico Scholz <enrico.scholz@ensc.de> 4 // 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; version 2 of the License. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with this program; if not, write to the Free Software 16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 18 19 // secure-mount <general mount(8) options> [--chroot] 20 // [--mtab <mtabfile>] [--fstab <fstabfile>] 21 // 22 // Executes mount-operations under the current directory: it assumes sources 23 // in the current root-dir while destinations are expected in the chroot 24 // environment. 25 26 27 #ifdef HAVE_CONFIG_H 28 # include <config.h> 29 #endif 30 31 #include "util.h" 32 #include "pathconfig.h" 33 34 #include <lib/internal.h> 35 36 #include <getopt.h> 37 #include <fcntl.h> 38 #include <errno.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <strings.h> 43 #include <unistd.h> 44 #include <stdbool.h> 45 #include <sys/mount.h> 46 #include <sys/stat.h> 47 #include <sys/types.h> 48 #include <sys/file.h> 49 #ifdef HAVE_LINUX_TYPES_H 50 # include <linux/types.h> 51 #endif 52 #include <linux/fs.h> 53 #include <assert.h> 54 #include <ctype.h> 55 #include <sys/wait.h> 56 #include <libgen.h> 57 #include <signal.h> 58 #include <stdlib.h> 59 60 #define ENSC_WRAPPERS_FCNTL 1 61 #define ENSC_WRAPPERS_UNISTD 1 62 #include <wrappers.h> 63 64 #define MNTPOINT "/etc" 65 66 typedef enum { rfsYES, rfsNO, rfsONLY } RootFsOption; 67 68 struct MountInfo { 69 char * src; 70 char * dst; 71 char * name; 72 char * type; 73 unsigned long flag; 74 unsigned long xflag; 75 unsigned long mask; 76 char * data; 77 char * data_parsed; 78 }; 79 80 struct Options { 81 char const * mtab; 82 char const * fstab; 83 bool do_chroot; 84 bool ignore_mtab; 85 bool mount_all; 86 bool trigger_automount; 87 RootFsOption rootfs; 88 89 char cur_dir_path[PATH_MAX]; 90 int cur_dir_fd; 91 int cur_rootdir_fd; 92 }; 93 94 #define OPTION_BIND 1024 95 #define OPTION_MOVE 1025 96 #define OPTION_MTAB 1026 97 #define OPTION_FSTAB 1027 98 #define OPTION_CHROOT 1028 99 #define OPTION_SECURE 1029 100 #define OPTION_RBIND 1030 101 #define OPTION_ROOTFS 1031 102 #define OPTION_TRIGGER_AUTOMOUNT 1032 103 104 #define XFLAG_NOAUTO 0x01 105 #define XFLAG_FILE 0x02 106 #define XFLAG_NOMTAB 0x04 107 108 static struct option const 109 CMDLINE_OPTIONS[] = { 110 { "help", no_argument, 0, 'h' }, 111 { "version", no_argument, 0, 'v' }, 112 { "bind", no_argument, 0, OPTION_BIND }, 113 { "move", no_argument, 0, OPTION_MOVE }, 114 { "mtab", required_argument, 0, OPTION_MTAB }, 115 { "fstab", required_argument, 0, OPTION_FSTAB }, 116 { "rootfs", required_argument, 0, OPTION_ROOTFS }, 117 { "chroot", no_argument, 0, OPTION_CHROOT }, 118 { "secure", no_argument, 0, OPTION_SECURE }, 119 { "rbind", no_argument, 0, OPTION_RBIND }, 120 { "trigger-automount", no_argument, 0, OPTION_TRIGGER_AUTOMOUNT }, 121 { 0, 0, 0, 0 } 122 }; 123 124 #ifndef MS_REC 125 #define MS_REC 0x4000 126 #endif 127 #ifndef MS_UNBINDABLE 128 #define MS_UNBINDABLE (1<<17) 129 #endif 130 #ifndef MS_PRIVATE 131 #define MS_PRIVATE (1<<18) 132 #endif 133 #ifndef MS_SLAVE 134 #define MS_SLAVE (1<<19) 135 #endif 136 #ifndef MS_SHARED 137 #define MS_SHARED (1<<20) 138 #endif 139 140 static struct FstabOption { 141 char const * const opt; 142 unsigned long const flag; 143 unsigned long const mask; 144 unsigned long const xflag; 145 bool const is_dflt; 146 } const FSTAB_OPTIONS[] = { 147 { "defaults", MS_NODEV, (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC| 148 MS_SYNCHRONOUS), 0, false }, 149 { "rbind", MS_BIND|MS_REC, MS_BIND|MS_REC, 0, false }, 150 { "bind", MS_BIND, MS_BIND, 0, false }, 151 { "move", MS_MOVE, MS_MOVE, 0, false }, 152 { "async", 0, MS_SYNCHRONOUS, 0, false }, 153 { "sync", MS_SYNCHRONOUS, MS_SYNCHRONOUS, 0, false }, 154 { "atime", 0, MS_NOATIME, 0, false }, 155 { "noatime", MS_NOATIME, MS_NOATIME, 0, false }, 156 { "dev", 0, MS_NODEV, 0, false }, 157 { "nodev", MS_NODEV, MS_NODEV, 0, false }, 158 { "exec", 0, MS_NOEXEC, 0, false }, 159 { "noexec", MS_NOEXEC, MS_NOEXEC, 0, false }, 160 { "suid", 0, MS_NOSUID, 0, false }, 161 { "nosuid", MS_NOSUID, MS_NOSUID, 0, false }, 162 { "ro", MS_RDONLY, MS_RDONLY, 0, false }, 163 { "rw", 0, MS_RDONLY, 0, false }, 164 165 { "remount", MS_REMOUNT, MS_REMOUNT, 0, false }, 166 { "users", MS_NOEXEC|MS_NOSUID|MS_NODEV, 167 MS_NOEXEC|MS_NOSUID|MS_NODEV, 0, false }, 168 { "mandlock", MS_MANDLOCK, MS_MANDLOCK, 0, false }, 169 { "nodiratime", MS_NODIRATIME, MS_NODIRATIME, 0, false }, 170 #ifdef MS_DIRSYNC 171 { "dirsync", MS_DIRSYNC, MS_DIRSYNC, 0, false }, 172 #endif 173 { "_netdev", 0, 0, 0, false }, 174 { "auto", 0, 0, 0, false }, 175 { "noauto", 0, 0, XFLAG_NOAUTO, false }, 176 { "user", 0, 0, 0, false }, 177 { "nouser", 0, 0, 0, false }, 178 { "rec", MS_REC, MS_REC, 0, false }, 179 { "unbindable", MS_UNBINDABLE, MS_UNBINDABLE, 0, false }, 180 { "private", MS_PRIVATE, MS_PRIVATE, 0, false }, 181 { "slave", MS_SLAVE, MS_SLAVE, 0, false }, 182 { "shared", MS_SHARED, MS_SHARED, 0, false }, 183 { "nomtab", 0, 0, XFLAG_NOMTAB, false }, 184 }; 185 186 int wrapper_exit_code = 1; 187 188 static void 189 showHelp(int fd, char const *cmd, int res) 190 { 191 VSERVER_DECLARE_CMD(cmd); 192 193 WRITE_MSG(fd, "Usage: "); 194 WRITE_STR(fd, cmd); 195 WRITE_MSG(fd, 196 " [--help] [--version] [--bind] [--move] [--rbind] [-t <type>] [--chroot]\n" 197 " [--mtab <filename>] [--fstab <filename>] [--rootfs yes|no|only]\n" 198 " [-n] -a|([-o <options>] [--] <src> <dst>)\n\n" 199 "Executes mount-operations under the current directory: it assumes sources in\n" 200 "the current root-dir while destinations are expected in the chroot environment.\n\n" 201 "For non-trivial mount-operations it uses the external 'mount' program which\n" 202 "can be overridden by the $MOUNT environment variable.\n\n" 203 "Options:\n" 204 " --bind|move|rbind ... set the correspond flags; with this options\n" 205 " the mount will be executed internally without\n" 206 " calling an external mount program.\n" 207 " -t <type> ... assume the given filesystem type\n" 208 " -o <options> ... set additional options; see mount(2) for details\n" 209 " -n ... do not update the mtab-file\n" 210 " --mtab <filename> ... use <filename> as an alternative mtab file\n" 211 " [default: /etc/mtab]\n" 212 " --chroot ... chroot into the current directory before\n" 213 " mounting the filesystem\n" 214 " --fstab <filename> ... use <filename> as an alternative fstab file;\n" 215 " this option has an effect only with the '-a'\n" 216 " option [default: /etc/fstab]\n" 217 " --rootfs yes|no|only ... specifies how to handle an entry for a rootfs\n" 218 " ('/') when processing an fstab file. 'yes' will\n" 219 " mount it among the other entries, 'only' will\n" 220 " mount only the rootfs entry, and 'no' will ignore\n" 221 " it and mount only the other entries [default: yes]\n" 222 " --trigger-automount ... trigger automounting of <src> paths but do not\n" 223 " mount <dst> nor touch mtab\n" 224 " -a ... mount everything listed in the fstab-file\n\n" 225 " <src> ... the source-filesystem; this path is absolute\n" 226 " to the current root-filesystem. Only valid\n" 227 " without the '-a' option.\n" 228 " <dst> ... the destination mount-point; when used with\n" 229 " '--chroot', this path is relative to the current\n" 230 " directory. Only valid without the '-a' option\n\n" 231 "Please report bugs to " PACKAGE_BUGREPORT "\n"); 232 233 exit(res); 234 } 235 236 static void 237 showVersion() 238 { 239 WRITE_MSG(1, 240 "secure-mount " VERSION " -- secure mounting of directories\n" 241 "This program is part of " PACKAGE_STRING "\n\n" 242 "Copyright (C) 2003 Enrico Scholz\n" 243 VERSION_COPYRIGHT_DISCLAIMER); 244 exit(0); 245 } 246 247 inline static bool 248 isSameObject(struct stat const *lhs, 249 struct stat const *rhs) 250 { 251 return (lhs->st_dev==rhs->st_dev && 252 lhs->st_ino==rhs->st_ino); 253 } 254 255 static int 256 fchroot(int fd) 257 { 258 if (fchdir(fd)==-1 || chroot(".")==-1) return -1; 259 return 0; 260 } 261 262 static int 263 writeX(int fd, void const *buf, size_t len) 264 { 265 if ((size_t)(write(fd, buf, len))!=len) return -1; 266 return 0; 267 } 268 269 static int 270 writeStrX(int fd, char const *str) 271 { 272 return writeX(fd, str, strlen(str)); 273 } 274 275 static inline char const * 276 getType(struct MountInfo const *mnt) 277 { 278 if (mnt->type==0) return "none"; 279 else if (strncmp(mnt->type, "ext", 3)==0) return "ufs"; 280 else return mnt->type; 281 } 282 283 inline static void 284 restoreRoot(struct Options const *opt) 285 { 286 if (opt->do_chroot!=0 && fchroot(opt->cur_rootdir_fd)==-1) { 287 perror("secure-mount: fchdir(\"/\")"); 288 WRITE_MSG(2, "Failed to restore root-directory; aborting\n"); 289 exit(1); 290 } 291 } 292 293 static int 294 updateMtab(struct MountInfo const *mnt, struct Options const *opt) 295 { 296 int res = -1; 297 int fd; 298 assert(opt->mtab!=0); 299 300 if (opt->do_chroot && fchroot(opt->cur_dir_fd)==-1) { 301 perror("secure-mount: fchroot(\".\")"); 302 return -1; 303 } 304 305 fd=open(opt->mtab, O_CREAT|O_APPEND|O_WRONLY, 0644); 306 307 if (fd==-1) { 308 perror("secure-mount: open(<mtab>)"); 309 goto err0; 310 } 311 312 if (lockf(fd, F_LOCK, 0)==-1) { 313 perror("secure-mount: lockf()"); 314 goto err1; 315 } 316 317 if (writeStrX(fd, mnt->src)==-1 || 318 writeStrX(fd, " ")==-1 || 319 writeStrX(fd, mnt->dst)==-1 || 320 writeStrX(fd, mnt->xflag & XFLAG_FILE ? "/" : "")==-1 || 321 writeStrX(fd, mnt->xflag & XFLAG_FILE ? mnt->name : "")==-1 || 322 writeStrX(fd, " ")==-1 || 323 writeStrX(fd, getType(mnt))==-1 || 324 writeStrX(fd, " ")==-1 || 325 writeStrX(fd, mnt->data ? mnt->data : "defaults")==-1 || 326 writeStrX(fd, " 0 0\n")==-1) { 327 perror("secure-mount: write()"); 328 goto err1; 329 } 330 331 res = 0; 332 333 err1: close(fd); 334 err0: 335 restoreRoot(opt); 336 return res; 337 } 338 339 static bool 340 callExternalMount(struct MountInfo const *mnt) 341 { 342 char const * argv[10]; 343 size_t idx = 0; 344 pid_t pid; 345 int status; 346 char const * mount_prog = getenv("MOUNT"); 347 348 if (mount_prog==0) mount_prog = MOUNT_PROG; 349 350 argv[idx++] = mount_prog; 351 argv[idx++] = "-n"; 352 if (mnt->flag & MS_BIND) argv[idx++] = "--bind"; 353 else if (mnt->flag & MS_MOVE) argv[idx++] = "--move"; 354 355 argv[idx++] = "-o"; 356 if (mnt->data && *mnt->data && 357 strcmp(mnt->data, "defaults")!=0) { 358 if (mnt->mask & MS_NODEV) 359 argv[idx++] = mnt->data; 360 else { 361 char * tmp = alloca(strlen(mnt->data) + sizeof("nodev,")); 362 strcpy(tmp, "nodev,"); 363 strcat(tmp, mnt->data); 364 argv[idx++] = tmp; 365 } 366 } 367 else 368 argv[idx++] = "nodev"; 369 370 if (mnt->type) { 371 argv[idx++] = "-t"; 372 argv[idx++] = mnt->type; 373 } 374 375 argv[idx++] = mnt->src; 376 argv[idx++] = mnt->name; 377 argv[idx] = 0; 378 379 pid = fork(); 380 if (pid==-1) { 381 perror("secure-mount: fork()"); 382 return false; 383 } 384 385 if (pid==0) { 386 execv(mount_prog, const_cast(char **)(argv)); 387 PERROR_Q("secure-mount: execv", mount_prog); 388 exit(1); 389 } 390 391 if (wait4(pid, &status, 0, 0)==-1) { 392 perror("secure-mount: wait4()"); 393 return false; 394 } 395 396 return (WIFEXITED(status)) && (WEXITSTATUS(status)==0); 397 } 398 399 inline static bool 400 secureChdir(char const *dir, struct Options const *opt) 401 { 402 int dir_fd; 403 bool res = false; 404 405 if (opt->do_chroot!=0 && fchroot(opt->cur_dir_fd)==-1) { 406 perror("secure-mount: fchroot(\".\")"); 407 return false; 408 } 409 410 if (chdir(dir)==-1) { 411 PERROR_Q("secure-mount: chdir", dir); 412 goto err; 413 } 414 415 dir_fd = open(".", O_RDONLY|O_DIRECTORY); 416 if (dir_fd==-1) { 417 perror("secure-mount: open(\".\")"); 418 goto err; 419 } 420 421 restoreRoot(opt); 422 if (fchdir(dir_fd)==-1) 423 PERROR_Q("secure-mount: fchdir", dir); 424 else 425 res = true; 426 427 close(dir_fd); 428 return res; 429 430 err: 431 restoreRoot(opt); 432 return false; 433 } 434 435 static bool 436 canHandleInternal(struct MountInfo const *mnt) 437 { 438 static char const * FS[] = { 439 "tmpfs", "sysfs", "proc", "sockfs", "pipefs", "futexfs", 440 "inotifyfs", "devpts", "ext3", "ext2", "ramfs", 441 "hugetlbfs", "usbfs", "binfmt_misc", 442 0 443 }; 444 char const ** i; 445 446 if (!mnt) return false; 447 else if ((mnt->flag & (MS_BIND|MS_MOVE))) return true; 448 else if ((mnt->flag & (MS_SHARED|MS_SLAVE|MS_PRIVATE| 449 MS_UNBINDABLE))) return true; 450 else if (mnt->type==0) return false; 451 452 for (i=FS+0; *i!=0; ++i) 453 if (strcmp(mnt->type, *i)==0) return true; 454 455 return false; 456 } 457 458 static bool 459 triggerAutomount(struct MountInfo const *mnt) 460 { 461 char src[strlen(mnt->src) + sizeof "/."]; 462 struct stat st; 463 bool rc; 464 465 if (mnt->src[0] != '/' || !canHandleInternal(mnt)) 466 /* we handle only absolute source paths with local protocols */ 467 return true; 468 469 strcpy(src, mnt->src); 470 strcat(src, "/."); 471 472 /* NOTE: using 'stat()' instead of 'lstat()' in this function is expected */ 473 474 /* try to stat the directory first and trigger automount by accessing '.' */ 475 if (stat(src, &st) == 0) { 476 rc = true; 477 } else if (stat(mnt->src, &st) == 0) { 478 /* when this failed (because src is a file), access it directly */ 479 rc = !S_ISDIR(st.st_mode); 480 if (!(mnt->xflag & XFLAG_FILE)) 481 WRITE_MSG(2, "unexpected src type\n"); 482 } else { 483 perror("stat()"); 484 rc = false; 485 } 486 487 return rc; 488 } 489 490 static bool 491 mountSingle(struct MountInfo const *mnt, struct Options *opt) 492 { 493 assert(mnt->dst!=0); 494 495 if (!secureChdir(mnt->dst, opt)) 496 return false; 497 498 if (canHandleInternal(mnt)) { 499 if (mount(mnt->src, mnt->name, 500 mnt->type ? mnt->type : "", 501 mnt->flag, mnt->data_parsed)==-1) { 502 perror("secure-mount: mount()"); 503 return false; 504 } 505 if (strcmp(mnt->dst, "/") == 0) { 506 /* XXX: Hack */ 507 if (chdir(opt->cur_dir_path) == -1) { 508 perror("secure-mount: chdir(.)"); 509 return false; 510 } 511 close(opt->cur_dir_fd); 512 opt->cur_dir_fd = Eopen(".", O_RDONLY|O_DIRECTORY, 0); 513 Efcntl(opt->cur_dir_fd, F_SETFD, FD_CLOEXEC); 514 } 515 if (!vc_isSupported(vcFEATURE_BME) && 516 (mnt->flag & MS_BIND) && 517 (mnt->mask & ~(MS_BIND|MS_REC))) { 518 /* This is needed to put us in the new mountpoint */ 519 if (!secureChdir(mnt->dst, opt)) 520 return false; 521 if (mount(mnt->src, mnt->name, 522 mnt->type ? mnt->type : "", 523 (mnt->flag | MS_REMOUNT), NULL) == -1 && 524 errno != EBUSY) { /* Returned on older kernels */ 525 perror("secure-mount: mount()"); 526 return false; 527 } 528 } 529 } 530 else if (!callExternalMount(mnt)) 531 return false; 532 533 if (!opt->ignore_mtab && (mnt->xflag & XFLAG_NOMTAB)==0 && 534 updateMtab(mnt, opt)==-1) { 535 WRITE_MSG(2, "Failed to update mtab-file\n"); 536 // no error 537 } 538 539 return true; 540 } 541 542 static struct FstabOption const * 543 searchOption(char const *opt, size_t len) 544 { 545 struct FstabOption const * i; 546 for (i=FSTAB_OPTIONS+0; i<FSTAB_OPTIONS+DIM_OF(FSTAB_OPTIONS); ++i) 547 if (strncmp(i->opt, opt, len)==0) return i; 548 549 return 0; 550 } 551 552 static bool 553 transformOptionList(struct MountInfo *info, size_t UNUSED *col) 554 { 555 char const * ptr = info->data; 556 char * data = malloc(strlen(info->data)); 557 char * dst = data; 558 559 do { 560 char const * pos = strchr(ptr, ','); 561 struct FstabOption const * opt; 562 563 if (pos==0) pos = ptr+strlen(ptr); 564 opt = searchOption(ptr, pos-ptr); 565 566 if (opt!=0) { 567 info->flag &= ~opt->mask; 568 info->flag |= opt->flag; 569 info->mask |= opt->mask; 570 info->xflag |= opt->xflag; 571 } 572 else { 573 if (dst != data) 574 *(dst++) = ','; 575 strncpy(dst, ptr, pos-ptr); 576 dst += pos - ptr; 577 *dst = '\0'; 578 } 579 580 if (*pos!='\0') 581 ptr = pos+1; 582 else 583 ptr = pos; 584 585 } while (*ptr!='\0'); 586 587 info->data_parsed = data; 588 return true; 589 } 590 591 #define MOVE_TO_NEXT_FIELD(PTR,ALLOW_EOL) \ 592 while (!isspace(*PTR) && *PTR!='\0') ++PTR; \ 593 if (col) *col = buf-start_buf+1; \ 594 if (!(ALLOW_EOL) && *PTR=='\0') return prFAIL; \ 595 *PTR++ = '\0'; \ 596 while (isspace(*PTR)) ++PTR 597 598 static enum {prDOIT, prFAIL, prIGNORE} 599 parseFstabLine(struct MountInfo *info, char *buf, size_t *col, 600 bool honor_noauto) 601 { 602 char const * const start_buf = buf; 603 size_t err_col; 604 605 while (isspace(*buf)) ++buf; 606 if (*buf=='#' || *buf=='\0') return prIGNORE; 607 608 info->src = buf; 609 MOVE_TO_NEXT_FIELD(buf, false); 610 info->dst = buf; 611 MOVE_TO_NEXT_FIELD(buf, false); 612 info->type = buf; 613 MOVE_TO_NEXT_FIELD(buf, false); 614 err_col = buf-start_buf+1; 615 info->data = buf; 616 MOVE_TO_NEXT_FIELD(buf, true); 617 618 info->flag = MS_NODEV; 619 info->mask = 0; 620 info->xflag = 0; 621 info->name = "."; 622 623 if (strcmp(info->type, "swap") ==0) return prIGNORE; 624 else if (strcmp(info->type, "none") ==0) info->type = 0; 625 else if (strcmp(info->type, "devpts")==0) { 626 info->mask |= MS_NODEV; 627 info->flag &= ~MS_NODEV; 628 } 629 630 if (col) *col = err_col; 631 if (!transformOptionList(info,col)) return prFAIL; 632 if (honor_noauto && (info->xflag & XFLAG_NOAUTO)) return prIGNORE; 633 634 return prDOIT; 635 } 636 637 #undef MOVE_TO_NEXT_FIELD 638 639 static void 640 showFstabPosition(int fd, char const *fname, size_t line_nr, size_t col_nr) 641 { 642 char buf[3*sizeof(line_nr)*2 + 4]; 643 size_t len = utilvserver_fmt_uint(buf+1, line_nr)+1; 644 645 buf[0] = ':'; 646 buf[len++] = ':'; 647 len += utilvserver_fmt_uint(buf+len, col_nr); 648 WRITE_STR(fd, fname); 649 Vwrite(fd, buf, len); 650 } 651 652 static void 653 adjustFileMount(struct MountInfo *mnt) 654 { 655 char *buf = strrchr(mnt->dst, '/'); 656 657 if (buf) 658 *buf++ = 0; 659 660 mnt->name = buf; 661 mnt->xflag |= XFLAG_FILE; 662 } 663 664 static bool 665 mountFstab(struct Options *opt) 666 { 667 bool res = false; 668 int fd; 669 off_t len; 670 671 assert(opt->fstab!=0); 672 fd = open(opt->fstab, O_RDONLY); 673 if (fd==-1) { 674 perror("secure-mount: open(<fstab>)"); 675 goto err0; 676 } 677 678 len = lseek(fd, 0, SEEK_END); 679 if (len==-1 || 680 lseek(fd, 0, SEEK_SET)==-1) { 681 perror("secure-mount: lseek(<fstab>)"); 682 goto err1; 683 } 684 685 { 686 char buf[len+2]; 687 char *ptr, *ptrptr; 688 size_t line_nr=0, col_nr; 689 690 if (read(fd, buf, len+1)!=len) { 691 perror("secure-mount: read()"); 692 goto err1; 693 } 694 buf[len] = '#'; // workaround for broken dietlibc strtok_r() 695 // implementation 696 buf[len+1] = '\0'; 697 ptrptr = buf; 698 699 while ((ptr=strsep(&ptrptr, "\n")) != 0) { 700 struct MountInfo mnt; 701 ++line_nr; 702 703 switch (parseFstabLine(&mnt, ptr, &col_nr, !opt->trigger_automount)) { 704 case prFAIL : 705 showFstabPosition(2, opt->fstab, line_nr, col_nr); 706 WRITE_MSG(2, ": syntax error\n"); 707 goto err1; 708 709 case prIGNORE : break; 710 case prDOIT : { 711 bool is_rootfs = (strcmp(mnt.dst, "/")==0); 712 Echdir("/"); 713 if (( is_rootfs && opt->rootfs==rfsNO) || 714 (!is_rootfs && opt->rootfs==rfsONLY)) { /* ignore the entry */ } 715 else { 716 if (utilvserver_isFile(mnt.src, 0)) 717 adjustFileMount(&mnt); 718 719 if (opt->trigger_automount) { 720 if (!triggerAutomount(&mnt)) { 721 showFstabPosition(2, opt->fstab, line_nr, 1); 722 WRITE_MSG(2, ": failed to stat src\n"); 723 } 724 } else if (!mountSingle(&mnt, opt)) { 725 showFstabPosition(2, opt->fstab, line_nr, 1); 726 WRITE_MSG(2, ": failed to mount fstab-entry\n"); 727 } 728 } 729 break; 730 } 731 default : 732 assert(false); 733 } 734 } 735 } 736 737 res = true; 738 739 err1: close(fd); 740 err0: return res; 741 } 742 743 static void 744 initFDs(struct Options *opt) 745 { 746 opt->cur_dir_fd = Eopen(".", O_RDONLY|O_DIRECTORY, 0); 747 opt->cur_rootdir_fd = Eopen("/", O_RDONLY|O_DIRECTORY, 0); 748 749 Efcntl(opt->cur_dir_fd, F_SETFD, FD_CLOEXEC); 750 Efcntl(opt->cur_rootdir_fd, F_SETFD, FD_CLOEXEC); 751 752 if (getcwd(opt->cur_dir_path, sizeof(opt->cur_dir_path)) == NULL) { 753 perror("secure-mount: getcwd()"); 754 exit(wrapper_exit_code); 755 } 756 } 757 758 static RootFsOption 759 parseRootFS(char const *str) 760 { 761 if (strcasecmp(str, "yes")==0) return rfsYES; 762 else if (strcasecmp(str, "no")==0) return rfsNO; 763 else if (strcasecmp(str, "only")==0) return rfsONLY; 764 else { 765 WRITE_MSG(2, "secure-mount: invalid option for '--rootfs': '"); 766 WRITE_STR(2, str); 767 WRITE_MSG(2, "'\n"); 768 exit(wrapper_exit_code); 769 } 770 } 771 772 int main(int argc, char *argv[]) 773 { 774 struct MountInfo mnt = { 775 .src = 0, 776 .dst = 0, 777 .name = ".", 778 .type = 0, 779 .flag = MS_NODEV, 780 .xflag = 0, 781 .data = 0, 782 }; 783 784 struct Options opt = { 785 .mtab = "/etc/mtab", 786 .fstab = "/etc/fstab", 787 .do_chroot = 0, 788 .ignore_mtab = false, 789 .mount_all = false, 790 .cur_dir_fd = -1, 791 .cur_rootdir_fd = -1, 792 .rootfs = rfsYES 793 }; 794 795 while (1) { 796 int c = getopt_long(argc, argv, "ht:nao:", CMDLINE_OPTIONS, 0); 797 if (c==-1) break; 798 799 switch (c) { 800 case 'h' : showHelp(1, argv[0], 0); 801 case 'v' : showVersion(); 802 case 't' : mnt.type = optarg; break; 803 case 'n' : opt.ignore_mtab = true; break; 804 case 'a' : opt.mount_all = true; break; 805 case 'o' : mnt.data = optarg; break; 806 case OPTION_RBIND : mnt.flag |= MS_REC; /*@fallthrough@*/ 807 case OPTION_BIND : mnt.flag |= MS_BIND; break; 808 case OPTION_MOVE : mnt.flag |= MS_MOVE; break; 809 case OPTION_MTAB : opt.mtab = optarg; break; 810 case OPTION_FSTAB : opt.fstab = optarg; break; 811 case OPTION_CHROOT: opt.do_chroot = true; break; 812 case OPTION_ROOTFS: opt.rootfs = parseRootFS(optarg); break; 813 case OPTION_TRIGGER_AUTOMOUNT: opt.trigger_automount = true; break; 814 case OPTION_SECURE: 815 WRITE_MSG(2, "secure-mount: The '--secure' option is deprecated...\n"); 816 break; 817 default : 818 WRITE_MSG(2, "Try '"); 819 WRITE_STR(2, argv[0]); 820 WRITE_MSG(2, " --help' for more information.\n"); 821 return EXIT_FAILURE; 822 break; 823 } 824 } 825 826 827 if (opt.mount_all && optind<argc) { 828 WRITE_MSG(2, "Can not specify <src> and '-a' at the same time\n"); 829 return EXIT_FAILURE; 830 } 831 832 initFDs(&opt); 833 signal(SIGCHLD, SIG_DFL); 834 835 if (opt.mount_all) { 836 if (!mountFstab(&opt)) return EXIT_FAILURE; 837 else return EXIT_SUCCESS; 838 } 839 840 if (optind+2!=argc) { 841 WRITE_MSG(2, "Invalid <src> <dst> pair specified\n"); 842 return EXIT_FAILURE; 843 } 844 845 if (mnt.data) { 846 mnt.data = strdup(mnt.data); 847 if (!transformOptionList(&mnt, 0)) { 848 WRITE_MSG(2, "Invalid options specified\n"); 849 return EXIT_FAILURE; 850 } 851 } 852 853 mnt.src = argv[optind++]; 854 mnt.dst = argv[optind++]; 855 856 if (utilvserver_isFile(mnt.src, 0)) 857 adjustFileMount(&mnt); 858 859 if (( opt.trigger_automount && !triggerAutomount(&mnt)) || 860 (!opt.trigger_automount && !mountSingle(&mnt, &opt))) 861 return EXIT_FAILURE; 862 863 return EXIT_SUCCESS; 864 }