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