busybox/util-linux/mount.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini mount implementation for busybox
   4 *
   5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
   6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
   7 * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net>
   8 *
   9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  10 */
  11// Design notes: There is no spec for mount.  Remind me to write one.
  12//
  13// mount_main() calls singlemount() which calls mount_it_now().
  14//
  15// mount_main() can loop through /etc/fstab for mount -a
  16// singlemount() can loop through /etc/filesystems for fstype detection.
  17// mount_it_now() does the actual mount.
  18//
  19
  20//config:config MOUNT
  21//config:       bool "mount (23 kb)"
  22//config:       default y
  23//config:       help
  24//config:       All files and filesystems in Unix are arranged into one big directory
  25//config:       tree. The 'mount' utility is used to graft a filesystem onto a
  26//config:       particular part of the tree. A filesystem can either live on a block
  27//config:       device, or it can be accessible over the network, as is the case with
  28//config:       NFS filesystems.
  29//config:
  30//config:config FEATURE_MOUNT_FAKE
  31//config:       bool "Support -f (fake mount)"
  32//config:       default y
  33//config:       depends on MOUNT
  34//config:       help
  35//config:       Enable support for faking a file system mount.
  36//config:
  37//config:config FEATURE_MOUNT_VERBOSE
  38//config:       bool "Support -v (verbose)"
  39//config:       default y
  40//config:       depends on MOUNT
  41//config:       help
  42//config:       Enable multi-level -v[vv...] verbose messages. Useful if you
  43//config:       debug mount problems and want to see what is exactly passed
  44//config:       to the kernel.
  45//config:
  46//config:config FEATURE_MOUNT_HELPERS
  47//config:       bool "Support mount helpers"
  48//config:       default n
  49//config:       depends on MOUNT
  50//config:       help
  51//config:       Enable mounting of virtual file systems via external helpers.
  52//config:       E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
  53//config:       "obexfs -b00.11.22.33.44.55 /mnt"
  54//config:       Also "mount -t sometype [-o opts] fs /mnt" will try
  55//config:       "sometype [-o opts] fs /mnt" if simple mount syscall fails.
  56//config:       The idea is to use such virtual filesystems in /etc/fstab.
  57//config:
  58//config:config FEATURE_MOUNT_LABEL
  59//config:       bool "Support specifying devices by label or UUID"
  60//config:       default y
  61//config:       depends on MOUNT
  62//config:       select VOLUMEID
  63//config:       help
  64//config:       This allows for specifying a device by label or uuid, rather than by
  65//config:       name. This feature utilizes the same functionality as blkid/findfs.
  66//config:
  67//config:config FEATURE_MOUNT_NFS
  68//config:       bool "Support mounting NFS file systems on Linux < 2.6.23"
  69//config:       default n
  70//config:       depends on MOUNT
  71//config:       select FEATURE_SYSLOG
  72//config:       help
  73//config:       Enable mounting of NFS file systems on Linux kernels prior
  74//config:       to version 2.6.23. Note that in this case mounting of NFS
  75//config:       over IPv6 will not be possible.
  76//config:
  77//config:       Note that this option links in RPC support from libc,
  78//config:       which is rather large (~10 kbytes on uclibc).
  79//config:
  80//config:config FEATURE_MOUNT_CIFS
  81//config:       bool "Support mounting CIFS/SMB file systems"
  82//config:       default y
  83//config:       depends on MOUNT
  84//config:       help
  85//config:       Enable support for samba mounts.
  86//config:
  87//config:config FEATURE_MOUNT_FLAGS
  88//config:       depends on MOUNT
  89//config:       bool "Support lots of -o flags"
  90//config:       default y
  91//config:       help
  92//config:       Without this, mount only supports ro/rw/remount. With this, it
  93//config:       supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
  94//config:       noatime, diratime, nodiratime, loud, bind, move, shared, slave,
  95//config:       private, unbindable, rshared, rslave, rprivate, and runbindable.
  96//config:
  97//config:config FEATURE_MOUNT_FSTAB
  98//config:       depends on MOUNT
  99//config:       bool "Support /etc/fstab and -a (mount all)"
 100//config:       default y
 101//config:       help
 102//config:       Support mount all and looking for files in /etc/fstab.
 103//config:
 104//config:config FEATURE_MOUNT_OTHERTAB
 105//config:       depends on FEATURE_MOUNT_FSTAB
 106//config:       bool "Support -T <alt_fstab>"
 107//config:       default y
 108//config:       help
 109//config:       Support mount -T (specifying an alternate fstab)
 110
 111/* On full-blown systems, requires suid for user mounts.
 112 * But it's not unthinkable to have it available in non-suid flavor on some systems,
 113 * for viewing mount table.
 114 * Therefore we use BB_SUID_MAYBE instead of BB_SUID_REQUIRE: */
 115//applet:IF_MOUNT(APPLET(mount, BB_DIR_BIN, IF_DESKTOP(BB_SUID_MAYBE) IF_NOT_DESKTOP(BB_SUID_DROP)))
 116
 117//kbuild:lib-$(CONFIG_MOUNT) += mount.o
 118
 119//usage:#define mount_trivial_usage
 120//usage:       "[OPTIONS] [-o OPT] DEVICE NODE"
 121//usage:#define mount_full_usage "\n\n"
 122//usage:       "Mount a filesystem. Filesystem autodetection requires /proc.\n"
 123//usage:     "\n        -a              Mount all filesystems in fstab"
 124//usage:        IF_FEATURE_MOUNT_FAKE(
 125//usage:        IF_FEATURE_MTAB_SUPPORT(
 126//usage:     "\n        -f              Update /etc/mtab, but don't mount"
 127//usage:        )
 128//usage:        IF_NOT_FEATURE_MTAB_SUPPORT(
 129//usage:     "\n        -f              Dry run"
 130//usage:        )
 131//usage:        )
 132//usage:        IF_FEATURE_MOUNT_HELPERS(
 133//usage:     "\n        -i              Don't run mount helper"
 134//usage:        )
 135//usage:        IF_FEATURE_MTAB_SUPPORT(
 136//usage:     "\n        -n              Don't update /etc/mtab"
 137//usage:        )
 138//usage:        IF_FEATURE_MOUNT_VERBOSE(
 139//usage:     "\n        -v              Verbose"
 140//usage:        )
 141////usage:   "\n        -s              Sloppy (ignored)"
 142//usage:     "\n        -r              Read-only mount"
 143////usage:     "\n      -w              Read-write mount (default)"
 144//usage:     "\n        -t FSTYPE[,...] Filesystem type(s)"
 145//usage:        IF_FEATURE_MOUNT_OTHERTAB(
 146//usage:     "\n        -T FILE         Read FILE instead of /etc/fstab"
 147//usage:        )
 148//usage:     "\n        -O OPT          Mount only filesystems with option OPT (-a only)"
 149//usage:     "\n-o OPT:"
 150//usage:        IF_FEATURE_MOUNT_LOOP(
 151//usage:     "\n        loop            Ignored (loop devices are autodetected)"
 152//usage:        )
 153//usage:        IF_FEATURE_MOUNT_FLAGS(
 154//usage:     "\n        [a]sync         Writes are [a]synchronous"
 155//usage:     "\n        [no]atime       Disable/enable updates to inode access times"
 156//usage:     "\n        [no]diratime    Disable/enable atime updates to directories"
 157//usage:     "\n        [no]relatime    Disable/enable atime updates relative to modification time"
 158//usage:     "\n        [no]dev         (Dis)allow use of special device files"
 159//usage:     "\n        [no]exec        (Dis)allow use of executable files"
 160//usage:     "\n        [no]suid        (Dis)allow set-user-id-root programs"
 161//usage:     "\n        [r]shared       Convert [recursively] to a shared subtree"
 162//usage:     "\n        [r]slave        Convert [recursively] to a slave subtree"
 163//usage:     "\n        [r]private      Convert [recursively] to a private subtree"
 164//usage:     "\n        [un]bindable    Make mount point [un]able to be bind mounted"
 165//usage:     "\n        [r]bind         Bind a file or directory [recursively] to another location"
 166//usage:     "\n        move            Relocate an existing mount point"
 167//usage:        )
 168//usage:     "\n        remount         Remount a mounted filesystem, changing flags"
 169//usage:     "\n        ro              Same as -r"
 170//usage:     "\n"
 171//usage:     "\nThere are filesystem-specific -o flags."
 172//usage:
 173//usage:#define mount_example_usage
 174//usage:       "$ mount\n"
 175//usage:       "/dev/hda3 on / type minix (rw)\n"
 176//usage:       "proc on /proc type proc (rw)\n"
 177//usage:       "devpts on /dev/pts type devpts (rw)\n"
 178//usage:       "$ mount /dev/fd0 /mnt -t msdos -o ro\n"
 179//usage:       "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
 180//usage:       "$ mount cd_image.iso mydir\n"
 181//usage:#define mount_notes_usage
 182//usage:       "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
 183
 184#include <mntent.h>
 185#if ENABLE_FEATURE_SYSLOG
 186#include <syslog.h>
 187#endif
 188#include <sys/mount.h>
 189// Grab more as needed from util-linux's mount/mount_constants.h
 190#ifndef MS_DIRSYNC
 191# define MS_DIRSYNC     (1 << 7) // Directory modifications are synchronous
 192#endif
 193#ifndef MS_NOSYMFOLLOW
 194# define MS_NOSYMFOLLOW (1 << 8)
 195#endif
 196#ifndef MS_BIND
 197# define MS_BIND        (1 << 12)
 198#endif
 199#ifndef MS_MOVE
 200# define MS_MOVE        (1 << 13)
 201#endif
 202#ifndef MS_RECURSIVE
 203# define MS_RECURSIVE   (1 << 14)
 204#endif
 205#ifndef MS_SILENT
 206# define MS_SILENT      (1 << 15)
 207#endif
 208// The shared subtree stuff, which went in around 2.6.15
 209#ifndef MS_UNBINDABLE
 210# define MS_UNBINDABLE  (1 << 17)
 211#endif
 212#ifndef MS_PRIVATE
 213# define MS_PRIVATE     (1 << 18)
 214#endif
 215#ifndef MS_SLAVE
 216# define MS_SLAVE       (1 << 19)
 217#endif
 218#ifndef MS_SHARED
 219# define MS_SHARED      (1 << 20)
 220#endif
 221
 222#ifndef MS_RELATIME
 223# define MS_RELATIME    (1 << 21)
 224#endif
 225#ifndef MS_STRICTATIME
 226# define MS_STRICTATIME (1 << 24)
 227#endif
 228#ifndef MS_LAZYTIME
 229# define MS_LAZYTIME    (1 << 25)
 230#endif
 231
 232/* Any ~MS_FOO value has this bit set: */
 233#define BB_MS_INVERTED_VALUE (1u << 31)
 234
 235#include "libbb.h"
 236#include "common_bufsiz.h"
 237#if ENABLE_FEATURE_MOUNT_LABEL
 238# include "volume_id.h"
 239#else
 240# define resolve_mount_spec(fsname) ((void)0)
 241#endif
 242
 243// Needed for nfs support only
 244#include <sys/utsname.h>
 245#undef TRUE
 246#undef FALSE
 247#if ENABLE_FEATURE_MOUNT_NFS
 248/* This is just a warning of a common mistake.  Possibly this should be a
 249 * uclibc faq entry rather than in busybox... */
 250# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
 251#  warning "You probably need to build uClibc with UCLIBC_HAS_RPC for NFS support"
 252   /* not #error, since user may be using e.g. libtirpc instead.
 253    * This might work:
 254    * CONFIG_EXTRA_CFLAGS="-I/usr/include/tirpc"
 255    * CONFIG_EXTRA_LDLIBS="tirpc"
 256    */
 257# endif
 258# include <rpc/rpc.h>
 259# include <rpc/pmap_prot.h>
 260# include <rpc/pmap_clnt.h>
 261#endif
 262
 263
 264#if defined(__dietlibc__)
 265// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
 266// dietlibc-0.30 does not have implementation of getmntent_r()
 267static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
 268                char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
 269{
 270        struct mntent* ment = getmntent(stream);
 271        return memcpy(result, ment, sizeof(*ment));
 272}
 273#endif
 274
 275
 276// Not real flags, but we want to be able to check for this.
 277enum {
 278        MOUNT_USERS  = (1 << 27) * ENABLE_DESKTOP,
 279        MOUNT_NOFAIL = (1 << 28) * ENABLE_DESKTOP,
 280        MOUNT_NOAUTO = (1 << 29),
 281        MOUNT_SWAP   = (1 << 30),
 282        MOUNT_FAKEFLAGS = MOUNT_USERS | MOUNT_NOFAIL | MOUNT_NOAUTO | MOUNT_SWAP
 283};
 284
 285
 286#define OPTION_STR "o:*t:rwanfvsiO:" IF_FEATURE_MOUNT_OTHERTAB("T:")
 287enum {
 288        OPT_o = (1 << 0),
 289        OPT_t = (1 << 1),
 290        OPT_r = (1 << 2),
 291        OPT_w = (1 << 3),
 292        OPT_a = (1 << 4),
 293        OPT_n = (1 << 5),
 294        OPT_f = (1 << 6),
 295        OPT_v = (1 << 7),
 296        OPT_s = (1 << 8),
 297        OPT_i = (1 << 9),
 298        OPT_O = (1 << 10),
 299        OPT_T = (1 << 11),
 300};
 301
 302#if ENABLE_FEATURE_MTAB_SUPPORT
 303#define USE_MTAB (!(option_mask32 & OPT_n))
 304#else
 305#define USE_MTAB 0
 306#endif
 307
 308#if ENABLE_FEATURE_MOUNT_FAKE
 309#define FAKE_IT (option_mask32 & OPT_f)
 310#else
 311#define FAKE_IT 0
 312#endif
 313
 314#if ENABLE_FEATURE_MOUNT_HELPERS
 315#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
 316#else
 317#define HELPERS_ALLOWED 0
 318#endif
 319
 320
 321// TODO: more "user" flag compatibility.
 322// "user" option (from mount manpage):
 323// Only the user that mounted a filesystem can unmount it again.
 324// If any user should be able to unmount, then use users instead of user
 325// in the fstab line.  The owner option is similar to the user option,
 326// with the restriction that the user must be the owner of the special file.
 327// This may be useful e.g. for /dev/fd if a login script makes
 328// the console user owner of this device.
 329
 330// Standard mount options (from -o options or --options),
 331// with corresponding flags
 332static const int32_t mount_options[] ALIGN4 = {
 333        // MS_FLAGS set a bit.  ~MS_FLAGS disable that bit.  0 flags are NOPs.
 334
 335        IF_FEATURE_MOUNT_LOOP(
 336                /* "loop" */ 0,
 337        )
 338
 339        IF_FEATURE_MOUNT_FSTAB(
 340                /* "defaults" */ 0,
 341                /* "quiet" 0 - do not filter out, vfat wants to see it */
 342                /* "noauto" */ MOUNT_NOAUTO,
 343                /* "sw"     */ MOUNT_SWAP,
 344                /* "swap"   */ MOUNT_SWAP,
 345                IF_DESKTOP(/* "user"  */ MOUNT_USERS,)
 346                IF_DESKTOP(/* "users" */ MOUNT_USERS,)
 347                IF_DESKTOP(/* "nofail" */ MOUNT_NOFAIL,)
 348                /* "_netdev" */ 0,
 349                IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */
 350        )
 351
 352        IF_FEATURE_MOUNT_FLAGS(
 353                // vfs flags
 354                /* "nosuid"      */ MS_NOSUID,
 355                /* "suid"        */ ~MS_NOSUID,
 356                /* "dev"         */ ~MS_NODEV,
 357                /* "nodev"       */ MS_NODEV,
 358                /* "exec"        */ ~MS_NOEXEC,
 359                /* "noexec"      */ MS_NOEXEC,
 360                /* "sync"        */ MS_SYNCHRONOUS,
 361                /* "dirsync"     */ MS_DIRSYNC,
 362                /* "async"       */ ~MS_SYNCHRONOUS,
 363                /* "atime"       */ ~MS_NOATIME,
 364                /* "noatime"     */ MS_NOATIME,
 365                /* "diratime"    */ ~MS_NODIRATIME,
 366                /* "nodiratime"  */ MS_NODIRATIME,
 367                /* "relatime"    */ MS_RELATIME,
 368                /* "norelatime"  */ ~MS_RELATIME,
 369                /* "strictatime" */ MS_STRICTATIME,
 370                /* "nostrictatime"*/ ~MS_STRICTATIME,
 371                /* "lazytime"    */ MS_LAZYTIME,
 372                /* "nolazytime"  */ ~MS_LAZYTIME,
 373                /* "nosymfollow" */ MS_NOSYMFOLLOW,
 374                /* "mand"        */ MS_MANDLOCK,
 375                /* "nomand"      */ ~MS_MANDLOCK,
 376                /* "loud"        */ ~MS_SILENT,
 377
 378                // action flags
 379                /* "rbind"       */ MS_BIND|MS_RECURSIVE,
 380                /* "bind"        */ MS_BIND,
 381                /* "move"        */ MS_MOVE,
 382                /* "shared"      */ MS_SHARED,
 383                /* "slave"       */ MS_SLAVE,
 384                /* "private"     */ MS_PRIVATE,
 385                /* "unbindable"  */ MS_UNBINDABLE,
 386                /* "rshared"     */ MS_SHARED|MS_RECURSIVE,
 387                /* "rslave"      */ MS_SLAVE|MS_RECURSIVE,
 388                /* "rprivate"    */ MS_PRIVATE|MS_RECURSIVE,
 389                /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
 390        )
 391
 392        // Always understood.
 393        /* "ro"      */ MS_RDONLY,  // vfs flag
 394        /* "rw"      */ ~MS_RDONLY, // vfs flag
 395        /* "remount" */ MS_REMOUNT  // action flag
 396};
 397
 398static const char mount_option_str[] ALIGN1 =
 399        IF_FEATURE_MOUNT_LOOP(
 400                "loop\0"
 401        )
 402        IF_FEATURE_MOUNT_FSTAB(
 403                "defaults\0"
 404                // "quiet\0" - do not filter out, vfat wants to see it
 405                "noauto\0"
 406                "sw\0"
 407                "swap\0"
 408                IF_DESKTOP("user\0")
 409                IF_DESKTOP("users\0")
 410                IF_DESKTOP("nofail\0")
 411                "_netdev\0"
 412                IF_DESKTOP("comment=\0") /* systemd uses this in fstab */
 413        )
 414        IF_FEATURE_MOUNT_FLAGS(
 415                // vfs flags
 416                "nosuid"       "\0"
 417                "suid"         "\0"
 418                "dev"          "\0"
 419                "nodev"        "\0"
 420                "exec"         "\0"
 421                "noexec"       "\0"
 422                "sync"         "\0"
 423                "dirsync"      "\0"
 424                "async"        "\0"
 425                "atime"        "\0"
 426                "noatime"      "\0"
 427                "diratime"     "\0"
 428                "nodiratime"   "\0"
 429                "relatime"     "\0"
 430                "norelatime"   "\0"
 431                "strictatime"  "\0"
 432                "nostrictatime""\0"
 433                "lazytime"     "\0"
 434                "nolazytime"   "\0"
 435                "nosymfollow"  "\0"
 436                "mand"         "\0"
 437                "nomand"       "\0"
 438                "loud"         "\0"
 439
 440                // action flags
 441                "rbind\0"
 442                "bind\0"
 443                "move\0"
 444                "make-shared\0"
 445                "make-slave\0"
 446                "make-private\0"
 447                "make-unbindable\0"
 448                "make-rshared\0"
 449                "make-rslave\0"
 450                "make-rprivate\0"
 451                "make-runbindable\0"
 452        )
 453
 454        // Always understood.
 455        "ro\0"        // vfs flag
 456        "rw\0"        // vfs flag
 457        "remount\0"   // action flag
 458;
 459
 460
 461struct globals {
 462#if ENABLE_FEATURE_MOUNT_NFS
 463        smalluint nfs_mount_version;
 464#endif
 465#if ENABLE_FEATURE_MOUNT_VERBOSE
 466        unsigned verbose;
 467#endif
 468        llist_t *fslist;
 469        char getmntent_buf[1];
 470} FIX_ALIASING;
 471enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
 472#define G (*(struct globals*)bb_common_bufsiz1)
 473#define nfs_mount_version (G.nfs_mount_version)
 474#if ENABLE_FEATURE_MOUNT_VERBOSE
 475#define verbose           (G.verbose          )
 476#else
 477#define verbose           0
 478#endif
 479#define fslist            (G.fslist           )
 480#define getmntent_buf     (G.getmntent_buf    )
 481#define INIT_G() do { setup_common_bufsiz(); } while (0)
 482
 483#if ENABLE_FEATURE_MTAB_SUPPORT
 484/*
 485 * update_mtab_entry_on_move() is used to update entry in case of mount --move.
 486 * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
 487 * input mntent and replace it by new one.
 488 */
 489static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
 490{
 491        struct mntent *entries, *m;
 492        int i, count;
 493        FILE *mountTable;
 494
 495        mountTable = setmntent(bb_path_mtab_file, "r");
 496        if (!mountTable) {
 497                bb_simple_perror_msg(bb_path_mtab_file);
 498                return;
 499        }
 500
 501        entries = NULL;
 502        count = 0;
 503        while ((m = getmntent(mountTable)) != NULL) {
 504                entries = xrealloc_vector(entries, 3, count);
 505                entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
 506                entries[count].mnt_dir = xstrdup(m->mnt_dir);
 507                entries[count].mnt_type = xstrdup(m->mnt_type);
 508                entries[count].mnt_opts = xstrdup(m->mnt_opts);
 509                entries[count].mnt_freq = m->mnt_freq;
 510                entries[count].mnt_passno = m->mnt_passno;
 511                count++;
 512        }
 513        endmntent(mountTable);
 514
 515        mountTable = setmntent(bb_path_mtab_file, "w");
 516        if (mountTable) {
 517                for (i = 0; i < count; i++) {
 518                        if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
 519                                addmntent(mountTable, &entries[i]);
 520                        else
 521                                addmntent(mountTable, mp);
 522                }
 523                endmntent(mountTable);
 524        } else if (errno != EROFS)
 525                bb_simple_perror_msg(bb_path_mtab_file);
 526
 527        if (ENABLE_FEATURE_CLEAN_UP) {
 528                for (i = 0; i < count; i++) {
 529                        free(entries[i].mnt_fsname);
 530                        free(entries[i].mnt_dir);
 531                        free(entries[i].mnt_type);
 532                        free(entries[i].mnt_opts);
 533                }
 534                free(entries);
 535        }
 536}
 537#endif
 538
 539#if ENABLE_FEATURE_MOUNT_VERBOSE
 540static int verbose_mount(const char *source, const char *target,
 541                const char *filesystemtype,
 542                unsigned long mountflags, const void *data)
 543{
 544        int rc;
 545
 546        errno = 0;
 547        rc = mount(source, target, filesystemtype, mountflags, data);
 548        if (verbose >= 2)
 549                bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
 550                        source, target, filesystemtype,
 551                        mountflags, (char*)data, rc);
 552        return rc;
 553}
 554#else
 555#define verbose_mount(...) mount(__VA_ARGS__)
 556#endif
 557
 558// Append mount options to string
 559static void append_mount_options(char **oldopts, const char *newopts)
 560{
 561        if (*oldopts && **oldopts) {
 562                // Do not insert options which are already there
 563                while (newopts[0]) {
 564                        char *p;
 565                        int len;
 566
 567                        len = strchrnul(newopts, ',') - newopts;
 568                        p = *oldopts;
 569                        while (1) {
 570                                if (!strncmp(p, newopts, len)
 571                                 && (p[len] == ',' || p[len] == '\0'))
 572                                        goto skip;
 573                                p = strchr(p,',');
 574                                if (!p) break;
 575                                p++;
 576                        }
 577                        p = xasprintf("%s,%.*s", *oldopts, len, newopts);
 578                        free(*oldopts);
 579                        *oldopts = p;
 580 skip:
 581                        newopts += len;
 582                        while (*newopts == ',') newopts++;
 583                }
 584        } else {
 585                if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
 586                *oldopts = xstrdup(newopts);
 587        }
 588}
 589
 590// Use the mount_options list to parse options into flags.
 591// Also update list of unrecognized options if unrecognized != NULL
 592static unsigned long parse_mount_options(char *options, char **unrecognized, uint32_t *opt)
 593{
 594        unsigned long flags = MS_SILENT;
 595
 596        // Loop through options
 597        for (;;) {
 598                unsigned i;
 599                char *comma = strchr(options, ',');
 600                const char *option_str = mount_option_str;
 601
 602                if (comma) *comma = '\0';
 603
 604// FIXME: use hasmntopt()
 605                // Find this option in mount_options
 606                for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
 607                        unsigned opt_len = strlen(option_str);
 608
 609                        if (strncasecmp(option_str, options, opt_len) == 0
 610                         && (options[opt_len] == '\0'
 611                            /* or is it "comment=" thingy in fstab? */
 612                            IF_FEATURE_MOUNT_FSTAB(IF_DESKTOP( || option_str[opt_len-1] == '=' ))
 613                            )
 614                        ) {
 615                                unsigned long fl = mount_options[i];
 616                                if (fl & BB_MS_INVERTED_VALUE)
 617                                        flags &= fl;
 618                                else
 619                                        flags |= fl;
 620                                /* If we see "-o rw" on command line, it's the same as -w:
 621                                 * "do not try to fall back to RO mounts"
 622                                 */
 623                                if (fl == ~MS_RDONLY && opt)
 624                                        (*opt) |= OPT_w;
 625                                goto found;
 626                        }
 627                        option_str += opt_len + 1;
 628                }
 629                // We did not recognize this option.
 630                // If "unrecognized" is not NULL, append option there.
 631                // Note that we should not append *empty* option -
 632                // in this case we want to pass NULL, not "", to "data"
 633                // parameter of mount(2) syscall.
 634                // This is crucial for filesystems that don't accept
 635                // any arbitrary mount options, like cgroup fs:
 636                // "mount -t cgroup none /mnt"
 637                if (options[0] && unrecognized) {
 638                        // Add it to strflags, to pass on to kernel
 639                        char *p = *unrecognized;
 640                        unsigned len = p ? strlen(p) : 0;
 641                        *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
 642
 643                        // Comma separated if it's not the first one
 644                        if (len) p[len++] = ',';
 645                        strcpy(p + len, options);
 646                }
 647 found:
 648                if (!comma)
 649                        break;
 650                // Advance to next option
 651                *comma = ',';
 652                options = ++comma;
 653        }
 654
 655        return flags;
 656}
 657
 658// Return a list of all block device backed filesystems
 659static llist_t *get_block_backed_filesystems(void)
 660{
 661        static const char filesystems[2][sizeof("/proc/filesystems")] ALIGN1 = {
 662                "/etc/filesystems",
 663                "/proc/filesystems",
 664        };
 665        char *fs, *buf;
 666        llist_t *list = NULL;
 667        int i;
 668        FILE *f;
 669
 670        for (i = 0; i < 2; i++) {
 671                f = fopen_for_read(filesystems[i]);
 672                if (!f) continue;
 673
 674                while ((buf = xmalloc_fgetline(f)) != NULL) {
 675                        if (is_prefixed_with(buf, "nodev") && isspace(buf[5]))
 676                                goto next;
 677                        fs = skip_whitespace(buf);
 678                        if (*fs == '#' || *fs == '*' || !*fs)
 679                                goto next;
 680
 681                        llist_add_to_end(&list, xstrdup(fs));
 682 next:
 683                        free(buf);
 684                }
 685                if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
 686        }
 687
 688        return list;
 689}
 690
 691#if ENABLE_FEATURE_CLEAN_UP
 692static void delete_block_backed_filesystems(void)
 693{
 694        llist_free(fslist, free);
 695}
 696#else
 697void delete_block_backed_filesystems(void);
 698#endif
 699
 700// Perform actual mount of specific filesystem at specific location.
 701// NB: mp->xxx fields may be trashed on exit
 702static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
 703{
 704        int rc = 0;
 705
 706        vfsflags &= ~(unsigned long)MOUNT_FAKEFLAGS;
 707
 708        if (FAKE_IT) {
 709                if (verbose >= 2)
 710                        bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
 711                                mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
 712                                vfsflags, filteropts);
 713                goto mtab;
 714        }
 715
 716        // Mount, with fallback to read-only if necessary.
 717        for (;;) {
 718                errno = 0;
 719                rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
 720                                vfsflags, filteropts);
 721                if (rc == 0)
 722                        goto mtab; // success
 723
 724                // mount failed, try helper program
 725                // mount.<mnt_type>
 726                if (HELPERS_ALLOWED && mp->mnt_type) {
 727                        char *args[8];
 728                        int errno_save = errno;
 729                        args[0] = xasprintf("mount.%s", mp->mnt_type);
 730                        rc = 1;
 731                        if (FAKE_IT)
 732                                args[rc++] = (char *)"-f";
 733                        if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
 734                                args[rc++] = (char *)"-n";
 735                        args[rc++] = mp->mnt_fsname;
 736                        args[rc++] = mp->mnt_dir;
 737                        if (filteropts) {
 738                                args[rc++] = (char *)"-o";
 739                                args[rc++] = filteropts;
 740                        }
 741                        args[rc] = NULL;
 742                        rc = spawn_and_wait(args);
 743                        free(args[0]);
 744                        if (rc == 0)
 745                                goto mtab; // success
 746                        errno = errno_save;
 747                }
 748
 749                // Should we retry read-only mount?
 750                if (vfsflags & MS_RDONLY)
 751                        break;          // no, already was tried
 752                if (option_mask32 & OPT_w)
 753                        break;          // no, "mount -w" never falls back to RO
 754                if (errno != EACCES && errno != EROFS)
 755                        break;          // no, error isn't hinting that RO may work
 756
 757                if (!(vfsflags & MS_SILENT))
 758                        bb_error_msg("%s is write-protected, mounting read-only",
 759                                                mp->mnt_fsname);
 760                vfsflags |= MS_RDONLY;
 761        }
 762
 763        // Abort entirely if permission denied.
 764
 765        if (rc && errno == EPERM)
 766                bb_simple_error_msg_and_die(bb_msg_perm_denied_are_you_root);
 767
 768        // If the mount was successful, and we're maintaining an old-style
 769        // mtab file by hand, add the new entry to it now.
 770 mtab:
 771        if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
 772                char *fsname;
 773                FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
 774                const char *option_str = mount_option_str;
 775                int i;
 776
 777                if (!mountTable) {
 778                        bb_simple_perror_msg(bb_path_mtab_file);
 779                        goto ret;
 780                }
 781
 782                // Add vfs string flags
 783                for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
 784                        if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
 785                                append_mount_options(&(mp->mnt_opts), option_str);
 786                        option_str += strlen(option_str) + 1;
 787                }
 788
 789                // Remove trailing / (if any) from directory we mounted on
 790                i = strlen(mp->mnt_dir) - 1;
 791                while (i > 0 && mp->mnt_dir[i] == '/')
 792                        mp->mnt_dir[i--] = '\0';
 793
 794                // Convert to canonical pathnames as needed
 795                mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
 796                fsname = NULL;
 797                if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
 798                        mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
 799                        mp->mnt_type = (char*)"bind";
 800                }
 801                mp->mnt_freq = mp->mnt_passno = 0;
 802
 803                // Write and close
 804#if ENABLE_FEATURE_MTAB_SUPPORT
 805                if (vfsflags & MS_MOVE)
 806                        update_mtab_entry_on_move(mp);
 807                else
 808#endif
 809                        addmntent(mountTable, mp);
 810                endmntent(mountTable);
 811
 812                if (ENABLE_FEATURE_CLEAN_UP) {
 813                        free(mp->mnt_dir);
 814                        free(fsname);
 815                }
 816        }
 817 ret:
 818        return rc;
 819}
 820
 821#if ENABLE_FEATURE_MOUNT_NFS
 822
 823/*
 824 * Linux NFS mount
 825 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
 826 *
 827 * Licensed under GPLv2, see file LICENSE in this source tree.
 828 *
 829 * Wed Feb  8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
 830 * numbers to be specified on the command line.
 831 *
 832 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
 833 * Omit the call to connect() for Linux version 1.3.11 or later.
 834 *
 835 * Wed Oct  1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
 836 * Implemented the "bg", "fg" and "retry" mount options for NFS.
 837 *
 838 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
 839 * - added Native Language Support
 840 *
 841 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
 842 * plus NFSv3 stuff.
 843 */
 844
 845#define MOUNTPORT 635
 846#define MNTPATHLEN 1024
 847#define MNTNAMLEN 255
 848#define FHSIZE 32
 849#define FHSIZE3 64
 850
 851typedef char fhandle[FHSIZE];
 852
 853typedef struct {
 854        unsigned int fhandle3_len;
 855        char *fhandle3_val;
 856} fhandle3;
 857
 858enum mountstat3 {
 859        MNT_OK = 0,
 860        MNT3ERR_PERM = 1,
 861        MNT3ERR_NOENT = 2,
 862        MNT3ERR_IO = 5,
 863        MNT3ERR_ACCES = 13,
 864        MNT3ERR_NOTDIR = 20,
 865        MNT3ERR_INVAL = 22,
 866        MNT3ERR_NAMETOOLONG = 63,
 867        MNT3ERR_NOTSUPP = 10004,
 868        MNT3ERR_SERVERFAULT = 10006,
 869};
 870typedef enum mountstat3 mountstat3;
 871
 872struct fhstatus {
 873        unsigned int fhs_status;
 874        union {
 875                fhandle fhs_fhandle;
 876        } fhstatus_u;
 877};
 878typedef struct fhstatus fhstatus;
 879
 880struct mountres3_ok {
 881        fhandle3 fhandle;
 882        struct {
 883                unsigned int auth_flavours_len;
 884                char *auth_flavours_val;
 885        } auth_flavours;
 886};
 887typedef struct mountres3_ok mountres3_ok;
 888
 889struct mountres3 {
 890        mountstat3 fhs_status;
 891        union {
 892                mountres3_ok mountinfo;
 893        } mountres3_u;
 894};
 895typedef struct mountres3 mountres3;
 896
 897typedef char *dirpath;
 898
 899typedef char *name;
 900
 901typedef struct mountbody *mountlist;
 902
 903struct mountbody {
 904        name ml_hostname;
 905        dirpath ml_directory;
 906        mountlist ml_next;
 907};
 908typedef struct mountbody mountbody;
 909
 910typedef struct groupnode *groups;
 911
 912struct groupnode {
 913        name gr_name;
 914        groups gr_next;
 915};
 916typedef struct groupnode groupnode;
 917
 918typedef struct exportnode *exports;
 919
 920struct exportnode {
 921        dirpath ex_dir;
 922        groups ex_groups;
 923        exports ex_next;
 924};
 925typedef struct exportnode exportnode;
 926
 927struct ppathcnf {
 928        int pc_link_max;
 929        short pc_max_canon;
 930        short pc_max_input;
 931        short pc_name_max;
 932        short pc_path_max;
 933        short pc_pipe_buf;
 934        uint8_t pc_vdisable;
 935        char pc_xxx;
 936        short pc_mask[2];
 937};
 938typedef struct ppathcnf ppathcnf;
 939
 940#define MOUNTPROG 100005
 941#define MOUNTVERS 1
 942
 943#define MOUNTPROC_NULL 0
 944#define MOUNTPROC_MNT 1
 945#define MOUNTPROC_DUMP 2
 946#define MOUNTPROC_UMNT 3
 947#define MOUNTPROC_UMNTALL 4
 948#define MOUNTPROC_EXPORT 5
 949#define MOUNTPROC_EXPORTALL 6
 950
 951#define MOUNTVERS_POSIX 2
 952
 953#define MOUNTPROC_PATHCONF 7
 954
 955#define MOUNT_V3 3
 956
 957#define MOUNTPROC3_NULL 0
 958#define MOUNTPROC3_MNT 1
 959#define MOUNTPROC3_DUMP 2
 960#define MOUNTPROC3_UMNT 3
 961#define MOUNTPROC3_UMNTALL 4
 962#define MOUNTPROC3_EXPORT 5
 963
 964enum {
 965#ifndef NFS_FHSIZE
 966        NFS_FHSIZE = 32,
 967#endif
 968#ifndef NFS_PORT
 969        NFS_PORT = 2049
 970#endif
 971};
 972
 973/*
 974 * We want to be able to compile mount on old kernels in such a way
 975 * that the binary will work well on more recent kernels.
 976 * Thus, if necessary we teach nfsmount.c the structure of new fields
 977 * that will come later.
 978 *
 979 * Moreover, the new kernel includes conflict with glibc includes
 980 * so it is easiest to ignore the kernel altogether (at compile time).
 981 */
 982
 983struct nfs2_fh {
 984        char            data[32];
 985};
 986struct nfs3_fh {
 987        unsigned short  size;
 988        unsigned char   data[64];
 989};
 990
 991struct nfs_mount_data {
 992        int             version;        /* 1 */
 993        int             fd;             /* 1 */
 994        struct nfs2_fh  old_root;       /* 1 */
 995        int             flags;          /* 1 */
 996        int             rsize;          /* 1 */
 997        int             wsize;          /* 1 */
 998        int             timeo;          /* 1 */
 999        int             retrans;        /* 1 */
1000        int             acregmin;       /* 1 */
1001        int             acregmax;       /* 1 */
1002        int             acdirmin;       /* 1 */
1003        int             acdirmax;       /* 1 */
1004        struct sockaddr_in addr;        /* 1 */
1005        char            hostname[256];  /* 1 */
1006        int             namlen;         /* 2 */
1007        unsigned int    bsize;          /* 3 */
1008        struct nfs3_fh  root;           /* 4 */
1009};
1010
1011/* bits in the flags field */
1012enum {
1013        NFS_MOUNT_SOFT = 0x0001,        /* 1 */
1014        NFS_MOUNT_INTR = 0x0002,        /* 1 */
1015        NFS_MOUNT_SECURE = 0x0004,      /* 1 */
1016        NFS_MOUNT_POSIX = 0x0008,       /* 1 */
1017        NFS_MOUNT_NOCTO = 0x0010,       /* 1 */
1018        NFS_MOUNT_NOAC = 0x0020,        /* 1 */
1019        NFS_MOUNT_TCP = 0x0040,         /* 2 */
1020        NFS_MOUNT_VER3 = 0x0080,        /* 3 */
1021        NFS_MOUNT_KERBEROS = 0x0100,    /* 3 */
1022        NFS_MOUNT_NONLM = 0x0200,       /* 3 */
1023        NFS_MOUNT_NOACL = 0x0800,       /* 4 */
1024        NFS_MOUNT_NORDIRPLUS = 0x4000
1025};
1026
1027
1028/*
1029 * We need to translate between nfs status return values and
1030 * the local errno values which may not be the same.
1031 *
1032 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
1033 * "after #include <errno.h> the symbol errno is reserved for any use,
1034 *  it cannot even be used as a struct tag or field name".
1035 */
1036#ifndef EDQUOT
1037# define EDQUOT ENOSPC
1038#endif
1039/* Convert each NFSERR_BLAH into EBLAH */
1040static const uint8_t nfs_err_stat[] ALIGN1 = {
1041         1,  2,  5,  6, 13, 17,
1042        19, 20, 21, 22, 27, 28,
1043        30, 63, 66, 69, 70, 71
1044};
1045#if ( \
1046        EPERM | ENOENT      | EIO      | ENXIO | EACCES| EEXIST | \
1047        ENODEV| ENOTDIR     | EISDIR   | EINVAL| EFBIG | ENOSPC | \
1048        EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
1049typedef uint8_t nfs_err_type;
1050#else
1051typedef uint16_t nfs_err_type;
1052#endif
1053static const nfs_err_type nfs_err_errnum[] ALIGN2 = {
1054        EPERM , ENOENT      , EIO      , ENXIO , EACCES, EEXIST,
1055        ENODEV, ENOTDIR     , EISDIR   , EINVAL, EFBIG , ENOSPC,
1056        EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
1057};
1058static char *nfs_strerror(int status)
1059{
1060        int i;
1061
1062        for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
1063                if (nfs_err_stat[i] == status)
1064                        return strerror(nfs_err_errnum[i]);
1065        }
1066        return xasprintf("unknown nfs status return value: %d", status);
1067}
1068
1069static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
1070{
1071        return xdr_opaque(xdrs, objp, FHSIZE);
1072}
1073
1074static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
1075{
1076        if (!xdr_u_int(xdrs, &objp->fhs_status))
1077                return FALSE;
1078        if (objp->fhs_status == 0)
1079                return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
1080        return TRUE;
1081}
1082
1083static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
1084{
1085        return xdr_string(xdrs, objp, MNTPATHLEN);
1086}
1087
1088static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
1089{
1090        return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
1091                        (unsigned int *) &objp->fhandle3_len,
1092                        FHSIZE3);
1093}
1094
1095static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
1096{
1097        if (!xdr_fhandle3(xdrs, &objp->fhandle))
1098                return FALSE;
1099        return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
1100                        &(objp->auth_flavours.auth_flavours_len),
1101                        ~0,
1102                        sizeof(int),
1103                        (xdrproc_t) xdr_int);
1104}
1105
1106static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
1107{
1108        return xdr_enum(xdrs, (enum_t *) objp);
1109}
1110
1111static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
1112{
1113        if (!xdr_mountstat3(xdrs, &objp->fhs_status))
1114                return FALSE;
1115        if (objp->fhs_status == MNT_OK)
1116                return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
1117        return TRUE;
1118}
1119
1120#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
1121
1122/*
1123 * Unfortunately, the kernel prints annoying console messages
1124 * in case of an unexpected nfs mount version (instead of
1125 * just returning some error).  Therefore we'll have to try
1126 * and figure out what version the kernel expects.
1127 *
1128 * Variables:
1129 *      KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
1130 *      NFS_MOUNT_VERSION: these nfsmount sources at compile time
1131 *      nfs_mount_version: version this source and running kernel can handle
1132 */
1133static void
1134find_kernel_nfs_mount_version(void)
1135{
1136        int kernel_version;
1137
1138        if (nfs_mount_version)
1139                return;
1140
1141        nfs_mount_version = 4; /* default */
1142
1143        kernel_version = get_linux_version_code();
1144        if (kernel_version) {
1145                if (kernel_version < KERNEL_VERSION(2,2,18))
1146                        nfs_mount_version = 3;
1147                /* else v4 since 2.3.99pre4 */
1148        }
1149}
1150
1151static void
1152get_mountport(struct pmap *pm_mnt,
1153        struct sockaddr_in *server_addr,
1154        long unsigned prog,
1155        long unsigned version,
1156        long unsigned proto,
1157        long unsigned port)
1158{
1159        struct pmaplist *pmap;
1160
1161        server_addr->sin_port = PMAPPORT;
1162/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1163 * I understand it like "IPv6 for this is not 100% ready" */
1164        pmap = pmap_getmaps(server_addr);
1165
1166        if (version > MAX_NFSPROT)
1167                version = MAX_NFSPROT;
1168        if (!prog)
1169                prog = MOUNTPROG;
1170        pm_mnt->pm_prog = prog;
1171        pm_mnt->pm_vers = version;
1172        pm_mnt->pm_prot = proto;
1173        pm_mnt->pm_port = port;
1174
1175        while (pmap) {
1176                if (pmap->pml_map.pm_prog != prog)
1177                        goto next;
1178                if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
1179                        goto next;
1180                if (version > 2 && pmap->pml_map.pm_vers != version)
1181                        goto next;
1182                if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1183                        goto next;
1184                if (pmap->pml_map.pm_vers > MAX_NFSPROT
1185                 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1186                 || (port && pmap->pml_map.pm_port != port)
1187                ) {
1188                        goto next;
1189                }
1190                memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1191 next:
1192                pmap = pmap->pml_next;
1193        }
1194        if (!pm_mnt->pm_vers)
1195                pm_mnt->pm_vers = MOUNTVERS;
1196        if (!pm_mnt->pm_port)
1197                pm_mnt->pm_port = MOUNTPORT;
1198        if (!pm_mnt->pm_prot)
1199                pm_mnt->pm_prot = IPPROTO_TCP;
1200}
1201
1202#if BB_MMU
1203static int daemonize(void)
1204{
1205        int pid = fork();
1206        if (pid < 0) /* error */
1207                return -errno;
1208        if (pid > 0) /* parent */
1209                return 0;
1210        /* child */
1211        close(0);
1212        xopen(bb_dev_null, O_RDWR);
1213        xdup2(0, 1);
1214        xdup2(0, 2);
1215        setsid();
1216        openlog(applet_name, LOG_PID, LOG_DAEMON);
1217        logmode = LOGMODE_SYSLOG;
1218        return 1;
1219}
1220#else
1221static inline int daemonize(void)
1222{
1223        return -ENOSYS;
1224}
1225#endif
1226
1227/* TODO */
1228static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
1229{
1230        return 0;
1231}
1232
1233/* RPC strerror analogs are terminally idiotic:
1234 * *mandatory* prefix and \n at end.
1235 * This hopefully helps. Usage:
1236 * error_msg_rpc(clnt_*error*(" ")) */
1237static void error_msg_rpc(const char *msg)
1238{
1239        int len;
1240        while (msg[0] == ' ' || msg[0] == ':') msg++;
1241        len = strlen(msg);
1242        while (len && msg[len-1] == '\n') len--;
1243        bb_error_msg("%.*s", len, msg);
1244}
1245
1246/* NB: mp->xxx fields may be trashed on exit */
1247static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1248{
1249        CLIENT *mclient;
1250        char *hostname;
1251        char *pathname;
1252        char *mounthost;
1253        /* prior to 2.6.23, kernel took NFS options in a form of this struct
1254         * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1255         * then data pointer is interpreted as a string. */
1256        struct nfs_mount_data data;
1257        char *opt;
1258        char *tokstate;
1259        struct hostent *hp;
1260        struct sockaddr_in server_addr;
1261        struct sockaddr_in mount_server_addr;
1262        int msock, fsock;
1263        union {
1264                struct fhstatus nfsv2;
1265                struct mountres3 nfsv3;
1266        } status;
1267        int daemonized;
1268        char *s;
1269        int port;
1270        int mountport;
1271        int proto;
1272#if BB_MMU
1273        smallint bg = 0;
1274#else
1275        enum { bg = 0 };
1276#endif
1277        int retry;
1278        int mountprog;
1279        int mountvers;
1280        int nfsprog;
1281        int nfsvers;
1282        int retval;
1283        /* these all are one-bit really. gcc 4.3.1 likes this combination: */
1284        smallint tcp;
1285        smallint soft;
1286        int intr;
1287        int posix;
1288        int nocto;
1289        int noac;
1290        int nordirplus;
1291        int nolock;
1292        int noacl;
1293
1294        find_kernel_nfs_mount_version();
1295
1296        daemonized = 0;
1297        mounthost = NULL;
1298        retval = ETIMEDOUT;
1299        msock = fsock = -1;
1300        mclient = NULL;
1301
1302        /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1303
1304        filteropts = xstrdup(filteropts); /* going to trash it later... */
1305
1306        hostname = xstrdup(mp->mnt_fsname);
1307        /* mount_main() guarantees that ':' is there */
1308        s = strchr(hostname, ':');
1309        pathname = s + 1;
1310        *s = '\0';
1311        /* Ignore all but first hostname in replicated mounts
1312         * until they can be fully supported. (mack@sgi.com) */
1313        s = strchr(hostname, ',');
1314        if (s) {
1315                *s = '\0';
1316                bb_simple_error_msg("warning: multiple hostnames not supported");
1317        }
1318
1319        server_addr.sin_family = AF_INET;
1320        if (!inet_aton(hostname, &server_addr.sin_addr)) {
1321                hp = gethostbyname(hostname);
1322                if (hp == NULL) {
1323                        bb_simple_herror_msg(hostname);
1324                        goto fail;
1325                }
1326                if (hp->h_length != (int)sizeof(struct in_addr)) {
1327                        bb_simple_error_msg_and_die("only IPv4 is supported");
1328                }
1329                memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1330        }
1331
1332        memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1333
1334        /* add IP address to mtab options for use when unmounting */
1335
1336        if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1337                mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1338        } else {
1339                char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1340                                        mp->mnt_opts[0] ? "," : "",
1341                                        inet_ntoa(server_addr.sin_addr));
1342                free(mp->mnt_opts);
1343                mp->mnt_opts = tmp;
1344        }
1345
1346        /* Set default options.
1347         * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1348         * let the kernel decide.
1349         * timeo is filled in after we know whether it'll be TCP or UDP. */
1350        memset(&data, 0, sizeof(data));
1351        data.retrans  = 3;
1352        data.acregmin = 3;
1353        data.acregmax = 60;
1354        data.acdirmin = 30;
1355        data.acdirmax = 60;
1356        data.namlen   = NAME_MAX;
1357
1358        soft = 0;
1359        intr = 0;
1360        posix = 0;
1361        nocto = 0;
1362        nolock = 0;
1363        noac = 0;
1364        nordirplus = 0;
1365        noacl = 0;
1366        retry = 10000;          /* 10000 minutes ~ 1 week */
1367        tcp = 1;                        /* nfs-utils uses tcp per default */
1368
1369        mountprog = MOUNTPROG;
1370        mountvers = 0;
1371        port = 0;
1372        mountport = 0;
1373        nfsprog = 100003;
1374        nfsvers = 0;
1375
1376        /* parse options */
1377        if (filteropts) for (opt = strtok_r(filteropts, ",", &tokstate); opt; opt = strtok_r(NULL, ",", &tokstate)) {
1378                char *opteq = strchr(opt, '=');
1379                if (opteq) {
1380                        int val, idx;
1381                        static const char options[] ALIGN1 =
1382                                /* 0 */ "rsize\0"
1383                                /* 1 */ "wsize\0"
1384                                /* 2 */ "timeo\0"
1385                                /* 3 */ "retrans\0"
1386                                /* 4 */ "acregmin\0"
1387                                /* 5 */ "acregmax\0"
1388                                /* 6 */ "acdirmin\0"
1389                                /* 7 */ "acdirmax\0"
1390                                /* 8 */ "actimeo\0"
1391                                /* 9 */ "retry\0"
1392                                /* 10 */ "port\0"
1393                                /* 11 */ "mountport\0"
1394                                /* 12 */ "mounthost\0"
1395                                /* 13 */ "mountprog\0"
1396                                /* 14 */ "mountvers\0"
1397                                /* 15 */ "nfsprog\0"
1398                                /* 16 */ "nfsvers\0"
1399                                /* 17 */ "vers\0"
1400                                /* 18 */ "proto\0"
1401                                /* 19 */ "namlen\0"
1402                                /* 20 */ "addr\0";
1403
1404                        *opteq++ = '\0';
1405                        idx = index_in_strings(options, opt);
1406                        switch (idx) {
1407                        case 12: // "mounthost"
1408                                mounthost = xstrndup(opteq,
1409                                                strcspn(opteq, " \t\n\r,"));
1410                                continue;
1411                        case 18: // "proto"
1412                                if (is_prefixed_with(opteq, "tcp"))
1413                                        tcp = 1;
1414                                else if (is_prefixed_with(opteq, "udp"))
1415                                        tcp = 0;
1416                                else
1417                                        bb_simple_error_msg("warning: unrecognized proto= option");
1418                                continue;
1419                        case 20: // "addr" - ignore
1420                                continue;
1421                        case -1: // unknown
1422                                if (vfsflags & MS_REMOUNT)
1423                                        continue;
1424                        }
1425
1426                        val = xatoi_positive(opteq);
1427                        switch (idx) {
1428                        case 0: // "rsize"
1429                                data.rsize = val;
1430                                continue;
1431                        case 1: // "wsize"
1432                                data.wsize = val;
1433                                continue;
1434                        case 2: // "timeo"
1435                                data.timeo = val;
1436                                continue;
1437                        case 3: // "retrans"
1438                                data.retrans = val;
1439                                continue;
1440                        case 4: // "acregmin"
1441                                data.acregmin = val;
1442                                continue;
1443                        case 5: // "acregmax"
1444                                data.acregmax = val;
1445                                continue;
1446                        case 6: // "acdirmin"
1447                                data.acdirmin = val;
1448                                continue;
1449                        case 7: // "acdirmax"
1450                                data.acdirmax = val;
1451                                continue;
1452                        case 8: // "actimeo"
1453                                data.acregmin = val;
1454                                data.acregmax = val;
1455                                data.acdirmin = val;
1456                                data.acdirmax = val;
1457                                continue;
1458                        case 9: // "retry"
1459                                retry = val;
1460                                continue;
1461                        case 10: // "port"
1462                                port = val;
1463                                continue;
1464                        case 11: // "mountport"
1465                                mountport = val;
1466                                continue;
1467                        case 13: // "mountprog"
1468                                mountprog = val;
1469                                continue;
1470                        case 14: // "mountvers"
1471                                mountvers = val;
1472                                continue;
1473                        case 15: // "nfsprog"
1474                                nfsprog = val;
1475                                continue;
1476                        case 16: // "nfsvers"
1477                        case 17: // "vers"
1478                                nfsvers = val;
1479                                continue;
1480                        case 19: // "namlen"
1481                                //if (nfs_mount_version >= 2)
1482                                        data.namlen = val;
1483                                //else
1484                                //      bb_error_msg("warning: option namlen is not supported\n");
1485                                continue;
1486                        default:
1487                                bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1488                                goto fail;
1489                        }
1490                }
1491                else { /* not of the form opt=val */
1492                        static const char options[] ALIGN1 =
1493                                "bg\0"
1494                                "fg\0"
1495                                "soft\0"
1496                                "hard\0"
1497                                "intr\0"
1498                                "posix\0"
1499                                "cto\0"
1500                                "ac\0"
1501                                "tcp\0"
1502                                "udp\0"
1503                                "lock\0"
1504                                "rdirplus\0"
1505                                "acl\0";
1506                        int val = 1;
1507                        if (is_prefixed_with(opt, "no")) {
1508                                val = 0;
1509                                opt += 2;
1510                        }
1511                        switch (index_in_strings(options, opt)) {
1512                        case 0: // "bg"
1513#if BB_MMU
1514                                bg = val;
1515#endif
1516                                break;
1517                        case 1: // "fg"
1518#if BB_MMU
1519                                bg = !val;
1520#endif
1521                                break;
1522                        case 2: // "soft"
1523                                soft = val;
1524                                break;
1525                        case 3: // "hard"
1526                                soft = !val;
1527                                break;
1528                        case 4: // "intr"
1529                                intr = val;
1530                                break;
1531                        case 5: // "posix"
1532                                posix = val;
1533                                break;
1534                        case 6: // "cto"
1535                                nocto = !val;
1536                                break;
1537                        case 7: // "ac"
1538                                noac = !val;
1539                                break;
1540                        case 8: // "tcp"
1541                                tcp = val;
1542                                break;
1543                        case 9: // "udp"
1544                                tcp = !val;
1545                                break;
1546                        case 10: // "lock"
1547                                if (nfs_mount_version >= 3)
1548                                        nolock = !val;
1549                                else
1550                                        bb_simple_error_msg("warning: option nolock is not supported");
1551                                break;
1552                        case 11: //rdirplus
1553                                nordirplus = !val;
1554                                break;
1555                        case 12: // acl
1556                                noacl = !val;
1557                                break;
1558                        default:
1559                                bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1560                                goto fail;
1561                        }
1562                }
1563        }
1564        proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1565
1566        data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1567                | (intr ? NFS_MOUNT_INTR : 0)
1568                | (posix ? NFS_MOUNT_POSIX : 0)
1569                | (nocto ? NFS_MOUNT_NOCTO : 0)
1570                | (noac ? NFS_MOUNT_NOAC : 0)
1571                | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
1572                | (noacl ? NFS_MOUNT_NOACL : 0);
1573        if (nfs_mount_version >= 2)
1574                data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1575        if (nfs_mount_version >= 3)
1576                data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1577        if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1578                bb_error_msg("NFSv%d not supported", nfsvers);
1579                goto fail;
1580        }
1581        if (nfsvers && !mountvers)
1582                mountvers = (nfsvers < 3) ? 1 : nfsvers;
1583        if (nfsvers && nfsvers < mountvers) {
1584                mountvers = nfsvers;
1585        }
1586
1587        /* Adjust options if none specified */
1588        if (!data.timeo)
1589                data.timeo = tcp ? 70 : 7;
1590
1591        data.version = nfs_mount_version;
1592
1593        if (vfsflags & MS_REMOUNT)
1594                goto do_mount;
1595
1596        /*
1597         * If the previous mount operation on the same host was
1598         * backgrounded, and the "bg" for this mount is also set,
1599         * give up immediately, to avoid the initial timeout.
1600         */
1601        if (bg && we_saw_this_host_before(hostname)) {
1602                daemonized = daemonize();
1603                if (daemonized <= 0) { /* parent or error */
1604                        retval = -daemonized;
1605                        goto ret;
1606                }
1607        }
1608
1609        /* Create mount daemon client */
1610        /* See if the nfs host = mount host. */
1611        if (mounthost) {
1612                if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1613                        mount_server_addr.sin_family = AF_INET;
1614                        mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1615                } else {
1616                        hp = gethostbyname(mounthost);
1617                        if (hp == NULL) {
1618                                bb_simple_herror_msg(mounthost);
1619                                goto fail;
1620                        }
1621                        if (hp->h_length != (int)sizeof(struct in_addr)) {
1622                                bb_simple_error_msg_and_die("only IPv4 is supported");
1623                        }
1624                        mount_server_addr.sin_family = AF_INET;
1625                        memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1626                }
1627        }
1628
1629        /*
1630         * The following loop implements the mount retries. When the mount
1631         * times out, and the "bg" option is set, we background ourself
1632         * and continue trying.
1633         *
1634         * The case where the mount point is not present and the "bg"
1635         * option is set, is treated as a timeout. This is done to
1636         * support nested mounts.
1637         *
1638         * The "retry" count specified by the user is the number of
1639         * minutes to retry before giving up.
1640         */
1641        {
1642                struct timeval total_timeout;
1643                struct timeval retry_timeout;
1644                struct pmap pm_mnt;
1645                time_t t;
1646                time_t prevt;
1647                time_t timeout;
1648
1649                retry_timeout.tv_sec = 3;
1650                retry_timeout.tv_usec = 0;
1651                total_timeout.tv_sec = 20;
1652                total_timeout.tv_usec = 0;
1653/* FIXME: use monotonic()? */
1654                timeout = time(NULL) + 60 * retry;
1655                prevt = 0;
1656                t = 30;
1657 retry:
1658                /* Be careful not to use too many CPU cycles */
1659                if (t - prevt < 30)
1660                        sleep(30);
1661
1662                get_mountport(&pm_mnt, &mount_server_addr,
1663                                mountprog,
1664                                mountvers,
1665                                proto,
1666                                mountport);
1667                nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
1668
1669                /* contact the mount daemon via TCP */
1670                mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1671                msock = RPC_ANYSOCK;
1672
1673                switch (pm_mnt.pm_prot) {
1674                case IPPROTO_UDP:
1675                        mclient = clntudp_create(&mount_server_addr,
1676                                                pm_mnt.pm_prog,
1677                                                pm_mnt.pm_vers,
1678                                                retry_timeout,
1679                                                &msock);
1680                        if (mclient)
1681                                break;
1682                        mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1683                        msock = RPC_ANYSOCK;
1684                case IPPROTO_TCP:
1685                        mclient = clnttcp_create(&mount_server_addr,
1686                                                pm_mnt.pm_prog,
1687                                                pm_mnt.pm_vers,
1688                                                &msock, 0, 0);
1689                        break;
1690                default:
1691                        mclient = NULL;
1692                }
1693                if (!mclient) {
1694                        if (!daemonized && prevt == 0)
1695                                error_msg_rpc(clnt_spcreateerror(" "));
1696                } else {
1697                        enum clnt_stat clnt_stat;
1698
1699                        /* Try to mount hostname:pathname */
1700                        mclient->cl_auth = authunix_create_default();
1701
1702                        /* Make pointers in xdr_mountres3 NULL so
1703                         * that xdr_array allocates memory for us
1704                         */
1705                        memset(&status, 0, sizeof(status));
1706
1707                        if (pm_mnt.pm_vers == 3)
1708                                clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1709                                                (xdrproc_t) xdr_dirpath,
1710                                                (caddr_t) &pathname,
1711                                                (xdrproc_t) xdr_mountres3,
1712                                                (caddr_t) &status,
1713                                                total_timeout);
1714                        else
1715                                clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1716                                                (xdrproc_t) xdr_dirpath,
1717                                                (caddr_t) &pathname,
1718                                                (xdrproc_t) xdr_fhstatus,
1719                                                (caddr_t) &status,
1720                                                total_timeout);
1721
1722                        if (clnt_stat == RPC_SUCCESS)
1723                                goto prepare_kernel_data; /* we're done */
1724                        if (errno != ECONNREFUSED) {
1725                                error_msg_rpc(clnt_sperror(mclient, " "));
1726                                goto fail;      /* don't retry */
1727                        }
1728                        /* Connection refused */
1729                        if (!daemonized && prevt == 0) /* print just once */
1730                                error_msg_rpc(clnt_sperror(mclient, " "));
1731                        auth_destroy(mclient->cl_auth);
1732                        clnt_destroy(mclient);
1733                        mclient = NULL;
1734                        close(msock);
1735                        msock = -1;
1736                }
1737
1738                /* Timeout. We are going to retry... maybe */
1739                if (!bg)
1740                        goto fail;
1741                if (!daemonized) {
1742                        daemonized = daemonize();
1743                        if (daemonized <= 0) { /* parent or error */
1744                                retval = -daemonized;
1745                                goto ret;
1746                        }
1747                }
1748                prevt = t;
1749                t = time(NULL);
1750                if (t >= timeout)
1751                        /* TODO error message */
1752                        goto fail;
1753
1754                goto retry;
1755        }
1756
1757 prepare_kernel_data:
1758
1759        if (nfsvers == 2) {
1760                if (status.nfsv2.fhs_status != 0) {
1761                        bb_error_msg("%s:%s failed, reason given by server: %s",
1762                                hostname, pathname,
1763                                nfs_strerror(status.nfsv2.fhs_status));
1764                        goto fail;
1765                }
1766                memcpy(data.root.data,
1767                                (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1768                                NFS_FHSIZE);
1769                data.root.size = NFS_FHSIZE;
1770                memcpy(data.old_root.data,
1771                                (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1772                                NFS_FHSIZE);
1773        } else {
1774                fhandle3 *my_fhandle;
1775                if (status.nfsv3.fhs_status != 0) {
1776                        bb_error_msg("%s:%s failed, reason given by server: %s",
1777                                hostname, pathname,
1778                                nfs_strerror(status.nfsv3.fhs_status));
1779                        goto fail;
1780                }
1781                my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1782                memset(data.old_root.data, 0, NFS_FHSIZE);
1783                memset(&data.root, 0, sizeof(data.root));
1784                data.root.size = my_fhandle->fhandle3_len;
1785                memcpy(data.root.data,
1786                                (char *) my_fhandle->fhandle3_val,
1787                                my_fhandle->fhandle3_len);
1788
1789                data.flags |= NFS_MOUNT_VER3;
1790        }
1791
1792        /* Create nfs socket for kernel */
1793        if (tcp) {
1794                if (nfs_mount_version < 3) {
1795                        bb_simple_error_msg("NFS over TCP is not supported");
1796                        goto fail;
1797                }
1798                fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1799        } else
1800                fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1801        if (fsock < 0) {
1802                bb_simple_perror_msg("nfs socket");
1803                goto fail;
1804        }
1805        if (bindresvport(fsock, 0) < 0) {
1806                bb_simple_perror_msg("nfs bindresvport");
1807                goto fail;
1808        }
1809        if (port == 0) {
1810                server_addr.sin_port = PMAPPORT;
1811                port = pmap_getport(&server_addr, nfsprog, nfsvers,
1812                                        tcp ? IPPROTO_TCP : IPPROTO_UDP);
1813                if (port == 0)
1814                        port = NFS_PORT;
1815        }
1816        server_addr.sin_port = htons(port);
1817
1818        /* Prepare data structure for kernel */
1819        data.fd = fsock;
1820        memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1821        strncpy(data.hostname, hostname, sizeof(data.hostname));
1822
1823        /* Clean up */
1824        auth_destroy(mclient->cl_auth);
1825        clnt_destroy(mclient);
1826        close(msock);
1827        msock = -1;
1828
1829        if (bg) {
1830                /* We must wait until mount directory is available */
1831                struct stat statbuf;
1832                int delay = 1;
1833                while (stat(mp->mnt_dir, &statbuf) == -1) {
1834                        if (!daemonized) {
1835                                daemonized = daemonize();
1836                                if (daemonized <= 0) { /* parent or error */
1837/* FIXME: parent doesn't close fsock - ??! */
1838                                        retval = -daemonized;
1839                                        goto ret;
1840                                }
1841                        }
1842                        sleep(delay);   /* 1, 2, 4, 8, 16, 30, ... */
1843                        delay *= 2;
1844                        if (delay > 30)
1845                                delay = 30;
1846                }
1847        }
1848
1849        /* Perform actual mount */
1850 do_mount:
1851        retval = mount_it_now(mp, vfsflags, (char*)&data);
1852        goto ret;
1853
1854        /* Abort */
1855 fail:
1856        if (msock >= 0) {
1857                if (mclient) {
1858                        auth_destroy(mclient->cl_auth);
1859                        clnt_destroy(mclient);
1860                }
1861                close(msock);
1862        }
1863        if (fsock >= 0)
1864                close(fsock);
1865
1866 ret:
1867        free(hostname);
1868        free(mounthost);
1869        free(filteropts);
1870        return retval;
1871}
1872
1873#else // !ENABLE_FEATURE_MOUNT_NFS
1874
1875/* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1876 * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1877 * (However, note that then you lose any chances that NFS over IPv6 would work).
1878 */
1879static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1880{
1881        len_and_sockaddr *lsa;
1882        char *opts;
1883        char *end;
1884        char *dotted;
1885        int ret;
1886
1887# if ENABLE_FEATURE_IPV6
1888        end = strchr(mp->mnt_fsname, ']');
1889        if (end && end[1] == ':')
1890                end++;
1891        else
1892# endif
1893                /* mount_main() guarantees that ':' is there */
1894                end = strchr(mp->mnt_fsname, ':');
1895
1896        *end = '\0';
1897        lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
1898        *end = ':';
1899        dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1900        if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1901        opts = xasprintf("%s%saddr=%s",
1902                filteropts ? filteropts : "",
1903                filteropts ? "," : "",
1904                dotted
1905        );
1906        if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1907        ret = mount_it_now(mp, vfsflags, opts);
1908        if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1909
1910        return ret;
1911}
1912
1913#endif // !ENABLE_FEATURE_MOUNT_NFS
1914
1915// Find "...,NAME=NUM,..." in the option string, remove "NAME=NUM" option
1916// and return NUM.
1917// Return 0 if not found.
1918// All instances must be parsed and removed (for example, since kernel 5.4
1919//      squashfs: Unknown parameter 'sizelimit'
1920// will result if loopback mount option "sizelimit=NNN" is not removed
1921// and squashfs sees it in option string).
1922static unsigned long long cut_out_ull_opt(char *opts, const char *name_eq)
1923{
1924        unsigned long long ret = 0;
1925
1926        if (!opts) // allow NULL opts (simplifies callers' work)
1927                return ret;
1928
1929        for (;;) {
1930                char *end;
1931                char *opt;
1932
1933                // Find comma-delimited "NAME="
1934                for (;;) {
1935                        opt = strstr(opts, name_eq);
1936                        if (!opt)
1937                                return ret;
1938                        if (opt == opts)
1939                                break; // found it (it's first opt)
1940                        if (opt[-1] == ',') {
1941                                opts = opt - 1;
1942                                break; // found it (it's not a first opt)
1943                        }
1944                        // False positive like "VNAME=", we are at "N".
1945                        // - skip it, loop back to searching
1946                        opts = opt + 1;
1947                }
1948
1949                ret = bb_strtoull(opt + strlen(name_eq), &end, 0);
1950                if (errno && errno != EINVAL) {
1951 err:
1952                        bb_error_msg_and_die("bad option '%s'", opt);
1953                }
1954                if (*end == '\0') {
1955                        // It is "[,]NAME=NUM\0" - truncate it and return
1956                        *opts = '\0';
1957                        return ret;
1958                }
1959                if (*end != ',')
1960                        goto err;
1961                // We are at trailing comma
1962                // Remove "NAME=NUM," and loop back to check for duplicate opts
1963                overlapping_strcpy(opt, end + 1);
1964        }
1965}
1966
1967// Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
1968// type detection.  Returns 0 for success, nonzero for failure.
1969// NB: mp->xxx fields may be trashed on exit
1970static int singlemount(struct mntent *mp, int ignore_busy)
1971{
1972        int loopfd = -1;
1973        int rc = -1;
1974        unsigned long vfsflags;
1975        char *loopFile = NULL, *filteropts = NULL;
1976        llist_t *fl = NULL;
1977        struct stat st;
1978
1979        errno = 0;
1980
1981        vfsflags = parse_mount_options(mp->mnt_opts, &filteropts, NULL);
1982
1983        // Treat fstype "auto" as unspecified
1984        if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1985                mp->mnt_type = NULL;
1986
1987        // Might this be a virtual filesystem?
1988        if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1989                char *args[35];
1990                char *s;
1991                int n;
1992                // fsname: "cmd#arg1#arg2..."
1993                // WARNING: allows execution of arbitrary commands!
1994                // Try "mount 'sh#-c#sh' bogus_dir".
1995                // It is safe ONLY because non-root
1996                // cannot use two-argument mount command
1997                // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1998                // "mount: can't find sh#-c#sh in /etc/fstab"
1999                // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
2000
2001                s = mp->mnt_fsname;
2002                n = 0;
2003                args[n++] = s;
2004                while (*s && n < 35 - 2) {
2005                        if (*s++ == '#' && *s != '#') {
2006                                s[-1] = '\0';
2007                                args[n++] = s;
2008                        }
2009                }
2010                args[n++] = mp->mnt_dir;
2011                args[n] = NULL;
2012                rc = spawn_and_wait(args);
2013                goto report_error;
2014        }
2015
2016        // Might this be an CIFS filesystem?
2017        if (ENABLE_FEATURE_MOUNT_CIFS
2018         && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
2019         && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
2020         && mp->mnt_fsname[0] == mp->mnt_fsname[1]
2021        ) {
2022                int len;
2023                char c;
2024                char *hostname, *share;
2025                len_and_sockaddr *lsa;
2026
2027                // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
2028
2029                hostname = mp->mnt_fsname + 2;
2030                len = strcspn(hostname, "/\\");
2031                share = hostname + len + 1;
2032                if (len == 0          // 3rd char is a [back]slash (IOW: empty hostname)
2033                 || share[-1] == '\0' // no [back]slash after hostname
2034                 || share[0] == '\0'  // empty share name
2035                ) {
2036                        goto report_error;
2037                }
2038                c = share[-1];
2039                share[-1] = '\0';
2040                len = strcspn(share, "/\\");
2041
2042                // "unc=\\hostname\share" option is mandatory
2043                // after CIFS option parsing was rewritten in Linux 3.4.
2044                // Must use backslashes.
2045                // If /dir1/dir2 is present, also add "prefixpath=dir1/dir2"
2046                {
2047                        char *unc = xasprintf(
2048                                share[len] != '\0'  /* "/dir1/dir2" exists? */
2049                                        ? "unc=\\\\%s\\%.*s,prefixpath=%s"
2050                                        : "unc=\\\\%s\\%.*s",
2051                                hostname,
2052                                len, share,
2053                                share + len + 1  /* "dir1/dir2" */
2054                        );
2055                        parse_mount_options(unc, &filteropts, NULL);
2056                        if (ENABLE_FEATURE_CLEAN_UP) free(unc);
2057                }
2058
2059                lsa = host2sockaddr(hostname, 0);
2060                share[-1] = c;
2061                if (!lsa)
2062                        goto report_error;
2063
2064                // If there is no "ip=..." option yet
2065                if (!is_prefixed_with(filteropts, ",ip="+1)
2066                 && !strstr(filteropts, ",ip=")
2067                ) {
2068                        char *dotted, *ip;
2069                        // Insert "ip=..." option into options
2070                        dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
2071                        if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
2072                        ip = xasprintf("ip=%s", dotted);
2073                        if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
2074// Note: IPv6 scoped addresses ("host%iface", see RFC 4007) should be
2075// handled by libc in getnameinfo() (inside xmalloc_sockaddr2dotted_noport()).
2076// Currently, glibc does not support that (has no NI_NUMERICSCOPE),
2077// musl apparently does. This results in "ip=numericIPv6%iface_name"
2078// (instead of _numeric_ iface_id) with glibc.
2079// This probably should be fixed in glibc, not here.
2080// The workaround is to manually specify correct "ip=ADDR%n" option.
2081                        parse_mount_options(ip, &filteropts, NULL);
2082                        if (ENABLE_FEATURE_CLEAN_UP) free(ip);
2083                }
2084
2085                mp->mnt_type = (char*)"cifs";
2086                rc = mount_it_now(mp, vfsflags, filteropts);
2087
2088                goto report_error;
2089        }
2090
2091        // Might this be an NFS filesystem?
2092        if (!(vfsflags & (MS_BIND | MS_MOVE))
2093         && (!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs"))
2094        ) {
2095                char *colon = strchr(mp->mnt_fsname, ':');
2096                if (colon // looks like "hostname:..."
2097                 && strchrnul(mp->mnt_fsname, '/') > colon // "hostname:" has no slashes
2098                ) {
2099                        if (!mp->mnt_type)
2100                                mp->mnt_type = (char*)"nfs";
2101                        rc = nfsmount(mp, vfsflags, filteropts);
2102                        goto report_error;
2103                }
2104        }
2105
2106        // Look at the file.  (Not found isn't a failure for remount, or for
2107        // a synthetic filesystem like proc or sysfs.)
2108        // (We use stat, not lstat, in order to allow
2109        // mount symlink_to_file_or_blkdev dir)
2110        if (!stat(mp->mnt_fsname, &st)
2111         && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
2112        ) {
2113                // Do we need to allocate a loopback device for it?
2114                if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
2115                        unsigned long long offset;
2116                        unsigned long long sizelimit;
2117
2118                        loopFile = bb_simplify_path(mp->mnt_fsname);
2119                        mp->mnt_fsname = NULL; // will receive malloced loop dev name
2120
2121                        // Parse and remove loopback options
2122                        offset = cut_out_ull_opt(filteropts, "offset=");
2123                        sizelimit = cut_out_ull_opt(filteropts, "sizelimit=");
2124
2125                        // mount always creates AUTOCLEARed loopdevs, so that umounting
2126                        // drops them without any code in the userspace.
2127                        // This happens since circa linux-2.6.25:
2128                        // commit 96c5865559cee0f9cbc5173f3c949f6ce3525581
2129                        // Date:    Wed Feb 6 01:36:27 2008 -0800
2130                        // Subject: Allow auto-destruction of loop devices
2131                        loopfd = set_loop(&mp->mnt_fsname,
2132                                        loopFile,
2133                                        offset,
2134                                        sizelimit,
2135                                        ((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0)
2136                                                | BB_LO_FLAGS_AUTOCLEAR
2137                        );
2138                        if (loopfd < 0) {
2139                                if (errno == EPERM || errno == EACCES)
2140                                        bb_simple_error_msg(bb_msg_perm_denied_are_you_root);
2141                                else
2142                                        bb_simple_perror_msg("can't setup loop device");
2143                                return loopfd; // was "return errno", but it can be 0 here
2144                        }
2145
2146                // Autodetect bind mounts
2147                } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
2148                        vfsflags |= MS_BIND;
2149        }
2150
2151        // If we know the fstype (or don't need to), jump straight
2152        // to the actual mount.
2153        if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
2154                char *next;
2155                for (;;) {
2156                        next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
2157                        if (next)
2158                                *next = '\0';
2159                        rc = mount_it_now(mp, vfsflags, filteropts);
2160                        if (rc == 0 || !next)
2161                                break;
2162                        mp->mnt_type = next + 1;
2163                }
2164        } else {
2165                // Loop through filesystem types until mount succeeds
2166                // or we run out
2167
2168                // Initialize list of block backed filesystems.
2169                // This has to be done here so that during "mount -a",
2170                // mounts after /proc shows up can autodetect.
2171                if (!fslist) {
2172                        fslist = get_block_backed_filesystems();
2173                        if (ENABLE_FEATURE_CLEAN_UP && fslist)
2174                                atexit(delete_block_backed_filesystems);
2175                }
2176
2177                for (fl = fslist; fl; fl = fl->link) {
2178                        mp->mnt_type = fl->data;
2179                        rc = mount_it_now(mp, vfsflags, filteropts);
2180                        if (rc == 0)
2181                                break;
2182                }
2183        }
2184
2185        // If mount failed, clean up loop file (if any).
2186        // (Newer kernels which support LO_FLAGS_AUTOCLEAR should not need this,
2187        // merely "close(loopfd)" should do it?)
2188        if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
2189                del_loop(mp->mnt_fsname);
2190                if (ENABLE_FEATURE_CLEAN_UP) {
2191                        free(loopFile);
2192                        /* No, "rc != 0" needs it: free(mp->mnt_fsname); */
2193                }
2194        }
2195
2196 report_error:
2197        if (ENABLE_FEATURE_CLEAN_UP)
2198                free(filteropts);
2199
2200        if (loopfd >= 0)
2201                close(loopfd);
2202
2203        if (errno == EBUSY && ignore_busy)
2204                return 0;
2205        if (errno == ENOENT && (vfsflags & MOUNT_NOFAIL))
2206                return 0;
2207        if (rc != 0)
2208                bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
2209        return rc;
2210}
2211
2212// -O support
2213//    -O interprets a list of filter options which select whether a mount
2214// point will be mounted: only mounts with options matching *all* filtering
2215// options will be selected.
2216//    By default each -O filter option must be present in the list of mount
2217// options, but if it is prefixed by "no" then it must be absent.
2218// For example,
2219//  -O a,nob,c  matches  -o a,c  but fails to match  -o a,b,c
2220//              (and also fails to match  -o a  because  -o c  is absent).
2221//
2222// It is different from -t in that each option is matched exactly; a leading
2223// "no" at the beginning of one option does not negate the rest.
2224static int match_opt(const char *fs_opt_in, const char *O_opt)
2225{
2226        if (!O_opt)
2227                return 1;
2228
2229        while (*O_opt) {
2230                const char *fs_opt = fs_opt_in;
2231                int O_len;
2232                int match;
2233
2234                // If option begins with "no" then treat as an inverted match:
2235                // matching is a failure
2236                match = 0;
2237                if (O_opt[0] == 'n' && O_opt[1] == 'o') {
2238                        match = 1;
2239                        O_opt += 2;
2240                }
2241                // Isolate the current O option
2242                O_len = strchrnul(O_opt, ',') - O_opt;
2243                // Check for a match against existing options
2244                while (1) {
2245                        if (strncmp(fs_opt, O_opt, O_len) == 0
2246                         && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
2247                        ) {
2248                                if (match)
2249                                        return 0;  // "no" prefix, but option found
2250                                match = 1;  // current O option found, go check next one
2251                                break;
2252                        }
2253                        fs_opt = strchr(fs_opt, ',');
2254                        if (!fs_opt)
2255                                break;
2256                        fs_opt++;
2257                }
2258                if (match == 0)
2259                        return 0;     // match wanted but not found
2260                if (O_opt[O_len] == '\0') // end?
2261                        break;
2262                // Step to the next O option
2263                O_opt += O_len + 1;
2264        }
2265        // If we get here then everything matched
2266        return 1;
2267}
2268
2269// Parse options, if necessary parse fstab/mtab, and call singlemount for
2270// each directory to be mounted.
2271int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
2272int mount_main(int argc UNUSED_PARAM, char **argv)
2273{
2274        char *cmdopts = xzalloc(1);
2275        char *fstype = NULL;
2276        char *O_optmatch = NULL;
2277        char *storage_path;
2278        llist_t *lst_o = NULL;
2279        const char *fstabname = "/etc/fstab";
2280        FILE *fstab;
2281        int i, j;
2282        int rc = EXIT_SUCCESS;
2283        unsigned long cmdopt_flags;
2284        unsigned opt;
2285        struct mntent mtpair[2], *mtcur = mtpair;
2286        IF_NOT_DESKTOP(const int nonroot = 0;)
2287
2288        IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
2289
2290        INIT_G();
2291
2292        // Parse long options, like --bind and --move.  Note that -o option
2293        // and --option are synonymous.  Yes, this means --remount,rw works.
2294        for (i = j = 1; argv[i]; i++) {
2295                if (argv[i][0] == '-' && argv[i][1] == '-')
2296                        append_mount_options(&cmdopts, argv[i] + 2);
2297                else
2298                        argv[j++] = argv[i];
2299        }
2300        argv[j] = NULL;
2301
2302        // Parse remaining options
2303        // Max 2 params; -o is a list, -v is a counter
2304        opt = getopt32(argv, "^"
2305                OPTION_STR
2306                "\0" "?2"IF_FEATURE_MOUNT_VERBOSE("vv"),
2307                &lst_o, &fstype, &O_optmatch
2308                IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
2309                IF_FEATURE_MOUNT_VERBOSE(, &verbose)
2310        );
2311
2312        while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
2313        if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2314        if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
2315        argv += optind;
2316
2317        // If we have no arguments, show currently mounted filesystems
2318        if (!argv[0]) {
2319                if (!(opt & OPT_a)) {
2320                        FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2321
2322                        if (!mountTable)
2323                                bb_error_msg_and_die("no %s", bb_path_mtab_file);
2324
2325                        while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
2326                                                                GETMNTENT_BUFSIZE))
2327                        {
2328                                // Don't show rootfs. FIXME: why??
2329                                // util-linux 2.12a happily shows rootfs...
2330                                //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
2331
2332                                if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
2333                                        printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2334                                                        mtpair->mnt_dir, mtpair->mnt_type,
2335                                                        mtpair->mnt_opts);
2336                        }
2337                        if (ENABLE_FEATURE_CLEAN_UP)
2338                                endmntent(mountTable);
2339                        return EXIT_SUCCESS;
2340                }
2341                storage_path = NULL;
2342        } else {
2343                // When we have two arguments, the second is the directory and we can
2344                // skip looking at fstab entirely.  We can always abspath() the directory
2345                // argument when we get it.
2346                if (argv[1]) {
2347                        if (nonroot)
2348                                bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
2349                        mtpair->mnt_fsname = argv[0];
2350                        mtpair->mnt_dir = argv[1];
2351                        mtpair->mnt_type = fstype;
2352                        mtpair->mnt_opts = cmdopts;
2353                        resolve_mount_spec(&mtpair->mnt_fsname);
2354                        rc = singlemount(mtpair, /*ignore_busy:*/ 0);
2355                        return rc;
2356                }
2357                storage_path = bb_simplify_path(argv[0]); // malloced
2358        }
2359
2360        // Past this point, we are handling either "mount -a [opts]"
2361        // or "mount [opts] single_param"
2362
2363        cmdopt_flags = parse_mount_options(cmdopts, NULL, &option_mask32);
2364        if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
2365                bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
2366
2367        // If we have a shared subtree flag, don't worry about fstab or mtab.
2368        if (ENABLE_FEATURE_MOUNT_FLAGS
2369         && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
2370        ) {
2371                // verbose_mount(source, target, type, flags, data)
2372                rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
2373                if (rc)
2374                        bb_simple_perror_msg_and_die(argv[0]);
2375                return rc;
2376        }
2377
2378        // A malicious user could overmount /usr without this.
2379        if (ENABLE_FEATURE_MOUNT_OTHERTAB && nonroot)
2380                fstabname = "/etc/fstab";
2381        // Open either fstab or mtab
2382        if (cmdopt_flags & MS_REMOUNT) {
2383                // WARNING. I am not sure this matches util-linux's
2384                // behavior. It's possible util-linux does not
2385                // take -o opts from mtab (takes only mount source).
2386                fstabname = bb_path_mtab_file;
2387        }
2388        fstab = setmntent(fstabname, "r");
2389        if (!fstab)
2390                bb_perror_msg_and_die("can't read '%s'", fstabname);
2391
2392        // Loop through entries until we find what we're looking for
2393        memset(mtpair, 0, sizeof(mtpair));
2394        for (;;) {
2395                struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
2396
2397                // Get next fstab entry
2398                if (!getmntent_r(fstab, mtcur, getmntent_buf
2399                                        + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
2400                                GETMNTENT_BUFSIZE/2)
2401                ) { // End of fstab/mtab is reached
2402                        mtcur = mtother; // the thing we found last time
2403                        break;
2404                }
2405
2406                // If we're trying to mount something specific and this isn't it,
2407                // skip it.  Note we must match the exact text in fstab (ala
2408                // "proc") or a full path from root
2409                if (argv[0]) {
2410
2411                        // Is this what we're looking for?
2412                        if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2413                         && strcmp(storage_path, mtcur->mnt_fsname) != 0
2414                         && strcmp(argv[0], mtcur->mnt_dir) != 0
2415                         && strcmp(storage_path, mtcur->mnt_dir) != 0
2416                        ) {
2417                                continue; // no
2418                        }
2419
2420                        // Remember this entry.  Something later may have
2421                        // overmounted it, and we want the _last_ match.
2422                        mtcur = mtother;
2423
2424                // If we're mounting all
2425                } else {
2426                        struct mntent *mp;
2427                        // No, mount -a won't mount anything,
2428                        // even user mounts, for mere humans
2429                        if (nonroot)
2430                                bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
2431
2432                        // Does type match? (NULL matches always)
2433                        if (!fstype_matches(mtcur->mnt_type, fstype))
2434                                continue;
2435
2436                        // Skip noauto and swap anyway
2437                        if ((parse_mount_options(mtcur->mnt_opts, NULL, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2438                        // swap is bogus "fstype", parse_mount_options can't check fstypes
2439                         || strcasecmp(mtcur->mnt_type, "swap") == 0
2440                        ) {
2441                                continue;
2442                        }
2443
2444                        // Does (at least one) option match?
2445                        // (NULL matches always)
2446                        if (!match_opt(mtcur->mnt_opts, O_optmatch))
2447                                continue;
2448
2449                        resolve_mount_spec(&mtcur->mnt_fsname);
2450
2451                        // NFS mounts want this to be xrealloc-able
2452                        mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2453
2454                        // If nothing is mounted on this directory...
2455                        // (otherwise repeated "mount -a" mounts everything again)
2456                        mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2457                        // We do not check fsname match of found mount point -
2458                        // "/" may have fsname of "/dev/root" while fstab
2459                        // says "/dev/something_else".
2460                        if (mp) {
2461                                if (verbose) {
2462                                        bb_error_msg("according to %s, "
2463                                                "%s is already mounted on %s",
2464                                                bb_path_mtab_file,
2465                                                mp->mnt_fsname, mp->mnt_dir);
2466                                }
2467                        } else {
2468                                // ...mount this thing
2469                                if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2470                                        // Count number of failed mounts
2471                                        rc++;
2472                                }
2473                        }
2474                        free(mtcur->mnt_opts);
2475                }
2476        }
2477
2478        // End of fstab/mtab is reached.
2479        // Were we looking for something specific?
2480        if (argv[0]) { // yes
2481                unsigned long l;
2482
2483                // If we didn't find anything, complain
2484                if (!mtcur->mnt_fsname)
2485                        bb_error_msg_and_die("can't find %s in %s",
2486                                argv[0], fstabname);
2487
2488                // What happens when we try to "mount swap_partition"?
2489                // (fstab containts "swap_partition swap swap defaults 0 0")
2490                // util-linux-ng 2.13.1 does this:
2491                // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2492                // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2493                // lstat("swap", 0x7fff62a3a640)           = -1 ENOENT (No such file or directory)
2494                // write(2, "mount: mount point swap does not exist\n", 39) = 39
2495                // exit_group(32)                          = ?
2496#if 0
2497                // In case we want to simply skip swap partitions:
2498                l = parse_mount_options(mtcur->mnt_opts, NULL, NULL);
2499                if ((l & MOUNT_SWAP)
2500                // swap is bogus "fstype", parse_mount_options can't check fstypes
2501                 || strcasecmp(mtcur->mnt_type, "swap") == 0
2502                ) {
2503                        goto ret;
2504                }
2505#endif
2506                if (nonroot) {
2507                        // fstab must have "users" or "user"
2508                        l = parse_mount_options(mtcur->mnt_opts, NULL, NULL);
2509                        if (!(l & MOUNT_USERS))
2510                                bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
2511                }
2512
2513                //util-linux-2.12 does not do this check.
2514                //// If nothing is mounted on this directory...
2515                //// (otherwise repeated "mount FOO" mounts FOO again)
2516                //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2517                //if (mp) {
2518                //      bb_error_msg("according to %s, "
2519                //              "%s is already mounted on %s",
2520                //              bb_path_mtab_file,
2521                //              mp->mnt_fsname, mp->mnt_dir);
2522                //} else {
2523                        // ...mount the last thing we found
2524                        mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2525                        append_mount_options(&(mtcur->mnt_opts), cmdopts);
2526                        resolve_mount_spec(&mtpair->mnt_fsname);
2527                        rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2528                        if (ENABLE_FEATURE_CLEAN_UP)
2529                                free(mtcur->mnt_opts);
2530                //}
2531        }
2532
2533 //ret:
2534        if (ENABLE_FEATURE_CLEAN_UP)
2535                endmntent(fstab);
2536        if (ENABLE_FEATURE_CLEAN_UP) {
2537                free(storage_path);
2538                free(cmdopts);
2539        }
2540
2541//TODO: exitcode should be ORed mask of (from "man mount"):
2542// 0 success
2543// 1 incorrect invocation or permissions
2544// 2 system error (out of memory, cannot fork, no more loop devices)
2545// 4 internal mount bug or missing nfs support in mount
2546// 8 user interrupt
2547//16 problems writing or locking /etc/mtab
2548//32 mount failure
2549//64 some mount succeeded
2550        return rc;
2551}
2552