Ignore:
Timestamp:
Nov 4, 2007, 3:16:40 AM (16 years ago)
Author:
Bruno Cornec
Message:

Update to busybox 1.7.2

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.5/mindi-busybox/util-linux/mount.c

    r902 r1765  
    1010 */
    1111
    12 /* todo:
    13  * bb_getopt_ulflags();
    14  */
    15 
    1612/* Design notes: There is no spec for mount.  Remind me to write one.
    1713
     
    2319*/
    2420
    25 #include "busybox.h"
    26 #include <unistd.h>
    27 #include <errno.h>
    28 #include <string.h>
     21#include "libbb.h"
    2922#include <mntent.h>
    30 #include <ctype.h>
    31 #include <fcntl.h>      // for CONFIG_FEATURE_MOUNT_LOOP
    32 #include <sys/ioctl.h>  // for CONFIG_FEATURE_MOUNT_LOOP
    33 
    34 // These two aren't always defined in old headers
    35 #ifndef MS_BIND
    36 #define MS_BIND     4096
     23
     24/* Needed for nfs support only... */
     25#include <syslog.h>
     26#include <sys/utsname.h>
     27#undef TRUE
     28#undef FALSE
     29#include <rpc/rpc.h>
     30#include <rpc/pmap_prot.h>
     31#include <rpc/pmap_clnt.h>
     32
     33
     34#if defined(__dietlibc__)
     35/* 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
     36 * dietlibc-0.30 does not have implementation of getmntent_r() */
     37/* OTOH: why we use getmntent_r instead of getmntent? TODO... */
     38struct mntent *getmntent_r(FILE* stream, struct mntent* result, char* buffer, int bufsize)
     39{
     40    /* *** XXX FIXME WARNING: This hack is NOT thread safe. --Sampo */
     41    struct mntent* ment = getmntent(stream);
     42    memcpy(result, ment, sizeof(struct mntent));
     43    return result;
     44}
    3745#endif
    38 #ifndef MS_MOVE
    39 #define MS_MOVE     8192
    40 #endif
    41 #ifndef MS_SILENT
    42 #define MS_SILENT   32768
    43 #endif
     46
    4447
    4548// Not real flags, but we want to be able to check for this.
    46 #define MOUNT_NOAUTO    (1<<29)
    47 #define MOUNT_SWAP      (1<<30)
     49enum {
     50    MOUNT_USERS  = (1<<28)*ENABLE_DESKTOP,
     51    MOUNT_NOAUTO = (1<<29),
     52    MOUNT_SWAP   = (1<<30),
     53};
     54// TODO: more "user" flag compatibility.
     55// "user" option (from mount manpage):
     56// Only the user that mounted a filesystem can unmount it again.
     57// If any user should be able to unmount, then use users instead of user
     58// in the fstab line.  The owner option is similar to the user option,
     59// with the restriction that the user must be the owner of the special file.
     60// This may be useful e.g. for /dev/fd if a login script makes
     61// the console user owner of this device.
     62
    4863/* Standard mount options (from -o options or --options), with corresponding
    4964 * flags */
     
    5267    const char *name;
    5368    long flags;
    54 } static const mount_options[] = {
    55     // NOP flags.
    56 
    57     {"loop", 0},
    58     {"defaults", 0},
    59     {"quiet", 0},
    60 
    61     // vfs flags
    62 
    63     {"ro", MS_RDONLY},
    64     {"rw", ~MS_RDONLY},
    65     {"nosuid", MS_NOSUID},
    66     {"suid", ~MS_NOSUID},
    67     {"dev", ~MS_NODEV},
    68     {"nodev", MS_NODEV},
    69     {"exec", ~MS_NOEXEC},
    70     {"noexec", MS_NOEXEC},
    71     {"sync", MS_SYNCHRONOUS},
    72     {"async", ~MS_SYNCHRONOUS},
    73     {"atime", ~MS_NOATIME},
    74     {"noatime", MS_NOATIME},
    75     {"diratime", ~MS_NODIRATIME},
    76     {"nodiratime", MS_NODIRATIME},
    77     {"loud", ~MS_SILENT},
    78 
    79     // action flags
    80 
    81     {"remount", MS_REMOUNT},
    82     {"bind", MS_BIND},
    83     {"move", MS_MOVE},
    84     {"noauto",MOUNT_NOAUTO},
    85     {"swap",MOUNT_SWAP}
     69} static mount_options[] = {
     70    // MS_FLAGS set a bit.  ~MS_FLAGS disable that bit.  0 flags are NOPs.
     71
     72    USE_FEATURE_MOUNT_LOOP(
     73        {"loop", 0},
     74    )
     75
     76    USE_FEATURE_MOUNT_FSTAB(
     77        {"defaults", 0},
     78        /* {"quiet", 0}, - do not filter out, vfat wants to see it */
     79        {"noauto", MOUNT_NOAUTO},
     80        {"sw", MOUNT_SWAP},
     81        {"swap", MOUNT_SWAP},
     82        USE_DESKTOP({"user",  MOUNT_USERS},)
     83        USE_DESKTOP({"users", MOUNT_USERS},)
     84    )
     85
     86    USE_FEATURE_MOUNT_FLAGS(
     87        // vfs flags
     88        {"nosuid", MS_NOSUID},
     89        {"suid", ~MS_NOSUID},
     90        {"dev", ~MS_NODEV},
     91        {"nodev", MS_NODEV},
     92        {"exec", ~MS_NOEXEC},
     93        {"noexec", MS_NOEXEC},
     94        {"sync", MS_SYNCHRONOUS},
     95        {"async", ~MS_SYNCHRONOUS},
     96        {"atime", ~MS_NOATIME},
     97        {"noatime", MS_NOATIME},
     98        {"diratime", ~MS_NODIRATIME},
     99        {"nodiratime", MS_NODIRATIME},
     100        {"loud", ~MS_SILENT},
     101
     102        // action flags
     103
     104        {"bind", MS_BIND},
     105        {"move", MS_MOVE},
     106        {"shared", MS_SHARED},
     107        {"slave", MS_SLAVE},
     108        {"private", MS_PRIVATE},
     109        {"unbindable", MS_UNBINDABLE},
     110        {"rshared", MS_SHARED|MS_RECURSIVE},
     111        {"rslave", MS_SLAVE|MS_RECURSIVE},
     112        {"rprivate", MS_SLAVE|MS_RECURSIVE},
     113        {"runbindable", MS_UNBINDABLE|MS_RECURSIVE},
     114    )
     115
     116    // Always understood.
     117
     118    {"ro", MS_RDONLY},        // vfs flag
     119    {"rw", ~MS_RDONLY},       // vfs flag
     120    {"remount", MS_REMOUNT},  // action flag
    86121};
    87122
     123
    88124/* Append mount options to string */
    89 static void append_mount_options(char **oldopts, char *newopts)
    90 {
    91     if(*oldopts && **oldopts) {
    92         char *temp=bb_xasprintf("%s,%s",*oldopts,newopts);
    93         free(*oldopts);
    94         *oldopts=temp;
     125static void append_mount_options(char **oldopts, const char *newopts)
     126{
     127    if (*oldopts && **oldopts) {
     128        /* do not insert options which are already there */
     129        while (newopts[0]) {
     130            char *p;
     131            int len = strlen(newopts);
     132            p = strchr(newopts, ',');
     133            if (p) len = p - newopts;
     134            p = *oldopts;
     135            while (1) {
     136                if (!strncmp(p, newopts, len)
     137                 && (p[len]==',' || p[len]==0))
     138                    goto skip;
     139                p = strchr(p,',');
     140                if (!p) break;
     141                p++;
     142            }
     143            p = xasprintf("%s,%.*s", *oldopts, len, newopts);
     144            free(*oldopts);
     145            *oldopts = p;
     146skip:
     147            newopts += len;
     148            while (newopts[0] == ',') newopts++;
     149        }
    95150    } else {
    96151        if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
    97         *oldopts = bb_xstrdup(newopts);
     152        *oldopts = xstrdup(newopts);
    98153    }
    99154}
    100155
    101156/* Use the mount_options list to parse options into flags.
    102  * Return list of unrecognized options in *strflags if strflags!=NULL */
     157 * Also return list of unrecognized options if unrecognized!=NULL */
    103158static int parse_mount_options(char *options, char **unrecognized)
    104159{
     
    113168
    114169        // Find this option in mount_options
    115         for (i = 0; i < (sizeof(mount_options) / sizeof(*mount_options)); i++) {
     170        for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
    116171            if (!strcasecmp(mount_options[i].name, options)) {
    117172                long fl = mount_options[i].flags;
    118                 if(fl < 0) flags &= fl;
     173                if (fl < 0) flags &= fl;
    119174                else flags |= fl;
    120175                break;
     
    122177        }
    123178        // If unrecognized not NULL, append unrecognized mount options */
    124         if (unrecognized
    125                 && i == (sizeof(mount_options) / sizeof(*mount_options)))
    126         {
     179        if (unrecognized && i == ARRAY_SIZE(mount_options)) {
    127180            // Add it to strflags, to pass on to kernel
    128181            i = *unrecognized ? strlen(*unrecognized) : 0;
     
    135188
    136189        // Advance to next option, or finish
    137         if(comma) {
     190        if (comma) {
    138191            *comma = ',';
    139192            options = ++comma;
     
    148201static llist_t *get_block_backed_filesystems(void)
    149202{
    150     char *fs, *buf,
    151          *filesystems[] = {"/etc/filesystems", "/proc/filesystems", 0};
     203    static const char filesystems[2][sizeof("/proc/filesystems")] = {
     204        "/etc/filesystems",
     205        "/proc/filesystems",
     206    };
     207    char *fs, *buf;
    152208    llist_t *list = 0;
    153209    int i;
    154210    FILE *f;
    155211
    156     for(i = 0; filesystems[i]; i++) {
    157         if(!(f = fopen(filesystems[i], "r"))) continue;
    158 
    159         for(fs = buf = 0; (fs = buf = bb_get_chomped_line_from_file(f));
    160             free(buf))
    161         {
    162             if(!strncmp(buf,"nodev",5) && isspace(buf[5])) continue;
    163 
    164             while(isspace(*fs)) fs++;
    165             if(*fs=='#' || *fs=='*') continue;
    166             if(!*fs) continue;
    167 
    168             llist_add_to_end(&list,bb_xstrdup(fs));
     212    for (i = 0; i < 2; i++) {
     213        f = fopen(filesystems[i], "r");
     214        if (!f) continue;
     215
     216        while ((buf = xmalloc_getline(f)) != 0) {
     217            if (!strncmp(buf, "nodev", 5) && isspace(buf[5]))
     218                continue;
     219            fs = skip_whitespace(buf);
     220            if (*fs=='#' || *fs=='*' || !*fs) continue;
     221
     222            llist_add_to_end(&list, xstrdup(fs));
     223            free(buf);
    169224        }
    170225        if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
     
    186241
    187242#if ENABLE_FEATURE_MTAB_SUPPORT
    188 static int useMtab;
     243static int useMtab = 1;
    189244static int fakeIt;
    190245#else
     
    194249
    195250// Perform actual mount of specific filesystem at specific location.
    196 
     251// NB: mp->xxx fields may be trashed on exit
    197252static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
    198253{
    199     int rc;
    200 
    201     if (fakeIt) { return 0; }
     254    int rc = 0;
     255
     256    if (fakeIt) goto mtab;
    202257
    203258    // Mount, with fallback to read-only if necessary.
    204259
    205     for(;;) {
     260    for (;;) {
    206261        rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
    207262                vfsflags, filteropts);
    208         if(!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
     263        if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
    209264            break;
    210265        bb_error_msg("%s is write-protected, mounting read-only",
     
    220275    /* If the mount was successful, and we're maintaining an old-style
    221276     * mtab file by hand, add the new entry to it now. */
    222 
    223     if(ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc) {
     277 mtab:
     278    if (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
     279        char *fsname;
    224280        FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
    225281        int i;
    226282
    227         if(!mountTable)
    228             bb_error_msg("No %s\n",bb_path_mtab_file);
     283        if (!mountTable) {
     284            bb_error_msg("no %s",bb_path_mtab_file);
     285            goto ret;
     286        }
    229287
    230288        // Add vfs string flags
    231289
    232         for(i=0; mount_options[i].flags != MS_REMOUNT; i++)
    233             if (mount_options[i].flags > 0)
    234                 append_mount_options(&(mp->mnt_opts),
    235 // Shut up about the darn const.  It's not important.  I don't care.
    236                         (char *)mount_options[i].name);
     290        for (i=0; mount_options[i].flags != MS_REMOUNT; i++)
     291            if (mount_options[i].flags > 0 && (mount_options[i].flags & vfsflags))
     292                append_mount_options(&(mp->mnt_opts), mount_options[i].name);
    237293
    238294        // Remove trailing / (if any) from directory we mounted on
    239295
    240         i = strlen(mp->mnt_dir);
    241         if(i>1 && mp->mnt_dir[i-1] == '/') mp->mnt_dir[i-1] = 0;
     296        i = strlen(mp->mnt_dir) - 1;
     297        if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = 0;
     298
     299        // Convert to canonical pathnames as needed
     300
     301        mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
     302        fsname = 0;
     303        if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
     304            mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
     305            mp->mnt_type = (char*)"bind";
     306        }
     307        mp->mnt_freq = mp->mnt_passno = 0;
    242308
    243309        // Write and close.
    244310
    245         if(!mp->mnt_type || !*mp->mnt_type) mp->mnt_type="--bind";
    246311        addmntent(mountTable, mp);
    247312        endmntent(mountTable);
    248         if (ENABLE_FEATURE_CLEAN_UP)
    249             if(strcmp(mp->mnt_type,"--bind")) mp->mnt_type = 0;
    250     }
    251 
     313        if (ENABLE_FEATURE_CLEAN_UP) {
     314            free(mp->mnt_dir);
     315            free(fsname);
     316        }
     317    }
     318 ret:
    252319    return rc;
    253320}
    254321
    255 
    256 // Mount one directory.  Handles NFS, loopback, autobind, and filesystem type
    257 // detection.  Returns 0 for success, nonzero for failure.
    258 
     322#if ENABLE_FEATURE_MOUNT_NFS
     323
     324/*
     325 * Linux NFS mount
     326 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
     327 *
     328 * Licensed under GPLv2, see file LICENSE in this tarball for details.
     329 *
     330 * Wed Feb  8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
     331 * numbers to be specified on the command line.
     332 *
     333 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
     334 * Omit the call to connect() for Linux version 1.3.11 or later.
     335 *
     336 * Wed Oct  1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
     337 * Implemented the "bg", "fg" and "retry" mount options for NFS.
     338 *
     339 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
     340 * - added Native Language Support
     341 *
     342 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
     343 * plus NFSv3 stuff.
     344 */
     345
     346/* This is just a warning of a common mistake.  Possibly this should be a
     347 * uclibc faq entry rather than in busybox... */
     348#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
     349#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
     350#endif
     351
     352#define MOUNTPORT 635
     353#define MNTPATHLEN 1024
     354#define MNTNAMLEN 255
     355#define FHSIZE 32
     356#define FHSIZE3 64
     357
     358typedef char fhandle[FHSIZE];
     359
     360typedef struct {
     361    unsigned int fhandle3_len;
     362    char *fhandle3_val;
     363} fhandle3;
     364
     365enum mountstat3 {
     366    MNT_OK = 0,
     367    MNT3ERR_PERM = 1,
     368    MNT3ERR_NOENT = 2,
     369    MNT3ERR_IO = 5,
     370    MNT3ERR_ACCES = 13,
     371    MNT3ERR_NOTDIR = 20,
     372    MNT3ERR_INVAL = 22,
     373    MNT3ERR_NAMETOOLONG = 63,
     374    MNT3ERR_NOTSUPP = 10004,
     375    MNT3ERR_SERVERFAULT = 10006,
     376};
     377typedef enum mountstat3 mountstat3;
     378
     379struct fhstatus {
     380    unsigned int fhs_status;
     381    union {
     382        fhandle fhs_fhandle;
     383    } fhstatus_u;
     384};
     385typedef struct fhstatus fhstatus;
     386
     387struct mountres3_ok {
     388    fhandle3 fhandle;
     389    struct {
     390        unsigned int auth_flavours_len;
     391        char *auth_flavours_val;
     392    } auth_flavours;
     393};
     394typedef struct mountres3_ok mountres3_ok;
     395
     396struct mountres3 {
     397    mountstat3 fhs_status;
     398    union {
     399        mountres3_ok mountinfo;
     400    } mountres3_u;
     401};
     402typedef struct mountres3 mountres3;
     403
     404typedef char *dirpath;
     405
     406typedef char *name;
     407
     408typedef struct mountbody *mountlist;
     409
     410struct mountbody {
     411    name ml_hostname;
     412    dirpath ml_directory;
     413    mountlist ml_next;
     414};
     415typedef struct mountbody mountbody;
     416
     417typedef struct groupnode *groups;
     418
     419struct groupnode {
     420    name gr_name;
     421    groups gr_next;
     422};
     423typedef struct groupnode groupnode;
     424
     425typedef struct exportnode *exports;
     426
     427struct exportnode {
     428    dirpath ex_dir;
     429    groups ex_groups;
     430    exports ex_next;
     431};
     432typedef struct exportnode exportnode;
     433
     434struct ppathcnf {
     435    int pc_link_max;
     436    short pc_max_canon;
     437    short pc_max_input;
     438    short pc_name_max;
     439    short pc_path_max;
     440    short pc_pipe_buf;
     441    uint8_t pc_vdisable;
     442    char pc_xxx;
     443    short pc_mask[2];
     444};
     445typedef struct ppathcnf ppathcnf;
     446
     447#define MOUNTPROG 100005
     448#define MOUNTVERS 1
     449
     450#define MOUNTPROC_NULL 0
     451#define MOUNTPROC_MNT 1
     452#define MOUNTPROC_DUMP 2
     453#define MOUNTPROC_UMNT 3
     454#define MOUNTPROC_UMNTALL 4
     455#define MOUNTPROC_EXPORT 5
     456#define MOUNTPROC_EXPORTALL 6
     457
     458#define MOUNTVERS_POSIX 2
     459
     460#define MOUNTPROC_PATHCONF 7
     461
     462#define MOUNT_V3 3
     463
     464#define MOUNTPROC3_NULL 0
     465#define MOUNTPROC3_MNT 1
     466#define MOUNTPROC3_DUMP 2
     467#define MOUNTPROC3_UMNT 3
     468#define MOUNTPROC3_UMNTALL 4
     469#define MOUNTPROC3_EXPORT 5
     470
     471enum {
     472#ifndef NFS_FHSIZE
     473    NFS_FHSIZE = 32,
     474#endif
     475#ifndef NFS_PORT
     476    NFS_PORT = 2049
     477#endif
     478};
     479
     480/*
     481 * We want to be able to compile mount on old kernels in such a way
     482 * that the binary will work well on more recent kernels.
     483 * Thus, if necessary we teach nfsmount.c the structure of new fields
     484 * that will come later.
     485 *
     486 * Moreover, the new kernel includes conflict with glibc includes
     487 * so it is easiest to ignore the kernel altogether (at compile time).
     488 */
     489
     490struct nfs2_fh {
     491    char                    data[32];
     492};
     493struct nfs3_fh {
     494    unsigned short          size;
     495    unsigned char           data[64];
     496};
     497
     498struct nfs_mount_data {
     499    int     version;        /* 1 */
     500    int     fd;         /* 1 */
     501    struct nfs2_fh  old_root;       /* 1 */
     502    int     flags;          /* 1 */
     503    int     rsize;          /* 1 */
     504    int     wsize;          /* 1 */
     505    int     timeo;          /* 1 */
     506    int     retrans;        /* 1 */
     507    int     acregmin;       /* 1 */
     508    int     acregmax;       /* 1 */
     509    int     acdirmin;       /* 1 */
     510    int     acdirmax;       /* 1 */
     511    struct sockaddr_in addr;        /* 1 */
     512    char        hostname[256];      /* 1 */
     513    int     namlen;         /* 2 */
     514    unsigned int    bsize;          /* 3 */
     515    struct nfs3_fh  root;           /* 4 */
     516};
     517
     518/* bits in the flags field */
     519enum {
     520    NFS_MOUNT_SOFT = 0x0001,    /* 1 */
     521    NFS_MOUNT_INTR = 0x0002,    /* 1 */
     522    NFS_MOUNT_SECURE = 0x0004,  /* 1 */
     523    NFS_MOUNT_POSIX = 0x0008,   /* 1 */
     524    NFS_MOUNT_NOCTO = 0x0010,   /* 1 */
     525    NFS_MOUNT_NOAC = 0x0020,    /* 1 */
     526    NFS_MOUNT_TCP = 0x0040,     /* 2 */
     527    NFS_MOUNT_VER3 = 0x0080,    /* 3 */
     528    NFS_MOUNT_KERBEROS = 0x0100,    /* 3 */
     529    NFS_MOUNT_NONLM = 0x0200    /* 3 */
     530};
     531
     532
     533/*
     534 * We need to translate between nfs status return values and
     535 * the local errno values which may not be the same.
     536 *
     537 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
     538 * "after #include <errno.h> the symbol errno is reserved for any use,
     539 *  it cannot even be used as a struct tag or field name".
     540 */
     541
     542#ifndef EDQUOT
     543#define EDQUOT  ENOSPC
     544#endif
     545
     546// Convert each NFSERR_BLAH into EBLAH
     547
     548static const struct {
     549    int stat;
     550    int errnum;
     551} nfs_errtbl[] = {
     552    {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
     553    {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
     554    {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
     555    {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
     556};
     557
     558static char *nfs_strerror(int status)
     559{
     560    int i;
     561    static char buf[sizeof("unknown nfs status return value: ") + sizeof(int)*3];
     562
     563    for (i = 0; nfs_errtbl[i].stat != -1; i++) {
     564        if (nfs_errtbl[i].stat == status)
     565            return strerror(nfs_errtbl[i].errnum);
     566    }
     567    sprintf(buf, "unknown nfs status return value: %d", status);
     568    return buf;
     569}
     570
     571static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
     572{
     573    if (!xdr_opaque(xdrs, objp, FHSIZE))
     574         return FALSE;
     575    return TRUE;
     576}
     577
     578static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
     579{
     580    if (!xdr_u_int(xdrs, &objp->fhs_status))
     581         return FALSE;
     582    switch (objp->fhs_status) {
     583    case 0:
     584        if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
     585             return FALSE;
     586        break;
     587    default:
     588        break;
     589    }
     590    return TRUE;
     591}
     592
     593static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
     594{
     595    if (!xdr_string(xdrs, objp, MNTPATHLEN))
     596         return FALSE;
     597    return TRUE;
     598}
     599
     600static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
     601{
     602    if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
     603         return FALSE;
     604    return TRUE;
     605}
     606
     607static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
     608{
     609    if (!xdr_fhandle3(xdrs, &objp->fhandle))
     610        return FALSE;
     611    if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
     612                sizeof (int), (xdrproc_t) xdr_int))
     613        return FALSE;
     614    return TRUE;
     615}
     616
     617static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
     618{
     619    if (!xdr_enum(xdrs, (enum_t *) objp))
     620         return FALSE;
     621    return TRUE;
     622}
     623
     624static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
     625{
     626    if (!xdr_mountstat3(xdrs, &objp->fhs_status))
     627        return FALSE;
     628    switch (objp->fhs_status) {
     629    case MNT_OK:
     630        if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
     631             return FALSE;
     632        break;
     633    default:
     634        break;
     635    }
     636    return TRUE;
     637}
     638
     639#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
     640
     641/*
     642 * nfs_mount_version according to the sources seen at compile time.
     643 */
     644static int nfs_mount_version;
     645static int kernel_version;
     646
     647/*
     648 * Unfortunately, the kernel prints annoying console messages
     649 * in case of an unexpected nfs mount version (instead of
     650 * just returning some error).  Therefore we'll have to try
     651 * and figure out what version the kernel expects.
     652 *
     653 * Variables:
     654 *  KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
     655 *  NFS_MOUNT_VERSION: these nfsmount sources at compile time
     656 *  nfs_mount_version: version this source and running kernel can handle
     657 */
     658static void
     659find_kernel_nfs_mount_version(void)
     660{
     661    if (kernel_version)
     662        return;
     663
     664    nfs_mount_version = 4; /* default */
     665
     666    kernel_version = get_linux_version_code();
     667    if (kernel_version) {
     668        if (kernel_version < KERNEL_VERSION(2,1,32))
     669            nfs_mount_version = 1;
     670        else if (kernel_version < KERNEL_VERSION(2,2,18) ||
     671                (kernel_version >= KERNEL_VERSION(2,3,0) &&
     672                 kernel_version < KERNEL_VERSION(2,3,99)))
     673            nfs_mount_version = 3;
     674        /* else v4 since 2.3.99pre4 */
     675    }
     676}
     677
     678static struct pmap *
     679get_mountport(struct sockaddr_in *server_addr,
     680    long unsigned prog,
     681    long unsigned version,
     682    long unsigned proto,
     683    long unsigned port)
     684{
     685    struct pmaplist *pmap;
     686    static struct pmap p = {0, 0, 0, 0};
     687
     688    server_addr->sin_port = PMAPPORT;
     689/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
     690 * I understand it like "IPv6 for this is not 100% ready" */
     691    pmap = pmap_getmaps(server_addr);
     692
     693    if (version > MAX_NFSPROT)
     694        version = MAX_NFSPROT;
     695    if (!prog)
     696        prog = MOUNTPROG;
     697    p.pm_prog = prog;
     698    p.pm_vers = version;
     699    p.pm_prot = proto;
     700    p.pm_port = port;
     701
     702    while (pmap) {
     703        if (pmap->pml_map.pm_prog != prog)
     704            goto next;
     705        if (!version && p.pm_vers > pmap->pml_map.pm_vers)
     706            goto next;
     707        if (version > 2 && pmap->pml_map.pm_vers != version)
     708            goto next;
     709        if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
     710            goto next;
     711        if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
     712            (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
     713            (port && pmap->pml_map.pm_port != port))
     714            goto next;
     715        memcpy(&p, &pmap->pml_map, sizeof(p));
     716next:
     717        pmap = pmap->pml_next;
     718    }
     719    if (!p.pm_vers)
     720        p.pm_vers = MOUNTVERS;
     721    if (!p.pm_port)
     722        p.pm_port = MOUNTPORT;
     723    if (!p.pm_prot)
     724        p.pm_prot = IPPROTO_TCP;
     725    return &p;
     726}
     727
     728static int daemonize(void)
     729{
     730    int fd;
     731    int pid = fork();
     732    if (pid < 0) /* error */
     733        return -errno;
     734    if (pid > 0) /* parent */
     735        return 0;
     736    /* child */
     737    fd = xopen(bb_dev_null, O_RDWR);
     738    dup2(fd, 0);
     739    dup2(fd, 1);
     740    dup2(fd, 2);
     741    while (fd > 2) close(fd--);
     742    setsid();
     743    openlog(applet_name, LOG_PID, LOG_DAEMON);
     744    logmode = LOGMODE_SYSLOG;
     745    return 1;
     746}
     747
     748// TODO
     749static inline int we_saw_this_host_before(const char *hostname)
     750{
     751    return 0;
     752}
     753
     754/* RPC strerror analogs are terminally idiotic:
     755 * *mandatory* prefix and \n at end.
     756 * This hopefully helps. Usage:
     757 * error_msg_rpc(clnt_*error*(" ")) */
     758static void error_msg_rpc(const char *msg)
     759{
     760    int len;
     761    while (msg[0] == ' ' || msg[0] == ':') msg++;
     762    len = strlen(msg);
     763    while (len && msg[len-1] == '\n') len--;
     764    bb_error_msg("%.*s", len, msg);
     765}
     766
     767// NB: mp->xxx fields may be trashed on exit
     768static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
     769{
     770    CLIENT *mclient;
     771    char *hostname;
     772    char *pathname;
     773    char *mounthost;
     774    struct nfs_mount_data data;
     775    char *opt;
     776    struct hostent *hp;
     777    struct sockaddr_in server_addr;
     778    struct sockaddr_in mount_server_addr;
     779    int msock, fsock;
     780    union {
     781        struct fhstatus nfsv2;
     782        struct mountres3 nfsv3;
     783    } status;
     784    int daemonized;
     785    char *s;
     786    int port;
     787    int mountport;
     788    int proto;
     789    int bg;
     790    int soft;
     791    int intr;
     792    int posix;
     793    int nocto;
     794    int noac;
     795    int nolock;
     796    int retry;
     797    int tcp;
     798    int mountprog;
     799    int mountvers;
     800    int nfsprog;
     801    int nfsvers;
     802    int retval;
     803
     804    find_kernel_nfs_mount_version();
     805
     806    daemonized = 0;
     807    mounthost = NULL;
     808    retval = ETIMEDOUT;
     809    msock = fsock = -1;
     810    mclient = NULL;
     811
     812    /* NB: hostname, mounthost, filteropts must be free()d prior to return */
     813
     814    filteropts = xstrdup(filteropts); /* going to trash it later... */
     815
     816    hostname = xstrdup(mp->mnt_fsname);
     817    /* mount_main() guarantees that ':' is there */
     818    s = strchr(hostname, ':');
     819    pathname = s + 1;
     820    *s = '\0';
     821    /* Ignore all but first hostname in replicated mounts
     822       until they can be fully supported. (mack@sgi.com) */
     823    s = strchr(hostname, ',');
     824    if (s) {
     825        *s = '\0';
     826        bb_error_msg("warning: multiple hostnames not supported");
     827    }
     828
     829    server_addr.sin_family = AF_INET;
     830    if (!inet_aton(hostname, &server_addr.sin_addr)) {
     831        hp = gethostbyname(hostname);
     832        if (hp == NULL) {
     833            bb_herror_msg("%s", hostname);
     834            goto fail;
     835        }
     836        if (hp->h_length > sizeof(struct in_addr)) {
     837            bb_error_msg("got bad hp->h_length");
     838            hp->h_length = sizeof(struct in_addr);
     839        }
     840        memcpy(&server_addr.sin_addr,
     841                hp->h_addr, hp->h_length);
     842    }
     843
     844    memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
     845
     846    /* add IP address to mtab options for use when unmounting */
     847
     848    if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
     849        mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
     850    } else {
     851        char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
     852                    mp->mnt_opts[0] ? "," : "",
     853                    inet_ntoa(server_addr.sin_addr));
     854        free(mp->mnt_opts);
     855        mp->mnt_opts = tmp;
     856    }
     857
     858    /* Set default options.
     859     * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
     860     * let the kernel decide.
     861     * timeo is filled in after we know whether it'll be TCP or UDP. */
     862    memset(&data, 0, sizeof(data));
     863    data.retrans    = 3;
     864    data.acregmin   = 3;
     865    data.acregmax   = 60;
     866    data.acdirmin   = 30;
     867    data.acdirmax   = 60;
     868    data.namlen = NAME_MAX;
     869
     870    bg = 0;
     871    soft = 0;
     872    intr = 0;
     873    posix = 0;
     874    nocto = 0;
     875    nolock = 0;
     876    noac = 0;
     877    retry = 10000;      /* 10000 minutes ~ 1 week */
     878    tcp = 0;
     879
     880    mountprog = MOUNTPROG;
     881    mountvers = 0;
     882    port = 0;
     883    mountport = 0;
     884    nfsprog = 100003;
     885    nfsvers = 0;
     886
     887    /* parse options */
     888    if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
     889        char *opteq = strchr(opt, '=');
     890        if (opteq) {
     891            static const char options[] ALIGN1 =
     892                /* 0 */ "rsize\0"
     893                /* 1 */ "wsize\0"
     894                /* 2 */ "timeo\0"
     895                /* 3 */ "retrans\0"
     896                /* 4 */ "acregmin\0"
     897                /* 5 */ "acregmax\0"
     898                /* 6 */ "acdirmin\0"
     899                /* 7 */ "acdirmax\0"
     900                /* 8 */ "actimeo\0"
     901                /* 9 */ "retry\0"
     902                /* 10 */ "port\0"
     903                /* 11 */ "mountport\0"
     904                /* 12 */ "mounthost\0"
     905                /* 13 */ "mountprog\0"
     906                /* 14 */ "mountvers\0"
     907                /* 15 */ "nfsprog\0"
     908                /* 16 */ "nfsvers\0"
     909                /* 17 */ "vers\0"
     910                /* 18 */ "proto\0"
     911                /* 19 */ "namlen\0"
     912                /* 20 */ "addr\0";
     913            int val = xatoi_u(opteq + 1);
     914            *opteq = '\0';
     915            switch (index_in_strings(options, opt)) {
     916            case 0: // "rsize"
     917                data.rsize = val;
     918                break;
     919            case 1: // "wsize"
     920                data.wsize = val;
     921                break;
     922            case 2: // "timeo"
     923                data.timeo = val;
     924                break;
     925            case 3: // "retrans"
     926                data.retrans = val;
     927                break;
     928            case 4: // "acregmin"
     929                data.acregmin = val;
     930                break;
     931            case 5: // "acregmax"
     932                data.acregmax = val;
     933                break;
     934            case 6: // "acdirmin"
     935                data.acdirmin = val;
     936                break;
     937            case 7: // "acdirmax"
     938                data.acdirmax = val;
     939                break;
     940            case 8: // "actimeo"
     941                data.acregmin = val;
     942                data.acregmax = val;
     943                data.acdirmin = val;
     944                data.acdirmax = val;
     945                break;
     946            case 9: // "retry"
     947                retry = val;
     948                break;
     949            case 10: // "port"
     950                port = val;
     951                break;
     952            case 11: // "mountport"
     953                mountport = val;
     954                break;
     955            case 12: // "mounthost"
     956                mounthost = xstrndup(opteq+1,
     957                        strcspn(opteq+1," \t\n\r,"));
     958                break;
     959            case 13: // "mountprog"
     960                mountprog = val;
     961                break;
     962            case 14: // "mountvers"
     963                mountvers = val;
     964                break;
     965            case 15: // "nfsprog"
     966                nfsprog = val;
     967                break;
     968            case 16: // "nfsvers"
     969            case 17: // "vers"
     970                nfsvers = val;
     971                break;
     972            case 18: // "proto"
     973                if (!strncmp(opteq+1, "tcp", 3))
     974                    tcp = 1;
     975                else if (!strncmp(opteq+1, "udp", 3))
     976                    tcp = 0;
     977                else
     978                    bb_error_msg("warning: unrecognized proto= option");
     979                break;
     980            case 19: // "namlen"
     981                if (nfs_mount_version >= 2)
     982                    data.namlen = val;
     983                else
     984                    bb_error_msg("warning: option namlen is not supported\n");
     985                break;
     986            case 20: // "addr" - ignore
     987                break;
     988            default:
     989                bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
     990                goto fail;
     991            }
     992        }
     993        else {
     994            static const char options[] ALIGN1 =
     995                "bg\0"
     996                "fg\0"
     997                "soft\0"
     998                "hard\0"
     999                "intr\0"
     1000                "posix\0"
     1001                "cto\0"
     1002                "ac\0"
     1003                "tcp\0"
     1004                "udp\0"
     1005                "lock\0";
     1006            int val = 1;
     1007            if (!strncmp(opt, "no", 2)) {
     1008                val = 0;
     1009                opt += 2;
     1010            }
     1011            switch (index_in_strings(options, opt)) {
     1012            case 0: // "bg"
     1013                bg = val;
     1014                break;
     1015            case 1: // "fg"
     1016                bg = !val;
     1017                break;
     1018            case 2: // "soft"
     1019                soft = val;
     1020                break;
     1021            case 3: // "hard"
     1022                soft = !val;
     1023                break;
     1024            case 4: // "intr"
     1025                intr = val;
     1026                break;
     1027            case 5: // "posix"
     1028                posix = val;
     1029                break;
     1030            case 6: // "cto"
     1031                nocto = !val;
     1032                break;
     1033            case 7: // "ac"
     1034                noac = !val;
     1035                break;
     1036            case 8: // "tcp"
     1037                tcp = val;
     1038                break;
     1039            case 9: // "udp"
     1040                tcp = !val;
     1041                break;
     1042            case 10: // "lock"
     1043                if (nfs_mount_version >= 3)
     1044                    nolock = !val;
     1045                else
     1046                    bb_error_msg("warning: option nolock is not supported");
     1047                break;
     1048            default:
     1049                bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
     1050                goto fail;
     1051            }
     1052        }
     1053    }
     1054    proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
     1055
     1056    data.flags = (soft ? NFS_MOUNT_SOFT : 0)
     1057        | (intr ? NFS_MOUNT_INTR : 0)
     1058        | (posix ? NFS_MOUNT_POSIX : 0)
     1059        | (nocto ? NFS_MOUNT_NOCTO : 0)
     1060        | (noac ? NFS_MOUNT_NOAC : 0);
     1061    if (nfs_mount_version >= 2)
     1062        data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
     1063    if (nfs_mount_version >= 3)
     1064        data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
     1065    if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
     1066        bb_error_msg("NFSv%d not supported", nfsvers);
     1067        goto fail;
     1068    }
     1069    if (nfsvers && !mountvers)
     1070        mountvers = (nfsvers < 3) ? 1 : nfsvers;
     1071    if (nfsvers && nfsvers < mountvers) {
     1072        mountvers = nfsvers;
     1073    }
     1074
     1075    /* Adjust options if none specified */
     1076    if (!data.timeo)
     1077        data.timeo = tcp ? 70 : 7;
     1078
     1079    data.version = nfs_mount_version;
     1080
     1081    if (vfsflags & MS_REMOUNT)
     1082        goto do_mount;
     1083
     1084    /*
     1085     * If the previous mount operation on the same host was
     1086     * backgrounded, and the "bg" for this mount is also set,
     1087     * give up immediately, to avoid the initial timeout.
     1088     */
     1089    if (bg && we_saw_this_host_before(hostname)) {
     1090        daemonized = daemonize(); /* parent or error */
     1091        if (daemonized <= 0) { /* parent or error */
     1092            retval = -daemonized;
     1093            goto ret;
     1094        }
     1095    }
     1096
     1097    /* create mount daemon client */
     1098    /* See if the nfs host = mount host. */
     1099    if (mounthost) {
     1100        if (mounthost[0] >= '0' && mounthost[0] <= '9') {
     1101            mount_server_addr.sin_family = AF_INET;
     1102            mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
     1103        } else {
     1104            hp = gethostbyname(mounthost);
     1105            if (hp == NULL) {
     1106                bb_herror_msg("%s", mounthost);
     1107                goto fail;
     1108            } else {
     1109                if (hp->h_length > sizeof(struct in_addr)) {
     1110                    bb_error_msg("got bad hp->h_length?");
     1111                    hp->h_length = sizeof(struct in_addr);
     1112                }
     1113                mount_server_addr.sin_family = AF_INET;
     1114                memcpy(&mount_server_addr.sin_addr,
     1115                        hp->h_addr, hp->h_length);
     1116            }
     1117        }
     1118    }
     1119
     1120    /*
     1121     * The following loop implements the mount retries. When the mount
     1122     * times out, and the "bg" option is set, we background ourself
     1123     * and continue trying.
     1124     *
     1125     * The case where the mount point is not present and the "bg"
     1126     * option is set, is treated as a timeout. This is done to
     1127     * support nested mounts.
     1128     *
     1129     * The "retry" count specified by the user is the number of
     1130     * minutes to retry before giving up.
     1131     */
     1132    {
     1133        struct timeval total_timeout;
     1134        struct timeval retry_timeout;
     1135        struct pmap* pm_mnt;
     1136        time_t t;
     1137        time_t prevt;
     1138        time_t timeout;
     1139
     1140        retry_timeout.tv_sec = 3;
     1141        retry_timeout.tv_usec = 0;
     1142        total_timeout.tv_sec = 20;
     1143        total_timeout.tv_usec = 0;
     1144        timeout = time(NULL) + 60 * retry;
     1145        prevt = 0;
     1146        t = 30;
     1147retry:
     1148        /* be careful not to use too many CPU cycles */
     1149        if (t - prevt < 30)
     1150            sleep(30);
     1151
     1152        pm_mnt = get_mountport(&mount_server_addr,
     1153                mountprog,
     1154                mountvers,
     1155                proto,
     1156                mountport);
     1157        nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
     1158
     1159        /* contact the mount daemon via TCP */
     1160        mount_server_addr.sin_port = htons(pm_mnt->pm_port);
     1161        msock = RPC_ANYSOCK;
     1162
     1163        switch (pm_mnt->pm_prot) {
     1164        case IPPROTO_UDP:
     1165            mclient = clntudp_create(&mount_server_addr,
     1166                         pm_mnt->pm_prog,
     1167                         pm_mnt->pm_vers,
     1168                         retry_timeout,
     1169                         &msock);
     1170            if (mclient)
     1171                break;
     1172            mount_server_addr.sin_port = htons(pm_mnt->pm_port);
     1173            msock = RPC_ANYSOCK;
     1174        case IPPROTO_TCP:
     1175            mclient = clnttcp_create(&mount_server_addr,
     1176                         pm_mnt->pm_prog,
     1177                         pm_mnt->pm_vers,
     1178                         &msock, 0, 0);
     1179            break;
     1180        default:
     1181            mclient = 0;
     1182        }
     1183        if (!mclient) {
     1184            if (!daemonized && prevt == 0)
     1185                error_msg_rpc(clnt_spcreateerror(" "));
     1186        } else {
     1187            enum clnt_stat clnt_stat;
     1188            /* try to mount hostname:pathname */
     1189            mclient->cl_auth = authunix_create_default();
     1190
     1191            /* make pointers in xdr_mountres3 NULL so
     1192             * that xdr_array allocates memory for us
     1193             */
     1194            memset(&status, 0, sizeof(status));
     1195
     1196            if (pm_mnt->pm_vers == 3)
     1197                clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
     1198                          (xdrproc_t) xdr_dirpath,
     1199                          (caddr_t) &pathname,
     1200                          (xdrproc_t) xdr_mountres3,
     1201                          (caddr_t) &status,
     1202                          total_timeout);
     1203            else
     1204                clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
     1205                          (xdrproc_t) xdr_dirpath,
     1206                          (caddr_t) &pathname,
     1207                          (xdrproc_t) xdr_fhstatus,
     1208                          (caddr_t) &status,
     1209                          total_timeout);
     1210
     1211            if (clnt_stat == RPC_SUCCESS)
     1212                goto prepare_kernel_data; /* we're done */
     1213            if (errno != ECONNREFUSED) {
     1214                error_msg_rpc(clnt_sperror(mclient, " "));
     1215                goto fail;  /* don't retry */
     1216            }
     1217            /* Connection refused */
     1218            if (!daemonized && prevt == 0) /* print just once */
     1219                error_msg_rpc(clnt_sperror(mclient, " "));
     1220            auth_destroy(mclient->cl_auth);
     1221            clnt_destroy(mclient);
     1222            mclient = 0;
     1223            close(msock);
     1224        }
     1225
     1226        /* Timeout. We are going to retry... maybe */
     1227
     1228        if (!bg)
     1229            goto fail;
     1230        if (!daemonized) {
     1231            daemonized = daemonize();
     1232            if (daemonized <= 0) { /* parent or error */
     1233                retval = -daemonized;
     1234                goto ret;
     1235            }
     1236        }
     1237        prevt = t;
     1238        t = time(NULL);
     1239        if (t >= timeout)
     1240            /* TODO error message */
     1241            goto fail;
     1242
     1243        goto retry;
     1244    }
     1245
     1246prepare_kernel_data:
     1247
     1248    if (nfsvers == 2) {
     1249        if (status.nfsv2.fhs_status != 0) {
     1250            bb_error_msg("%s:%s failed, reason given by server: %s",
     1251                hostname, pathname,
     1252                nfs_strerror(status.nfsv2.fhs_status));
     1253            goto fail;
     1254        }
     1255        memcpy(data.root.data,
     1256                (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
     1257                NFS_FHSIZE);
     1258        data.root.size = NFS_FHSIZE;
     1259        memcpy(data.old_root.data,
     1260                (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
     1261                NFS_FHSIZE);
     1262    } else {
     1263        fhandle3 *my_fhandle;
     1264        if (status.nfsv3.fhs_status != 0) {
     1265            bb_error_msg("%s:%s failed, reason given by server: %s",
     1266                hostname, pathname,
     1267                nfs_strerror(status.nfsv3.fhs_status));
     1268            goto fail;
     1269        }
     1270        my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
     1271        memset(data.old_root.data, 0, NFS_FHSIZE);
     1272        memset(&data.root, 0, sizeof(data.root));
     1273        data.root.size = my_fhandle->fhandle3_len;
     1274        memcpy(data.root.data,
     1275                (char *) my_fhandle->fhandle3_val,
     1276                my_fhandle->fhandle3_len);
     1277
     1278        data.flags |= NFS_MOUNT_VER3;
     1279    }
     1280
     1281    /* create nfs socket for kernel */
     1282
     1283    if (tcp) {
     1284        if (nfs_mount_version < 3) {
     1285            bb_error_msg("NFS over TCP is not supported");
     1286            goto fail;
     1287        }
     1288        fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
     1289    } else
     1290        fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
     1291    if (fsock < 0) {
     1292        bb_perror_msg("nfs socket");
     1293        goto fail;
     1294    }
     1295    if (bindresvport(fsock, 0) < 0) {
     1296        bb_perror_msg("nfs bindresvport");
     1297        goto fail;
     1298    }
     1299    if (port == 0) {
     1300        server_addr.sin_port = PMAPPORT;
     1301        port = pmap_getport(&server_addr, nfsprog, nfsvers,
     1302                    tcp ? IPPROTO_TCP : IPPROTO_UDP);
     1303        if (port == 0)
     1304            port = NFS_PORT;
     1305    }
     1306    server_addr.sin_port = htons(port);
     1307
     1308    /* prepare data structure for kernel */
     1309
     1310    data.fd = fsock;
     1311    memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
     1312    strncpy(data.hostname, hostname, sizeof(data.hostname));
     1313
     1314    /* clean up */
     1315
     1316    auth_destroy(mclient->cl_auth);
     1317    clnt_destroy(mclient);
     1318    close(msock);
     1319
     1320    if (bg) {
     1321        /* We must wait until mount directory is available */
     1322        struct stat statbuf;
     1323        int delay = 1;
     1324        while (stat(mp->mnt_dir, &statbuf) == -1) {
     1325            if (!daemonized) {
     1326                daemonized = daemonize();
     1327                if (daemonized <= 0) { /* parent or error */
     1328                    retval = -daemonized;
     1329                    goto ret;
     1330                }
     1331            }
     1332            sleep(delay);   /* 1, 2, 4, 8, 16, 30, ... */
     1333            delay *= 2;
     1334            if (delay > 30)
     1335                delay = 30;
     1336        }
     1337    }
     1338
     1339do_mount: /* perform actual mount */
     1340
     1341    mp->mnt_type = (char*)"nfs";
     1342    retval = mount_it_now(mp, vfsflags, (char*)&data);
     1343    goto ret;
     1344
     1345fail:   /* abort */
     1346
     1347    if (msock != -1) {
     1348        if (mclient) {
     1349            auth_destroy(mclient->cl_auth);
     1350            clnt_destroy(mclient);
     1351        }
     1352        close(msock);
     1353    }
     1354    if (fsock != -1)
     1355        close(fsock);
     1356
     1357ret:
     1358    free(hostname);
     1359    free(mounthost);
     1360    free(filteropts);
     1361    return retval;
     1362}
     1363
     1364#else /* !ENABLE_FEATURE_MOUNT_NFS */
     1365
     1366/* Never called. Call should be optimized out. */
     1367int nfsmount(struct mntent *mp, int vfsflags, char *filteropts);
     1368
     1369#endif /* !ENABLE_FEATURE_MOUNT_NFS */
     1370
     1371// Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
     1372// type detection.  Returns 0 for success, nonzero for failure.
     1373// NB: mp->xxx fields may be trashed on exit
    2591374static int singlemount(struct mntent *mp, int ignore_busy)
    2601375{
     
    2681383    // Treat fstype "auto" as unspecified.
    2691384
    270     if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0;
     1385    if (mp->mnt_type && strcmp(mp->mnt_type,"auto") == 0)
     1386        mp->mnt_type = 0;
     1387
     1388    // Might this be an CIFS filesystem?
     1389
     1390    if (ENABLE_FEATURE_MOUNT_CIFS
     1391     && (!mp->mnt_type || strcmp(mp->mnt_type,"cifs") == 0)
     1392     && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')
     1393     && mp->mnt_fsname[0]==mp->mnt_fsname[1]
     1394    ) {
     1395        len_and_sockaddr *lsa;
     1396        char *ip, *dotted;
     1397        char *s;
     1398
     1399        rc = 1;
     1400        // Replace '/' with '\' and verify that unc points to "//server/share".
     1401
     1402        for (s = mp->mnt_fsname; *s; ++s)
     1403            if (*s == '/') *s = '\\';
     1404
     1405        // get server IP
     1406
     1407        s = strrchr(mp->mnt_fsname, '\\');
     1408        if (s <= mp->mnt_fsname+1) goto report_error;
     1409        *s = '\0';
     1410        lsa = host2sockaddr(mp->mnt_fsname+2, 0);
     1411        *s = '\\';
     1412        if (!lsa) goto report_error;
     1413
     1414        // insert ip=... option into string flags.
     1415
     1416        dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa);
     1417        ip = xasprintf("ip=%s", dotted);
     1418        parse_mount_options(ip, &filteropts);
     1419
     1420        // compose new unc '\\server-ip\share'
     1421        // (s => slash after hostname)
     1422
     1423        mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
     1424
     1425        // lock is required
     1426        vfsflags |= MS_MANDLOCK;
     1427
     1428        mp->mnt_type = (char*)"cifs";
     1429        rc = mount_it_now(mp, vfsflags, filteropts);
     1430        if (ENABLE_FEATURE_CLEAN_UP) {
     1431            free(mp->mnt_fsname);
     1432            free(ip);
     1433            free(dotted);
     1434            free(lsa);
     1435        }
     1436        goto report_error;
     1437    }
    2711438
    2721439    // Might this be an NFS filesystem?
    2731440
    274     if (ENABLE_FEATURE_MOUNT_NFS &&
    275         (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) &&
    276         strchr(mp->mnt_fsname, ':') != NULL)
    277     {
    278         if (nfsmount(mp->mnt_fsname, mp->mnt_dir, &vfsflags, &filteropts, 1)) {
    279             bb_perror_msg("nfsmount failed");
    280             goto report_error;
    281         } else {
    282             // Strangely enough, nfsmount() doesn't actually mount() anything.
    283             mp->mnt_type = "nfs";
    284             rc = mount_it_now(mp, vfsflags, filteropts);
    285             if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
    286            
    287             goto report_error;
    288         }
     1441    if (ENABLE_FEATURE_MOUNT_NFS
     1442     && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
     1443     && strchr(mp->mnt_fsname, ':') != NULL
     1444    ) {
     1445        rc = nfsmount(mp, vfsflags, filteropts);
     1446        goto report_error;
    2891447    }
    2901448
    2911449    // Look at the file.  (Not found isn't a failure for remount, or for
    2921450    // a synthetic filesystem like proc or sysfs.)
    293 
    294     if (stat(mp->mnt_fsname, &st));
    295     else if (!(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
     1451    // (We use stat, not lstat, in order to allow
     1452    // mount symlink_to_file_or_blkdev dir)
     1453
     1454    if (!stat(mp->mnt_fsname, &st)
     1455     && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
     1456    ) {
    2961457        // Do we need to allocate a loopback device for it?
    2971458
    2981459        if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
    2991460            loopFile = bb_simplify_path(mp->mnt_fsname);
    300             mp->mnt_fsname = 0;
    301             switch(set_loop(&(mp->mnt_fsname), loopFile, 0)) {
    302                 case 0:
    303                 case 1:
    304                     break;
    305                 default:
    306                     bb_error_msg( errno == EPERM || errno == EACCES
    307                         ? bb_msg_perm_denied_are_you_root
    308                         : "Couldn't setup loop device");
    309                     return errno;
     1461            mp->mnt_fsname = NULL; /* will receive malloced loop dev name */
     1462            if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
     1463                if (errno == EPERM || errno == EACCES)
     1464                    bb_error_msg(bb_msg_perm_denied_are_you_root);
     1465                else
     1466                    bb_perror_msg("cannot setup loop device");
     1467                return errno;
    3101468            }
    3111469
    3121470        // Autodetect bind mounts
    3131471
    314         } else if (S_ISDIR(st.st_mode) && !mp->mnt_type) vfsflags |= MS_BIND;
     1472        } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
     1473            vfsflags |= MS_BIND;
    3151474    }
    3161475
     
    3201479    if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
    3211480        rc = mount_it_now(mp, vfsflags, filteropts);
    322 
    323     // Loop through filesystem types until mount succeeds or we run out
    324 
    3251481    else {
     1482        // Loop through filesystem types until mount succeeds
     1483        // or we run out
    3261484
    3271485        /* Initialize list of block backed filesystems.  This has to be
     
    3371495        for (fl = fslist; fl; fl = fl->link) {
    3381496            mp->mnt_type = fl->data;
    339 
    340             if (!(rc = mount_it_now(mp,vfsflags, filteropts))) break;
    341 
    342             mp->mnt_type = 0;
    343         }
    344     }
    345 
    346     if (ENABLE_FEATURE_CLEAN_UP) free(filteropts);
     1497            rc = mount_it_now(mp, vfsflags, filteropts);
     1498            if (!rc) break;
     1499        }
     1500    }
    3471501
    3481502    // If mount failed, clean up loop file (if any).
     
    3551509        }
    3561510    }
    357 report_error:
    358     if (rc && errno == EBUSY && ignore_busy) rc = 0;
     1511
     1512 report_error:
     1513    if (ENABLE_FEATURE_CLEAN_UP)
     1514        free(filteropts);
     1515
     1516    if (rc && errno == EBUSY && ignore_busy)
     1517        rc = 0;
    3591518    if (rc < 0)
    360         bb_perror_msg("Mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
     1519        bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
    3611520
    3621521    return rc;
     
    3661525// each directory to be mounted.
    3671526
     1527static const char must_be_root[] ALIGN1 = "you must be root";
     1528
     1529int mount_main(int argc, char **argv);
    3681530int mount_main(int argc, char **argv)
    3691531{
    370     char *cmdopts = bb_xstrdup(""), *fstabname, *fstype=0, *storage_path=0;
     1532    enum { OPT_ALL = 0x10 };
     1533
     1534    char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0;
     1535    char *opt_o;
     1536    const char *fstabname;
    3711537    FILE *fstab;
    372     int i, opt, all = FALSE, rc = 0;
     1538    int i, j, rc = 0;
     1539    unsigned opt;
    3731540    struct mntent mtpair[2], *mtcur = mtpair;
     1541    SKIP_DESKTOP(const int nonroot = 0;)
     1542    USE_DESKTOP( int nonroot = (getuid() != 0);)
    3741543
    3751544    /* parse long options, like --bind and --move.  Note that -o option
    3761545     * and --option are synonymous.  Yes, this means --remount,rw works. */
    3771546
    378     for (i = opt = 0; i < argc; i++) {
     1547    for (i = j = 0; i < argc; i++) {
    3791548        if (argv[i][0] == '-' && argv[i][1] == '-') {
    380             append_mount_options(&cmdopts,argv[i]+2);
    381         } else argv[opt++] = argv[i];
    382     }
    383     argc = opt;
     1549            append_mount_options(&cmdopts, argv[i]+2);
     1550        } else argv[j++] = argv[i];
     1551    }
     1552    argv[j] = 0;
     1553    argc = j;
    3841554
    3851555    // Parse remaining options
    3861556
    387     while ((opt = getopt(argc, argv, "o:t:rwavnf")) > 0) {
    388         switch (opt) {
    389             case 'o':
    390                 append_mount_options(&cmdopts, optarg);
    391                 break;
    392             case 't':
    393                 fstype = optarg;
    394                 break;
    395             case 'r':
    396                 append_mount_options(&cmdopts, "ro");
    397                 break;
    398             case 'w':
    399                 append_mount_options(&cmdopts, "rw");
    400                 break;
    401             case 'a':
    402                 all = TRUE;
    403                 break;
    404             case 'n':
    405                 USE_FEATURE_MTAB_SUPPORT(useMtab = FALSE;)
    406                 break;
    407             case 'f':
    408                 USE_FEATURE_MTAB_SUPPORT(fakeIt = FALSE;)
    409                 break;
    410             case 'v':
    411                 break;      // ignore -v
    412             default:
    413                 bb_show_usage();
    414         }
    415     }
     1557    opt = getopt32(argv, "o:t:rwanfvs", &opt_o, &fstype);
     1558    if (opt & 0x1) append_mount_options(&cmdopts, opt_o); // -o
     1559    //if (opt & 0x2) // -t
     1560    if (opt & 0x4) append_mount_options(&cmdopts, "ro"); // -r
     1561    if (opt & 0x8) append_mount_options(&cmdopts, "rw"); // -w
     1562    //if (opt & 0x10) // -a
     1563    if (opt & 0x20) USE_FEATURE_MTAB_SUPPORT(useMtab = 0); // -n
     1564    if (opt & 0x40) USE_FEATURE_MTAB_SUPPORT(fakeIt = 1); // -f
     1565    //if (opt & 0x80) // -v: verbose (ignore)
     1566    //if (opt & 0x100) // -s: sloppy (ignore)
     1567    argv += optind;
     1568    argc -= optind;
    4161569
    4171570    // Three or more non-option arguments?  Die with a usage message.
    4181571
    419     if (optind-argc>2) bb_show_usage();
     1572    if (argc > 2) bb_show_usage();
    4201573
    4211574    // If we have no arguments, show currently mounted filesystems
    4221575
    423     if (optind == argc) {
    424         if (!all) {
     1576    if (!argc) {
     1577        if (!(opt & OPT_ALL)) {
    4251578            FILE *mountTable = setmntent(bb_path_mtab_file, "r");
    4261579
    427             if(!mountTable) bb_error_msg_and_die("No %s",bb_path_mtab_file);
    428 
    429             while (getmntent_r(mountTable,mtpair,bb_common_bufsiz1,
     1580            if (!mountTable) bb_error_msg_and_die("no %s", bb_path_mtab_file);
     1581
     1582            while (getmntent_r(mountTable, mtpair, bb_common_bufsiz1,
    4301583                                sizeof(bb_common_bufsiz1)))
    4311584            {
    432                 // Don't show rootfs.
    433                 if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
     1585                // Don't show rootfs. FIXME: why??
     1586                // util-linux 2.12a happily shows rootfs...
     1587                //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
    4341588
    4351589                if (!fstype || !strcmp(mtpair->mnt_type, fstype))
     
    4411595            return EXIT_SUCCESS;
    4421596        }
    443     }
     1597    } else storage_path = bb_simplify_path(argv[0]);
    4441598
    4451599    // When we have two arguments, the second is the directory and we can
     
    4471601    // argument when we get it.
    4481602
    449     if (optind+2 == argc) {
    450         mtpair->mnt_fsname = argv[optind];
    451         mtpair->mnt_dir = argv[optind+1];
     1603    if (argc == 2) {
     1604        if (nonroot)
     1605            bb_error_msg_and_die(must_be_root);
     1606        mtpair->mnt_fsname = argv[0];
     1607        mtpair->mnt_dir = argv[1];
    4521608        mtpair->mnt_type = fstype;
    4531609        mtpair->mnt_opts = cmdopts;
     
    4561612    }
    4571613
    458     // If we have at least one argument, it's the storage location
    459 
    460     if (optind < argc) storage_path = bb_simplify_path(argv[optind]);
     1614    i = parse_mount_options(cmdopts, 0);
     1615    if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
     1616        bb_error_msg_and_die(must_be_root);
     1617
     1618    // If we have a shared subtree flag, don't worry about fstab or mtab.
     1619
     1620    if (ENABLE_FEATURE_MOUNT_FLAGS
     1621     && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
     1622    ) {
     1623        rc = mount("", argv[0], "", i, "");
     1624        if (rc) bb_perror_msg_and_die("%s", argv[0]);
     1625        goto clean_up;
     1626    }
    4611627
    4621628    // Open either fstab or mtab
    4631629
    464     if (parse_mount_options(cmdopts,0) & MS_REMOUNT)
    465         fstabname = (char *)bb_path_mtab_file;  // Again with the evil const.
    466     else fstabname="/etc/fstab";
    467 
    468     if (!(fstab=setmntent(fstabname,"r")))
    469         bb_perror_msg_and_die("Cannot read %s",fstabname);
     1630    fstabname = "/etc/fstab";
     1631    if (i & MS_REMOUNT) {
     1632        fstabname = bb_path_mtab_file;
     1633    }
     1634    fstab = setmntent(fstabname, "r");
     1635    if (!fstab)
     1636        bb_perror_msg_and_die("cannot read %s", fstabname);
    4701637
    4711638    // Loop through entries until we find what we're looking for.
    4721639
    473     memset(mtpair,0,sizeof(mtpair));
     1640    memset(mtpair, 0, sizeof(mtpair));
    4741641    for (;;) {
    475         struct mntent *mtnext = mtpair + (mtcur==mtpair ? 1 : 0);
     1642        struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
    4761643
    4771644        // Get next fstab entry
     
    4831650            // Were we looking for something specific?
    4841651
    485             if (optind != argc) {
     1652            if (argc) {
    4861653
    4871654                // If we didn't find anything, complain.
    4881655
    4891656                if (!mtnext->mnt_fsname)
    490                     bb_error_msg_and_die("Can't find %s in %s",
    491                         argv[optind], fstabname);
     1657                    bb_error_msg_and_die("can't find %s in %s",
     1658                        argv[0], fstabname);
     1659
     1660                mtcur = mtnext;
     1661                if (nonroot) {
     1662                    // fstab must have "users" or "user"
     1663                    if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS))
     1664                        bb_error_msg_and_die(must_be_root);
     1665                }
    4921666
    4931667                // Mount the last thing we found.
    4941668
    495                 mtcur = mtnext;
    496                 mtcur->mnt_opts=bb_xstrdup(mtcur->mnt_opts);
    497                 append_mount_options(&(mtcur->mnt_opts),cmdopts);
     1669                mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
     1670                append_mount_options(&(mtcur->mnt_opts), cmdopts);
    4981671                rc = singlemount(mtcur, 0);
    4991672                free(mtcur->mnt_opts);
     
    5061679         * "proc") or a full path from root */
    5071680
    508         if (optind != argc) {
     1681        if (argc) {
    5091682
    5101683            // Is this what we're looking for?
    5111684
    512             if(strcmp(argv[optind],mtcur->mnt_fsname) &&
    513                strcmp(storage_path,mtcur->mnt_fsname) &&
    514                strcmp(argv[optind],mtcur->mnt_dir) &&
    515                strcmp(storage_path,mtcur->mnt_dir)) continue;
     1685            if (strcmp(argv[0], mtcur->mnt_fsname) &&
     1686               strcmp(storage_path, mtcur->mnt_fsname) &&
     1687               strcmp(argv[0], mtcur->mnt_dir) &&
     1688               strcmp(storage_path, mtcur->mnt_dir)) continue;
    5161689
    5171690            // Remember this entry.  Something later may have overmounted
     
    5231696
    5241697        } else {
    525 
    5261698            // Do we need to match a filesystem type?
    527             if (fstype && strcmp(mtcur->mnt_type,fstype)) continue;
     1699            if (fstype && match_fstype(mtcur, fstype)) continue;
    5281700
    5291701            // Skip noauto and swap anyway.
    5301702
    531             if (parse_mount_options(mtcur->mnt_opts,0)
     1703            if (parse_mount_options(mtcur->mnt_opts, 0)
    5321704                & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
    5331705
     1706            // No, mount -a won't mount anything,
     1707            // even user mounts, for mere humans.
     1708
     1709            if (nonroot)
     1710                bb_error_msg_and_die(must_be_root);
     1711
    5341712            // Mount this thing.
    5351713
     1714            // NFS mounts want this to be xrealloc-able
     1715            mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
    5361716            if (singlemount(mtcur, 1)) {
    5371717                /* Count number of failed mounts */
    5381718                rc++;
    5391719            }
     1720            free(mtcur->mnt_opts);
    5401721        }
    5411722    }
Note: See TracChangeset for help on using the changeset viewer.