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

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 16 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
RevLine 
[821]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
[1770]21#include "libbb.h"
[821]22#include <mntent.h>
23
[1770]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}
[821]45#endif
46
[1770]47
[821]48// Not real flags, but we want to be able to check for this.
[1770]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
[821]63/* Standard mount options (from -o options or --options), with corresponding
64 * flags */
65
66struct {
67 const char *name;
68 long flags;
[1770]69} static mount_options[] = {
70 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
[821]71
[1770]72 USE_FEATURE_MOUNT_LOOP(
73 {"loop", 0},
74 )
[821]75
[1770]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 )
[821]85
[1770]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},
[821]101
[1770]102 // action flags
[821]103
[1770]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
[821]121};
122
[1770]123
[821]124/* Append mount options to string */
[1770]125static void append_mount_options(char **oldopts, const char *newopts)
[821]126{
[1770]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 }
[821]150 } else {
151 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
[1770]152 *oldopts = xstrdup(newopts);
[821]153 }
154}
155
156/* Use the mount_options list to parse options into flags.
[1770]157 * Also return list of unrecognized options if unrecognized!=NULL */
[821]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
[1770]170 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
[821]171 if (!strcasecmp(mount_options[i].name, options)) {
172 long fl = mount_options[i].flags;
[1770]173 if (fl < 0) flags &= fl;
[821]174 else flags |= fl;
175 break;
176 }
177 }
178 // If unrecognized not NULL, append unrecognized mount options */
[1770]179 if (unrecognized && i == ARRAY_SIZE(mount_options)) {
[821]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
[1770]190 if (comma) {
[821]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{
[1770]203 static const char filesystems[2][sizeof("/proc/filesystems")] = {
204 "/etc/filesystems",
205 "/proc/filesystems",
206 };
207 char *fs, *buf;
[821]208 llist_t *list = 0;
209 int i;
210 FILE *f;
211
[1770]212 for (i = 0; i < 2; i++) {
213 f = fopen(filesystems[i], "r");
214 if (!f) continue;
[821]215
[1770]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;
[821]221
[1770]222 llist_add_to_end(&list, xstrdup(fs));
223 free(buf);
[821]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
[1770]243static int useMtab = 1;
[821]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.
[1770]251// NB: mp->xxx fields may be trashed on exit
[821]252static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
253{
[1770]254 int rc = 0;
[821]255
[1770]256 if (fakeIt) goto mtab;
[821]257
258 // Mount, with fallback to read-only if necessary.
259
[1770]260 for (;;) {
[821]261 rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
262 vfsflags, filteropts);
[1770]263 if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
[821]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. */
[1770]277 mtab:
278 if (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
279 char *fsname;
[821]280 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
281 int i;
282
[1770]283 if (!mountTable) {
284 bb_error_msg("no %s",bb_path_mtab_file);
285 goto ret;
286 }
[821]287
288 // Add vfs string flags
289
[1770]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);
[821]293
294 // Remove trailing / (if any) from directory we mounted on
295
[1770]296 i = strlen(mp->mnt_dir) - 1;
297 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = 0;
[821]298
[1770]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
[821]309 // Write and close.
310
311 addmntent(mountTable, mp);
312 endmntent(mountTable);
[1770]313 if (ENABLE_FEATURE_CLEAN_UP) {
314 free(mp->mnt_dir);
315 free(fsname);
316 }
[821]317 }
[1770]318 ret:
[821]319 return rc;
320}
321
[1770]322#if ENABLE_FEATURE_MOUNT_NFS
[821]323
[1770]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 */
[821]345
[1770]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
[821]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
[1770]1385 if (mp->mnt_type && strcmp(mp->mnt_type,"auto") == 0)
1386 mp->mnt_type = 0;
[821]1387
[1770]1388 // Might this be an CIFS filesystem?
[821]1389
[1770]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);
[821]1435 }
[1770]1436 goto report_error;
[821]1437 }
1438
[1770]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
[821]1449 // Look at the file. (Not found isn't a failure for remount, or for
1450 // a synthetic filesystem like proc or sysfs.)
[1770]1451 // (We use stat, not lstat, in order to allow
1452 // mount symlink_to_file_or_blkdev dir)
[821]1453
[1770]1454 if (!stat(mp->mnt_fsname, &st)
1455 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1456 ) {
[821]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);
[1770]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;
[821]1468 }
1469
1470 // Autodetect bind mounts
1471
[1770]1472 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1473 vfsflags |= MS_BIND;
[821]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 {
[1770]1482 // Loop through filesystem types until mount succeeds
1483 // or we run out
[821]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;
[1770]1497 rc = mount_it_now(mp, vfsflags, filteropts);
1498 if (!rc) break;
[821]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 }
[1770]1511
1512 report_error:
1513 if (ENABLE_FEATURE_CLEAN_UP)
1514 free(filteropts);
1515
1516 if (rc && errno == EBUSY && ignore_busy)
1517 rc = 0;
[821]1518 if (rc < 0)
[1770]1519 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
[821]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
[1770]1527static const char must_be_root[] ALIGN1 = "you must be root";
1528
1529int mount_main(int argc, char **argv);
[821]1530int mount_main(int argc, char **argv)
1531{
[1770]1532 enum { OPT_ALL = 0x10 };
1533
1534 char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0;
1535 char *opt_o;
1536 const char *fstabname;
[821]1537 FILE *fstab;
[1770]1538 int i, j, rc = 0;
1539 unsigned opt;
[821]1540 struct mntent mtpair[2], *mtcur = mtpair;
[1770]1541 SKIP_DESKTOP(const int nonroot = 0;)
1542 USE_DESKTOP( int nonroot = (getuid() != 0);)
[821]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
[1770]1547 for (i = j = 0; i < argc; i++) {
[821]1548 if (argv[i][0] == '-' && argv[i][1] == '-') {
[1770]1549 append_mount_options(&cmdopts, argv[i]+2);
1550 } else argv[j++] = argv[i];
[821]1551 }
[1770]1552 argv[j] = 0;
1553 argc = j;
[821]1554
1555 // Parse remaining options
1556
[1770]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;
[821]1569
1570 // Three or more non-option arguments? Die with a usage message.
1571
[1770]1572 if (argc > 2) bb_show_usage();
[821]1573
1574 // If we have no arguments, show currently mounted filesystems
1575
[1770]1576 if (!argc) {
1577 if (!(opt & OPT_ALL)) {
[821]1578 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1579
[1770]1580 if (!mountTable) bb_error_msg_and_die("no %s", bb_path_mtab_file);
[821]1581
[1770]1582 while (getmntent_r(mountTable, mtpair, bb_common_bufsiz1,
[821]1583 sizeof(bb_common_bufsiz1)))
1584 {
[1770]1585 // Don't show rootfs. FIXME: why??
1586 // util-linux 2.12a happily shows rootfs...
1587 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
[821]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 }
[1770]1597 } else storage_path = bb_simplify_path(argv[0]);
[821]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
[1770]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];
[821]1608 mtpair->mnt_type = fstype;
1609 mtpair->mnt_opts = cmdopts;
1610 rc = singlemount(mtpair, 0);
1611 goto clean_up;
1612 }
1613
[1770]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);
[821]1617
[1770]1618 // If we have a shared subtree flag, don't worry about fstab or mtab.
[821]1619
[1770]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
[821]1628 // Open either fstab or mtab
1629
[1770]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);
[821]1637
1638 // Loop through entries until we find what we're looking for.
1639
[1770]1640 memset(mtpair, 0, sizeof(mtpair));
[821]1641 for (;;) {
[1770]1642 struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
[821]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
[1770]1652 if (argc) {
[821]1653
1654 // If we didn't find anything, complain.
1655
1656 if (!mtnext->mnt_fsname)
[1770]1657 bb_error_msg_and_die("can't find %s in %s",
1658 argv[0], fstabname);
[821]1659
[1770]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
[821]1667 // Mount the last thing we found.
1668
[1770]1669 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1670 append_mount_options(&(mtcur->mnt_opts), cmdopts);
[821]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
[1770]1681 if (argc) {
[821]1682
1683 // Is this what we're looking for?
1684
[1770]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;
[821]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?
[1770]1699 if (fstype && match_fstype(mtcur, fstype)) continue;
[821]1700
1701 // Skip noauto and swap anyway.
1702
[1770]1703 if (parse_mount_options(mtcur->mnt_opts, 0)
[821]1704 & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
1705
[1770]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
[821]1712 // Mount this thing.
1713
[1770]1714 // NFS mounts want this to be xrealloc-able
1715 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
[821]1716 if (singlemount(mtcur, 1)) {
1717 /* Count number of failed mounts */
1718 rc++;
1719 }
[1770]1720 free(mtcur->mnt_opts);
[821]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.