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