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