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