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) { return -ENOSYS; }
1198#endif
1199
1200/* TODO */
1201static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
1202{
1203        return 0;
1204}
1205
1206/* RPC strerror analogs are terminally idiotic:
1207 * *mandatory* prefix and \n at end.
1208 * This hopefully helps. Usage:
1209 * error_msg_rpc(clnt_*error*(" ")) */
1210static void error_msg_rpc(const char *msg)
1211{
1212        int len;
1213        while (msg[0] == ' ' || msg[0] == ':') msg++;
1214        len = strlen(msg);
1215        while (len && msg[len-1] == '\n') len--;
1216        bb_error_msg("%.*s", len, msg);
1217}
1218
1219/* NB: mp->xxx fields may be trashed on exit */
1220static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1221{
1222        CLIENT *mclient;
1223        char *hostname;
1224        char *pathname;
1225        char *mounthost;
1226        /* prior to 2.6.23, kernel took NFS options in a form of this struct
1227         * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1228         * then data pointer is interpreted as a string. */
1229        struct nfs_mount_data data;
1230        char *opt;
1231        struct hostent *hp;
1232        struct sockaddr_in server_addr;
1233        struct sockaddr_in mount_server_addr;
1234        int msock, fsock;
1235        union {
1236                struct fhstatus nfsv2;
1237                struct mountres3 nfsv3;
1238        } status;
1239        int daemonized;
1240        char *s;
1241        int port;
1242        int mountport;
1243        int proto;
1244#if BB_MMU
1245        smallint bg = 0;
1246#else
1247        enum { bg = 0 };
1248#endif
1249        int retry;
1250        int mountprog;
1251        int mountvers;
1252        int nfsprog;
1253        int nfsvers;
1254        int retval;
1255        /* these all are one-bit really. gcc 4.3.1 likes this combination: */
1256        smallint tcp;
1257        smallint soft;
1258        int intr;
1259        int posix;
1260        int nocto;
1261        int noac;
1262        int nordirplus;
1263        int nolock;
1264        int noacl;
1265
1266        find_kernel_nfs_mount_version();
1267
1268        daemonized = 0;
1269        mounthost = NULL;
1270        retval = ETIMEDOUT;
1271        msock = fsock = -1;
1272        mclient = NULL;
1273
1274        /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1275
1276        filteropts = xstrdup(filteropts); /* going to trash it later... */
1277
1278        hostname = xstrdup(mp->mnt_fsname);
1279        /* mount_main() guarantees that ':' is there */
1280        s = strchr(hostname, ':');
1281        pathname = s + 1;
1282        *s = '\0';
1283        /* Ignore all but first hostname in replicated mounts
1284         * until they can be fully supported. (mack@sgi.com) */
1285        s = strchr(hostname, ',');
1286        if (s) {
1287                *s = '\0';
1288                bb_error_msg("warning: multiple hostnames not supported");
1289        }
1290
1291        server_addr.sin_family = AF_INET;
1292        if (!inet_aton(hostname, &server_addr.sin_addr)) {
1293                hp = gethostbyname(hostname);
1294                if (hp == NULL) {
1295                        bb_herror_msg("%s", hostname);
1296                        goto fail;
1297                }
1298                if (hp->h_length != (int)sizeof(struct in_addr)) {
1299                        bb_error_msg_and_die("only IPv4 is supported");
1300                }
1301                memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1302        }
1303
1304        memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1305
1306        /* add IP address to mtab options for use when unmounting */
1307
1308        if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1309                mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1310        } else {
1311                char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1312                                        mp->mnt_opts[0] ? "," : "",
1313                                        inet_ntoa(server_addr.sin_addr));
1314                free(mp->mnt_opts);
1315                mp->mnt_opts = tmp;
1316        }
1317
1318        /* Set default options.
1319         * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1320         * let the kernel decide.
1321         * timeo is filled in after we know whether it'll be TCP or UDP. */
1322        memset(&data, 0, sizeof(data));
1323        data.retrans  = 3;
1324        data.acregmin = 3;
1325        data.acregmax = 60;
1326        data.acdirmin = 30;
1327        data.acdirmax = 60;
1328        data.namlen   = NAME_MAX;
1329
1330        soft = 0;
1331        intr = 0;
1332        posix = 0;
1333        nocto = 0;
1334        nolock = 0;
1335        noac = 0;
1336        nordirplus = 0;
1337        noacl = 0;
1338        retry = 10000;          /* 10000 minutes ~ 1 week */
1339        tcp = 1;                        /* nfs-utils uses tcp per default */
1340
1341        mountprog = MOUNTPROG;
1342        mountvers = 0;
1343        port = 0;
1344        mountport = 0;
1345        nfsprog = 100003;
1346        nfsvers = 0;
1347
1348        /* parse options */
1349        if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
1350                char *opteq = strchr(opt, '=');
1351                if (opteq) {
1352                        int val, idx;
1353                        static const char options[] ALIGN1 =
1354                                /* 0 */ "rsize\0"
1355                                /* 1 */ "wsize\0"
1356                                /* 2 */ "timeo\0"
1357                                /* 3 */ "retrans\0"
1358                                /* 4 */ "acregmin\0"
1359                                /* 5 */ "acregmax\0"
1360                                /* 6 */ "acdirmin\0"
1361                                /* 7 */ "acdirmax\0"
1362                                /* 8 */ "actimeo\0"
1363                                /* 9 */ "retry\0"
1364                                /* 10 */ "port\0"
1365                                /* 11 */ "mountport\0"
1366                                /* 12 */ "mounthost\0"
1367                                /* 13 */ "mountprog\0"
1368                                /* 14 */ "mountvers\0"
1369                                /* 15 */ "nfsprog\0"
1370                                /* 16 */ "nfsvers\0"
1371                                /* 17 */ "vers\0"
1372                                /* 18 */ "proto\0"
1373                                /* 19 */ "namlen\0"
1374                                /* 20 */ "addr\0";
1375
1376                        *opteq++ = '\0';
1377                        idx = index_in_strings(options, opt);
1378                        switch (idx) {
1379                        case 12: // "mounthost"
1380                                mounthost = xstrndup(opteq,
1381                                                strcspn(opteq, " \t\n\r,"));
1382                                continue;
1383                        case 18: // "proto"
1384                                if (is_prefixed_with(opteq, "tcp"))
1385                                        tcp = 1;
1386                                else if (is_prefixed_with(opteq, "udp"))
1387                                        tcp = 0;
1388                                else
1389                                        bb_error_msg("warning: unrecognized proto= option");
1390                                continue;
1391                        case 20: // "addr" - ignore
1392                                continue;
1393                        case -1: // unknown
1394                                if (vfsflags & MS_REMOUNT)
1395                                        continue;
1396                        }
1397
1398                        val = xatoi_positive(opteq);
1399                        switch (idx) {
1400                        case 0: // "rsize"
1401                                data.rsize = val;
1402                                continue;
1403                        case 1: // "wsize"
1404                                data.wsize = val;
1405                                continue;
1406                        case 2: // "timeo"
1407                                data.timeo = val;
1408                                continue;
1409                        case 3: // "retrans"
1410                                data.retrans = val;
1411                                continue;
1412                        case 4: // "acregmin"
1413                                data.acregmin = val;
1414                                continue;
1415                        case 5: // "acregmax"
1416                                data.acregmax = val;
1417                                continue;
1418                        case 6: // "acdirmin"
1419                                data.acdirmin = val;
1420                                continue;
1421                        case 7: // "acdirmax"
1422                                data.acdirmax = val;
1423                                continue;
1424                        case 8: // "actimeo"
1425                                data.acregmin = val;
1426                                data.acregmax = val;
1427                                data.acdirmin = val;
1428                                data.acdirmax = val;
1429                                continue;
1430                        case 9: // "retry"
1431                                retry = val;
1432                                continue;
1433                        case 10: // "port"
1434                                port = val;
1435                                continue;
1436                        case 11: // "mountport"
1437                                mountport = val;
1438                                continue;
1439                        case 13: // "mountprog"
1440                                mountprog = val;
1441                                continue;
1442                        case 14: // "mountvers"
1443                                mountvers = val;
1444                                continue;
1445                        case 15: // "nfsprog"
1446                                nfsprog = val;
1447                                continue;
1448                        case 16: // "nfsvers"
1449                        case 17: // "vers"
1450                                nfsvers = val;
1451                                continue;
1452                        case 19: // "namlen"
1453                                //if (nfs_mount_version >= 2)
1454                                        data.namlen = val;
1455                                //else
1456                                //      bb_error_msg("warning: option namlen is not supported\n");
1457                                continue;
1458                        default:
1459                                bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1460                                goto fail;
1461                        }
1462                }
1463                else { /* not of the form opt=val */
1464                        static const char options[] ALIGN1 =
1465                                "bg\0"
1466                                "fg\0"
1467                                "soft\0"
1468                                "hard\0"
1469                                "intr\0"
1470                                "posix\0"
1471                                "cto\0"
1472                                "ac\0"
1473                                "tcp\0"
1474                                "udp\0"
1475                                "lock\0"
1476                                "rdirplus\0"
1477                                "acl\0";
1478                        int val = 1;
1479                        if (is_prefixed_with(opt, "no")) {
1480                                val = 0;
1481                                opt += 2;
1482                        }
1483                        switch (index_in_strings(options, opt)) {
1484                        case 0: // "bg"
1485#if BB_MMU
1486                                bg = val;
1487#endif
1488                                break;
1489                        case 1: // "fg"
1490#if BB_MMU
1491                                bg = !val;
1492#endif
1493                                break;
1494                        case 2: // "soft"
1495                                soft = val;
1496                                break;
1497                        case 3: // "hard"
1498                                soft = !val;
1499                                break;
1500                        case 4: // "intr"
1501                                intr = val;
1502                                break;
1503                        case 5: // "posix"
1504                                posix = val;
1505                                break;
1506                        case 6: // "cto"
1507                                nocto = !val;
1508                                break;
1509                        case 7: // "ac"
1510                                noac = !val;
1511                                break;
1512                        case 8: // "tcp"
1513                                tcp = val;
1514                                break;
1515                        case 9: // "udp"
1516                                tcp = !val;
1517                                break;
1518                        case 10: // "lock"
1519                                if (nfs_mount_version >= 3)
1520                                        nolock = !val;
1521                                else
1522                                        bb_error_msg("warning: option nolock is not supported");
1523                                break;
1524                        case 11: //rdirplus
1525                                nordirplus = !val;
1526                                break;
1527                        case 12: // acl
1528                                noacl = !val;
1529                                break;
1530                        default:
1531                                bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1532                                goto fail;
1533                        }
1534                }
1535        }
1536        proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1537
1538        data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1539                | (intr ? NFS_MOUNT_INTR : 0)
1540                | (posix ? NFS_MOUNT_POSIX : 0)
1541                | (nocto ? NFS_MOUNT_NOCTO : 0)
1542                | (noac ? NFS_MOUNT_NOAC : 0)
1543                | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
1544                | (noacl ? NFS_MOUNT_NOACL : 0);
1545        if (nfs_mount_version >= 2)
1546                data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1547        if (nfs_mount_version >= 3)
1548                data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1549        if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1550                bb_error_msg("NFSv%d not supported", nfsvers);
1551                goto fail;
1552        }
1553        if (nfsvers && !mountvers)
1554                mountvers = (nfsvers < 3) ? 1 : nfsvers;
1555        if (nfsvers && nfsvers < mountvers) {
1556                mountvers = nfsvers;
1557        }
1558
1559        /* Adjust options if none specified */
1560        if (!data.timeo)
1561                data.timeo = tcp ? 70 : 7;
1562
1563        data.version = nfs_mount_version;
1564
1565        if (vfsflags & MS_REMOUNT)
1566                goto do_mount;
1567
1568        /*
1569         * If the previous mount operation on the same host was
1570         * backgrounded, and the "bg" for this mount is also set,
1571         * give up immediately, to avoid the initial timeout.
1572         */
1573        if (bg && we_saw_this_host_before(hostname)) {
1574                daemonized = daemonize();
1575                if (daemonized <= 0) { /* parent or error */
1576                        retval = -daemonized;
1577                        goto ret;
1578                }
1579        }
1580
1581        /* Create mount daemon client */
1582        /* See if the nfs host = mount host. */
1583        if (mounthost) {
1584                if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1585                        mount_server_addr.sin_family = AF_INET;
1586                        mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1587                } else {
1588                        hp = gethostbyname(mounthost);
1589                        if (hp == NULL) {
1590                                bb_herror_msg("%s", mounthost);
1591                                goto fail;
1592                        }
1593                        if (hp->h_length != (int)sizeof(struct in_addr)) {
1594                                bb_error_msg_and_die("only IPv4 is supported");
1595                        }
1596                        mount_server_addr.sin_family = AF_INET;
1597                        memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1598                }
1599        }
1600
1601        /*
1602         * The following loop implements the mount retries. When the mount
1603         * times out, and the "bg" option is set, we background ourself
1604         * and continue trying.
1605         *
1606         * The case where the mount point is not present and the "bg"
1607         * option is set, is treated as a timeout. This is done to
1608         * support nested mounts.
1609         *
1610         * The "retry" count specified by the user is the number of
1611         * minutes to retry before giving up.
1612         */
1613        {
1614                struct timeval total_timeout;
1615                struct timeval retry_timeout;
1616                struct pmap pm_mnt;
1617                time_t t;
1618                time_t prevt;
1619                time_t timeout;
1620
1621                retry_timeout.tv_sec = 3;
1622                retry_timeout.tv_usec = 0;
1623                total_timeout.tv_sec = 20;
1624                total_timeout.tv_usec = 0;
1625/* FIXME: use monotonic()? */
1626                timeout = time(NULL) + 60 * retry;
1627                prevt = 0;
1628                t = 30;
1629 retry:
1630                /* Be careful not to use too many CPU cycles */
1631                if (t - prevt < 30)
1632                        sleep(30);
1633
1634                get_mountport(&pm_mnt, &mount_server_addr,
1635                                mountprog,
1636                                mountvers,
1637                                proto,
1638                                mountport);
1639                nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
1640
1641                /* contact the mount daemon via TCP */
1642                mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1643                msock = RPC_ANYSOCK;
1644
1645                switch (pm_mnt.pm_prot) {
1646                case IPPROTO_UDP:
1647                        mclient = clntudp_create(&mount_server_addr,
1648                                                pm_mnt.pm_prog,
1649                                                pm_mnt.pm_vers,
1650                                                retry_timeout,
1651                                                &msock);
1652                        if (mclient)
1653                                break;
1654                        mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1655                        msock = RPC_ANYSOCK;
1656                case IPPROTO_TCP:
1657                        mclient = clnttcp_create(&mount_server_addr,
1658                                                pm_mnt.pm_prog,
1659                                                pm_mnt.pm_vers,
1660                                                &msock, 0, 0);
1661                        break;
1662                default:
1663                        mclient = NULL;
1664                }
1665                if (!mclient) {
1666                        if (!daemonized && prevt == 0)
1667                                error_msg_rpc(clnt_spcreateerror(" "));
1668                } else {
1669                        enum clnt_stat clnt_stat;
1670
1671                        /* Try to mount hostname:pathname */
1672                        mclient->cl_auth = authunix_create_default();
1673
1674                        /* Make pointers in xdr_mountres3 NULL so
1675                         * that xdr_array allocates memory for us
1676                         */
1677                        memset(&status, 0, sizeof(status));
1678
1679                        if (pm_mnt.pm_vers == 3)
1680                                clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1681                                                (xdrproc_t) xdr_dirpath,
1682                                                (caddr_t) &pathname,
1683                                                (xdrproc_t) xdr_mountres3,
1684                                                (caddr_t) &status,
1685                                                total_timeout);
1686                        else
1687                                clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1688                                                (xdrproc_t) xdr_dirpath,
1689                                                (caddr_t) &pathname,
1690                                                (xdrproc_t) xdr_fhstatus,
1691                                                (caddr_t) &status,
1692                                                total_timeout);
1693
1694                        if (clnt_stat == RPC_SUCCESS)
1695                                goto prepare_kernel_data; /* we're done */
1696                        if (errno != ECONNREFUSED) {
1697                                error_msg_rpc(clnt_sperror(mclient, " "));
1698                                goto fail;      /* don't retry */
1699                        }
1700                        /* Connection refused */
1701                        if (!daemonized && prevt == 0) /* print just once */
1702                                error_msg_rpc(clnt_sperror(mclient, " "));
1703                        auth_destroy(mclient->cl_auth);
1704                        clnt_destroy(mclient);
1705                        mclient = NULL;
1706                        close(msock);
1707                        msock = -1;
1708                }
1709
1710                /* Timeout. We are going to retry... maybe */
1711                if (!bg)
1712                        goto fail;
1713                if (!daemonized) {
1714                        daemonized = daemonize();
1715                        if (daemonized <= 0) { /* parent or error */
1716                                retval = -daemonized;
1717                                goto ret;
1718                        }
1719                }
1720                prevt = t;
1721                t = time(NULL);
1722                if (t >= timeout)
1723                        /* TODO error message */
1724                        goto fail;
1725
1726                goto retry;
1727        }
1728
1729 prepare_kernel_data:
1730
1731        if (nfsvers == 2) {
1732                if (status.nfsv2.fhs_status != 0) {
1733                        bb_error_msg("%s:%s failed, reason given by server: %s",
1734                                hostname, pathname,
1735                                nfs_strerror(status.nfsv2.fhs_status));
1736                        goto fail;
1737                }
1738                memcpy(data.root.data,
1739                                (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1740                                NFS_FHSIZE);
1741                data.root.size = NFS_FHSIZE;
1742                memcpy(data.old_root.data,
1743                                (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1744                                NFS_FHSIZE);
1745        } else {
1746                fhandle3 *my_fhandle;
1747                if (status.nfsv3.fhs_status != 0) {
1748                        bb_error_msg("%s:%s failed, reason given by server: %s",
1749                                hostname, pathname,
1750                                nfs_strerror(status.nfsv3.fhs_status));
1751                        goto fail;
1752                }
1753                my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1754                memset(data.old_root.data, 0, NFS_FHSIZE);
1755                memset(&data.root, 0, sizeof(data.root));
1756                data.root.size = my_fhandle->fhandle3_len;
1757                memcpy(data.root.data,
1758                                (char *) my_fhandle->fhandle3_val,
1759                                my_fhandle->fhandle3_len);
1760
1761                data.flags |= NFS_MOUNT_VER3;
1762        }
1763
1764        /* Create nfs socket for kernel */
1765        if (tcp) {
1766                if (nfs_mount_version < 3) {
1767                        bb_error_msg("NFS over TCP is not supported");
1768                        goto fail;
1769                }
1770                fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1771        } else
1772                fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1773        if (fsock < 0) {
1774                bb_perror_msg("nfs socket");
1775                goto fail;
1776        }
1777        if (bindresvport(fsock, 0) < 0) {
1778                bb_perror_msg("nfs bindresvport");
1779                goto fail;
1780        }
1781        if (port == 0) {
1782                server_addr.sin_port = PMAPPORT;
1783                port = pmap_getport(&server_addr, nfsprog, nfsvers,
1784                                        tcp ? IPPROTO_TCP : IPPROTO_UDP);
1785                if (port == 0)
1786                        port = NFS_PORT;
1787        }
1788        server_addr.sin_port = htons(port);
1789
1790        /* Prepare data structure for kernel */
1791        data.fd = fsock;
1792        memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1793        strncpy(data.hostname, hostname, sizeof(data.hostname));
1794
1795        /* Clean up */
1796        auth_destroy(mclient->cl_auth);
1797        clnt_destroy(mclient);
1798        close(msock);
1799        msock = -1;
1800
1801        if (bg) {
1802                /* We must wait until mount directory is available */
1803                struct stat statbuf;
1804                int delay = 1;
1805                while (stat(mp->mnt_dir, &statbuf) == -1) {
1806                        if (!daemonized) {
1807                                daemonized = daemonize();
1808                                if (daemonized <= 0) { /* parent or error */
1809/* FIXME: parent doesn't close fsock - ??! */
1810                                        retval = -daemonized;
1811                                        goto ret;
1812                                }
1813                        }
1814                        sleep(delay);   /* 1, 2, 4, 8, 16, 30, ... */
1815                        delay *= 2;
1816                        if (delay > 30)
1817                                delay = 30;
1818                }
1819        }
1820
1821        /* Perform actual mount */
1822 do_mount:
1823        retval = mount_it_now(mp, vfsflags, (char*)&data);
1824        goto ret;
1825
1826        /* Abort */
1827 fail:
1828        if (msock >= 0) {
1829                if (mclient) {
1830                        auth_destroy(mclient->cl_auth);
1831                        clnt_destroy(mclient);
1832                }
1833                close(msock);
1834        }
1835        if (fsock >= 0)
1836                close(fsock);
1837
1838 ret:
1839        free(hostname);
1840        free(mounthost);
1841        free(filteropts);
1842        return retval;
1843}
1844
1845#else // !ENABLE_FEATURE_MOUNT_NFS
1846
1847/* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1848 * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1849 * (However, note that then you lose any chances that NFS over IPv6 would work).
1850 */
1851static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1852{
1853        len_and_sockaddr *lsa;
1854        char *opts;
1855        char *end;
1856        char *dotted;
1857        int ret;
1858
1859# if ENABLE_FEATURE_IPV6
1860        end = strchr(mp->mnt_fsname, ']');
1861        if (end && end[1] == ':')
1862                end++;
1863        else
1864# endif
1865                /* mount_main() guarantees that ':' is there */
1866                end = strchr(mp->mnt_fsname, ':');
1867
1868        *end = '\0';
1869        lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
1870        *end = ':';
1871        dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1872        if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1873        opts = xasprintf("%s%saddr=%s",
1874                filteropts ? filteropts : "",
1875                filteropts ? "," : "",
1876                dotted
1877        );
1878        if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1879        ret = mount_it_now(mp, vfsflags, opts);
1880        if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1881
1882        return ret;
1883}
1884
1885#endif // !ENABLE_FEATURE_MOUNT_NFS
1886
1887// Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
1888// type detection.  Returns 0 for success, nonzero for failure.
1889// NB: mp->xxx fields may be trashed on exit
1890static int singlemount(struct mntent *mp, int ignore_busy)
1891{
1892        int loopfd = -1;
1893        int rc = -1;
1894        unsigned long vfsflags;
1895        char *loopFile = NULL, *filteropts = NULL;
1896        llist_t *fl = NULL;
1897        struct stat st;
1898
1899        errno = 0;
1900
1901        vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1902
1903        // Treat fstype "auto" as unspecified
1904        if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1905                mp->mnt_type = NULL;
1906
1907        // Might this be a virtual filesystem?
1908        if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1909                char *args[35];
1910                char *s;
1911                int n;
1912                // fsname: "cmd#arg1#arg2..."
1913                // WARNING: allows execution of arbitrary commands!
1914                // Try "mount 'sh#-c#sh' bogus_dir".
1915                // It is safe ONLY because non-root
1916                // cannot use two-argument mount command
1917                // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1918                // "mount: can't find sh#-c#sh in /etc/fstab"
1919                // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1920
1921                s = mp->mnt_fsname;
1922                n = 0;
1923                args[n++] = s;
1924                while (*s && n < 35 - 2) {
1925                        if (*s++ == '#' && *s != '#') {
1926                                s[-1] = '\0';
1927                                args[n++] = s;
1928                        }
1929                }
1930                args[n++] = mp->mnt_dir;
1931                args[n] = NULL;
1932                rc = spawn_and_wait(args);
1933                goto report_error;
1934        }
1935
1936        // Might this be an CIFS filesystem?
1937        if (ENABLE_FEATURE_MOUNT_CIFS
1938         && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1939         && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1940         && mp->mnt_fsname[0] == mp->mnt_fsname[1]
1941        ) {
1942                int len;
1943                char c;
1944                char *hostname, *share;
1945                len_and_sockaddr *lsa;
1946
1947                // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
1948
1949                hostname = mp->mnt_fsname + 2;
1950                len = strcspn(hostname, "/\\");
1951                share = hostname + len + 1;
1952                if (len == 0          // 3rd char is a [back]slash (IOW: empty hostname)
1953                 || share[-1] == '\0' // no [back]slash after hostname
1954                 || share[0] == '\0'  // empty share name
1955                ) {
1956                        goto report_error;
1957                }
1958                c = share[-1];
1959                share[-1] = '\0';
1960                len = strcspn(share, "/\\");
1961
1962                // "unc=\\hostname\share" option is mandatory
1963                // after CIFS option parsing was rewritten in Linux 3.4.
1964                // Must use backslashes.
1965                // If /dir1/dir2 is present, also add "prefixpath=dir1/dir2"
1966                {
1967                        char *unc = xasprintf(
1968                                share[len] != '\0'  /* "/dir1/dir2" exists? */
1969                                        ? "unc=\\\\%s\\%.*s,prefixpath=%s"
1970                                        : "unc=\\\\%s\\%.*s",
1971                                hostname,
1972                                len, share,
1973                                share + len + 1  /* "dir1/dir2" */
1974                        );
1975                        parse_mount_options(unc, &filteropts);
1976                        if (ENABLE_FEATURE_CLEAN_UP) free(unc);
1977                }
1978
1979                lsa = host2sockaddr(hostname, 0);
1980                share[-1] = c;
1981                if (!lsa)
1982                        goto report_error;
1983
1984                // If there is no "ip=..." option yet
1985                if (!is_prefixed_with(filteropts, ",ip="+1)
1986                 && !strstr(filteropts, ",ip=")
1987                ) {
1988                        char *dotted, *ip;
1989                        // Insert "ip=..." option into options
1990                        dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1991                        if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1992                        ip = xasprintf("ip=%s", dotted);
1993                        if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1994// Note: IPv6 scoped addresses ("host%iface", see RFC 4007) should be
1995// handled by libc in getnameinfo() (inside xmalloc_sockaddr2dotted_noport()).
1996// Currently, glibc does not support that (has no NI_NUMERICSCOPE),
1997// musl apparently does. This results in "ip=numericIPv6%iface_name"
1998// (instead of _numeric_ iface_id) with glibc.
1999// This probably should be fixed in glibc, not here.
2000// The workaround is to manually specify correct "ip=ADDR%n" option.
2001                        parse_mount_options(ip, &filteropts);
2002                        if (ENABLE_FEATURE_CLEAN_UP) free(ip);
2003                }
2004
2005                mp->mnt_type = (char*)"cifs";
2006                rc = mount_it_now(mp, vfsflags, filteropts);
2007
2008                goto report_error;
2009        }
2010
2011        // Might this be an NFS filesystem?
2012        if ((!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs"))
2013         && strchr(mp->mnt_fsname, ':') != NULL
2014        ) {
2015                if (!mp->mnt_type)
2016                        mp->mnt_type = (char*)"nfs";
2017                rc = nfsmount(mp, vfsflags, filteropts);
2018                goto report_error;
2019        }
2020
2021        // Look at the file.  (Not found isn't a failure for remount, or for
2022        // a synthetic filesystem like proc or sysfs.)
2023        // (We use stat, not lstat, in order to allow
2024        // mount symlink_to_file_or_blkdev dir)
2025        if (!stat(mp->mnt_fsname, &st)
2026         && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
2027        ) {
2028                // Do we need to allocate a loopback device for it?
2029                if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
2030                        loopFile = bb_simplify_path(mp->mnt_fsname);
2031                        mp->mnt_fsname = NULL; // will receive malloced loop dev name
2032
2033                        // mount always creates AUTOCLEARed loopdevs, so that umounting
2034                        // drops them without any code in the userspace.
2035                        // This happens since circa linux-2.6.25:
2036                        // commit 96c5865559cee0f9cbc5173f3c949f6ce3525581
2037                        // Date:    Wed Feb 6 01:36:27 2008 -0800
2038                        // Subject: Allow auto-destruction of loop devices
2039                        loopfd = set_loop(&mp->mnt_fsname,
2040                                        loopFile,
2041                                        0,
2042                                        ((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0)
2043                                                | BB_LO_FLAGS_AUTOCLEAR
2044                        );
2045                        if (loopfd < 0) {
2046                                if (errno == EPERM || errno == EACCES)
2047                                        bb_error_msg(bb_msg_perm_denied_are_you_root);
2048                                else
2049                                        bb_perror_msg("can't setup loop device");
2050                                return errno;
2051                        }
2052
2053                // Autodetect bind mounts
2054                } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
2055                        vfsflags |= MS_BIND;
2056        }
2057
2058        // If we know the fstype (or don't need to), jump straight
2059        // to the actual mount.
2060        if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
2061                char *next;
2062                for (;;) {
2063                        next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
2064                        if (next)
2065                                *next = '\0';
2066                        rc = mount_it_now(mp, vfsflags, filteropts);
2067                        if (rc == 0 || !next)
2068                                break;
2069                        mp->mnt_type = next + 1;
2070                }
2071        } else {
2072                // Loop through filesystem types until mount succeeds
2073                // or we run out
2074
2075                // Initialize list of block backed filesystems.
2076                // This has to be done here so that during "mount -a",
2077                // mounts after /proc shows up can autodetect.
2078                if (!fslist) {
2079                        fslist = get_block_backed_filesystems();
2080                        if (ENABLE_FEATURE_CLEAN_UP && fslist)
2081                                atexit(delete_block_backed_filesystems);
2082                }
2083
2084                for (fl = fslist; fl; fl = fl->link) {
2085                        mp->mnt_type = fl->data;
2086                        rc = mount_it_now(mp, vfsflags, filteropts);
2087                        if (rc == 0)
2088                                break;
2089                }
2090        }
2091
2092        // If mount failed, clean up loop file (if any).
2093        // (Newer kernels which support LO_FLAGS_AUTOCLEAR should not need this,
2094        // merely "close(loopfd)" should do it?)
2095        if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
2096                del_loop(mp->mnt_fsname);
2097                if (ENABLE_FEATURE_CLEAN_UP) {
2098                        free(loopFile);
2099                        /* No, "rc != 0" needs it: free(mp->mnt_fsname); */
2100                }
2101        }
2102
2103 report_error:
2104        if (ENABLE_FEATURE_CLEAN_UP)
2105                free(filteropts);
2106
2107        if (loopfd >= 0)
2108                close(loopfd);
2109
2110        if (errno == EBUSY && ignore_busy)
2111                return 0;
2112        if (errno == ENOENT && (vfsflags & MOUNT_NOFAIL))
2113                return 0;
2114        if (rc != 0)
2115                bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
2116        return rc;
2117}
2118
2119// -O support
2120//    -O interprets a list of filter options which select whether a mount
2121// point will be mounted: only mounts with options matching *all* filtering
2122// options will be selected.
2123//    By default each -O filter option must be present in the list of mount
2124// options, but if it is prefixed by "no" then it must be absent.
2125// For example,
2126//  -O a,nob,c  matches  -o a,c  but fails to match  -o a,b,c
2127//              (and also fails to match  -o a  because  -o c  is absent).
2128//
2129// It is different from -t in that each option is matched exactly; a leading
2130// "no" at the beginning of one option does not negate the rest.
2131static int match_opt(const char *fs_opt_in, const char *O_opt)
2132{
2133        if (!O_opt)
2134                return 1;
2135
2136        while (*O_opt) {
2137                const char *fs_opt = fs_opt_in;
2138                int O_len;
2139                int match;
2140
2141                // If option begins with "no" then treat as an inverted match:
2142                // matching is a failure
2143                match = 0;
2144                if (O_opt[0] == 'n' && O_opt[1] == 'o') {
2145                        match = 1;
2146                        O_opt += 2;
2147                }
2148                // Isolate the current O option
2149                O_len = strchrnul(O_opt, ',') - O_opt;
2150                // Check for a match against existing options
2151                while (1) {
2152                        if (strncmp(fs_opt, O_opt, O_len) == 0
2153                         && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
2154                        ) {
2155                                if (match)
2156                                        return 0;  // "no" prefix, but option found
2157                                match = 1;  // current O option found, go check next one
2158                                break;
2159                        }
2160                        fs_opt = strchr(fs_opt, ',');
2161                        if (!fs_opt)
2162                                break;
2163                        fs_opt++;
2164                }
2165                if (match == 0)
2166                        return 0;     // match wanted but not found
2167                if (O_opt[O_len] == '\0') // end?
2168                        break;
2169                // Step to the next O option
2170                O_opt += O_len + 1;
2171        }
2172        // If we get here then everything matched
2173        return 1;
2174}
2175
2176// Parse options, if necessary parse fstab/mtab, and call singlemount for
2177// each directory to be mounted.
2178int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
2179int mount_main(int argc UNUSED_PARAM, char **argv)
2180{
2181        char *cmdopts = xzalloc(1);
2182        char *fstype = NULL;
2183        char *O_optmatch = NULL;
2184        char *storage_path;
2185        llist_t *lst_o = NULL;
2186        const char *fstabname = "/etc/fstab";
2187        FILE *fstab;
2188        int i, j;
2189        int rc = EXIT_SUCCESS;
2190        unsigned long cmdopt_flags;
2191        unsigned opt;
2192        struct mntent mtpair[2], *mtcur = mtpair;
2193        IF_NOT_DESKTOP(const int nonroot = 0;)
2194
2195        IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
2196
2197        INIT_G();
2198
2199        // Parse long options, like --bind and --move.  Note that -o option
2200        // and --option are synonymous.  Yes, this means --remount,rw works.
2201        for (i = j = 1; argv[i]; i++) {
2202                if (argv[i][0] == '-' && argv[i][1] == '-')
2203                        append_mount_options(&cmdopts, argv[i] + 2);
2204                else
2205                        argv[j++] = argv[i];
2206        }
2207        argv[j] = NULL;
2208
2209        // Parse remaining options
2210        // Max 2 params; -o is a list, -v is a counter
2211        opt = getopt32(argv, "^"
2212                OPTION_STR
2213                "\0" "?2"IF_FEATURE_MOUNT_VERBOSE("vv"),
2214                &lst_o, &fstype, &O_optmatch
2215                IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
2216                IF_FEATURE_MOUNT_VERBOSE(, &verbose)
2217        );
2218
2219        while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
2220        if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2221        if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
2222        argv += optind;
2223
2224        // If we have no arguments, show currently mounted filesystems
2225        if (!argv[0]) {
2226                if (!(opt & OPT_a)) {
2227                        FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2228
2229                        if (!mountTable)
2230                                bb_error_msg_and_die("no %s", bb_path_mtab_file);
2231
2232                        while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
2233                                                                GETMNTENT_BUFSIZE))
2234                        {
2235                                // Don't show rootfs. FIXME: why??
2236                                // util-linux 2.12a happily shows rootfs...
2237                                //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
2238
2239                                if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
2240                                        printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2241                                                        mtpair->mnt_dir, mtpair->mnt_type,
2242                                                        mtpair->mnt_opts);
2243                        }
2244                        if (ENABLE_FEATURE_CLEAN_UP)
2245                                endmntent(mountTable);
2246                        return EXIT_SUCCESS;
2247                }
2248                storage_path = NULL;
2249        } else {
2250                // When we have two arguments, the second is the directory and we can
2251                // skip looking at fstab entirely.  We can always abspath() the directory
2252                // argument when we get it.
2253                if (argv[1]) {
2254                        if (nonroot)
2255                                bb_error_msg_and_die(bb_msg_you_must_be_root);
2256                        mtpair->mnt_fsname = argv[0];
2257                        mtpair->mnt_dir = argv[1];
2258                        mtpair->mnt_type = fstype;
2259                        mtpair->mnt_opts = cmdopts;
2260                        resolve_mount_spec(&mtpair->mnt_fsname);
2261                        rc = singlemount(mtpair, /*ignore_busy:*/ 0);
2262                        return rc;
2263                }
2264                storage_path = bb_simplify_path(argv[0]); // malloced
2265        }
2266
2267        // Past this point, we are handling either "mount -a [opts]"
2268        // or "mount [opts] single_param"
2269
2270        cmdopt_flags = parse_mount_options(cmdopts, NULL);
2271        if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
2272                bb_error_msg_and_die(bb_msg_you_must_be_root);
2273
2274        // If we have a shared subtree flag, don't worry about fstab or mtab.
2275        if (ENABLE_FEATURE_MOUNT_FLAGS
2276         && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
2277        ) {
2278                // verbose_mount(source, target, type, flags, data)
2279                rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
2280                if (rc)
2281                        bb_simple_perror_msg_and_die(argv[0]);
2282                return rc;
2283        }
2284
2285        // A malicious user could overmount /usr without this.
2286        if (ENABLE_FEATURE_MOUNT_OTHERTAB && nonroot)
2287                fstabname = "/etc/fstab";
2288        // Open either fstab or mtab
2289        if (cmdopt_flags & MS_REMOUNT) {
2290                // WARNING. I am not sure this matches util-linux's
2291                // behavior. It's possible util-linux does not
2292                // take -o opts from mtab (takes only mount source).
2293                fstabname = bb_path_mtab_file;
2294        }
2295        fstab = setmntent(fstabname, "r");
2296        if (!fstab)
2297                bb_perror_msg_and_die("can't read '%s'", fstabname);
2298
2299        // Loop through entries until we find what we're looking for
2300        memset(mtpair, 0, sizeof(mtpair));
2301        for (;;) {
2302                struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
2303
2304                // Get next fstab entry
2305                if (!getmntent_r(fstab, mtcur, getmntent_buf
2306                                        + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
2307                                GETMNTENT_BUFSIZE/2)
2308                ) { // End of fstab/mtab is reached
2309                        mtcur = mtother; // the thing we found last time
2310                        break;
2311                }
2312
2313                // If we're trying to mount something specific and this isn't it,
2314                // skip it.  Note we must match the exact text in fstab (ala
2315                // "proc") or a full path from root
2316                if (argv[0]) {
2317
2318                        // Is this what we're looking for?
2319                        if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2320                         && strcmp(storage_path, mtcur->mnt_fsname) != 0
2321                         && strcmp(argv[0], mtcur->mnt_dir) != 0
2322                         && strcmp(storage_path, mtcur->mnt_dir) != 0
2323                        ) {
2324                                continue; // no
2325                        }
2326
2327                        // Remember this entry.  Something later may have
2328                        // overmounted it, and we want the _last_ match.
2329                        mtcur = mtother;
2330
2331                // If we're mounting all
2332                } else {
2333                        struct mntent *mp;
2334                        // No, mount -a won't mount anything,
2335                        // even user mounts, for mere humans
2336                        if (nonroot)
2337                                bb_error_msg_and_die(bb_msg_you_must_be_root);
2338
2339                        // Does type match? (NULL matches always)
2340                        if (!fstype_matches(mtcur->mnt_type, fstype))
2341                                continue;
2342
2343                        // Skip noauto and swap anyway
2344                        if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2345                        // swap is bogus "fstype", parse_mount_options can't check fstypes
2346                         || strcasecmp(mtcur->mnt_type, "swap") == 0
2347                        ) {
2348                                continue;
2349                        }
2350
2351                        // Does (at least one) option match?
2352                        // (NULL matches always)
2353                        if (!match_opt(mtcur->mnt_opts, O_optmatch))
2354                                continue;
2355
2356                        resolve_mount_spec(&mtcur->mnt_fsname);
2357
2358                        // NFS mounts want this to be xrealloc-able
2359                        mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2360
2361                        // If nothing is mounted on this directory...
2362                        // (otherwise repeated "mount -a" mounts everything again)
2363                        mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2364                        // We do not check fsname match of found mount point -
2365                        // "/" may have fsname of "/dev/root" while fstab
2366                        // says "/dev/something_else".
2367                        if (mp) {
2368                                if (verbose) {
2369                                        bb_error_msg("according to %s, "
2370                                                "%s is already mounted on %s",
2371                                                bb_path_mtab_file,
2372                                                mp->mnt_fsname, mp->mnt_dir);
2373                                }
2374                        } else {
2375                                // ...mount this thing
2376                                if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2377                                        // Count number of failed mounts
2378                                        rc++;
2379                                }
2380                        }
2381                        free(mtcur->mnt_opts);
2382                }
2383        }
2384
2385        // End of fstab/mtab is reached.
2386        // Were we looking for something specific?
2387        if (argv[0]) { // yes
2388                unsigned long l;
2389
2390                // If we didn't find anything, complain
2391                if (!mtcur->mnt_fsname)
2392                        bb_error_msg_and_die("can't find %s in %s",
2393                                argv[0], fstabname);
2394
2395                // What happens when we try to "mount swap_partition"?
2396                // (fstab containts "swap_partition swap swap defaults 0 0")
2397                // util-linux-ng 2.13.1 does this:
2398                // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2399                // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2400                // lstat("swap", 0x7fff62a3a640)           = -1 ENOENT (No such file or directory)
2401                // write(2, "mount: mount point swap does not exist\n", 39) = 39
2402                // exit_group(32)                          = ?
2403#if 0
2404                // In case we want to simply skip swap partitions:
2405                l = parse_mount_options(mtcur->mnt_opts, NULL);
2406                if ((l & MOUNT_SWAP)
2407                // swap is bogus "fstype", parse_mount_options can't check fstypes
2408                 || strcasecmp(mtcur->mnt_type, "swap") == 0
2409                ) {
2410                        goto ret;
2411                }
2412#endif
2413                if (nonroot) {
2414                        // fstab must have "users" or "user"
2415                        l = parse_mount_options(mtcur->mnt_opts, NULL);
2416                        if (!(l & MOUNT_USERS))
2417                                bb_error_msg_and_die(bb_msg_you_must_be_root);
2418                }
2419
2420                //util-linux-2.12 does not do this check.
2421                //// If nothing is mounted on this directory...
2422                //// (otherwise repeated "mount FOO" mounts FOO again)
2423                //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2424                //if (mp) {
2425                //      bb_error_msg("according to %s, "
2426                //              "%s is already mounted on %s",
2427                //              bb_path_mtab_file,
2428                //              mp->mnt_fsname, mp->mnt_dir);
2429                //} else {
2430                        // ...mount the last thing we found
2431                        mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2432                        append_mount_options(&(mtcur->mnt_opts), cmdopts);
2433                        resolve_mount_spec(&mtpair->mnt_fsname);
2434                        rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2435                        if (ENABLE_FEATURE_CLEAN_UP)
2436                                free(mtcur->mnt_opts);
2437                //}
2438        }
2439
2440 //ret:
2441        if (ENABLE_FEATURE_CLEAN_UP)
2442                endmntent(fstab);
2443        if (ENABLE_FEATURE_CLEAN_UP) {
2444                free(storage_path);
2445                free(cmdopts);
2446        }
2447
2448//TODO: exitcode should be ORed mask of (from "man mount"):
2449// 0 success
2450// 1 incorrect invocation or permissions
2451// 2 system error (out of memory, cannot fork, no more loop devices)
2452// 4 internal mount bug or missing nfs support in mount
2453// 8 user interrupt
2454//16 problems writing or locking /etc/mtab
2455//32 mount failure
2456//64 some mount succeeded
2457        return rc;
2458}
2459