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