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