source: MondoRescue/branches/2.2.9/mindi-busybox/util-linux/mount.c@ 3320

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