Magellan Linux

Contents of /trunk/mkinitrd-magellan/busybox/util-linux/mount.c

Parent Directory Parent Directory | Revision Log Revision Log


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