Magellan Linux

Contents of /tags/mkinitrd-6.1.5/busybox/util-linux/mount.c

Parent Directory Parent Directory | Revision Log Revision Log


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