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