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