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