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