blob: 1ebc7247b9004aaf749ed55de4d18ffece4d9c14 [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
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +000066#define OPTION_STR "o:t:rwanfvsiO:"
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +000067enum {
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),
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +000078 OPT_O = (1 << 10),
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +000079};
80
81#if ENABLE_FEATURE_MTAB_SUPPORT
82#define useMtab (!(option_mask32 & OPT_n))
83#else
84#define useMtab 0
85#endif
86
87#if ENABLE_FEATURE_MOUNT_FAKE
88#define fakeIt (option_mask32 & OPT_f)
89#else
90#define fakeIt 0
91#endif
92
93
Denis Vlasenko13c5a682006-10-16 22:39:51 +000094// TODO: more "user" flag compatibility.
95// "user" option (from mount manpage):
96// Only the user that mounted a filesystem can unmount it again.
97// If any user should be able to unmount, then use users instead of user
98// in the fstab line. The owner option is similar to the user option,
99// with the restriction that the user must be the owner of the special file.
100// This may be useful e.g. for /dev/fd if a login script makes
101// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +0000102
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000103// Standard mount options (from -o options or --options),
104// with corresponding flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000105static const int32_t mount_options[] = {
Rob Landleye3781b72006-08-08 01:39:49 +0000106 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +0000107
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000108 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000109 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000110 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000111
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000112 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000113 /* "defaults" */ 0,
114 /* "quiet" 0 - do not filter out, vfat wants to see it */
115 /* "noauto" */ MOUNT_NOAUTO,
116 /* "sw" */ MOUNT_SWAP,
117 /* "swap" */ MOUNT_SWAP,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000118 IF_DESKTOP(/* "user" */ MOUNT_USERS,)
119 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000120 /* "_netdev" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000121 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000122
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000123 IF_FEATURE_MOUNT_FLAGS(
Rob Landleye3781b72006-08-08 01:39:49 +0000124 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000125 /* "nosuid" */ MS_NOSUID,
126 /* "suid" */ ~MS_NOSUID,
127 /* "dev" */ ~MS_NODEV,
128 /* "nodev" */ MS_NODEV,
129 /* "exec" */ ~MS_NOEXEC,
130 /* "noexec" */ MS_NOEXEC,
131 /* "sync" */ MS_SYNCHRONOUS,
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000132 /* "dirsync" */ MS_DIRSYNC,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000133 /* "async" */ ~MS_SYNCHRONOUS,
134 /* "atime" */ ~MS_NOATIME,
135 /* "noatime" */ MS_NOATIME,
136 /* "diratime" */ ~MS_NODIRATIME,
137 /* "nodiratime" */ MS_NODIRATIME,
Denis Vlasenko580ce2d2008-07-08 02:56:53 +0000138 /* "mand" */ MS_MANDLOCK,
139 /* "nomand" */ ~MS_MANDLOCK,
Bernhard Reutner-Fischerfb5902c2008-08-06 18:14:38 +0000140 /* "relatime" */ MS_RELATIME,
141 /* "norelatime" */ ~MS_RELATIME,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000142 /* "loud" */ ~MS_SILENT,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000143
Rob Landleye3781b72006-08-08 01:39:49 +0000144 // action flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000145 /* "bind" */ MS_BIND,
146 /* "move" */ MS_MOVE,
147 /* "shared" */ MS_SHARED,
148 /* "slave" */ MS_SLAVE,
149 /* "private" */ MS_PRIVATE,
150 /* "unbindable" */ MS_UNBINDABLE,
151 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
152 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
153 /* "rprivate" */ MS_SLAVE|MS_RECURSIVE,
154 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000155 )
156
157 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000158 /* "ro" */ MS_RDONLY, // vfs flag
159 /* "rw" */ ~MS_RDONLY, // vfs flag
160 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000161};
162
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000163static const char mount_option_str[] =
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000164 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000165 "loop\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000166 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000167 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000168 "defaults\0"
169 // "quiet\0" - do not filter out, vfat wants to see it
170 "noauto\0"
171 "sw\0"
172 "swap\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000173 IF_DESKTOP("user\0")
174 IF_DESKTOP("users\0")
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000175 "_netdev\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000176 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000177 IF_FEATURE_MOUNT_FLAGS(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000178 // vfs flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000179 "nosuid\0"
180 "suid\0"
181 "dev\0"
182 "nodev\0"
183 "exec\0"
184 "noexec\0"
185 "sync\0"
186 "dirsync\0"
187 "async\0"
188 "atime\0"
189 "noatime\0"
190 "diratime\0"
191 "nodiratime\0"
192 "mand\0"
193 "nomand\0"
194 "relatime\0"
195 "norelatime\0"
196 "loud\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000197
198 // action flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000199 "bind\0"
200 "move\0"
201 "shared\0"
202 "slave\0"
203 "private\0"
204 "unbindable\0"
205 "rshared\0"
206 "rslave\0"
207 "rprivate\0"
208 "runbindable\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000209 )
210
211 // Always understood.
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000212 "ro\0" // vfs flag
213 "rw\0" // vfs flag
214 "remount\0" // action flag
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000215;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000216
Denis Vlasenkof732e962008-02-18 12:07:49 +0000217
218struct globals {
219#if ENABLE_FEATURE_MOUNT_NFS
220 smalluint nfs_mount_version;
221#endif
222#if ENABLE_FEATURE_MOUNT_VERBOSE
223 unsigned verbose;
224#endif
225 llist_t *fslist;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000226 char getmntent_buf[1];
Denis Vlasenkof732e962008-02-18 12:07:49 +0000227
228};
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000229enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denis Vlasenkof732e962008-02-18 12:07:49 +0000230#define G (*(struct globals*)&bb_common_bufsiz1)
231#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000232#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000233#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000234#else
235#define verbose 0
236#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000237#define fslist (G.fslist )
238#define getmntent_buf (G.getmntent_buf )
239
240
241#if ENABLE_FEATURE_MOUNT_VERBOSE
242static int verbose_mount(const char *source, const char *target,
243 const char *filesystemtype,
244 unsigned long mountflags, const void *data)
245{
246 int rc;
247
248 errno = 0;
249 rc = mount(source, target, filesystemtype, mountflags, data);
250 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000251 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000252 source, target, filesystemtype,
253 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000254 return rc;
255}
256#else
257#define verbose_mount(...) mount(__VA_ARGS__)
258#endif
259
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +0000260#if ENABLE_FEATURE_MOUNT_LABEL
261static void resolve_mount_spec(char **fsname)
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000262{
263 char *tmp = NULL;
264
265 if (!strncmp(*fsname, "UUID=", 5))
266 tmp = get_devname_from_uuid(*fsname + 5);
267 else if (!strncmp(*fsname, "LABEL=", 6))
268 tmp = get_devname_from_label(*fsname + 6);
269
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +0000270 if (tmp)
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000271 *fsname = tmp;
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000272}
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +0000273#else
274#define resolve_mount_spec(fsname) ((void)0)
275#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000276
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000277// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000278static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000279{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000280 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000281 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000282 while (newopts[0]) {
283 char *p;
284 int len = strlen(newopts);
285 p = strchr(newopts, ',');
286 if (p) len = p - newopts;
287 p = *oldopts;
288 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000289 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000290 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000291 goto skip;
292 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000293 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000294 p++;
295 }
296 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
297 free(*oldopts);
298 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000299 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000300 newopts += len;
301 while (newopts[0] == ',') newopts++;
302 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000303 } else {
304 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000305 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000306 }
307}
308
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000309// Use the mount_options list to parse options into flags.
310// Also return list of unrecognized options if unrecognized != NULL
Denis Vlasenkob4133682008-02-18 13:05:38 +0000311static long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000312{
Denis Vlasenkob4133682008-02-18 13:05:38 +0000313 long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000314
Rob Landley6a6798b2005-08-10 20:35:54 +0000315 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000316 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000317 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000318 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000319 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000320
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000321 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000322
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000323// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000324 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000325 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000326 if (!strcasecmp(option_str, options)) {
327 long fl = mount_options[i];
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000328 if (fl < 0) flags &= fl;
Rob Landleydc0955b2006-03-14 18:16:25 +0000329 else flags |= fl;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000330 break;
331 }
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000332 option_str += strlen(option_str) + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000333 }
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000334 // If unrecognized not NULL, append unrecognized mount options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000335 if (unrecognized && i == ARRAY_SIZE(mount_options)) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000336 // Add it to strflags, to pass on to kernel
Rob Landleydc0955b2006-03-14 18:16:25 +0000337 i = *unrecognized ? strlen(*unrecognized) : 0;
Denis Vlasenkodeeed592008-07-08 05:14:36 +0000338 *unrecognized = xrealloc(*unrecognized, i + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000339
Rob Landley6a6798b2005-08-10 20:35:54 +0000340 // Comma separated if it's not the first one
Rob Landleydc0955b2006-03-14 18:16:25 +0000341 if (i) (*unrecognized)[i++] = ',';
342 strcpy((*unrecognized)+i, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000343 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000344
Denis Vlasenko2535f122007-09-15 13:28:30 +0000345 if (!comma)
346 break;
347 // Advance to next option
348 *comma = ',';
349 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000350 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000351
Rob Landleydc0955b2006-03-14 18:16:25 +0000352 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000353}
354
Rob Landleydc0955b2006-03-14 18:16:25 +0000355// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000356static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000357{
Denis Vlasenko87468852007-04-13 23:22:00 +0000358 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000359 "/etc/filesystems",
360 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000361 };
362 char *fs, *buf;
Rob Landleydc0955b2006-03-14 18:16:25 +0000363 llist_t *list = 0;
364 int i;
365 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000366
Denis Vlasenko87468852007-04-13 23:22:00 +0000367 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000368 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000369 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000370
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000371 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000372 if (!strncmp(buf, "nodev", 5) && isspace(buf[5]))
373 continue;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000374 fs = skip_whitespace(buf);
Denis Vlasenko372686b2006-10-12 22:42:33 +0000375 if (*fs=='#' || *fs=='*' || !*fs) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000376
Denis Vlasenko372686b2006-10-12 22:42:33 +0000377 llist_add_to_end(&list, xstrdup(fs));
378 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000379 }
380 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
381 }
382
383 return list;
384}
385
Rob Landleydc0955b2006-03-14 18:16:25 +0000386#if ENABLE_FEATURE_CLEAN_UP
387static void delete_block_backed_filesystems(void)
388{
Rob Landleya6b5b602006-05-08 19:03:07 +0000389 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000390}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000391#else
392void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000393#endif
394
Rob Landleydc0955b2006-03-14 18:16:25 +0000395// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000396// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000397static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000398{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000399 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000400
Denis Vlasenkob4133682008-02-18 13:05:38 +0000401 if (fakeIt) {
402 if (verbose >= 2)
403 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
404 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
405 vfsflags, filteropts);
406 goto mtab;
407 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000408
Rob Landleydc0955b2006-03-14 18:16:25 +0000409 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000410 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000411 errno = 0;
412 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000413 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000414
415 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000416 // helper program mount.<mnt_type>
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000417 if (ENABLE_FEATURE_MOUNT_HELPERS && rc) {
418 char *args[6];
419 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000420 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000421 rc = 1;
Denis Vlasenko5c329932009-04-12 12:16:21 +0000422 args[rc++] = mp->mnt_fsname;
423 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000424 if (filteropts) {
425 args[rc++] = (char *)"-o";
426 args[rc++] = filteropts;
427 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000428 args[rc] = NULL;
429 rc = wait4pid(spawn(args));
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000430 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000431 if (!rc)
432 break;
433 errno = errno_save;
434 }
435
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000436 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000437 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000438 if (!(vfsflags & MS_SILENT))
439 bb_error_msg("%s is write-protected, mounting read-only",
440 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000441 vfsflags |= MS_RDONLY;
442 }
443
Rob Landleydc0955b2006-03-14 18:16:25 +0000444 // Abort entirely if permission denied.
445
446 if (rc && errno == EPERM)
447 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
448
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000449 // If the mount was successful, and we're maintaining an old-style
450 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000451 mtab:
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000452 if (useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000453 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000454 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000455 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000456 int i;
457
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000458 if (!mountTable) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000459 bb_error_msg("no %s", bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000460 goto ret;
461 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000462
463 // Add vfs string flags
464
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000465 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
466 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
467 append_mount_options(&(mp->mnt_opts), option_str);
468 option_str += strlen(option_str) + 1;
469 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000470
471 // Remove trailing / (if any) from directory we mounted on
472
Denis Vlasenko727ef942006-09-14 13:19:19 +0000473 i = strlen(mp->mnt_dir) - 1;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000474 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000475
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000476 // Convert to canonical pathnames as needed
Denis Vlasenko727ef942006-09-14 13:19:19 +0000477
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000478 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000479 fsname = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000480 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000481 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000482 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000483 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000484 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000485
486 // Write and close.
487
Denis Vlasenko727ef942006-09-14 13:19:19 +0000488 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000489 endmntent(mountTable);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000490 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000491 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000492 free(fsname);
493 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000494 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000495 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000496 return rc;
497}
498
Denis Vlasenko25098f72006-09-14 15:46:33 +0000499#if ENABLE_FEATURE_MOUNT_NFS
500
501/*
502 * Linux NFS mount
503 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
504 *
505 * Licensed under GPLv2, see file LICENSE in this tarball for details.
506 *
507 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
508 * numbers to be specified on the command line.
509 *
510 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
511 * Omit the call to connect() for Linux version 1.3.11 or later.
512 *
513 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
514 * Implemented the "bg", "fg" and "retry" mount options for NFS.
515 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000516 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000517 * - added Native Language Support
518 *
519 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
520 * plus NFSv3 stuff.
521 */
522
Denis Vlasenko25098f72006-09-14 15:46:33 +0000523/* This is just a warning of a common mistake. Possibly this should be a
524 * uclibc faq entry rather than in busybox... */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000525#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000526#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
527#endif
528
529#define MOUNTPORT 635
530#define MNTPATHLEN 1024
531#define MNTNAMLEN 255
532#define FHSIZE 32
533#define FHSIZE3 64
534
535typedef char fhandle[FHSIZE];
536
537typedef struct {
538 unsigned int fhandle3_len;
539 char *fhandle3_val;
540} fhandle3;
541
542enum mountstat3 {
543 MNT_OK = 0,
544 MNT3ERR_PERM = 1,
545 MNT3ERR_NOENT = 2,
546 MNT3ERR_IO = 5,
547 MNT3ERR_ACCES = 13,
548 MNT3ERR_NOTDIR = 20,
549 MNT3ERR_INVAL = 22,
550 MNT3ERR_NAMETOOLONG = 63,
551 MNT3ERR_NOTSUPP = 10004,
552 MNT3ERR_SERVERFAULT = 10006,
553};
554typedef enum mountstat3 mountstat3;
555
556struct fhstatus {
557 unsigned int fhs_status;
558 union {
559 fhandle fhs_fhandle;
560 } fhstatus_u;
561};
562typedef struct fhstatus fhstatus;
563
564struct mountres3_ok {
565 fhandle3 fhandle;
566 struct {
567 unsigned int auth_flavours_len;
568 char *auth_flavours_val;
569 } auth_flavours;
570};
571typedef struct mountres3_ok mountres3_ok;
572
573struct mountres3 {
574 mountstat3 fhs_status;
575 union {
576 mountres3_ok mountinfo;
577 } mountres3_u;
578};
579typedef struct mountres3 mountres3;
580
581typedef char *dirpath;
582
583typedef char *name;
584
585typedef struct mountbody *mountlist;
586
587struct mountbody {
588 name ml_hostname;
589 dirpath ml_directory;
590 mountlist ml_next;
591};
592typedef struct mountbody mountbody;
593
594typedef struct groupnode *groups;
595
596struct groupnode {
597 name gr_name;
598 groups gr_next;
599};
600typedef struct groupnode groupnode;
601
602typedef struct exportnode *exports;
603
604struct exportnode {
605 dirpath ex_dir;
606 groups ex_groups;
607 exports ex_next;
608};
609typedef struct exportnode exportnode;
610
611struct ppathcnf {
612 int pc_link_max;
613 short pc_max_canon;
614 short pc_max_input;
615 short pc_name_max;
616 short pc_path_max;
617 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000618 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000619 char pc_xxx;
620 short pc_mask[2];
621};
622typedef struct ppathcnf ppathcnf;
623
624#define MOUNTPROG 100005
625#define MOUNTVERS 1
626
627#define MOUNTPROC_NULL 0
628#define MOUNTPROC_MNT 1
629#define MOUNTPROC_DUMP 2
630#define MOUNTPROC_UMNT 3
631#define MOUNTPROC_UMNTALL 4
632#define MOUNTPROC_EXPORT 5
633#define MOUNTPROC_EXPORTALL 6
634
635#define MOUNTVERS_POSIX 2
636
637#define MOUNTPROC_PATHCONF 7
638
639#define MOUNT_V3 3
640
641#define MOUNTPROC3_NULL 0
642#define MOUNTPROC3_MNT 1
643#define MOUNTPROC3_DUMP 2
644#define MOUNTPROC3_UMNT 3
645#define MOUNTPROC3_UMNTALL 4
646#define MOUNTPROC3_EXPORT 5
647
648enum {
649#ifndef NFS_FHSIZE
650 NFS_FHSIZE = 32,
651#endif
652#ifndef NFS_PORT
653 NFS_PORT = 2049
654#endif
655};
656
Denis Vlasenko25098f72006-09-14 15:46:33 +0000657/*
658 * We want to be able to compile mount on old kernels in such a way
659 * that the binary will work well on more recent kernels.
660 * Thus, if necessary we teach nfsmount.c the structure of new fields
661 * that will come later.
662 *
663 * Moreover, the new kernel includes conflict with glibc includes
664 * so it is easiest to ignore the kernel altogether (at compile time).
665 */
666
667struct nfs2_fh {
668 char data[32];
669};
670struct nfs3_fh {
671 unsigned short size;
672 unsigned char data[64];
673};
674
675struct nfs_mount_data {
676 int version; /* 1 */
677 int fd; /* 1 */
678 struct nfs2_fh old_root; /* 1 */
679 int flags; /* 1 */
680 int rsize; /* 1 */
681 int wsize; /* 1 */
682 int timeo; /* 1 */
683 int retrans; /* 1 */
684 int acregmin; /* 1 */
685 int acregmax; /* 1 */
686 int acdirmin; /* 1 */
687 int acdirmax; /* 1 */
688 struct sockaddr_in addr; /* 1 */
689 char hostname[256]; /* 1 */
690 int namlen; /* 2 */
691 unsigned int bsize; /* 3 */
692 struct nfs3_fh root; /* 4 */
693};
694
695/* bits in the flags field */
696enum {
697 NFS_MOUNT_SOFT = 0x0001, /* 1 */
698 NFS_MOUNT_INTR = 0x0002, /* 1 */
699 NFS_MOUNT_SECURE = 0x0004, /* 1 */
700 NFS_MOUNT_POSIX = 0x0008, /* 1 */
701 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
702 NFS_MOUNT_NOAC = 0x0020, /* 1 */
703 NFS_MOUNT_TCP = 0x0040, /* 2 */
704 NFS_MOUNT_VER3 = 0x0080, /* 3 */
705 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000706 NFS_MOUNT_NONLM = 0x0200, /* 3 */
707 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +0000708};
709
710
711/*
712 * We need to translate between nfs status return values and
713 * the local errno values which may not be the same.
714 *
715 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
716 * "after #include <errno.h> the symbol errno is reserved for any use,
717 * it cannot even be used as a struct tag or field name".
718 */
719
720#ifndef EDQUOT
721#define EDQUOT ENOSPC
722#endif
723
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000724/* Convert each NFSERR_BLAH into EBLAH */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000725static const struct {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000726 short stat;
727 short errnum;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000728} nfs_errtbl[] = {
729 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
730 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
731 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
732 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
733};
Denis Vlasenko25098f72006-09-14 15:46:33 +0000734static char *nfs_strerror(int status)
735{
736 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000737
738 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
739 if (nfs_errtbl[i].stat == status)
740 return strerror(nfs_errtbl[i].errnum);
741 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000742 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000743}
744
745static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
746{
747 if (!xdr_opaque(xdrs, objp, FHSIZE))
748 return FALSE;
749 return TRUE;
750}
751
752static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
753{
754 if (!xdr_u_int(xdrs, &objp->fhs_status))
755 return FALSE;
756 switch (objp->fhs_status) {
757 case 0:
758 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
759 return FALSE;
760 break;
761 default:
762 break;
763 }
764 return TRUE;
765}
766
767static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
768{
769 if (!xdr_string(xdrs, objp, MNTPATHLEN))
770 return FALSE;
771 return TRUE;
772}
773
774static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
775{
776 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
777 return FALSE;
778 return TRUE;
779}
780
781static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
782{
783 if (!xdr_fhandle3(xdrs, &objp->fhandle))
784 return FALSE;
785 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
786 sizeof (int), (xdrproc_t) xdr_int))
787 return FALSE;
788 return TRUE;
789}
790
791static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
792{
793 if (!xdr_enum(xdrs, (enum_t *) objp))
794 return FALSE;
795 return TRUE;
796}
797
798static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
799{
800 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
801 return FALSE;
802 switch (objp->fhs_status) {
803 case MNT_OK:
804 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
805 return FALSE;
806 break;
807 default:
808 break;
809 }
810 return TRUE;
811}
812
813#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
814
Denis Vlasenko25098f72006-09-14 15:46:33 +0000815/*
816 * Unfortunately, the kernel prints annoying console messages
817 * in case of an unexpected nfs mount version (instead of
818 * just returning some error). Therefore we'll have to try
819 * and figure out what version the kernel expects.
820 *
821 * Variables:
822 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
823 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
824 * nfs_mount_version: version this source and running kernel can handle
825 */
826static void
827find_kernel_nfs_mount_version(void)
828{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000829 int kernel_version;
830
831 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000832 return;
833
834 nfs_mount_version = 4; /* default */
835
836 kernel_version = get_linux_version_code();
837 if (kernel_version) {
838 if (kernel_version < KERNEL_VERSION(2,1,32))
839 nfs_mount_version = 1;
840 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
841 (kernel_version >= KERNEL_VERSION(2,3,0) &&
842 kernel_version < KERNEL_VERSION(2,3,99)))
843 nfs_mount_version = 3;
844 /* else v4 since 2.3.99pre4 */
845 }
846}
847
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +0000848static void
Denis Vlasenkob9256052007-09-28 10:29:17 +0000849get_mountport(struct pmap *pm_mnt,
850 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +0000851 long unsigned prog,
852 long unsigned version,
853 long unsigned proto,
854 long unsigned port)
855{
856 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000857
858 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +0000859/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
860 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000861 pmap = pmap_getmaps(server_addr);
862
863 if (version > MAX_NFSPROT)
864 version = MAX_NFSPROT;
865 if (!prog)
866 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000867 pm_mnt->pm_prog = prog;
868 pm_mnt->pm_vers = version;
869 pm_mnt->pm_prot = proto;
870 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000871
Denis Vlasenko25098f72006-09-14 15:46:33 +0000872 while (pmap) {
873 if (pmap->pml_map.pm_prog != prog)
874 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000875 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000876 goto next;
877 if (version > 2 && pmap->pml_map.pm_vers != version)
878 goto next;
879 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
880 goto next;
881 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
Denis Vlasenkob9256052007-09-28 10:29:17 +0000882 (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) ||
Denis Vlasenko25098f72006-09-14 15:46:33 +0000883 (port && pmap->pml_map.pm_port != port))
884 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000885 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
886 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +0000887 pmap = pmap->pml_next;
888 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000889 if (!pm_mnt->pm_vers)
890 pm_mnt->pm_vers = MOUNTVERS;
891 if (!pm_mnt->pm_port)
892 pm_mnt->pm_port = MOUNTPORT;
893 if (!pm_mnt->pm_prot)
894 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000895}
896
Denis Vlasenkof0000652007-09-04 18:30:26 +0000897#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +0000898static int daemonize(void)
899{
Denis Vlasenko25098f72006-09-14 15:46:33 +0000900 int pid = fork();
901 if (pid < 0) /* error */
902 return -errno;
903 if (pid > 0) /* parent */
904 return 0;
905 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000906 close(0);
907 xopen(bb_dev_null, O_RDWR);
908 xdup2(0, 1);
909 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000910 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +0000911 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000912 logmode = LOGMODE_SYSLOG;
913 return 1;
914}
Denis Vlasenkof0000652007-09-04 18:30:26 +0000915#else
916static inline int daemonize(void) { return -ENOSYS; }
917#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000918
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000919/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000920static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000921{
922 return 0;
923}
924
925/* RPC strerror analogs are terminally idiotic:
926 * *mandatory* prefix and \n at end.
927 * This hopefully helps. Usage:
928 * error_msg_rpc(clnt_*error*(" ")) */
929static void error_msg_rpc(const char *msg)
930{
Denis Vlasenko23514fe2006-09-19 14:07:52 +0000931 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000932 while (msg[0] == ' ' || msg[0] == ':') msg++;
933 len = strlen(msg);
934 while (len && msg[len-1] == '\n') len--;
935 bb_error_msg("%.*s", len, msg);
936}
937
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000938/* NB: mp->xxx fields may be trashed on exit */
Denis Vlasenkob4133682008-02-18 13:05:38 +0000939static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000940{
941 CLIENT *mclient;
942 char *hostname;
943 char *pathname;
944 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +0200945 /* prior to 2.6.23, kernel took NFS options in a form of this struct
946 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
947 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000948 struct nfs_mount_data data;
949 char *opt;
950 struct hostent *hp;
951 struct sockaddr_in server_addr;
952 struct sockaddr_in mount_server_addr;
953 int msock, fsock;
954 union {
955 struct fhstatus nfsv2;
956 struct mountres3 nfsv3;
957 } status;
958 int daemonized;
959 char *s;
960 int port;
961 int mountport;
962 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +0000963#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000964 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +0000965#else
966 enum { bg = 0 };
967#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000968 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000969 int mountprog;
970 int mountvers;
971 int nfsprog;
972 int nfsvers;
973 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +0200974 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000975 smallint tcp;
976 smallint soft;
977 int intr;
978 int posix;
979 int nocto;
980 int noac;
981 int nordirplus;
982 int nolock;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000983
984 find_kernel_nfs_mount_version();
985
986 daemonized = 0;
987 mounthost = NULL;
988 retval = ETIMEDOUT;
989 msock = fsock = -1;
990 mclient = NULL;
991
992 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
993
994 filteropts = xstrdup(filteropts); /* going to trash it later... */
995
996 hostname = xstrdup(mp->mnt_fsname);
997 /* mount_main() guarantees that ':' is there */
998 s = strchr(hostname, ':');
999 pathname = s + 1;
1000 *s = '\0';
1001 /* Ignore all but first hostname in replicated mounts
1002 until they can be fully supported. (mack@sgi.com) */
1003 s = strchr(hostname, ',');
1004 if (s) {
1005 *s = '\0';
1006 bb_error_msg("warning: multiple hostnames not supported");
1007 }
1008
1009 server_addr.sin_family = AF_INET;
1010 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1011 hp = gethostbyname(hostname);
1012 if (hp == NULL) {
1013 bb_herror_msg("%s", hostname);
1014 goto fail;
1015 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001016 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001017 bb_error_msg("got bad hp->h_length");
1018 hp->h_length = sizeof(struct in_addr);
1019 }
1020 memcpy(&server_addr.sin_addr,
1021 hp->h_addr, hp->h_length);
1022 }
1023
1024 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1025
1026 /* add IP address to mtab options for use when unmounting */
1027
1028 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1029 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1030 } else {
1031 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1032 mp->mnt_opts[0] ? "," : "",
1033 inet_ntoa(server_addr.sin_addr));
1034 free(mp->mnt_opts);
1035 mp->mnt_opts = tmp;
1036 }
1037
1038 /* Set default options.
1039 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1040 * let the kernel decide.
1041 * timeo is filled in after we know whether it'll be TCP or UDP. */
1042 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001043 data.retrans = 3;
1044 data.acregmin = 3;
1045 data.acregmax = 60;
1046 data.acdirmin = 30;
1047 data.acdirmax = 60;
1048 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001049
Denis Vlasenko25098f72006-09-14 15:46:33 +00001050 soft = 0;
1051 intr = 0;
1052 posix = 0;
1053 nocto = 0;
1054 nolock = 0;
1055 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001056 nordirplus = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001057 retry = 10000; /* 10000 minutes ~ 1 week */
1058 tcp = 0;
1059
1060 mountprog = MOUNTPROG;
1061 mountvers = 0;
1062 port = 0;
1063 mountport = 0;
1064 nfsprog = 100003;
1065 nfsvers = 0;
1066
1067 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001068 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001069 char *opteq = strchr(opt, '=');
1070 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001071 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001072 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001073 /* 0 */ "rsize\0"
1074 /* 1 */ "wsize\0"
1075 /* 2 */ "timeo\0"
1076 /* 3 */ "retrans\0"
1077 /* 4 */ "acregmin\0"
1078 /* 5 */ "acregmax\0"
1079 /* 6 */ "acdirmin\0"
1080 /* 7 */ "acdirmax\0"
1081 /* 8 */ "actimeo\0"
1082 /* 9 */ "retry\0"
1083 /* 10 */ "port\0"
1084 /* 11 */ "mountport\0"
1085 /* 12 */ "mounthost\0"
1086 /* 13 */ "mountprog\0"
1087 /* 14 */ "mountvers\0"
1088 /* 15 */ "nfsprog\0"
1089 /* 16 */ "nfsvers\0"
1090 /* 17 */ "vers\0"
1091 /* 18 */ "proto\0"
1092 /* 19 */ "namlen\0"
1093 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001094
1095 *opteq++ = '\0';
1096 idx = index_in_strings(options, opt);
1097 switch (idx) {
1098 case 12: // "mounthost"
1099 mounthost = xstrndup(opteq,
1100 strcspn(opteq, " \t\n\r,"));
1101 continue;
1102 case 18: // "proto"
1103 if (!strncmp(opteq, "tcp", 3))
1104 tcp = 1;
1105 else if (!strncmp(opteq, "udp", 3))
1106 tcp = 0;
1107 else
1108 bb_error_msg("warning: unrecognized proto= option");
1109 continue;
1110 case 20: // "addr" - ignore
1111 continue;
1112 }
1113
1114 val = xatoi_u(opteq);
1115 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001116 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001117 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001118 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001119 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001120 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001121 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001122 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001123 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001124 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001125 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001126 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001127 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001128 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001129 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001130 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001131 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001132 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001133 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001134 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001135 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001136 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001137 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001138 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001139 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001140 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001141 data.acregmin = val;
1142 data.acregmax = val;
1143 data.acdirmin = val;
1144 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001145 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001146 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001147 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001148 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001149 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001150 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001151 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001152 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001153 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001154 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001155 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001156 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001157 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001158 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001159 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001160 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001161 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001162 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001163 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001164 case 16: // "nfsvers"
1165 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001166 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001167 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001168 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001169 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001170 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001171 //else
1172 // bb_error_msg("warning: option namlen is not supported\n");
1173 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001174 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001175 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1176 goto fail;
1177 }
1178 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001179 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001180 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001181 "bg\0"
1182 "fg\0"
1183 "soft\0"
1184 "hard\0"
1185 "intr\0"
1186 "posix\0"
1187 "cto\0"
1188 "ac\0"
1189 "tcp\0"
1190 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001191 "lock\0"
1192 "rdirplus\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001193 int val = 1;
1194 if (!strncmp(opt, "no", 2)) {
1195 val = 0;
1196 opt += 2;
1197 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001198 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001199 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001200#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001201 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001202#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001203 break;
1204 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001205#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001206 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001207#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001208 break;
1209 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001210 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001211 break;
1212 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001213 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001214 break;
1215 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001216 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001217 break;
1218 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001219 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001220 break;
1221 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001222 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001223 break;
1224 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001225 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001226 break;
1227 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001228 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001229 break;
1230 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001231 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001232 break;
1233 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001234 if (nfs_mount_version >= 3)
1235 nolock = !val;
1236 else
1237 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001238 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001239 case 11: //rdirplus
1240 nordirplus = !val;
1241 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001242 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001243 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1244 goto fail;
1245 }
1246 }
1247 }
1248 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1249
1250 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1251 | (intr ? NFS_MOUNT_INTR : 0)
1252 | (posix ? NFS_MOUNT_POSIX : 0)
1253 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001254 | (noac ? NFS_MOUNT_NOAC : 0)
1255 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001256 if (nfs_mount_version >= 2)
1257 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1258 if (nfs_mount_version >= 3)
1259 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1260 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1261 bb_error_msg("NFSv%d not supported", nfsvers);
1262 goto fail;
1263 }
1264 if (nfsvers && !mountvers)
1265 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1266 if (nfsvers && nfsvers < mountvers) {
1267 mountvers = nfsvers;
1268 }
1269
1270 /* Adjust options if none specified */
1271 if (!data.timeo)
1272 data.timeo = tcp ? 70 : 7;
1273
Denis Vlasenko25098f72006-09-14 15:46:33 +00001274 data.version = nfs_mount_version;
1275
1276 if (vfsflags & MS_REMOUNT)
1277 goto do_mount;
1278
1279 /*
1280 * If the previous mount operation on the same host was
1281 * backgrounded, and the "bg" for this mount is also set,
1282 * give up immediately, to avoid the initial timeout.
1283 */
1284 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001285 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001286 if (daemonized <= 0) { /* parent or error */
1287 retval = -daemonized;
1288 goto ret;
1289 }
1290 }
1291
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001292 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001293 /* See if the nfs host = mount host. */
1294 if (mounthost) {
1295 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1296 mount_server_addr.sin_family = AF_INET;
1297 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1298 } else {
1299 hp = gethostbyname(mounthost);
1300 if (hp == NULL) {
1301 bb_herror_msg("%s", mounthost);
1302 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001303 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001304 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
1305 bb_error_msg("got bad hp->h_length");
1306 hp->h_length = sizeof(struct in_addr);
1307 }
1308 mount_server_addr.sin_family = AF_INET;
1309 memcpy(&mount_server_addr.sin_addr,
1310 hp->h_addr, hp->h_length);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001311 }
1312 }
1313
1314 /*
1315 * The following loop implements the mount retries. When the mount
1316 * times out, and the "bg" option is set, we background ourself
1317 * and continue trying.
1318 *
1319 * The case where the mount point is not present and the "bg"
1320 * option is set, is treated as a timeout. This is done to
1321 * support nested mounts.
1322 *
1323 * The "retry" count specified by the user is the number of
1324 * minutes to retry before giving up.
1325 */
1326 {
1327 struct timeval total_timeout;
1328 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001329 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001330 time_t t;
1331 time_t prevt;
1332 time_t timeout;
1333
1334 retry_timeout.tv_sec = 3;
1335 retry_timeout.tv_usec = 0;
1336 total_timeout.tv_sec = 20;
1337 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001338/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001339 timeout = time(NULL) + 60 * retry;
1340 prevt = 0;
1341 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001342 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001343 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001344 if (t - prevt < 30)
1345 sleep(30);
1346
Denis Vlasenkob9256052007-09-28 10:29:17 +00001347 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001348 mountprog,
1349 mountvers,
1350 proto,
1351 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001352 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001353
1354 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001355 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001356 msock = RPC_ANYSOCK;
1357
Denis Vlasenkob9256052007-09-28 10:29:17 +00001358 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001359 case IPPROTO_UDP:
1360 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001361 pm_mnt.pm_prog,
1362 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001363 retry_timeout,
1364 &msock);
1365 if (mclient)
1366 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001367 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001368 msock = RPC_ANYSOCK;
1369 case IPPROTO_TCP:
1370 mclient = clnttcp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001371 pm_mnt.pm_prog,
1372 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001373 &msock, 0, 0);
1374 break;
1375 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001376 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001377 }
1378 if (!mclient) {
1379 if (!daemonized && prevt == 0)
1380 error_msg_rpc(clnt_spcreateerror(" "));
1381 } else {
1382 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001383
1384 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001385 mclient->cl_auth = authunix_create_default();
1386
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001387 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001388 * that xdr_array allocates memory for us
1389 */
1390 memset(&status, 0, sizeof(status));
1391
Denis Vlasenkob9256052007-09-28 10:29:17 +00001392 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001393 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1394 (xdrproc_t) xdr_dirpath,
1395 (caddr_t) &pathname,
1396 (xdrproc_t) xdr_mountres3,
1397 (caddr_t) &status,
1398 total_timeout);
1399 else
1400 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1401 (xdrproc_t) xdr_dirpath,
1402 (caddr_t) &pathname,
1403 (xdrproc_t) xdr_fhstatus,
1404 (caddr_t) &status,
1405 total_timeout);
1406
1407 if (clnt_stat == RPC_SUCCESS)
1408 goto prepare_kernel_data; /* we're done */
1409 if (errno != ECONNREFUSED) {
1410 error_msg_rpc(clnt_sperror(mclient, " "));
1411 goto fail; /* don't retry */
1412 }
1413 /* Connection refused */
1414 if (!daemonized && prevt == 0) /* print just once */
1415 error_msg_rpc(clnt_sperror(mclient, " "));
1416 auth_destroy(mclient->cl_auth);
1417 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001418 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001419 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001420 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001421 }
1422
1423 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001424 if (!bg)
1425 goto fail;
1426 if (!daemonized) {
1427 daemonized = daemonize();
1428 if (daemonized <= 0) { /* parent or error */
1429 retval = -daemonized;
1430 goto ret;
1431 }
1432 }
1433 prevt = t;
1434 t = time(NULL);
1435 if (t >= timeout)
1436 /* TODO error message */
1437 goto fail;
1438
1439 goto retry;
1440 }
1441
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001442 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001443
1444 if (nfsvers == 2) {
1445 if (status.nfsv2.fhs_status != 0) {
1446 bb_error_msg("%s:%s failed, reason given by server: %s",
1447 hostname, pathname,
1448 nfs_strerror(status.nfsv2.fhs_status));
1449 goto fail;
1450 }
1451 memcpy(data.root.data,
1452 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1453 NFS_FHSIZE);
1454 data.root.size = NFS_FHSIZE;
1455 memcpy(data.old_root.data,
1456 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1457 NFS_FHSIZE);
1458 } else {
1459 fhandle3 *my_fhandle;
1460 if (status.nfsv3.fhs_status != 0) {
1461 bb_error_msg("%s:%s failed, reason given by server: %s",
1462 hostname, pathname,
1463 nfs_strerror(status.nfsv3.fhs_status));
1464 goto fail;
1465 }
1466 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1467 memset(data.old_root.data, 0, NFS_FHSIZE);
1468 memset(&data.root, 0, sizeof(data.root));
1469 data.root.size = my_fhandle->fhandle3_len;
1470 memcpy(data.root.data,
1471 (char *) my_fhandle->fhandle3_val,
1472 my_fhandle->fhandle3_len);
1473
1474 data.flags |= NFS_MOUNT_VER3;
1475 }
1476
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001477 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001478 if (tcp) {
1479 if (nfs_mount_version < 3) {
1480 bb_error_msg("NFS over TCP is not supported");
1481 goto fail;
1482 }
1483 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1484 } else
1485 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1486 if (fsock < 0) {
1487 bb_perror_msg("nfs socket");
1488 goto fail;
1489 }
1490 if (bindresvport(fsock, 0) < 0) {
1491 bb_perror_msg("nfs bindresvport");
1492 goto fail;
1493 }
1494 if (port == 0) {
1495 server_addr.sin_port = PMAPPORT;
1496 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1497 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1498 if (port == 0)
1499 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001500 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001501 server_addr.sin_port = htons(port);
1502
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001503 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001504 data.fd = fsock;
1505 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1506 strncpy(data.hostname, hostname, sizeof(data.hostname));
1507
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001508 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001509 auth_destroy(mclient->cl_auth);
1510 clnt_destroy(mclient);
1511 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001512 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001513
1514 if (bg) {
1515 /* We must wait until mount directory is available */
1516 struct stat statbuf;
1517 int delay = 1;
1518 while (stat(mp->mnt_dir, &statbuf) == -1) {
1519 if (!daemonized) {
1520 daemonized = daemonize();
1521 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001522/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001523 retval = -daemonized;
1524 goto ret;
1525 }
1526 }
1527 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1528 delay *= 2;
1529 if (delay > 30)
1530 delay = 30;
1531 }
1532 }
1533
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001534 /* Perform actual mount */
1535 do_mount:
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001536 mp->mnt_type = (char*)"nfs";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001537 retval = mount_it_now(mp, vfsflags, (char*)&data);
1538 goto ret;
1539
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001540 /* Abort */
1541 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001542 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001543 if (mclient) {
1544 auth_destroy(mclient->cl_auth);
1545 clnt_destroy(mclient);
1546 }
1547 close(msock);
1548 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001549 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001550 close(fsock);
1551
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001552 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001553 free(hostname);
1554 free(mounthost);
1555 free(filteropts);
1556 return retval;
1557}
1558
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001559#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001560
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001561// Never called. Call should be optimized out.
Denis Vlasenkob4133682008-02-18 13:05:38 +00001562int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001563
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001564#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001565
1566// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1567// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001568// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001569static int singlemount(struct mntent *mp, int ignore_busy)
1570{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001571 int rc = -1;
1572 long vfsflags;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001573 char *loopFile = 0, *filteropts = 0;
1574 llist_t *fl = 0;
1575 struct stat st;
1576
1577 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1578
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001579 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001580 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1581 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001582
Denis Vlasenko2535f122007-09-15 13:28:30 +00001583 // Might this be a virtual filesystem?
Denis Vlasenko2535f122007-09-15 13:28:30 +00001584 if (ENABLE_FEATURE_MOUNT_HELPERS
Denis Vlasenkoc03e8722007-12-26 20:56:55 +00001585 && (strchr(mp->mnt_fsname, '#'))
Denis Vlasenko2535f122007-09-15 13:28:30 +00001586 ) {
1587 char *s, *p, *args[35];
1588 int n = 0;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001589// FIXME: does it allow execution of arbitrary commands?!
1590// What args[0] can end up with?
Denis Vlasenko2535f122007-09-15 13:28:30 +00001591 for (s = p = mp->mnt_fsname; *s && n < 35-3; ++s) {
1592 if (s[0] == '#' && s[1] != '#') {
1593 *s = '\0';
1594 args[n++] = p;
1595 p = s + 1;
1596 }
1597 }
1598 args[n++] = p;
1599 args[n++] = mp->mnt_dir;
1600 args[n] = NULL;
1601 rc = wait4pid(xspawn(args));
1602 goto report_error;
1603 }
1604
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001605 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001606 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001607 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1608 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1609 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001610 ) {
Denis Vlasenko5c329932009-04-12 12:16:21 +00001611#if 0 /* reported to break things */
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001612 len_and_sockaddr *lsa;
1613 char *ip, *dotted;
1614 char *s;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001615
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001616 // Replace '/' with '\' and verify that unc points to "//server/share".
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001617 for (s = mp->mnt_fsname; *s; ++s)
1618 if (*s == '/') *s = '\\';
1619
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001620 // Get server IP
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001621 s = strrchr(mp->mnt_fsname, '\\');
Denis Vlasenko5c329932009-04-12 12:16:21 +00001622 if (s <= mp->mnt_fsname+1)
1623 goto report_error;
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001624 *s = '\0';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001625 lsa = host2sockaddr(mp->mnt_fsname+2, 0);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001626 *s = '\\';
Denis Vlasenko5c329932009-04-12 12:16:21 +00001627 if (!lsa)
1628 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001629
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001630 // Insert ip=... option into string flags.
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001631 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001632 ip = xasprintf("ip=%s", dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001633 parse_mount_options(ip, &filteropts);
1634
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001635 // Compose new unc '\\server-ip\share'
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001636 // (s => slash after hostname)
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001637 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
Denis Vlasenko5c329932009-04-12 12:16:21 +00001638#endif
1639 // Lock is required [why?]
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001640 vfsflags |= MS_MANDLOCK;
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001641 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001642 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko5c329932009-04-12 12:16:21 +00001643#if 0
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001644 if (ENABLE_FEATURE_CLEAN_UP) {
1645 free(mp->mnt_fsname);
1646 free(ip);
1647 free(dotted);
1648 free(lsa);
1649 }
Denis Vlasenko5c329932009-04-12 12:16:21 +00001650#endif
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001651 goto report_error;
1652 }
1653
1654 // Might this be an NFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001655 if (ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001656 && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001657 && strchr(mp->mnt_fsname, ':') != NULL
1658 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001659 rc = nfsmount(mp, vfsflags, filteropts);
1660 goto report_error;
1661 }
1662
1663 // Look at the file. (Not found isn't a failure for remount, or for
1664 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001665 // (We use stat, not lstat, in order to allow
1666 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001667 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001668 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1669 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001670 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001671 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1672 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001673 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001674 if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
1675 if (errno == EPERM || errno == EACCES)
1676 bb_error_msg(bb_msg_perm_denied_are_you_root);
1677 else
1678 bb_perror_msg("cannot setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001679 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001680 }
1681
1682 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001683 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1684 vfsflags |= MS_BIND;
1685 }
1686
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001687 // If we know the fstype (or don't need to), jump straight
1688 // to the actual mount.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001689 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1690 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001691 else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001692 // Loop through filesystem types until mount succeeds
1693 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001694
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001695 // Initialize list of block backed filesystems. This has to be
1696 // done here so that during "mount -a", mounts after /proc shows up
1697 // can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001698 if (!fslist) {
1699 fslist = get_block_backed_filesystems();
1700 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1701 atexit(delete_block_backed_filesystems);
1702 }
1703
1704 for (fl = fslist; fl; fl = fl->link) {
1705 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001706 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001707 if (!rc) break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001708 }
1709 }
1710
1711 // If mount failed, clean up loop file (if any).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001712 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1713 del_loop(mp->mnt_fsname);
1714 if (ENABLE_FEATURE_CLEAN_UP) {
1715 free(loopFile);
1716 free(mp->mnt_fsname);
1717 }
1718 }
1719
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001720 report_error:
1721 if (ENABLE_FEATURE_CLEAN_UP)
1722 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001723
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001724 if (errno == EBUSY && ignore_busy)
1725 return 0;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001726 if (rc < 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001727 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001728 return rc;
1729}
1730
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001731/* -O support
1732 * Unlike -t, -O should interpret "no" prefix differently:
1733 * -t noa,b,c = -t no(a,b,c) = mount all except fs'es with types a,b, and c
1734 * -O noa,b,c = -O noa,b,c = mount all with without option a,
1735 * or with option b or c.
1736 * But for now we do not support -O a,b,c at all (only -O a).
1737 *
1738 * Another difference from -t support (match_fstype) is that
1739 * we need to examine the _list_ of options in fsopt, not just a string.
1740 */
1741static int match_opt(const char *fs_opt, const char *O_opt)
1742{
1743 int match = 1;
1744 int len;
1745
1746 if (!O_opt)
1747 return match;
1748
1749 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1750 match--;
1751 O_opt += 2;
1752 }
1753
1754 len = strlen(O_opt);
1755 while (1) {
1756 if (strncmp(fs_opt, O_opt, len) == 0
1757 && (fs_opt[len] == '\0' || fs_opt[len] == ',')
1758 ) {
1759 return match;
1760 }
1761 fs_opt = strchr(fs_opt, ',');
1762 if (!fs_opt)
1763 break;
1764 fs_opt++;
1765 }
1766
1767 return !match;
1768}
1769
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001770// Parse options, if necessary parse fstab/mtab, and call singlemount for
1771// each directory to be mounted.
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001772static const char must_be_root[] ALIGN1 = "you must be root";
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001773
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001774int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001775int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001776{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001777 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001778 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001779 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001780 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001781 llist_t *lst_o = NULL;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001782 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001783 FILE *fstab;
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001784 int i, j, rc = 0;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001785 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001786 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001787 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001788
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001789 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00001790
Denis Vlasenkof732e962008-02-18 12:07:49 +00001791 // Parse long options, like --bind and --move. Note that -o option
1792 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001793 for (i = j = 1; argv[i]; i++) {
1794 if (argv[i][0] == '-' && argv[i][1] == '-')
1795 append_mount_options(&cmdopts, argv[i] + 2);
1796 else
1797 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001798 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001799 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001800
1801 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001802 // Max 2 params; -o is a list, -v is a counter
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001803 opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001804 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001805 IF_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001806 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00001807 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1808 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001809 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001810
1811 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001812 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00001813 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001814 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1815
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001816 if (!mountTable)
1817 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001818
Denis Vlasenko2535f122007-09-15 13:28:30 +00001819 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001820 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001821 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001822 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00001823 // util-linux 2.12a happily shows rootfs...
1824 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001825
1826 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1827 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1828 mtpair->mnt_dir, mtpair->mnt_type,
1829 mtpair->mnt_opts);
1830 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001831 if (ENABLE_FEATURE_CLEAN_UP)
1832 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001833 return EXIT_SUCCESS;
1834 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001835 storage_path = NULL;
1836 } else {
1837 // When we have two arguments, the second is the directory and we can
1838 // skip looking at fstab entirely. We can always abspath() the directory
1839 // argument when we get it.
1840 if (argv[1]) {
1841 if (nonroot)
1842 bb_error_msg_and_die(must_be_root);
1843 mtpair->mnt_fsname = argv[0];
1844 mtpair->mnt_dir = argv[1];
1845 mtpair->mnt_type = fstype;
1846 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00001847 resolve_mount_spec(&mtpair->mnt_fsname);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001848 rc = singlemount(mtpair, 0);
1849 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00001850 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001851 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001852 }
1853
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001854 // Past this point, we are handling either "mount -a [opts]"
1855 // or "mount [opts] single_param"
1856
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001857 i = parse_mount_options(cmdopts, NULL); // FIXME: should be "long", not "int"
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001858 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1859 bb_error_msg_and_die(must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00001860
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001861 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001862 if (ENABLE_FEATURE_MOUNT_FLAGS
1863 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1864 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001865 // verbose_mount(source, target, type, flags, data)
1866 rc = verbose_mount("", argv[0], "", i, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001867 if (rc)
1868 bb_simple_perror_msg_and_die(argv[0]);
1869 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001870 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001871
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001872 // Open either fstab or mtab
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001873 fstabname = "/etc/fstab";
1874 if (i & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001875 // WARNING. I am not sure this matches util-linux's
1876 // behavior. It's possible util-linux does not
1877 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001878 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001879 }
1880 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001881 if (!fstab)
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001882 bb_perror_msg_and_die("cannot read %s", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001883
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001884 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00001885 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001886 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001887 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001888
1889 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00001890 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001891 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001892 GETMNTENT_BUFSIZE/2)
1893 ) { // End of fstab/mtab is reached
1894 mtcur = mtother; // the thing we found last time
1895 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001896 }
1897
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001898 // If we're trying to mount something specific and this isn't it,
1899 // skip it. Note we must match the exact text in fstab (ala
1900 // "proc") or a full path from root
1901 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001902
1903 // Is this what we're looking for?
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001904 if (strcmp(argv[0], mtcur->mnt_fsname) &&
1905 strcmp(storage_path, mtcur->mnt_fsname) &&
1906 strcmp(argv[0], mtcur->mnt_dir) &&
1907 strcmp(storage_path, mtcur->mnt_dir)) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001908
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001909 // Remember this entry. Something later may have
1910 // overmounted it, and we want the _last_ match.
1911 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001912
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001913 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001914 } else {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001915 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001916 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001917 if (nonroot)
1918 bb_error_msg_and_die(must_be_root);
1919
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001920 // Does type match? (NULL matches always)
1921 if (!match_fstype(mtcur, fstype))
1922 continue;
1923
1924 // Skip noauto and swap anyway.
1925 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
1926 // swap is bogus "fstype", parse_mount_options can't check fstypes
1927 || strcasecmp(mtcur->mnt_type, "swap") == 0
1928 ) {
1929 continue;
1930 }
1931
1932 // Does (at least one) option match?
1933 // (NULL matches always)
1934 if (!match_opt(mtcur->mnt_opts, O_optmatch))
1935 continue;
1936
1937 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001938
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001939 // NFS mounts want this to be xrealloc-able
1940 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00001941
1942 // Mount this thing
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001943 if (singlemount(mtcur, 1)) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001944 // Count number of failed mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001945 rc++;
1946 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001947 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001948 }
1949 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001950
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001951 // End of fstab/mtab is reached.
1952 // Were we looking for something specific?
1953 if (argv[0]) {
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001954 long l;
1955
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001956 // If we didn't find anything, complain
1957 if (!mtcur->mnt_fsname)
1958 bb_error_msg_and_die("can't find %s in %s",
1959 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001960
1961 // What happens when we try to "mount swap_partition"?
1962 // (fstab containts "swap_partition swap swap defaults 0 0")
1963 // util-linux-ng 2.13.1 does this:
1964 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
1965 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
1966 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
1967 // write(2, "mount: mount point swap does not exist\n", 39) = 39
1968 // exit_group(32) = ?
1969#if 0
1970 // In case we want to simply skip swap partitions:
1971 l = parse_mount_options(mtcur->mnt_opts, NULL);
1972 if ((l & MOUNT_SWAP)
1973 // swap is bogus "fstype", parse_mount_options can't check fstypes
1974 || strcasecmp(mtcur->mnt_type, "swap") == 0
1975 ) {
1976 goto ret;
1977 }
1978#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001979 if (nonroot) {
1980 // fstab must have "users" or "user"
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001981 l = parse_mount_options(mtcur->mnt_opts, NULL);
1982 if (!(l & MOUNT_USERS))
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001983 bb_error_msg_and_die(must_be_root);
1984 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001985
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001986 // Mount the last thing we found
1987 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1988 append_mount_options(&(mtcur->mnt_opts), cmdopts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00001989 resolve_mount_spec(&mtpair->mnt_fsname);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001990 rc = singlemount(mtcur, 0);
1991 if (ENABLE_FEATURE_CLEAN_UP)
1992 free(mtcur->mnt_opts);
1993 }
1994
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001995 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001996 if (ENABLE_FEATURE_CLEAN_UP)
1997 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001998 if (ENABLE_FEATURE_CLEAN_UP) {
1999 free(storage_path);
2000 free(cmdopts);
2001 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002002 return rc;
2003}