Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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