vshost-util-vserver

Build script and sources for util-vserver.
git clone https://ccx.te2000.cz/git/vshost-util-vserver
Log | Files | Refs

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 }