source: branches/stable/mindi-busybox/util-linux/mount.c

Last change on this file was 1770, checked in by Bruno Cornec, 13 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

File size: 41.4 KB
Line 
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 "libbb.h"
22#include <mntent.h>
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}
45#endif
46
47
48// Not real flags, but we want to be able to check for this.
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
63/* Standard mount options (from -o options or --options), with corresponding
64 * flags */
65
66struct {
67    const char *name;
68    long flags;
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
121};
122
123
124/* Append mount options to string */
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        }
150    } else {
151        if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
152        *oldopts = xstrdup(newopts);
153    }
154}
155
156/* Use the mount_options list to parse options into flags.
157 * Also return list of unrecognized options if unrecognized!=NULL */
158static int parse_mount_options(char *options, char **unrecognized)
159{
160    int flags = MS_SILENT;
161
162    // Loop through options
163    for (;;) {
164        int i;
165        char *comma = strchr(options, ',');
166
167        if (comma) *comma = 0;
168
169        // Find this option in mount_options
170        for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
171            if (!strcasecmp(mount_options[i].name, options)) {
172                long fl = mount_options[i].flags;
173                if (fl < 0) flags &= fl;
174                else flags |= fl;
175                break;
176            }
177        }
178        // If unrecognized not NULL, append unrecognized mount options */
179        if (unrecognized && i == ARRAY_SIZE(mount_options)) {
180            // Add it to strflags, to pass on to kernel
181            i = *unrecognized ? strlen(*unrecognized) : 0;
182            *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
183
184            // Comma separated if it's not the first one
185            if (i) (*unrecognized)[i++] = ',';
186            strcpy((*unrecognized)+i, options);
187        }
188
189        // Advance to next option, or finish
190        if (comma) {
191            *comma = ',';
192            options = ++comma;
193        } else break;
194    }
195
196    return flags;
197}
198
199// Return a list of all block device backed filesystems
200
201static llist_t *get_block_backed_filesystems(void)
202{
203    static const char filesystems[2][sizeof("/proc/filesystems")] = {
204        "/etc/filesystems",
205        "/proc/filesystems",
206    };
207    char *fs, *buf;
208    llist_t *list = 0;
209    int i;
210    FILE *f;
211
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);
224        }
225        if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
226    }
227
228    return list;
229}
230
231llist_t *fslist = 0;
232
233#if ENABLE_FEATURE_CLEAN_UP
234static void delete_block_backed_filesystems(void)
235{
236    llist_free(fslist, free);
237}
238#else
239void delete_block_backed_filesystems(void);
240#endif
241
242#if ENABLE_FEATURE_MTAB_SUPPORT
243static int useMtab = 1;
244static int fakeIt;
245#else
246#define useMtab 0
247#define fakeIt 0
248#endif
249
250// Perform actual mount of specific filesystem at specific location.
251// NB: mp->xxx fields may be trashed on exit
252static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
253{
254    int rc = 0;
255
256    if (fakeIt) goto mtab;
257
258    // Mount, with fallback to read-only if necessary.
259
260    for (;;) {
261        rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
262                vfsflags, filteropts);
263        if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
264            break;
265        bb_error_msg("%s is write-protected, mounting read-only",
266                mp->mnt_fsname);
267        vfsflags |= MS_RDONLY;
268    }
269
270    // Abort entirely if permission denied.
271
272    if (rc && errno == EPERM)
273        bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
274
275    /* If the mount was successful, and we're maintaining an old-style
276     * mtab file by hand, add the new entry to it now. */
277 mtab:
278    if (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
279        char *fsname;
280        FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
281        int i;
282
283        if (!mountTable) {
284            bb_error_msg("no %s",bb_path_mtab_file);
285            goto ret;
286        }
287
288        // Add vfs string flags
289
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);
293
294        // Remove trailing / (if any) from directory we mounted on
295
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;
308
309        // Write and close.
310
311        addmntent(mountTable, mp);
312        endmntent(mountTable);
313        if (ENABLE_FEATURE_CLEAN_UP) {
314            free(mp->mnt_dir);
315            free(fsname);
316        }
317    }
318 ret:
319    return rc;
320}
321
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
1374static int singlemount(struct mntent *mp, int ignore_busy)
1375{
1376    int rc = -1, vfsflags;
1377    char *loopFile = 0, *filteropts = 0;
1378    llist_t *fl = 0;
1379    struct stat st;
1380
1381    vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1382
1383    // Treat fstype "auto" as unspecified.
1384
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    }
1438
1439    // Might this be an NFS filesystem?
1440
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;
1447    }
1448
1449    // Look at the file.  (Not found isn't a failure for remount, or for
1450    // a synthetic filesystem like proc or sysfs.)
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    ) {
1457        // Do we need to allocate a loopback device for it?
1458
1459        if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1460            loopFile = bb_simplify_path(mp->mnt_fsname);
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;
1468            }
1469
1470        // Autodetect bind mounts
1471
1472        } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1473            vfsflags |= MS_BIND;
1474    }
1475
1476    /* If we know the fstype (or don't need to), jump straight
1477     * to the actual mount. */
1478
1479    if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1480        rc = mount_it_now(mp, vfsflags, filteropts);
1481    else {
1482        // Loop through filesystem types until mount succeeds
1483        // or we run out
1484
1485        /* Initialize list of block backed filesystems.  This has to be
1486         * done here so that during "mount -a", mounts after /proc shows up
1487         * can autodetect. */
1488
1489        if (!fslist) {
1490            fslist = get_block_backed_filesystems();
1491            if (ENABLE_FEATURE_CLEAN_UP && fslist)
1492                atexit(delete_block_backed_filesystems);
1493        }
1494
1495        for (fl = fslist; fl; fl = fl->link) {
1496            mp->mnt_type = fl->data;
1497            rc = mount_it_now(mp, vfsflags, filteropts);
1498            if (!rc) break;
1499        }
1500    }
1501
1502    // If mount failed, clean up loop file (if any).
1503
1504    if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1505        del_loop(mp->mnt_fsname);
1506        if (ENABLE_FEATURE_CLEAN_UP) {
1507            free(loopFile);
1508            free(mp->mnt_fsname);
1509        }
1510    }
1511
1512 report_error:
1513    if (ENABLE_FEATURE_CLEAN_UP)
1514        free(filteropts);
1515
1516    if (rc && errno == EBUSY && ignore_busy)
1517        rc = 0;
1518    if (rc < 0)
1519        bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1520
1521    return rc;
1522}
1523
1524// Parse options, if necessary parse fstab/mtab, and call singlemount for
1525// each directory to be mounted.
1526
1527static const char must_be_root[] ALIGN1 = "you must be root";
1528
1529int mount_main(int argc, char **argv);
1530int mount_main(int argc, char **argv)
1531{
1532    enum { OPT_ALL = 0x10 };
1533
1534    char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0;
1535    char *opt_o;
1536    const char *fstabname;
1537    FILE *fstab;
1538    int i, j, rc = 0;
1539    unsigned opt;
1540    struct mntent mtpair[2], *mtcur = mtpair;
1541    SKIP_DESKTOP(const int nonroot = 0;)
1542    USE_DESKTOP( int nonroot = (getuid() != 0);)
1543
1544    /* parse long options, like --bind and --move.  Note that -o option
1545     * and --option are synonymous.  Yes, this means --remount,rw works. */
1546
1547    for (i = j = 0; i < argc; i++) {
1548        if (argv[i][0] == '-' && argv[i][1] == '-') {
1549            append_mount_options(&cmdopts, argv[i]+2);
1550        } else argv[j++] = argv[i];
1551    }
1552    argv[j] = 0;
1553    argc = j;
1554
1555    // Parse remaining options
1556
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;
1569
1570    // Three or more non-option arguments?  Die with a usage message.
1571
1572    if (argc > 2) bb_show_usage();
1573
1574    // If we have no arguments, show currently mounted filesystems
1575
1576    if (!argc) {
1577        if (!(opt & OPT_ALL)) {
1578            FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1579
1580            if (!mountTable) bb_error_msg_and_die("no %s", bb_path_mtab_file);
1581
1582            while (getmntent_r(mountTable, mtpair, bb_common_bufsiz1,
1583                                sizeof(bb_common_bufsiz1)))
1584            {
1585                // Don't show rootfs. FIXME: why??
1586                // util-linux 2.12a happily shows rootfs...
1587                //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
1588
1589                if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1590                    printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1591                            mtpair->mnt_dir, mtpair->mnt_type,
1592                            mtpair->mnt_opts);
1593            }
1594            if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
1595            return EXIT_SUCCESS;
1596        }
1597    } else storage_path = bb_simplify_path(argv[0]);
1598
1599    // When we have two arguments, the second is the directory and we can
1600    // skip looking at fstab entirely.  We can always abspath() the directory
1601    // argument when we get it.
1602
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];
1608        mtpair->mnt_type = fstype;
1609        mtpair->mnt_opts = cmdopts;
1610        rc = singlemount(mtpair, 0);
1611        goto clean_up;
1612    }
1613
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    }
1627
1628    // Open either fstab or mtab
1629
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);
1637
1638    // Loop through entries until we find what we're looking for.
1639
1640    memset(mtpair, 0, sizeof(mtpair));
1641    for (;;) {
1642        struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
1643
1644        // Get next fstab entry
1645
1646        if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1
1647                    + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0),
1648                sizeof(bb_common_bufsiz1)/2))
1649        {
1650            // Were we looking for something specific?
1651
1652            if (argc) {
1653
1654                // If we didn't find anything, complain.
1655
1656                if (!mtnext->mnt_fsname)
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                }
1666
1667                // Mount the last thing we found.
1668
1669                mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1670                append_mount_options(&(mtcur->mnt_opts), cmdopts);
1671                rc = singlemount(mtcur, 0);
1672                free(mtcur->mnt_opts);
1673            }
1674            goto clean_up;
1675        }
1676
1677        /* If we're trying to mount something specific and this isn't it,
1678         * skip it.  Note we must match both the exact text in fstab (ala
1679         * "proc") or a full path from root */
1680
1681        if (argc) {
1682
1683            // Is this what we're looking for?
1684
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;
1689
1690            // Remember this entry.  Something later may have overmounted
1691            // it, and we want the _last_ match.
1692
1693            mtcur = mtnext;
1694
1695        // If we're mounting all.
1696
1697        } else {
1698            // Do we need to match a filesystem type?
1699            if (fstype && match_fstype(mtcur, fstype)) continue;
1700
1701            // Skip noauto and swap anyway.
1702
1703            if (parse_mount_options(mtcur->mnt_opts, 0)
1704                & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
1705
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
1712            // Mount this thing.
1713
1714            // NFS mounts want this to be xrealloc-able
1715            mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1716            if (singlemount(mtcur, 1)) {
1717                /* Count number of failed mounts */
1718                rc++;
1719            }
1720            free(mtcur->mnt_opts);
1721        }
1722    }
1723    if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
1724
1725clean_up:
1726
1727    if (ENABLE_FEATURE_CLEAN_UP) {
1728        free(storage_path);
1729        free(cmdopts);
1730    }
1731
1732    return rc;
1733}
Note: See TracBrowser for help on using the repository browser.