source: MondoRescue/branches/3.3/mindi-busybox/util-linux/mount.c@ 3621

Last change on this file since 3621 was 3621, checked in by Bruno Cornec, 7 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

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