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