blob: 3b8311318201d87224b8d75d6cfec79a880c09d6 [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersencc8ed391999-10-05 16:24:54 +00002/*
Eric Andersen596e5461999-10-07 08:30:23 +00003 * Mini mount implementation for busybox
4 *
Eric Andersenc4996011999-10-20 22:08:37 +00005 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
Eric Andersenc7bda1c2004-03-15 08:29:22 +00006 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
Rob Landleydc0955b2006-03-14 18:16:25 +00007 * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net>
Eric Andersen596e5461999-10-07 08:30:23 +00008 *
Rob Landley7b363fd2005-12-20 17:18:01 +00009 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Erik Andersenb7cc49d2000-01-13 06:38:14 +000010 */
Eric Andersencc8ed391999-10-05 16:24:54 +000011
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000012// 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//
Rob Landleydc0955b2006-03-14 18:16:25 +000020
Eric Andersencc8ed391999-10-05 16:24:54 +000021#include <mntent.h>
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +000022#include <syslog.h>
Denis Vlasenkode7684a2008-02-18 21:08:49 +000023#include "libbb.h"
Eric Andersenbd22ed82000-07-08 18:55:24 +000024
Denis Vlasenko6aa76962008-03-18 01:44:52 +000025#if ENABLE_FEATURE_MOUNT_LABEL
Denis Vlasenkode7684a2008-02-18 21:08:49 +000026#include "volume_id.h"
Denis Vlasenko6aa76962008-03-18 01:44:52 +000027#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +000028
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000029// Needed for nfs support only
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000030#include <sys/utsname.h>
31#undef TRUE
32#undef FALSE
33#include <rpc/rpc.h>
34#include <rpc/pmap_prot.h>
35#include <rpc/pmap_clnt.h>
36
Denis Vlasenko2535f122007-09-15 13:28:30 +000037#ifndef MS_SILENT
38#define MS_SILENT (1 << 15)
39#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000040// Grab more as needed from util-linux's mount/mount_constants.h
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +000041#ifndef MS_DIRSYNC
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000042#define MS_DIRSYNC 128 // Directory modifications are synchronous
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +000043#endif
44
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000045
Denis Vlasenko908d6b72006-12-18 23:07:42 +000046#if defined(__dietlibc__)
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000047// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
48// dietlibc-0.30 does not have implementation of getmntent_r()
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +000049static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000050 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
Denis Vlasenko908d6b72006-12-18 23:07:42 +000051{
Denis Vlasenko908d6b72006-12-18 23:07:42 +000052 struct mntent* ment = getmntent(stream);
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +000053 return memcpy(result, ment, sizeof(*ment));
Denis Vlasenko908d6b72006-12-18 23:07:42 +000054}
55#endif
56
57
Rob Landleydc0955b2006-03-14 18:16:25 +000058// Not real flags, but we want to be able to check for this.
Denis Vlasenko13c5a682006-10-16 22:39:51 +000059enum {
Denis Vlasenkoa4522c52008-03-17 08:46:43 +000060 MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP,
61 MOUNT_NOAUTO = (1 << 29),
62 MOUNT_SWAP = (1 << 30),
Denis Vlasenko13c5a682006-10-16 22:39:51 +000063};
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +000064
65
66#define OPTION_STR "o:t:rwanfvsi"
67enum {
68 OPT_o = (1 << 0),
69 OPT_t = (1 << 1),
70 OPT_r = (1 << 2),
71 OPT_w = (1 << 3),
72 OPT_a = (1 << 4),
73 OPT_n = (1 << 5),
74 OPT_f = (1 << 6),
75 OPT_v = (1 << 7),
76 OPT_s = (1 << 8),
77 OPT_i = (1 << 9),
78};
79
80#if ENABLE_FEATURE_MTAB_SUPPORT
81#define useMtab (!(option_mask32 & OPT_n))
82#else
83#define useMtab 0
84#endif
85
86#if ENABLE_FEATURE_MOUNT_FAKE
87#define fakeIt (option_mask32 & OPT_f)
88#else
89#define fakeIt 0
90#endif
91
92
Denis Vlasenko13c5a682006-10-16 22:39:51 +000093// TODO: more "user" flag compatibility.
94// "user" option (from mount manpage):
95// Only the user that mounted a filesystem can unmount it again.
96// If any user should be able to unmount, then use users instead of user
97// in the fstab line. The owner option is similar to the user option,
98// with the restriction that the user must be the owner of the special file.
99// This may be useful e.g. for /dev/fd if a login script makes
100// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +0000101
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000102// Standard mount options (from -o options or --options),
103// with corresponding flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000104static const int32_t mount_options[] = {
Rob Landleye3781b72006-08-08 01:39:49 +0000105 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +0000106
Rob Landleye3781b72006-08-08 01:39:49 +0000107 USE_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000108 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000109 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000110
Rob Landleye3781b72006-08-08 01:39:49 +0000111 USE_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000112 /* "defaults" */ 0,
113 /* "quiet" 0 - do not filter out, vfat wants to see it */
114 /* "noauto" */ MOUNT_NOAUTO,
115 /* "sw" */ MOUNT_SWAP,
116 /* "swap" */ MOUNT_SWAP,
117 USE_DESKTOP(/* "user" */ MOUNT_USERS,)
118 USE_DESKTOP(/* "users" */ MOUNT_USERS,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000119 /* "_netdev" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000120 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000121
Rob Landleye3781b72006-08-08 01:39:49 +0000122 USE_FEATURE_MOUNT_FLAGS(
123 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000124 /* "nosuid" */ MS_NOSUID,
125 /* "suid" */ ~MS_NOSUID,
126 /* "dev" */ ~MS_NODEV,
127 /* "nodev" */ MS_NODEV,
128 /* "exec" */ ~MS_NOEXEC,
129 /* "noexec" */ MS_NOEXEC,
130 /* "sync" */ MS_SYNCHRONOUS,
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000131 /* "dirsync" */ MS_DIRSYNC,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000132 /* "async" */ ~MS_SYNCHRONOUS,
133 /* "atime" */ ~MS_NOATIME,
134 /* "noatime" */ MS_NOATIME,
135 /* "diratime" */ ~MS_NODIRATIME,
136 /* "nodiratime" */ MS_NODIRATIME,
Denis Vlasenko580ce2d2008-07-08 02:56:53 +0000137 /* "mand" */ MS_MANDLOCK,
138 /* "nomand" */ ~MS_MANDLOCK,
Bernhard Reutner-Fischerfb5902c2008-08-06 18:14:38 +0000139 /* "relatime" */ MS_RELATIME,
140 /* "norelatime" */ ~MS_RELATIME,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000141 /* "loud" */ ~MS_SILENT,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000142
Rob Landleye3781b72006-08-08 01:39:49 +0000143 // action flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000144 /* "bind" */ MS_BIND,
145 /* "move" */ MS_MOVE,
146 /* "shared" */ MS_SHARED,
147 /* "slave" */ MS_SLAVE,
148 /* "private" */ MS_PRIVATE,
149 /* "unbindable" */ MS_UNBINDABLE,
150 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
151 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
152 /* "rprivate" */ MS_SLAVE|MS_RECURSIVE,
153 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000154 )
155
156 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000157 /* "ro" */ MS_RDONLY, // vfs flag
158 /* "rw" */ ~MS_RDONLY, // vfs flag
159 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000160};
161
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000162static const char mount_option_str[] =
163 USE_FEATURE_MOUNT_LOOP(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000164 "loop\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000165 )
166 USE_FEATURE_MOUNT_FSTAB(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000167 "defaults\0"
168 // "quiet\0" - do not filter out, vfat wants to see it
169 "noauto\0"
170 "sw\0"
171 "swap\0"
172 USE_DESKTOP("user\0")
173 USE_DESKTOP("users\0")
174 "_netdev\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000175 )
176 USE_FEATURE_MOUNT_FLAGS(
177 // vfs flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000178 "nosuid\0"
179 "suid\0"
180 "dev\0"
181 "nodev\0"
182 "exec\0"
183 "noexec\0"
184 "sync\0"
185 "dirsync\0"
186 "async\0"
187 "atime\0"
188 "noatime\0"
189 "diratime\0"
190 "nodiratime\0"
191 "mand\0"
192 "nomand\0"
193 "relatime\0"
194 "norelatime\0"
195 "loud\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000196
197 // action flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000198 "bind\0"
199 "move\0"
200 "shared\0"
201 "slave\0"
202 "private\0"
203 "unbindable\0"
204 "rshared\0"
205 "rslave\0"
206 "rprivate\0"
207 "runbindable\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000208 )
209
210 // Always understood.
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000211 "ro\0" // vfs flag
212 "rw\0" // vfs flag
213 "remount\0" // action flag
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000214;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000215
Denis Vlasenkof732e962008-02-18 12:07:49 +0000216
217struct globals {
218#if ENABLE_FEATURE_MOUNT_NFS
219 smalluint nfs_mount_version;
220#endif
221#if ENABLE_FEATURE_MOUNT_VERBOSE
222 unsigned verbose;
223#endif
224 llist_t *fslist;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000225 char getmntent_buf[1];
Denis Vlasenkof732e962008-02-18 12:07:49 +0000226
227};
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000228enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denis Vlasenkof732e962008-02-18 12:07:49 +0000229#define G (*(struct globals*)&bb_common_bufsiz1)
230#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000231#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000232#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000233#else
234#define verbose 0
235#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000236#define fslist (G.fslist )
237#define getmntent_buf (G.getmntent_buf )
238
239
240#if ENABLE_FEATURE_MOUNT_VERBOSE
241static int verbose_mount(const char *source, const char *target,
242 const char *filesystemtype,
243 unsigned long mountflags, const void *data)
244{
245 int rc;
246
247 errno = 0;
248 rc = mount(source, target, filesystemtype, mountflags, data);
249 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000250 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000251 source, target, filesystemtype,
252 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000253 return rc;
254}
255#else
256#define verbose_mount(...) mount(__VA_ARGS__)
257#endif
258
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +0000259#if ENABLE_FEATURE_MOUNT_LABEL
260static void resolve_mount_spec(char **fsname)
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000261{
262 char *tmp = NULL;
263
264 if (!strncmp(*fsname, "UUID=", 5))
265 tmp = get_devname_from_uuid(*fsname + 5);
266 else if (!strncmp(*fsname, "LABEL=", 6))
267 tmp = get_devname_from_label(*fsname + 6);
268
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +0000269 if (tmp)
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000270 *fsname = tmp;
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000271}
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +0000272#else
273#define resolve_mount_spec(fsname) ((void)0)
274#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000275
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000276// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000277static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000278{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000279 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000280 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000281 while (newopts[0]) {
282 char *p;
283 int len = strlen(newopts);
284 p = strchr(newopts, ',');
285 if (p) len = p - newopts;
286 p = *oldopts;
287 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000288 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000289 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000290 goto skip;
291 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000292 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000293 p++;
294 }
295 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
296 free(*oldopts);
297 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000298 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000299 newopts += len;
300 while (newopts[0] == ',') newopts++;
301 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000302 } else {
303 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000304 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000305 }
306}
307
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000308// Use the mount_options list to parse options into flags.
309// Also return list of unrecognized options if unrecognized != NULL
Denis Vlasenkob4133682008-02-18 13:05:38 +0000310static long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000311{
Denis Vlasenkob4133682008-02-18 13:05:38 +0000312 long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000313
Rob Landley6a6798b2005-08-10 20:35:54 +0000314 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000315 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000316 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000317 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000318 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000319
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000320 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000321
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000322// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000323 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000324 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000325 if (!strcasecmp(option_str, options)) {
326 long fl = mount_options[i];
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000327 if (fl < 0) flags &= fl;
Rob Landleydc0955b2006-03-14 18:16:25 +0000328 else flags |= fl;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000329 break;
330 }
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000331 option_str += strlen(option_str) + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000332 }
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000333 // If unrecognized not NULL, append unrecognized mount options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000334 if (unrecognized && i == ARRAY_SIZE(mount_options)) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000335 // Add it to strflags, to pass on to kernel
Rob Landleydc0955b2006-03-14 18:16:25 +0000336 i = *unrecognized ? strlen(*unrecognized) : 0;
Denis Vlasenkodeeed592008-07-08 05:14:36 +0000337 *unrecognized = xrealloc(*unrecognized, i + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000338
Rob Landley6a6798b2005-08-10 20:35:54 +0000339 // Comma separated if it's not the first one
Rob Landleydc0955b2006-03-14 18:16:25 +0000340 if (i) (*unrecognized)[i++] = ',';
341 strcpy((*unrecognized)+i, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000342 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000343
Denis Vlasenko2535f122007-09-15 13:28:30 +0000344 if (!comma)
345 break;
346 // Advance to next option
347 *comma = ',';
348 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000349 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000350
Rob Landleydc0955b2006-03-14 18:16:25 +0000351 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000352}
353
Rob Landleydc0955b2006-03-14 18:16:25 +0000354// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000355static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000356{
Denis Vlasenko87468852007-04-13 23:22:00 +0000357 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000358 "/etc/filesystems",
359 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000360 };
361 char *fs, *buf;
Rob Landleydc0955b2006-03-14 18:16:25 +0000362 llist_t *list = 0;
363 int i;
364 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000365
Denis Vlasenko87468852007-04-13 23:22:00 +0000366 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000367 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000368 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000369
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000370 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000371 if (!strncmp(buf, "nodev", 5) && isspace(buf[5]))
372 continue;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000373 fs = skip_whitespace(buf);
Denis Vlasenko372686b2006-10-12 22:42:33 +0000374 if (*fs=='#' || *fs=='*' || !*fs) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000375
Denis Vlasenko372686b2006-10-12 22:42:33 +0000376 llist_add_to_end(&list, xstrdup(fs));
377 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000378 }
379 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
380 }
381
382 return list;
383}
384
Rob Landleydc0955b2006-03-14 18:16:25 +0000385#if ENABLE_FEATURE_CLEAN_UP
386static void delete_block_backed_filesystems(void)
387{
Rob Landleya6b5b602006-05-08 19:03:07 +0000388 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000389}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000390#else
391void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000392#endif
393
Rob Landleydc0955b2006-03-14 18:16:25 +0000394// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000395// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000396static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000397{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000398 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000399
Denis Vlasenkob4133682008-02-18 13:05:38 +0000400 if (fakeIt) {
401 if (verbose >= 2)
402 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
403 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
404 vfsflags, filteropts);
405 goto mtab;
406 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000407
Rob Landleydc0955b2006-03-14 18:16:25 +0000408 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000409 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000410 errno = 0;
411 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000412 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000413
414 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000415 // helper program mount.<mnt_type>
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000416 if (ENABLE_FEATURE_MOUNT_HELPERS && rc) {
417 char *args[6];
418 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000419 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000420 rc = 1;
421 if (filteropts) {
422 args[rc++] = (char *)"-o";
423 args[rc++] = filteropts;
424 }
425 args[rc++] = mp->mnt_fsname;
426 args[rc++] = mp->mnt_dir;
427 args[rc] = NULL;
428 rc = wait4pid(spawn(args));
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000429 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000430 if (!rc)
431 break;
432 errno = errno_save;
433 }
434
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000435 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000436 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000437 if (!(vfsflags & MS_SILENT))
438 bb_error_msg("%s is write-protected, mounting read-only",
439 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000440 vfsflags |= MS_RDONLY;
441 }
442
Rob Landleydc0955b2006-03-14 18:16:25 +0000443 // Abort entirely if permission denied.
444
445 if (rc && errno == EPERM)
446 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
447
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000448 // If the mount was successful, and we're maintaining an old-style
449 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000450 mtab:
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000451 if (useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000452 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000453 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000454 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000455 int i;
456
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000457 if (!mountTable) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000458 bb_error_msg("no %s", bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000459 goto ret;
460 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000461
462 // Add vfs string flags
463
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000464 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
465 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
466 append_mount_options(&(mp->mnt_opts), option_str);
467 option_str += strlen(option_str) + 1;
468 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000469
470 // Remove trailing / (if any) from directory we mounted on
471
Denis Vlasenko727ef942006-09-14 13:19:19 +0000472 i = strlen(mp->mnt_dir) - 1;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000473 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000474
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000475 // Convert to canonical pathnames as needed
Denis Vlasenko727ef942006-09-14 13:19:19 +0000476
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000477 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000478 fsname = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000479 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000480 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000481 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000482 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000483 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000484
485 // Write and close.
486
Denis Vlasenko727ef942006-09-14 13:19:19 +0000487 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000488 endmntent(mountTable);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000489 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000490 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000491 free(fsname);
492 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000493 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000494 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000495 return rc;
496}
497
Denis Vlasenko25098f72006-09-14 15:46:33 +0000498#if ENABLE_FEATURE_MOUNT_NFS
499
500/*
501 * Linux NFS mount
502 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
503 *
504 * Licensed under GPLv2, see file LICENSE in this tarball for details.
505 *
506 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
507 * numbers to be specified on the command line.
508 *
509 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
510 * Omit the call to connect() for Linux version 1.3.11 or later.
511 *
512 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
513 * Implemented the "bg", "fg" and "retry" mount options for NFS.
514 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000515 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000516 * - added Native Language Support
517 *
518 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
519 * plus NFSv3 stuff.
520 */
521
Denis Vlasenko25098f72006-09-14 15:46:33 +0000522/* This is just a warning of a common mistake. Possibly this should be a
523 * uclibc faq entry rather than in busybox... */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000524#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000525#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
526#endif
527
528#define MOUNTPORT 635
529#define MNTPATHLEN 1024
530#define MNTNAMLEN 255
531#define FHSIZE 32
532#define FHSIZE3 64
533
534typedef char fhandle[FHSIZE];
535
536typedef struct {
537 unsigned int fhandle3_len;
538 char *fhandle3_val;
539} fhandle3;
540
541enum mountstat3 {
542 MNT_OK = 0,
543 MNT3ERR_PERM = 1,
544 MNT3ERR_NOENT = 2,
545 MNT3ERR_IO = 5,
546 MNT3ERR_ACCES = 13,
547 MNT3ERR_NOTDIR = 20,
548 MNT3ERR_INVAL = 22,
549 MNT3ERR_NAMETOOLONG = 63,
550 MNT3ERR_NOTSUPP = 10004,
551 MNT3ERR_SERVERFAULT = 10006,
552};
553typedef enum mountstat3 mountstat3;
554
555struct fhstatus {
556 unsigned int fhs_status;
557 union {
558 fhandle fhs_fhandle;
559 } fhstatus_u;
560};
561typedef struct fhstatus fhstatus;
562
563struct mountres3_ok {
564 fhandle3 fhandle;
565 struct {
566 unsigned int auth_flavours_len;
567 char *auth_flavours_val;
568 } auth_flavours;
569};
570typedef struct mountres3_ok mountres3_ok;
571
572struct mountres3 {
573 mountstat3 fhs_status;
574 union {
575 mountres3_ok mountinfo;
576 } mountres3_u;
577};
578typedef struct mountres3 mountres3;
579
580typedef char *dirpath;
581
582typedef char *name;
583
584typedef struct mountbody *mountlist;
585
586struct mountbody {
587 name ml_hostname;
588 dirpath ml_directory;
589 mountlist ml_next;
590};
591typedef struct mountbody mountbody;
592
593typedef struct groupnode *groups;
594
595struct groupnode {
596 name gr_name;
597 groups gr_next;
598};
599typedef struct groupnode groupnode;
600
601typedef struct exportnode *exports;
602
603struct exportnode {
604 dirpath ex_dir;
605 groups ex_groups;
606 exports ex_next;
607};
608typedef struct exportnode exportnode;
609
610struct ppathcnf {
611 int pc_link_max;
612 short pc_max_canon;
613 short pc_max_input;
614 short pc_name_max;
615 short pc_path_max;
616 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000617 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000618 char pc_xxx;
619 short pc_mask[2];
620};
621typedef struct ppathcnf ppathcnf;
622
623#define MOUNTPROG 100005
624#define MOUNTVERS 1
625
626#define MOUNTPROC_NULL 0
627#define MOUNTPROC_MNT 1
628#define MOUNTPROC_DUMP 2
629#define MOUNTPROC_UMNT 3
630#define MOUNTPROC_UMNTALL 4
631#define MOUNTPROC_EXPORT 5
632#define MOUNTPROC_EXPORTALL 6
633
634#define MOUNTVERS_POSIX 2
635
636#define MOUNTPROC_PATHCONF 7
637
638#define MOUNT_V3 3
639
640#define MOUNTPROC3_NULL 0
641#define MOUNTPROC3_MNT 1
642#define MOUNTPROC3_DUMP 2
643#define MOUNTPROC3_UMNT 3
644#define MOUNTPROC3_UMNTALL 4
645#define MOUNTPROC3_EXPORT 5
646
647enum {
648#ifndef NFS_FHSIZE
649 NFS_FHSIZE = 32,
650#endif
651#ifndef NFS_PORT
652 NFS_PORT = 2049
653#endif
654};
655
Denis Vlasenko25098f72006-09-14 15:46:33 +0000656/*
657 * We want to be able to compile mount on old kernels in such a way
658 * that the binary will work well on more recent kernels.
659 * Thus, if necessary we teach nfsmount.c the structure of new fields
660 * that will come later.
661 *
662 * Moreover, the new kernel includes conflict with glibc includes
663 * so it is easiest to ignore the kernel altogether (at compile time).
664 */
665
666struct nfs2_fh {
667 char data[32];
668};
669struct nfs3_fh {
670 unsigned short size;
671 unsigned char data[64];
672};
673
674struct nfs_mount_data {
675 int version; /* 1 */
676 int fd; /* 1 */
677 struct nfs2_fh old_root; /* 1 */
678 int flags; /* 1 */
679 int rsize; /* 1 */
680 int wsize; /* 1 */
681 int timeo; /* 1 */
682 int retrans; /* 1 */
683 int acregmin; /* 1 */
684 int acregmax; /* 1 */
685 int acdirmin; /* 1 */
686 int acdirmax; /* 1 */
687 struct sockaddr_in addr; /* 1 */
688 char hostname[256]; /* 1 */
689 int namlen; /* 2 */
690 unsigned int bsize; /* 3 */
691 struct nfs3_fh root; /* 4 */
692};
693
694/* bits in the flags field */
695enum {
696 NFS_MOUNT_SOFT = 0x0001, /* 1 */
697 NFS_MOUNT_INTR = 0x0002, /* 1 */
698 NFS_MOUNT_SECURE = 0x0004, /* 1 */
699 NFS_MOUNT_POSIX = 0x0008, /* 1 */
700 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
701 NFS_MOUNT_NOAC = 0x0020, /* 1 */
702 NFS_MOUNT_TCP = 0x0040, /* 2 */
703 NFS_MOUNT_VER3 = 0x0080, /* 3 */
704 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000705 NFS_MOUNT_NONLM = 0x0200, /* 3 */
706 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +0000707};
708
709
710/*
711 * We need to translate between nfs status return values and
712 * the local errno values which may not be the same.
713 *
714 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
715 * "after #include <errno.h> the symbol errno is reserved for any use,
716 * it cannot even be used as a struct tag or field name".
717 */
718
719#ifndef EDQUOT
720#define EDQUOT ENOSPC
721#endif
722
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000723/* Convert each NFSERR_BLAH into EBLAH */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000724static const struct {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000725 short stat;
726 short errnum;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000727} nfs_errtbl[] = {
728 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
729 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
730 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
731 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
732};
Denis Vlasenko25098f72006-09-14 15:46:33 +0000733static char *nfs_strerror(int status)
734{
735 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000736
737 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
738 if (nfs_errtbl[i].stat == status)
739 return strerror(nfs_errtbl[i].errnum);
740 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000741 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000742}
743
744static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
745{
746 if (!xdr_opaque(xdrs, objp, FHSIZE))
747 return FALSE;
748 return TRUE;
749}
750
751static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
752{
753 if (!xdr_u_int(xdrs, &objp->fhs_status))
754 return FALSE;
755 switch (objp->fhs_status) {
756 case 0:
757 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
758 return FALSE;
759 break;
760 default:
761 break;
762 }
763 return TRUE;
764}
765
766static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
767{
768 if (!xdr_string(xdrs, objp, MNTPATHLEN))
769 return FALSE;
770 return TRUE;
771}
772
773static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
774{
775 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
776 return FALSE;
777 return TRUE;
778}
779
780static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
781{
782 if (!xdr_fhandle3(xdrs, &objp->fhandle))
783 return FALSE;
784 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
785 sizeof (int), (xdrproc_t) xdr_int))
786 return FALSE;
787 return TRUE;
788}
789
790static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
791{
792 if (!xdr_enum(xdrs, (enum_t *) objp))
793 return FALSE;
794 return TRUE;
795}
796
797static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
798{
799 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
800 return FALSE;
801 switch (objp->fhs_status) {
802 case MNT_OK:
803 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
804 return FALSE;
805 break;
806 default:
807 break;
808 }
809 return TRUE;
810}
811
812#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
813
Denis Vlasenko25098f72006-09-14 15:46:33 +0000814/*
815 * Unfortunately, the kernel prints annoying console messages
816 * in case of an unexpected nfs mount version (instead of
817 * just returning some error). Therefore we'll have to try
818 * and figure out what version the kernel expects.
819 *
820 * Variables:
821 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
822 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
823 * nfs_mount_version: version this source and running kernel can handle
824 */
825static void
826find_kernel_nfs_mount_version(void)
827{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000828 int kernel_version;
829
830 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000831 return;
832
833 nfs_mount_version = 4; /* default */
834
835 kernel_version = get_linux_version_code();
836 if (kernel_version) {
837 if (kernel_version < KERNEL_VERSION(2,1,32))
838 nfs_mount_version = 1;
839 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
840 (kernel_version >= KERNEL_VERSION(2,3,0) &&
841 kernel_version < KERNEL_VERSION(2,3,99)))
842 nfs_mount_version = 3;
843 /* else v4 since 2.3.99pre4 */
844 }
845}
846
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +0000847static void
Denis Vlasenkob9256052007-09-28 10:29:17 +0000848get_mountport(struct pmap *pm_mnt,
849 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +0000850 long unsigned prog,
851 long unsigned version,
852 long unsigned proto,
853 long unsigned port)
854{
855 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000856
857 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +0000858/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
859 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000860 pmap = pmap_getmaps(server_addr);
861
862 if (version > MAX_NFSPROT)
863 version = MAX_NFSPROT;
864 if (!prog)
865 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000866 pm_mnt->pm_prog = prog;
867 pm_mnt->pm_vers = version;
868 pm_mnt->pm_prot = proto;
869 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000870
Denis Vlasenko25098f72006-09-14 15:46:33 +0000871 while (pmap) {
872 if (pmap->pml_map.pm_prog != prog)
873 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000874 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000875 goto next;
876 if (version > 2 && pmap->pml_map.pm_vers != version)
877 goto next;
878 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
879 goto next;
880 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
Denis Vlasenkob9256052007-09-28 10:29:17 +0000881 (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) ||
Denis Vlasenko25098f72006-09-14 15:46:33 +0000882 (port && pmap->pml_map.pm_port != port))
883 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000884 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
885 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +0000886 pmap = pmap->pml_next;
887 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000888 if (!pm_mnt->pm_vers)
889 pm_mnt->pm_vers = MOUNTVERS;
890 if (!pm_mnt->pm_port)
891 pm_mnt->pm_port = MOUNTPORT;
892 if (!pm_mnt->pm_prot)
893 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000894}
895
Denis Vlasenkof0000652007-09-04 18:30:26 +0000896#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +0000897static int daemonize(void)
898{
Denis Vlasenko25098f72006-09-14 15:46:33 +0000899 int pid = fork();
900 if (pid < 0) /* error */
901 return -errno;
902 if (pid > 0) /* parent */
903 return 0;
904 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000905 close(0);
906 xopen(bb_dev_null, O_RDWR);
907 xdup2(0, 1);
908 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000909 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +0000910 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000911 logmode = LOGMODE_SYSLOG;
912 return 1;
913}
Denis Vlasenkof0000652007-09-04 18:30:26 +0000914#else
915static inline int daemonize(void) { return -ENOSYS; }
916#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000917
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000918/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000919static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000920{
921 return 0;
922}
923
924/* RPC strerror analogs are terminally idiotic:
925 * *mandatory* prefix and \n at end.
926 * This hopefully helps. Usage:
927 * error_msg_rpc(clnt_*error*(" ")) */
928static void error_msg_rpc(const char *msg)
929{
Denis Vlasenko23514fe2006-09-19 14:07:52 +0000930 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000931 while (msg[0] == ' ' || msg[0] == ':') msg++;
932 len = strlen(msg);
933 while (len && msg[len-1] == '\n') len--;
934 bb_error_msg("%.*s", len, msg);
935}
936
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000937/* NB: mp->xxx fields may be trashed on exit */
Denis Vlasenkob4133682008-02-18 13:05:38 +0000938static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000939{
940 CLIENT *mclient;
941 char *hostname;
942 char *pathname;
943 char *mounthost;
944 struct nfs_mount_data data;
945 char *opt;
946 struct hostent *hp;
947 struct sockaddr_in server_addr;
948 struct sockaddr_in mount_server_addr;
949 int msock, fsock;
950 union {
951 struct fhstatus nfsv2;
952 struct mountres3 nfsv3;
953 } status;
954 int daemonized;
955 char *s;
956 int port;
957 int mountport;
958 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +0000959#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000960 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +0000961#else
962 enum { bg = 0 };
963#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000964 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000965 int mountprog;
966 int mountvers;
967 int nfsprog;
968 int nfsvers;
969 int retval;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000970 /* these all are one-bit really. 4.3.1 likes this combination: */
971 smallint tcp;
972 smallint soft;
973 int intr;
974 int posix;
975 int nocto;
976 int noac;
977 int nordirplus;
978 int nolock;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000979
980 find_kernel_nfs_mount_version();
981
982 daemonized = 0;
983 mounthost = NULL;
984 retval = ETIMEDOUT;
985 msock = fsock = -1;
986 mclient = NULL;
987
988 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
989
990 filteropts = xstrdup(filteropts); /* going to trash it later... */
991
992 hostname = xstrdup(mp->mnt_fsname);
993 /* mount_main() guarantees that ':' is there */
994 s = strchr(hostname, ':');
995 pathname = s + 1;
996 *s = '\0';
997 /* Ignore all but first hostname in replicated mounts
998 until they can be fully supported. (mack@sgi.com) */
999 s = strchr(hostname, ',');
1000 if (s) {
1001 *s = '\0';
1002 bb_error_msg("warning: multiple hostnames not supported");
1003 }
1004
1005 server_addr.sin_family = AF_INET;
1006 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1007 hp = gethostbyname(hostname);
1008 if (hp == NULL) {
1009 bb_herror_msg("%s", hostname);
1010 goto fail;
1011 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001012 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001013 bb_error_msg("got bad hp->h_length");
1014 hp->h_length = sizeof(struct in_addr);
1015 }
1016 memcpy(&server_addr.sin_addr,
1017 hp->h_addr, hp->h_length);
1018 }
1019
1020 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1021
1022 /* add IP address to mtab options for use when unmounting */
1023
1024 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1025 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1026 } else {
1027 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1028 mp->mnt_opts[0] ? "," : "",
1029 inet_ntoa(server_addr.sin_addr));
1030 free(mp->mnt_opts);
1031 mp->mnt_opts = tmp;
1032 }
1033
1034 /* Set default options.
1035 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1036 * let the kernel decide.
1037 * timeo is filled in after we know whether it'll be TCP or UDP. */
1038 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001039 data.retrans = 3;
1040 data.acregmin = 3;
1041 data.acregmax = 60;
1042 data.acdirmin = 30;
1043 data.acdirmax = 60;
1044 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001045
Denis Vlasenko25098f72006-09-14 15:46:33 +00001046 soft = 0;
1047 intr = 0;
1048 posix = 0;
1049 nocto = 0;
1050 nolock = 0;
1051 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001052 nordirplus = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001053 retry = 10000; /* 10000 minutes ~ 1 week */
1054 tcp = 0;
1055
1056 mountprog = MOUNTPROG;
1057 mountvers = 0;
1058 port = 0;
1059 mountport = 0;
1060 nfsprog = 100003;
1061 nfsvers = 0;
1062
1063 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001064 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001065 char *opteq = strchr(opt, '=');
1066 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001067 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001068 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001069 /* 0 */ "rsize\0"
1070 /* 1 */ "wsize\0"
1071 /* 2 */ "timeo\0"
1072 /* 3 */ "retrans\0"
1073 /* 4 */ "acregmin\0"
1074 /* 5 */ "acregmax\0"
1075 /* 6 */ "acdirmin\0"
1076 /* 7 */ "acdirmax\0"
1077 /* 8 */ "actimeo\0"
1078 /* 9 */ "retry\0"
1079 /* 10 */ "port\0"
1080 /* 11 */ "mountport\0"
1081 /* 12 */ "mounthost\0"
1082 /* 13 */ "mountprog\0"
1083 /* 14 */ "mountvers\0"
1084 /* 15 */ "nfsprog\0"
1085 /* 16 */ "nfsvers\0"
1086 /* 17 */ "vers\0"
1087 /* 18 */ "proto\0"
1088 /* 19 */ "namlen\0"
1089 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001090
1091 *opteq++ = '\0';
1092 idx = index_in_strings(options, opt);
1093 switch (idx) {
1094 case 12: // "mounthost"
1095 mounthost = xstrndup(opteq,
1096 strcspn(opteq, " \t\n\r,"));
1097 continue;
1098 case 18: // "proto"
1099 if (!strncmp(opteq, "tcp", 3))
1100 tcp = 1;
1101 else if (!strncmp(opteq, "udp", 3))
1102 tcp = 0;
1103 else
1104 bb_error_msg("warning: unrecognized proto= option");
1105 continue;
1106 case 20: // "addr" - ignore
1107 continue;
1108 }
1109
1110 val = xatoi_u(opteq);
1111 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001112 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001113 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001114 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001115 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001116 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001117 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001118 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001119 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001120 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001121 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001122 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001123 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001124 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001125 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001126 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001127 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001128 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001129 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001130 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001131 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001132 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001133 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001134 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001135 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001136 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001137 data.acregmin = val;
1138 data.acregmax = val;
1139 data.acdirmin = val;
1140 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001141 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001142 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001143 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001144 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001145 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001146 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001147 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001148 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001149 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001150 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001151 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001152 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001153 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001154 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001155 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001156 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001157 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001158 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001159 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001160 case 16: // "nfsvers"
1161 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001162 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001163 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001164 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001165 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001166 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001167 //else
1168 // bb_error_msg("warning: option namlen is not supported\n");
1169 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001170 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001171 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1172 goto fail;
1173 }
1174 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001175 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001176 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001177 "bg\0"
1178 "fg\0"
1179 "soft\0"
1180 "hard\0"
1181 "intr\0"
1182 "posix\0"
1183 "cto\0"
1184 "ac\0"
1185 "tcp\0"
1186 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001187 "lock\0"
1188 "rdirplus\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001189 int val = 1;
1190 if (!strncmp(opt, "no", 2)) {
1191 val = 0;
1192 opt += 2;
1193 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001194 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001195 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001196#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001197 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001198#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001199 break;
1200 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001201#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001202 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001203#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001204 break;
1205 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001206 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001207 break;
1208 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001209 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001210 break;
1211 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001212 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001213 break;
1214 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001215 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001216 break;
1217 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001218 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001219 break;
1220 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001221 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001222 break;
1223 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001224 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001225 break;
1226 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001227 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001228 break;
1229 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001230 if (nfs_mount_version >= 3)
1231 nolock = !val;
1232 else
1233 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001234 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001235 case 11: //rdirplus
1236 nordirplus = !val;
1237 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001238 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001239 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1240 goto fail;
1241 }
1242 }
1243 }
1244 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1245
1246 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1247 | (intr ? NFS_MOUNT_INTR : 0)
1248 | (posix ? NFS_MOUNT_POSIX : 0)
1249 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001250 | (noac ? NFS_MOUNT_NOAC : 0)
1251 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001252 if (nfs_mount_version >= 2)
1253 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1254 if (nfs_mount_version >= 3)
1255 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1256 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1257 bb_error_msg("NFSv%d not supported", nfsvers);
1258 goto fail;
1259 }
1260 if (nfsvers && !mountvers)
1261 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1262 if (nfsvers && nfsvers < mountvers) {
1263 mountvers = nfsvers;
1264 }
1265
1266 /* Adjust options if none specified */
1267 if (!data.timeo)
1268 data.timeo = tcp ? 70 : 7;
1269
Denis Vlasenko25098f72006-09-14 15:46:33 +00001270 data.version = nfs_mount_version;
1271
1272 if (vfsflags & MS_REMOUNT)
1273 goto do_mount;
1274
1275 /*
1276 * If the previous mount operation on the same host was
1277 * backgrounded, and the "bg" for this mount is also set,
1278 * give up immediately, to avoid the initial timeout.
1279 */
1280 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001281 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001282 if (daemonized <= 0) { /* parent or error */
1283 retval = -daemonized;
1284 goto ret;
1285 }
1286 }
1287
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001288 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001289 /* See if the nfs host = mount host. */
1290 if (mounthost) {
1291 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1292 mount_server_addr.sin_family = AF_INET;
1293 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1294 } else {
1295 hp = gethostbyname(mounthost);
1296 if (hp == NULL) {
1297 bb_herror_msg("%s", mounthost);
1298 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001299 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001300 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
1301 bb_error_msg("got bad hp->h_length");
1302 hp->h_length = sizeof(struct in_addr);
1303 }
1304 mount_server_addr.sin_family = AF_INET;
1305 memcpy(&mount_server_addr.sin_addr,
1306 hp->h_addr, hp->h_length);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001307 }
1308 }
1309
1310 /*
1311 * The following loop implements the mount retries. When the mount
1312 * times out, and the "bg" option is set, we background ourself
1313 * and continue trying.
1314 *
1315 * The case where the mount point is not present and the "bg"
1316 * option is set, is treated as a timeout. This is done to
1317 * support nested mounts.
1318 *
1319 * The "retry" count specified by the user is the number of
1320 * minutes to retry before giving up.
1321 */
1322 {
1323 struct timeval total_timeout;
1324 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001325 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001326 time_t t;
1327 time_t prevt;
1328 time_t timeout;
1329
1330 retry_timeout.tv_sec = 3;
1331 retry_timeout.tv_usec = 0;
1332 total_timeout.tv_sec = 20;
1333 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001334/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001335 timeout = time(NULL) + 60 * retry;
1336 prevt = 0;
1337 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001338 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001339 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001340 if (t - prevt < 30)
1341 sleep(30);
1342
Denis Vlasenkob9256052007-09-28 10:29:17 +00001343 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001344 mountprog,
1345 mountvers,
1346 proto,
1347 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001348 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001349
1350 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001351 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001352 msock = RPC_ANYSOCK;
1353
Denis Vlasenkob9256052007-09-28 10:29:17 +00001354 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001355 case IPPROTO_UDP:
1356 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001357 pm_mnt.pm_prog,
1358 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001359 retry_timeout,
1360 &msock);
1361 if (mclient)
1362 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001363 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001364 msock = RPC_ANYSOCK;
1365 case IPPROTO_TCP:
1366 mclient = clnttcp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001367 pm_mnt.pm_prog,
1368 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001369 &msock, 0, 0);
1370 break;
1371 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001372 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001373 }
1374 if (!mclient) {
1375 if (!daemonized && prevt == 0)
1376 error_msg_rpc(clnt_spcreateerror(" "));
1377 } else {
1378 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001379
1380 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001381 mclient->cl_auth = authunix_create_default();
1382
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001383 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001384 * that xdr_array allocates memory for us
1385 */
1386 memset(&status, 0, sizeof(status));
1387
Denis Vlasenkob9256052007-09-28 10:29:17 +00001388 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001389 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1390 (xdrproc_t) xdr_dirpath,
1391 (caddr_t) &pathname,
1392 (xdrproc_t) xdr_mountres3,
1393 (caddr_t) &status,
1394 total_timeout);
1395 else
1396 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1397 (xdrproc_t) xdr_dirpath,
1398 (caddr_t) &pathname,
1399 (xdrproc_t) xdr_fhstatus,
1400 (caddr_t) &status,
1401 total_timeout);
1402
1403 if (clnt_stat == RPC_SUCCESS)
1404 goto prepare_kernel_data; /* we're done */
1405 if (errno != ECONNREFUSED) {
1406 error_msg_rpc(clnt_sperror(mclient, " "));
1407 goto fail; /* don't retry */
1408 }
1409 /* Connection refused */
1410 if (!daemonized && prevt == 0) /* print just once */
1411 error_msg_rpc(clnt_sperror(mclient, " "));
1412 auth_destroy(mclient->cl_auth);
1413 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001414 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001415 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001416 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001417 }
1418
1419 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001420 if (!bg)
1421 goto fail;
1422 if (!daemonized) {
1423 daemonized = daemonize();
1424 if (daemonized <= 0) { /* parent or error */
1425 retval = -daemonized;
1426 goto ret;
1427 }
1428 }
1429 prevt = t;
1430 t = time(NULL);
1431 if (t >= timeout)
1432 /* TODO error message */
1433 goto fail;
1434
1435 goto retry;
1436 }
1437
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001438 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001439
1440 if (nfsvers == 2) {
1441 if (status.nfsv2.fhs_status != 0) {
1442 bb_error_msg("%s:%s failed, reason given by server: %s",
1443 hostname, pathname,
1444 nfs_strerror(status.nfsv2.fhs_status));
1445 goto fail;
1446 }
1447 memcpy(data.root.data,
1448 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1449 NFS_FHSIZE);
1450 data.root.size = NFS_FHSIZE;
1451 memcpy(data.old_root.data,
1452 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1453 NFS_FHSIZE);
1454 } else {
1455 fhandle3 *my_fhandle;
1456 if (status.nfsv3.fhs_status != 0) {
1457 bb_error_msg("%s:%s failed, reason given by server: %s",
1458 hostname, pathname,
1459 nfs_strerror(status.nfsv3.fhs_status));
1460 goto fail;
1461 }
1462 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1463 memset(data.old_root.data, 0, NFS_FHSIZE);
1464 memset(&data.root, 0, sizeof(data.root));
1465 data.root.size = my_fhandle->fhandle3_len;
1466 memcpy(data.root.data,
1467 (char *) my_fhandle->fhandle3_val,
1468 my_fhandle->fhandle3_len);
1469
1470 data.flags |= NFS_MOUNT_VER3;
1471 }
1472
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001473 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001474 if (tcp) {
1475 if (nfs_mount_version < 3) {
1476 bb_error_msg("NFS over TCP is not supported");
1477 goto fail;
1478 }
1479 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1480 } else
1481 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1482 if (fsock < 0) {
1483 bb_perror_msg("nfs socket");
1484 goto fail;
1485 }
1486 if (bindresvport(fsock, 0) < 0) {
1487 bb_perror_msg("nfs bindresvport");
1488 goto fail;
1489 }
1490 if (port == 0) {
1491 server_addr.sin_port = PMAPPORT;
1492 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1493 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1494 if (port == 0)
1495 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001496 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001497 server_addr.sin_port = htons(port);
1498
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001499 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001500 data.fd = fsock;
1501 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1502 strncpy(data.hostname, hostname, sizeof(data.hostname));
1503
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001504 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001505 auth_destroy(mclient->cl_auth);
1506 clnt_destroy(mclient);
1507 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001508 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001509
1510 if (bg) {
1511 /* We must wait until mount directory is available */
1512 struct stat statbuf;
1513 int delay = 1;
1514 while (stat(mp->mnt_dir, &statbuf) == -1) {
1515 if (!daemonized) {
1516 daemonized = daemonize();
1517 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001518/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001519 retval = -daemonized;
1520 goto ret;
1521 }
1522 }
1523 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1524 delay *= 2;
1525 if (delay > 30)
1526 delay = 30;
1527 }
1528 }
1529
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001530 /* Perform actual mount */
1531 do_mount:
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001532 mp->mnt_type = (char*)"nfs";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001533 retval = mount_it_now(mp, vfsflags, (char*)&data);
1534 goto ret;
1535
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001536 /* Abort */
1537 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001538 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001539 if (mclient) {
1540 auth_destroy(mclient->cl_auth);
1541 clnt_destroy(mclient);
1542 }
1543 close(msock);
1544 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001545 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001546 close(fsock);
1547
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001548 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001549 free(hostname);
1550 free(mounthost);
1551 free(filteropts);
1552 return retval;
1553}
1554
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001555#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001556
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001557// Never called. Call should be optimized out.
Denis Vlasenkob4133682008-02-18 13:05:38 +00001558int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001559
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001560#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001561
1562// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1563// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001564// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001565static int singlemount(struct mntent *mp, int ignore_busy)
1566{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001567 int rc = -1;
1568 long vfsflags;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001569 char *loopFile = 0, *filteropts = 0;
1570 llist_t *fl = 0;
1571 struct stat st;
1572
1573 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1574
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001575 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001576 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1577 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001578
Denis Vlasenko2535f122007-09-15 13:28:30 +00001579 // Might this be a virtual filesystem?
Denis Vlasenko2535f122007-09-15 13:28:30 +00001580 if (ENABLE_FEATURE_MOUNT_HELPERS
Denis Vlasenkoc03e8722007-12-26 20:56:55 +00001581 && (strchr(mp->mnt_fsname, '#'))
Denis Vlasenko2535f122007-09-15 13:28:30 +00001582 ) {
1583 char *s, *p, *args[35];
1584 int n = 0;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001585// FIXME: does it allow execution of arbitrary commands?!
1586// What args[0] can end up with?
Denis Vlasenko2535f122007-09-15 13:28:30 +00001587 for (s = p = mp->mnt_fsname; *s && n < 35-3; ++s) {
1588 if (s[0] == '#' && s[1] != '#') {
1589 *s = '\0';
1590 args[n++] = p;
1591 p = s + 1;
1592 }
1593 }
1594 args[n++] = p;
1595 args[n++] = mp->mnt_dir;
1596 args[n] = NULL;
1597 rc = wait4pid(xspawn(args));
1598 goto report_error;
1599 }
1600
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001601 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001602 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001603 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1604 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1605 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001606 ) {
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001607 len_and_sockaddr *lsa;
1608 char *ip, *dotted;
1609 char *s;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001610
1611 rc = 1;
1612 // Replace '/' with '\' and verify that unc points to "//server/share".
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001613 for (s = mp->mnt_fsname; *s; ++s)
1614 if (*s == '/') *s = '\\';
1615
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001616 // Get server IP
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001617 s = strrchr(mp->mnt_fsname, '\\');
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001618 if (s <= mp->mnt_fsname+1) goto report_error;
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001619 *s = '\0';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001620 lsa = host2sockaddr(mp->mnt_fsname+2, 0);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001621 *s = '\\';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001622 if (!lsa) goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001623
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001624 // Insert ip=... option into string flags.
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001625 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001626 ip = xasprintf("ip=%s", dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001627 parse_mount_options(ip, &filteropts);
1628
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001629 // Compose new unc '\\server-ip\share'
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001630 // (s => slash after hostname)
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001631 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001632
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001633 // Lock is required
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001634 vfsflags |= MS_MANDLOCK;
1635
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001636 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001637 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001638 if (ENABLE_FEATURE_CLEAN_UP) {
1639 free(mp->mnt_fsname);
1640 free(ip);
1641 free(dotted);
1642 free(lsa);
1643 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001644 goto report_error;
1645 }
1646
1647 // Might this be an NFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001648 if (ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001649 && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001650 && strchr(mp->mnt_fsname, ':') != NULL
1651 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001652 rc = nfsmount(mp, vfsflags, filteropts);
1653 goto report_error;
1654 }
1655
1656 // Look at the file. (Not found isn't a failure for remount, or for
1657 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001658 // (We use stat, not lstat, in order to allow
1659 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001660 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001661 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1662 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001663 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001664 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1665 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001666 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001667 if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
1668 if (errno == EPERM || errno == EACCES)
1669 bb_error_msg(bb_msg_perm_denied_are_you_root);
1670 else
1671 bb_perror_msg("cannot setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001672 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001673 }
1674
1675 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001676 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1677 vfsflags |= MS_BIND;
1678 }
1679
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001680 // If we know the fstype (or don't need to), jump straight
1681 // to the actual mount.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001682 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1683 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001684 else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001685 // Loop through filesystem types until mount succeeds
1686 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001687
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001688 // Initialize list of block backed filesystems. This has to be
1689 // done here so that during "mount -a", mounts after /proc shows up
1690 // can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001691 if (!fslist) {
1692 fslist = get_block_backed_filesystems();
1693 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1694 atexit(delete_block_backed_filesystems);
1695 }
1696
1697 for (fl = fslist; fl; fl = fl->link) {
1698 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001699 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001700 if (!rc) break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001701 }
1702 }
1703
1704 // If mount failed, clean up loop file (if any).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001705 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1706 del_loop(mp->mnt_fsname);
1707 if (ENABLE_FEATURE_CLEAN_UP) {
1708 free(loopFile);
1709 free(mp->mnt_fsname);
1710 }
1711 }
1712
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001713 report_error:
1714 if (ENABLE_FEATURE_CLEAN_UP)
1715 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001716
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001717 if (errno == EBUSY && ignore_busy)
1718 return 0;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001719 if (rc < 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001720 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001721 return rc;
1722}
1723
1724// Parse options, if necessary parse fstab/mtab, and call singlemount for
1725// each directory to be mounted.
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001726static const char must_be_root[] ALIGN1 = "you must be root";
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001727
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001728int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001729int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001730{
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001731 char *cmdopts = xstrdup("");
1732 char *fstype = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001733 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001734 llist_t *lst_o = NULL;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001735 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001736 FILE *fstab;
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001737 int i, j, rc = 0;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001738 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001739 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001740 SKIP_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001741
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001742 USE_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00001743
Denis Vlasenkof732e962008-02-18 12:07:49 +00001744 // Parse long options, like --bind and --move. Note that -o option
1745 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001746 for (i = j = 1; argv[i]; i++) {
1747 if (argv[i][0] == '-' && argv[i][1] == '-')
1748 append_mount_options(&cmdopts, argv[i] + 2);
1749 else
1750 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001751 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001752 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001753
1754 // Parse remaining options
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001755 // Max 2 params; -v is a counter
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001756 opt_complementary = "?2o::" USE_FEATURE_MOUNT_VERBOSE(":vv");
1757 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype
Denis Vlasenkof732e962008-02-18 12:07:49 +00001758 USE_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001759 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00001760 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1761 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001762 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001763
1764 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001765 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00001766 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001767 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1768
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001769 if (!mountTable)
1770 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001771
Denis Vlasenko2535f122007-09-15 13:28:30 +00001772 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001773 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001774 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001775 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00001776 // util-linux 2.12a happily shows rootfs...
1777 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001778
1779 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1780 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1781 mtpair->mnt_dir, mtpair->mnt_type,
1782 mtpair->mnt_opts);
1783 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001784 if (ENABLE_FEATURE_CLEAN_UP)
1785 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001786 return EXIT_SUCCESS;
1787 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001788 storage_path = NULL;
1789 } else {
1790 // When we have two arguments, the second is the directory and we can
1791 // skip looking at fstab entirely. We can always abspath() the directory
1792 // argument when we get it.
1793 if (argv[1]) {
1794 if (nonroot)
1795 bb_error_msg_and_die(must_be_root);
1796 mtpair->mnt_fsname = argv[0];
1797 mtpair->mnt_dir = argv[1];
1798 mtpair->mnt_type = fstype;
1799 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00001800 resolve_mount_spec(&mtpair->mnt_fsname);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001801 rc = singlemount(mtpair, 0);
1802 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00001803 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001804 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001805 }
1806
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001807 // Past this point, we are handling either "mount -a [opts]"
1808 // or "mount [opts] single_param"
1809
Denis Vlasenkob4133682008-02-18 13:05:38 +00001810 i = parse_mount_options(cmdopts, 0); // FIXME: should be "long", not "int"
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001811 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1812 bb_error_msg_and_die(must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00001813
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001814 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001815 if (ENABLE_FEATURE_MOUNT_FLAGS
1816 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1817 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001818 // verbose_mount(source, target, type, flags, data)
1819 rc = verbose_mount("", argv[0], "", i, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001820 if (rc)
1821 bb_simple_perror_msg_and_die(argv[0]);
1822 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001823 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001824
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001825 // Open either fstab or mtab
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001826 fstabname = "/etc/fstab";
1827 if (i & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001828 // WARNING. I am not sure this matches util-linux's
1829 // behavior. It's possible util-linux does not
1830 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001831 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001832 }
1833 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001834 if (!fstab)
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001835 bb_perror_msg_and_die("cannot read %s", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001836
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001837 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00001838 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001839 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001840 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001841
1842 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00001843 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001844 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001845 GETMNTENT_BUFSIZE/2)
1846 ) { // End of fstab/mtab is reached
1847 mtcur = mtother; // the thing we found last time
1848 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001849 }
1850
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001851 // If we're trying to mount something specific and this isn't it,
1852 // skip it. Note we must match the exact text in fstab (ala
1853 // "proc") or a full path from root
1854 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001855
1856 // Is this what we're looking for?
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001857 if (strcmp(argv[0], mtcur->mnt_fsname) &&
1858 strcmp(storage_path, mtcur->mnt_fsname) &&
1859 strcmp(argv[0], mtcur->mnt_dir) &&
1860 strcmp(storage_path, mtcur->mnt_dir)) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001861
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001862 // Remember this entry. Something later may have
1863 // overmounted it, and we want the _last_ match.
1864 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001865
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001866 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001867 } else {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001868 // Do we need to match a filesystem type?
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001869 if (fstype && match_fstype(mtcur, fstype))
1870 continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001871
1872 // Skip noauto and swap anyway.
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001873 if (parse_mount_options(mtcur->mnt_opts, 0) & (MOUNT_NOAUTO | MOUNT_SWAP))
1874 continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001875
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001876 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001877 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001878 if (nonroot)
1879 bb_error_msg_and_die(must_be_root);
1880
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00001881 resolve_mount_spec(&mtpair->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001882
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001883 // NFS mounts want this to be xrealloc-able
1884 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00001885
1886 // Mount this thing
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001887 if (singlemount(mtcur, 1)) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001888 // Count number of failed mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001889 rc++;
1890 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001891 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001892 }
1893 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001894
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001895 // End of fstab/mtab is reached.
1896 // Were we looking for something specific?
1897 if (argv[0]) {
1898 // If we didn't find anything, complain
1899 if (!mtcur->mnt_fsname)
1900 bb_error_msg_and_die("can't find %s in %s",
1901 argv[0], fstabname);
1902 if (nonroot) {
1903 // fstab must have "users" or "user"
1904 if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS))
1905 bb_error_msg_and_die(must_be_root);
1906 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001907
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001908 // Mount the last thing we found
1909 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1910 append_mount_options(&(mtcur->mnt_opts), cmdopts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00001911 resolve_mount_spec(&mtpair->mnt_fsname);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001912 rc = singlemount(mtcur, 0);
1913 if (ENABLE_FEATURE_CLEAN_UP)
1914 free(mtcur->mnt_opts);
1915 }
1916
1917 if (ENABLE_FEATURE_CLEAN_UP)
1918 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001919 if (ENABLE_FEATURE_CLEAN_UP) {
1920 free(storage_path);
1921 free(cmdopts);
1922 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001923 return rc;
1924}