blob: 9f9249f0a5edf708a47c06e00cd1ecfe6a957d26 [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
Rob Landley22d26fc2006-06-15 15:49:36 +000012/* Design notes: There is no spec for mount. Remind me to write one.
Rob Landleydc0955b2006-03-14 18:16:25 +000013
14 mount_main() calls singlemount() which calls mount_it_now().
Eric Andersen9601a1c2006-03-20 18:07:50 +000015
Rob Landleydc0955b2006-03-14 18:16:25 +000016 mount_main() can loop through /etc/fstab for mount -a
17 singlemount() can loop through /etc/filesystems for fstype detection.
18 mount_it_now() does the actual mount.
19*/
20
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
29/* 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 Vlasenkoc9ca0a32008-02-18 11:08:33 +000040/* Grab more as needed from util-linux's mount/mount_constants.h */
41#ifndef MS_DIRSYNC
42#define MS_DIRSYNC 128 /* Directory modifications are synchronous */
43#endif
44
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000045
Denis Vlasenko908d6b72006-12-18 23:07:42 +000046#if defined(__dietlibc__)
47/* 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
Rob Landleydc0955b2006-03-14 18:16:25 +0000102/* Standard mount options (from -o options or --options), with corresponding
103 * flags */
Eric Andersencc8ed391999-10-05 16:24:54 +0000104
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
Rob Landleye3781b72006-08-08 01:39:49 +0000108 USE_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
Rob Landleye3781b72006-08-08 01:39:49 +0000112 USE_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,
118 USE_DESKTOP(/* "user" */ MOUNT_USERS,)
119 USE_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
Rob Landleye3781b72006-08-08 01:39:49 +0000123 USE_FEATURE_MOUNT_FLAGS(
124 // 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,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000140 /* "loud" */ ~MS_SILENT,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000141
Rob Landleye3781b72006-08-08 01:39:49 +0000142 // action flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000143 /* "bind" */ MS_BIND,
144 /* "move" */ MS_MOVE,
145 /* "shared" */ MS_SHARED,
146 /* "slave" */ MS_SLAVE,
147 /* "private" */ MS_PRIVATE,
148 /* "unbindable" */ MS_UNBINDABLE,
149 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
150 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
151 /* "rprivate" */ MS_SLAVE|MS_RECURSIVE,
152 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000153 )
154
155 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000156 /* "ro" */ MS_RDONLY, // vfs flag
157 /* "rw" */ ~MS_RDONLY, // vfs flag
158 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000159};
160
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000161static const char mount_option_str[] =
162 USE_FEATURE_MOUNT_LOOP(
163 "loop" "\0"
164 )
165 USE_FEATURE_MOUNT_FSTAB(
166 "defaults" "\0"
167 /* "quiet" "\0" - do not filter out, vfat wants to see it */
168 "noauto" "\0"
169 "sw" "\0"
170 "swap" "\0"
171 USE_DESKTOP("user" "\0")
172 USE_DESKTOP("users" "\0")
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000173 "_netdev" "\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000174 )
175 USE_FEATURE_MOUNT_FLAGS(
176 // vfs flags
177 "nosuid" "\0"
178 "suid" "\0"
179 "dev" "\0"
180 "nodev" "\0"
181 "exec" "\0"
182 "noexec" "\0"
183 "sync" "\0"
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000184 "dirsync" "\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000185 "async" "\0"
186 "atime" "\0"
187 "noatime" "\0"
188 "diratime" "\0"
189 "nodiratime" "\0"
Denis Vlasenko580ce2d2008-07-08 02:56:53 +0000190 "mand" "\0"
191 "nomand" "\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000192 "loud" "\0"
193
194 // action flags
195 "bind" "\0"
196 "move" "\0"
197 "shared" "\0"
198 "slave" "\0"
199 "private" "\0"
200 "unbindable" "\0"
201 "rshared" "\0"
202 "rslave" "\0"
203 "rprivate" "\0"
204 "runbindable" "\0"
205 )
206
207 // Always understood.
208 "ro" "\0" // vfs flag
209 "rw" "\0" // vfs flag
210 "remount" "\0" // action flag
211;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000212
Denis Vlasenkof732e962008-02-18 12:07:49 +0000213
214struct globals {
215#if ENABLE_FEATURE_MOUNT_NFS
216 smalluint nfs_mount_version;
217#endif
218#if ENABLE_FEATURE_MOUNT_VERBOSE
219 unsigned verbose;
220#endif
221 llist_t *fslist;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000222 char getmntent_buf[1];
Denis Vlasenkof732e962008-02-18 12:07:49 +0000223
224};
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000225enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denis Vlasenkof732e962008-02-18 12:07:49 +0000226#define G (*(struct globals*)&bb_common_bufsiz1)
227#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000228#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000229#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000230#else
231#define verbose 0
232#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000233#define fslist (G.fslist )
234#define getmntent_buf (G.getmntent_buf )
235
236
237#if ENABLE_FEATURE_MOUNT_VERBOSE
238static int verbose_mount(const char *source, const char *target,
239 const char *filesystemtype,
240 unsigned long mountflags, const void *data)
241{
242 int rc;
243
244 errno = 0;
245 rc = mount(source, target, filesystemtype, mountflags, data);
246 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000247 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000248 source, target, filesystemtype,
249 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000250 return rc;
251}
252#else
253#define verbose_mount(...) mount(__VA_ARGS__)
254#endif
255
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000256static int resolve_mount_spec(char **fsname)
257{
258 char *tmp = NULL;
259
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000260#if ENABLE_FEATURE_MOUNT_LABEL
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000261 if (!strncmp(*fsname, "UUID=", 5))
262 tmp = get_devname_from_uuid(*fsname + 5);
263 else if (!strncmp(*fsname, "LABEL=", 6))
264 tmp = get_devname_from_label(*fsname + 6);
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000265#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000266
267 if (tmp) {
268 *fsname = tmp;
269 return 1;
270 }
271 return 0;
272}
273
Rob Landleydc0955b2006-03-14 18:16:25 +0000274/* Append mount options to string */
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000275static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000276{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000277 if (*oldopts && **oldopts) {
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000278 /* do not insert options which are already there */
279 while (newopts[0]) {
280 char *p;
281 int len = strlen(newopts);
282 p = strchr(newopts, ',');
283 if (p) len = p - newopts;
284 p = *oldopts;
285 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000286 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000287 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000288 goto skip;
289 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000290 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000291 p++;
292 }
293 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
294 free(*oldopts);
295 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000296 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000297 newopts += len;
298 while (newopts[0] == ',') newopts++;
299 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000300 } else {
301 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000302 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000303 }
304}
305
306/* Use the mount_options list to parse options into flags.
Denis Vlasenko00d7d6c2006-09-11 17:42:44 +0000307 * Also return list of unrecognized options if unrecognized!=NULL */
Denis Vlasenkob4133682008-02-18 13:05:38 +0000308static long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000309{
Denis Vlasenkob4133682008-02-18 13:05:38 +0000310 long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000311
Rob Landley6a6798b2005-08-10 20:35:54 +0000312 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000313 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000314 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000315 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000316 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000317
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000318 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000319
Bernhard Reutner-Fischerf9a07842008-05-30 10:44:37 +0000320/* FIXME: use hasmntopt() */
Rob Landley6a6798b2005-08-10 20:35:54 +0000321 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000322 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000323 if (!strcasecmp(option_str, options)) {
324 long fl = mount_options[i];
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000325 if (fl < 0) flags &= fl;
Rob Landleydc0955b2006-03-14 18:16:25 +0000326 else flags |= fl;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000327 break;
328 }
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000329 option_str += strlen(option_str) + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000330 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000331 // If unrecognized not NULL, append unrecognized mount options */
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000332 if (unrecognized && i == ARRAY_SIZE(mount_options)) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000333 // Add it to strflags, to pass on to kernel
Rob Landleydc0955b2006-03-14 18:16:25 +0000334 i = *unrecognized ? strlen(*unrecognized) : 0;
Denis Vlasenkodeeed592008-07-08 05:14:36 +0000335 *unrecognized = xrealloc(*unrecognized, i + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000336
Rob Landley6a6798b2005-08-10 20:35:54 +0000337 // Comma separated if it's not the first one
Rob Landleydc0955b2006-03-14 18:16:25 +0000338 if (i) (*unrecognized)[i++] = ',';
339 strcpy((*unrecognized)+i, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000340 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000341
Denis Vlasenko2535f122007-09-15 13:28:30 +0000342 if (!comma)
343 break;
344 // Advance to next option
345 *comma = ',';
346 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000347 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000348
Rob Landleydc0955b2006-03-14 18:16:25 +0000349 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000350}
351
Rob Landleydc0955b2006-03-14 18:16:25 +0000352// Return a list of all block device backed filesystems
Matt Kraai12400822001-04-17 04:32:50 +0000353
Rob Landleydc0955b2006-03-14 18:16:25 +0000354static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000355{
Denis Vlasenko87468852007-04-13 23:22:00 +0000356 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000357 "/etc/filesystems",
358 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000359 };
360 char *fs, *buf;
Rob Landleydc0955b2006-03-14 18:16:25 +0000361 llist_t *list = 0;
362 int i;
363 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000364
Denis Vlasenko87468852007-04-13 23:22:00 +0000365 for (i = 0; i < 2; i++) {
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000366 f = fopen(filesystems[i], "r");
367 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000368
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000369 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000370 if (!strncmp(buf, "nodev", 5) && isspace(buf[5]))
371 continue;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000372 fs = skip_whitespace(buf);
Denis Vlasenko372686b2006-10-12 22:42:33 +0000373 if (*fs=='#' || *fs=='*' || !*fs) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000374
Denis Vlasenko372686b2006-10-12 22:42:33 +0000375 llist_add_to_end(&list, xstrdup(fs));
376 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000377 }
378 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
379 }
380
381 return list;
382}
383
Rob Landleydc0955b2006-03-14 18:16:25 +0000384#if ENABLE_FEATURE_CLEAN_UP
385static void delete_block_backed_filesystems(void)
386{
Rob Landleya6b5b602006-05-08 19:03:07 +0000387 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000388}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000389#else
390void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000391#endif
392
Rob Landleydc0955b2006-03-14 18:16:25 +0000393// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000394// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000395static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000396{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000397 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000398
Denis Vlasenkob4133682008-02-18 13:05:38 +0000399 if (fakeIt) {
400 if (verbose >= 2)
401 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
402 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
403 vfsflags, filteropts);
404 goto mtab;
405 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000406
Rob Landleydc0955b2006-03-14 18:16:25 +0000407 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000408 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000409 errno = 0;
410 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000411 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000412
413 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000414 // helper program mount.<mnt_type>
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000415 if (ENABLE_FEATURE_MOUNT_HELPERS && rc) {
416 char *args[6];
417 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000418 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000419 rc = 1;
420 if (filteropts) {
421 args[rc++] = (char *)"-o";
422 args[rc++] = filteropts;
423 }
424 args[rc++] = mp->mnt_fsname;
425 args[rc++] = mp->mnt_dir;
426 args[rc] = NULL;
427 rc = wait4pid(spawn(args));
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000428 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000429 if (!rc)
430 break;
431 errno = errno_save;
432 }
433
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000434 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000435 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000436 if (!(vfsflags & MS_SILENT))
437 bb_error_msg("%s is write-protected, mounting read-only",
438 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000439 vfsflags |= MS_RDONLY;
440 }
441
Rob Landleydc0955b2006-03-14 18:16:25 +0000442 // Abort entirely if permission denied.
443
444 if (rc && errno == EPERM)
445 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
446
447 /* If the mount was successful, and we're maintaining an old-style
448 * mtab file by hand, add the new entry to it now. */
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000449 mtab:
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000450 if (useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000451 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000452 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000453 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000454 int i;
455
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000456 if (!mountTable) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000457 bb_error_msg("no %s", bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000458 goto ret;
459 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000460
461 // Add vfs string flags
462
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000463 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
464 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
465 append_mount_options(&(mp->mnt_opts), option_str);
466 option_str += strlen(option_str) + 1;
467 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000468
469 // Remove trailing / (if any) from directory we mounted on
470
Denis Vlasenko727ef942006-09-14 13:19:19 +0000471 i = strlen(mp->mnt_dir) - 1;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000472 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000473
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000474 // Convert to canonical pathnames as needed
Denis Vlasenko727ef942006-09-14 13:19:19 +0000475
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000476 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000477 fsname = 0;
Denis Vlasenko727ef942006-09-14 13:19:19 +0000478 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000479 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000480 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000481 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000482 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000483
484 // Write and close.
485
Denis Vlasenko727ef942006-09-14 13:19:19 +0000486 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000487 endmntent(mountTable);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000488 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000489 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000490 free(fsname);
491 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000492 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000493 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000494 return rc;
495}
496
Denis Vlasenko25098f72006-09-14 15:46:33 +0000497#if ENABLE_FEATURE_MOUNT_NFS
498
499/*
500 * Linux NFS mount
501 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
502 *
503 * Licensed under GPLv2, see file LICENSE in this tarball for details.
504 *
505 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
506 * numbers to be specified on the command line.
507 *
508 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
509 * Omit the call to connect() for Linux version 1.3.11 or later.
510 *
511 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
512 * Implemented the "bg", "fg" and "retry" mount options for NFS.
513 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000514 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000515 * - added Native Language Support
516 *
517 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
518 * plus NFSv3 stuff.
519 */
520
Denis Vlasenko25098f72006-09-14 15:46:33 +0000521/* This is just a warning of a common mistake. Possibly this should be a
522 * uclibc faq entry rather than in busybox... */
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000523#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000524#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
525#endif
526
527#define MOUNTPORT 635
528#define MNTPATHLEN 1024
529#define MNTNAMLEN 255
530#define FHSIZE 32
531#define FHSIZE3 64
532
533typedef char fhandle[FHSIZE];
534
535typedef struct {
536 unsigned int fhandle3_len;
537 char *fhandle3_val;
538} fhandle3;
539
540enum mountstat3 {
541 MNT_OK = 0,
542 MNT3ERR_PERM = 1,
543 MNT3ERR_NOENT = 2,
544 MNT3ERR_IO = 5,
545 MNT3ERR_ACCES = 13,
546 MNT3ERR_NOTDIR = 20,
547 MNT3ERR_INVAL = 22,
548 MNT3ERR_NAMETOOLONG = 63,
549 MNT3ERR_NOTSUPP = 10004,
550 MNT3ERR_SERVERFAULT = 10006,
551};
552typedef enum mountstat3 mountstat3;
553
554struct fhstatus {
555 unsigned int fhs_status;
556 union {
557 fhandle fhs_fhandle;
558 } fhstatus_u;
559};
560typedef struct fhstatus fhstatus;
561
562struct mountres3_ok {
563 fhandle3 fhandle;
564 struct {
565 unsigned int auth_flavours_len;
566 char *auth_flavours_val;
567 } auth_flavours;
568};
569typedef struct mountres3_ok mountres3_ok;
570
571struct mountres3 {
572 mountstat3 fhs_status;
573 union {
574 mountres3_ok mountinfo;
575 } mountres3_u;
576};
577typedef struct mountres3 mountres3;
578
579typedef char *dirpath;
580
581typedef char *name;
582
583typedef struct mountbody *mountlist;
584
585struct mountbody {
586 name ml_hostname;
587 dirpath ml_directory;
588 mountlist ml_next;
589};
590typedef struct mountbody mountbody;
591
592typedef struct groupnode *groups;
593
594struct groupnode {
595 name gr_name;
596 groups gr_next;
597};
598typedef struct groupnode groupnode;
599
600typedef struct exportnode *exports;
601
602struct exportnode {
603 dirpath ex_dir;
604 groups ex_groups;
605 exports ex_next;
606};
607typedef struct exportnode exportnode;
608
609struct ppathcnf {
610 int pc_link_max;
611 short pc_max_canon;
612 short pc_max_input;
613 short pc_name_max;
614 short pc_path_max;
615 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000616 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000617 char pc_xxx;
618 short pc_mask[2];
619};
620typedef struct ppathcnf ppathcnf;
621
622#define MOUNTPROG 100005
623#define MOUNTVERS 1
624
625#define MOUNTPROC_NULL 0
626#define MOUNTPROC_MNT 1
627#define MOUNTPROC_DUMP 2
628#define MOUNTPROC_UMNT 3
629#define MOUNTPROC_UMNTALL 4
630#define MOUNTPROC_EXPORT 5
631#define MOUNTPROC_EXPORTALL 6
632
633#define MOUNTVERS_POSIX 2
634
635#define MOUNTPROC_PATHCONF 7
636
637#define MOUNT_V3 3
638
639#define MOUNTPROC3_NULL 0
640#define MOUNTPROC3_MNT 1
641#define MOUNTPROC3_DUMP 2
642#define MOUNTPROC3_UMNT 3
643#define MOUNTPROC3_UMNTALL 4
644#define MOUNTPROC3_EXPORT 5
645
646enum {
647#ifndef NFS_FHSIZE
648 NFS_FHSIZE = 32,
649#endif
650#ifndef NFS_PORT
651 NFS_PORT = 2049
652#endif
653};
654
Denis Vlasenko25098f72006-09-14 15:46:33 +0000655/*
656 * We want to be able to compile mount on old kernels in such a way
657 * that the binary will work well on more recent kernels.
658 * Thus, if necessary we teach nfsmount.c the structure of new fields
659 * that will come later.
660 *
661 * Moreover, the new kernel includes conflict with glibc includes
662 * so it is easiest to ignore the kernel altogether (at compile time).
663 */
664
665struct nfs2_fh {
666 char data[32];
667};
668struct nfs3_fh {
669 unsigned short size;
670 unsigned char data[64];
671};
672
673struct nfs_mount_data {
674 int version; /* 1 */
675 int fd; /* 1 */
676 struct nfs2_fh old_root; /* 1 */
677 int flags; /* 1 */
678 int rsize; /* 1 */
679 int wsize; /* 1 */
680 int timeo; /* 1 */
681 int retrans; /* 1 */
682 int acregmin; /* 1 */
683 int acregmax; /* 1 */
684 int acdirmin; /* 1 */
685 int acdirmax; /* 1 */
686 struct sockaddr_in addr; /* 1 */
687 char hostname[256]; /* 1 */
688 int namlen; /* 2 */
689 unsigned int bsize; /* 3 */
690 struct nfs3_fh root; /* 4 */
691};
692
693/* bits in the flags field */
694enum {
695 NFS_MOUNT_SOFT = 0x0001, /* 1 */
696 NFS_MOUNT_INTR = 0x0002, /* 1 */
697 NFS_MOUNT_SECURE = 0x0004, /* 1 */
698 NFS_MOUNT_POSIX = 0x0008, /* 1 */
699 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
700 NFS_MOUNT_NOAC = 0x0020, /* 1 */
701 NFS_MOUNT_TCP = 0x0040, /* 2 */
702 NFS_MOUNT_VER3 = 0x0080, /* 3 */
703 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
704 NFS_MOUNT_NONLM = 0x0200 /* 3 */
705};
706
707
708/*
709 * We need to translate between nfs status return values and
710 * the local errno values which may not be the same.
711 *
712 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
713 * "after #include <errno.h> the symbol errno is reserved for any use,
714 * it cannot even be used as a struct tag or field name".
715 */
716
717#ifndef EDQUOT
718#define EDQUOT ENOSPC
719#endif
720
721// Convert each NFSERR_BLAH into EBLAH
722
723static const struct {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000724 short stat;
725 short errnum;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000726} nfs_errtbl[] = {
727 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
728 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
729 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
730 {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
731};
732
733static 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{
899 int fd;
900 int pid = fork();
901 if (pid < 0) /* error */
902 return -errno;
903 if (pid > 0) /* parent */
904 return 0;
905 /* child */
906 fd = xopen(bb_dev_null, O_RDWR);
907 dup2(fd, 0);
908 dup2(fd, 1);
909 dup2(fd, 2);
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000910 while (fd > 2) close(fd--);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000911 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +0000912 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000913 logmode = LOGMODE_SYSLOG;
914 return 1;
915}
Denis Vlasenkof0000652007-09-04 18:30:26 +0000916#else
917static inline int daemonize(void) { return -ENOSYS; }
918#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000919
920// TODO
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000921static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000922{
923 return 0;
924}
925
926/* RPC strerror analogs are terminally idiotic:
927 * *mandatory* prefix and \n at end.
928 * This hopefully helps. Usage:
929 * error_msg_rpc(clnt_*error*(" ")) */
930static void error_msg_rpc(const char *msg)
931{
Denis Vlasenko23514fe2006-09-19 14:07:52 +0000932 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000933 while (msg[0] == ' ' || msg[0] == ':') msg++;
934 len = strlen(msg);
935 while (len && msg[len-1] == '\n') len--;
936 bb_error_msg("%.*s", len, msg);
937}
938
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +0000939// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000940static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000941{
942 CLIENT *mclient;
943 char *hostname;
944 char *pathname;
945 char *mounthost;
946 struct nfs_mount_data data;
947 char *opt;
948 struct hostent *hp;
949 struct sockaddr_in server_addr;
950 struct sockaddr_in mount_server_addr;
951 int msock, fsock;
952 union {
953 struct fhstatus nfsv2;
954 struct mountres3 nfsv3;
955 } status;
956 int daemonized;
957 char *s;
958 int port;
959 int mountport;
960 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +0000961#if BB_MMU
962 int bg = 0;
963#else
964 enum { bg = 0 };
965#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +0000966 int soft;
967 int intr;
968 int posix;
969 int nocto;
970 int noac;
971 int nolock;
972 int retry;
973 int tcp;
974 int mountprog;
975 int mountvers;
976 int nfsprog;
977 int nfsvers;
978 int retval;
979
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));
1039 data.retrans = 3;
1040 data.acregmin = 3;
1041 data.acregmax = 60;
1042 data.acdirmin = 30;
1043 data.acdirmax = 60;
1044 data.namlen = NAME_MAX;
1045
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;
1052 retry = 10000; /* 10000 minutes ~ 1 week */
1053 tcp = 0;
1054
1055 mountprog = MOUNTPROG;
1056 mountvers = 0;
1057 port = 0;
1058 mountport = 0;
1059 nfsprog = 100003;
1060 nfsvers = 0;
1061
1062 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001063 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001064 char *opteq = strchr(opt, '=');
1065 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001066 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001067 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001068 /* 0 */ "rsize\0"
1069 /* 1 */ "wsize\0"
1070 /* 2 */ "timeo\0"
1071 /* 3 */ "retrans\0"
1072 /* 4 */ "acregmin\0"
1073 /* 5 */ "acregmax\0"
1074 /* 6 */ "acdirmin\0"
1075 /* 7 */ "acdirmax\0"
1076 /* 8 */ "actimeo\0"
1077 /* 9 */ "retry\0"
1078 /* 10 */ "port\0"
1079 /* 11 */ "mountport\0"
1080 /* 12 */ "mounthost\0"
1081 /* 13 */ "mountprog\0"
1082 /* 14 */ "mountvers\0"
1083 /* 15 */ "nfsprog\0"
1084 /* 16 */ "nfsvers\0"
1085 /* 17 */ "vers\0"
1086 /* 18 */ "proto\0"
1087 /* 19 */ "namlen\0"
1088 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001089
1090 *opteq++ = '\0';
1091 idx = index_in_strings(options, opt);
1092 switch (idx) {
1093 case 12: // "mounthost"
1094 mounthost = xstrndup(opteq,
1095 strcspn(opteq, " \t\n\r,"));
1096 continue;
1097 case 18: // "proto"
1098 if (!strncmp(opteq, "tcp", 3))
1099 tcp = 1;
1100 else if (!strncmp(opteq, "udp", 3))
1101 tcp = 0;
1102 else
1103 bb_error_msg("warning: unrecognized proto= option");
1104 continue;
1105 case 20: // "addr" - ignore
1106 continue;
1107 }
1108
1109 val = xatoi_u(opteq);
1110 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001111 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001112 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001113 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001114 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001115 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001116 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001117 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001118 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001119 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001120 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001121 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001122 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001123 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001124 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001125 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001126 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001127 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001128 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001129 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001130 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001131 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001132 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001133 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001134 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001135 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001136 data.acregmin = val;
1137 data.acregmax = val;
1138 data.acdirmin = val;
1139 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001140 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001141 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001142 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001143 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001144 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001145 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001146 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001147 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001148 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001149 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001150 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001151 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001152 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001153 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001154 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001155 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001156 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001157 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001158 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001159 case 16: // "nfsvers"
1160 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001161 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001162 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001163 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001164 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001165 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001166 //else
1167 // bb_error_msg("warning: option namlen is not supported\n");
1168 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001169 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001170 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1171 goto fail;
1172 }
1173 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001174 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001175 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001176 "bg\0"
1177 "fg\0"
1178 "soft\0"
1179 "hard\0"
1180 "intr\0"
1181 "posix\0"
1182 "cto\0"
1183 "ac\0"
1184 "tcp\0"
1185 "udp\0"
1186 "lock\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001187 int val = 1;
1188 if (!strncmp(opt, "no", 2)) {
1189 val = 0;
1190 opt += 2;
1191 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001192 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001193 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001194#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001195 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001196#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001197 break;
1198 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001199#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001200 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001201#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001202 break;
1203 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001204 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001205 break;
1206 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001207 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001208 break;
1209 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001210 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001211 break;
1212 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001213 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001214 break;
1215 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001216 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001217 break;
1218 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001219 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001220 break;
1221 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001222 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001223 break;
1224 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001225 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001226 break;
1227 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001228 if (nfs_mount_version >= 3)
1229 nolock = !val;
1230 else
1231 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001232 break;
1233 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001234 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1235 goto fail;
1236 }
1237 }
1238 }
1239 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1240
1241 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1242 | (intr ? NFS_MOUNT_INTR : 0)
1243 | (posix ? NFS_MOUNT_POSIX : 0)
1244 | (nocto ? NFS_MOUNT_NOCTO : 0)
1245 | (noac ? NFS_MOUNT_NOAC : 0);
1246 if (nfs_mount_version >= 2)
1247 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1248 if (nfs_mount_version >= 3)
1249 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1250 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1251 bb_error_msg("NFSv%d not supported", nfsvers);
1252 goto fail;
1253 }
1254 if (nfsvers && !mountvers)
1255 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1256 if (nfsvers && nfsvers < mountvers) {
1257 mountvers = nfsvers;
1258 }
1259
1260 /* Adjust options if none specified */
1261 if (!data.timeo)
1262 data.timeo = tcp ? 70 : 7;
1263
Denis Vlasenko25098f72006-09-14 15:46:33 +00001264 data.version = nfs_mount_version;
1265
1266 if (vfsflags & MS_REMOUNT)
1267 goto do_mount;
1268
1269 /*
1270 * If the previous mount operation on the same host was
1271 * backgrounded, and the "bg" for this mount is also set,
1272 * give up immediately, to avoid the initial timeout.
1273 */
1274 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001275 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001276 if (daemonized <= 0) { /* parent or error */
1277 retval = -daemonized;
1278 goto ret;
1279 }
1280 }
1281
1282 /* create mount daemon client */
1283 /* See if the nfs host = mount host. */
1284 if (mounthost) {
1285 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1286 mount_server_addr.sin_family = AF_INET;
1287 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1288 } else {
1289 hp = gethostbyname(mounthost);
1290 if (hp == NULL) {
1291 bb_herror_msg("%s", mounthost);
1292 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001293 }
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001294 if ((size_t)hp->h_length > sizeof(struct in_addr)) {
1295 bb_error_msg("got bad hp->h_length");
1296 hp->h_length = sizeof(struct in_addr);
1297 }
1298 mount_server_addr.sin_family = AF_INET;
1299 memcpy(&mount_server_addr.sin_addr,
1300 hp->h_addr, hp->h_length);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001301 }
1302 }
1303
1304 /*
1305 * The following loop implements the mount retries. When the mount
1306 * times out, and the "bg" option is set, we background ourself
1307 * and continue trying.
1308 *
1309 * The case where the mount point is not present and the "bg"
1310 * option is set, is treated as a timeout. This is done to
1311 * support nested mounts.
1312 *
1313 * The "retry" count specified by the user is the number of
1314 * minutes to retry before giving up.
1315 */
1316 {
1317 struct timeval total_timeout;
1318 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001319 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001320 time_t t;
1321 time_t prevt;
1322 time_t timeout;
1323
1324 retry_timeout.tv_sec = 3;
1325 retry_timeout.tv_usec = 0;
1326 total_timeout.tv_sec = 20;
1327 total_timeout.tv_usec = 0;
1328 timeout = time(NULL) + 60 * retry;
1329 prevt = 0;
1330 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001331 retry:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001332 /* be careful not to use too many CPU cycles */
1333 if (t - prevt < 30)
1334 sleep(30);
1335
Denis Vlasenkob9256052007-09-28 10:29:17 +00001336 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001337 mountprog,
1338 mountvers,
1339 proto,
1340 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001341 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001342
1343 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001344 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001345 msock = RPC_ANYSOCK;
1346
Denis Vlasenkob9256052007-09-28 10:29:17 +00001347 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001348 case IPPROTO_UDP:
1349 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001350 pm_mnt.pm_prog,
1351 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001352 retry_timeout,
1353 &msock);
1354 if (mclient)
1355 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001356 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001357 msock = RPC_ANYSOCK;
1358 case IPPROTO_TCP:
1359 mclient = clnttcp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001360 pm_mnt.pm_prog,
1361 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001362 &msock, 0, 0);
1363 break;
1364 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001365 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001366 }
1367 if (!mclient) {
1368 if (!daemonized && prevt == 0)
1369 error_msg_rpc(clnt_spcreateerror(" "));
1370 } else {
1371 enum clnt_stat clnt_stat;
1372 /* try to mount hostname:pathname */
1373 mclient->cl_auth = authunix_create_default();
1374
1375 /* make pointers in xdr_mountres3 NULL so
1376 * that xdr_array allocates memory for us
1377 */
1378 memset(&status, 0, sizeof(status));
1379
Denis Vlasenkob9256052007-09-28 10:29:17 +00001380 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001381 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1382 (xdrproc_t) xdr_dirpath,
1383 (caddr_t) &pathname,
1384 (xdrproc_t) xdr_mountres3,
1385 (caddr_t) &status,
1386 total_timeout);
1387 else
1388 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1389 (xdrproc_t) xdr_dirpath,
1390 (caddr_t) &pathname,
1391 (xdrproc_t) xdr_fhstatus,
1392 (caddr_t) &status,
1393 total_timeout);
1394
1395 if (clnt_stat == RPC_SUCCESS)
1396 goto prepare_kernel_data; /* we're done */
1397 if (errno != ECONNREFUSED) {
1398 error_msg_rpc(clnt_sperror(mclient, " "));
1399 goto fail; /* don't retry */
1400 }
1401 /* Connection refused */
1402 if (!daemonized && prevt == 0) /* print just once */
1403 error_msg_rpc(clnt_sperror(mclient, " "));
1404 auth_destroy(mclient->cl_auth);
1405 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001406 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001407 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001408 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001409 }
1410
1411 /* Timeout. We are going to retry... maybe */
1412
1413 if (!bg)
1414 goto fail;
1415 if (!daemonized) {
1416 daemonized = daemonize();
1417 if (daemonized <= 0) { /* parent or error */
1418 retval = -daemonized;
1419 goto ret;
1420 }
1421 }
1422 prevt = t;
1423 t = time(NULL);
1424 if (t >= timeout)
1425 /* TODO error message */
1426 goto fail;
1427
1428 goto retry;
1429 }
1430
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001431 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001432
1433 if (nfsvers == 2) {
1434 if (status.nfsv2.fhs_status != 0) {
1435 bb_error_msg("%s:%s failed, reason given by server: %s",
1436 hostname, pathname,
1437 nfs_strerror(status.nfsv2.fhs_status));
1438 goto fail;
1439 }
1440 memcpy(data.root.data,
1441 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1442 NFS_FHSIZE);
1443 data.root.size = NFS_FHSIZE;
1444 memcpy(data.old_root.data,
1445 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1446 NFS_FHSIZE);
1447 } else {
1448 fhandle3 *my_fhandle;
1449 if (status.nfsv3.fhs_status != 0) {
1450 bb_error_msg("%s:%s failed, reason given by server: %s",
1451 hostname, pathname,
1452 nfs_strerror(status.nfsv3.fhs_status));
1453 goto fail;
1454 }
1455 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1456 memset(data.old_root.data, 0, NFS_FHSIZE);
1457 memset(&data.root, 0, sizeof(data.root));
1458 data.root.size = my_fhandle->fhandle3_len;
1459 memcpy(data.root.data,
1460 (char *) my_fhandle->fhandle3_val,
1461 my_fhandle->fhandle3_len);
1462
1463 data.flags |= NFS_MOUNT_VER3;
1464 }
1465
1466 /* create nfs socket for kernel */
1467
1468 if (tcp) {
1469 if (nfs_mount_version < 3) {
1470 bb_error_msg("NFS over TCP is not supported");
1471 goto fail;
1472 }
1473 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1474 } else
1475 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1476 if (fsock < 0) {
1477 bb_perror_msg("nfs socket");
1478 goto fail;
1479 }
1480 if (bindresvport(fsock, 0) < 0) {
1481 bb_perror_msg("nfs bindresvport");
1482 goto fail;
1483 }
1484 if (port == 0) {
1485 server_addr.sin_port = PMAPPORT;
1486 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1487 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1488 if (port == 0)
1489 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001490 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001491 server_addr.sin_port = htons(port);
1492
1493 /* prepare data structure for kernel */
1494
1495 data.fd = fsock;
1496 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1497 strncpy(data.hostname, hostname, sizeof(data.hostname));
1498
1499 /* clean up */
1500
1501 auth_destroy(mclient->cl_auth);
1502 clnt_destroy(mclient);
1503 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001504 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001505
1506 if (bg) {
1507 /* We must wait until mount directory is available */
1508 struct stat statbuf;
1509 int delay = 1;
1510 while (stat(mp->mnt_dir, &statbuf) == -1) {
1511 if (!daemonized) {
1512 daemonized = daemonize();
1513 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001514 // FIXME: parent doesn't close fsock - ??!
Denis Vlasenko25098f72006-09-14 15:46:33 +00001515 retval = -daemonized;
1516 goto ret;
1517 }
1518 }
1519 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1520 delay *= 2;
1521 if (delay > 30)
1522 delay = 30;
1523 }
1524 }
1525
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001526 do_mount: /* perform actual mount */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001527
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001528 mp->mnt_type = (char*)"nfs";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001529 retval = mount_it_now(mp, vfsflags, (char*)&data);
1530 goto ret;
1531
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001532 fail: /* abort */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001533
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001534 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001535 if (mclient) {
1536 auth_destroy(mclient->cl_auth);
1537 clnt_destroy(mclient);
1538 }
1539 close(msock);
1540 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001541 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001542 close(fsock);
1543
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001544 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001545 free(hostname);
1546 free(mounthost);
1547 free(filteropts);
1548 return retval;
1549}
1550
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001551#else /* !ENABLE_FEATURE_MOUNT_NFS */
1552
1553/* Never called. Call should be optimized out. */
Denis Vlasenkob4133682008-02-18 13:05:38 +00001554int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001555
1556#endif /* !ENABLE_FEATURE_MOUNT_NFS */
1557
1558// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1559// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001560// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001561static int singlemount(struct mntent *mp, int ignore_busy)
1562{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001563 int rc = -1;
1564 long vfsflags;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001565 char *loopFile = 0, *filteropts = 0;
1566 llist_t *fl = 0;
1567 struct stat st;
1568
1569 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1570
1571 // Treat fstype "auto" as unspecified.
1572
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001573 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1574 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001575
Denis Vlasenko2535f122007-09-15 13:28:30 +00001576 // Might this be a virtual filesystem?
1577
1578 if (ENABLE_FEATURE_MOUNT_HELPERS
Denis Vlasenkoc03e8722007-12-26 20:56:55 +00001579 && (strchr(mp->mnt_fsname, '#'))
Denis Vlasenko2535f122007-09-15 13:28:30 +00001580 ) {
1581 char *s, *p, *args[35];
1582 int n = 0;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001583// FIXME: does it allow execution of arbitrary commands?!
1584// What args[0] can end up with?
Denis Vlasenko2535f122007-09-15 13:28:30 +00001585 for (s = p = mp->mnt_fsname; *s && n < 35-3; ++s) {
1586 if (s[0] == '#' && s[1] != '#') {
1587 *s = '\0';
1588 args[n++] = p;
1589 p = s + 1;
1590 }
1591 }
1592 args[n++] = p;
1593 args[n++] = mp->mnt_dir;
1594 args[n] = NULL;
1595 rc = wait4pid(xspawn(args));
1596 goto report_error;
1597 }
1598
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001599 // Might this be an CIFS filesystem?
1600
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001601 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001602 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1603 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1604 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001605 ) {
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001606 len_and_sockaddr *lsa;
1607 char *ip, *dotted;
1608 char *s;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001609
1610 rc = 1;
1611 // Replace '/' with '\' and verify that unc points to "//server/share".
1612
1613 for (s = mp->mnt_fsname; *s; ++s)
1614 if (*s == '/') *s = '\\';
1615
1616 // get server IP
1617
1618 s = strrchr(mp->mnt_fsname, '\\');
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001619 if (s <= mp->mnt_fsname+1) goto report_error;
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001620 *s = '\0';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001621 lsa = host2sockaddr(mp->mnt_fsname+2, 0);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001622 *s = '\\';
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001623 if (!lsa) goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001624
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001625 // insert ip=... option into string flags.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001626
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001627 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001628 ip = xasprintf("ip=%s", dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001629 parse_mount_options(ip, &filteropts);
1630
1631 // compose new unc '\\server-ip\share'
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001632 // (s => slash after hostname)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001633
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001634 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001635
1636 // lock is required
1637 vfsflags |= MS_MANDLOCK;
1638
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001639 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001640 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001641 if (ENABLE_FEATURE_CLEAN_UP) {
1642 free(mp->mnt_fsname);
1643 free(ip);
1644 free(dotted);
1645 free(lsa);
1646 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001647 goto report_error;
1648 }
1649
1650 // Might this be an NFS filesystem?
1651
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001652 if (ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001653 && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001654 && strchr(mp->mnt_fsname, ':') != NULL
1655 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001656 rc = nfsmount(mp, vfsflags, filteropts);
1657 goto report_error;
1658 }
1659
1660 // Look at the file. (Not found isn't a failure for remount, or for
1661 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001662 // (We use stat, not lstat, in order to allow
1663 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001664
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001665 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001666 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1667 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001668 // Do we need to allocate a loopback device for it?
1669
1670 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1671 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001672 mp->mnt_fsname = NULL; /* will receive malloced loop dev name */
1673 if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) {
1674 if (errno == EPERM || errno == EACCES)
1675 bb_error_msg(bb_msg_perm_denied_are_you_root);
1676 else
1677 bb_perror_msg("cannot setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001678 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001679 }
1680
1681 // Autodetect bind mounts
1682
1683 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1684 vfsflags |= MS_BIND;
1685 }
1686
1687 /* If we know the fstype (or don't need to), jump straight
1688 * to the actual mount. */
1689
1690 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
1691 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001692 else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001693 // Loop through filesystem types until mount succeeds
1694 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001695
1696 /* Initialize list of block backed filesystems. This has to be
1697 * done here so that during "mount -a", mounts after /proc shows up
1698 * can autodetect. */
1699
1700 if (!fslist) {
1701 fslist = get_block_backed_filesystems();
1702 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1703 atexit(delete_block_backed_filesystems);
1704 }
1705
1706 for (fl = fslist; fl; fl = fl->link) {
1707 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001708 rc = mount_it_now(mp, vfsflags, filteropts);
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001709 if (!rc) break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001710 }
1711 }
1712
1713 // If mount failed, clean up loop file (if any).
1714
1715 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1716 del_loop(mp->mnt_fsname);
1717 if (ENABLE_FEATURE_CLEAN_UP) {
1718 free(loopFile);
1719 free(mp->mnt_fsname);
1720 }
1721 }
1722
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001723 report_error:
1724 if (ENABLE_FEATURE_CLEAN_UP)
1725 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001726
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001727 if (errno == EBUSY && ignore_busy)
1728 return 0;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001729 if (rc < 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001730 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001731 return rc;
1732}
1733
1734// Parse options, if necessary parse fstab/mtab, and call singlemount for
1735// each directory to be mounted.
1736
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001737static const char must_be_root[] ALIGN1 = "you must be root";
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001738
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001739int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001740int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001741{
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001742 char *cmdopts = xstrdup("");
1743 char *fstype = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001744 char *storage_path;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001745 char *opt_o;
1746 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001747 FILE *fstab;
Denis Vlasenko9c99b622006-09-17 15:05:31 +00001748 int i, j, rc = 0;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001749 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001750 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001751 SKIP_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001752
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001753 USE_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00001754
Denis Vlasenkof732e962008-02-18 12:07:49 +00001755 // Parse long options, like --bind and --move. Note that -o option
1756 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001757 for (i = j = 1; argv[i]; i++) {
1758 if (argv[i][0] == '-' && argv[i][1] == '-')
1759 append_mount_options(&cmdopts, argv[i] + 2);
1760 else
1761 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001762 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001763 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001764
1765 // Parse remaining options
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001766 // Max 2 params; -v is a counter
1767 opt_complementary = "?2" USE_FEATURE_MOUNT_VERBOSE(":vv");
Denis Vlasenkof732e962008-02-18 12:07:49 +00001768 opt = getopt32(argv, OPTION_STR, &opt_o, &fstype
1769 USE_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00001770 if (opt & OPT_o) append_mount_options(&cmdopts, opt_o); // -o
1771 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1772 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001773 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001774
1775 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001776 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00001777 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001778 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1779
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001780 if (!mountTable)
1781 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001782
Denis Vlasenko2535f122007-09-15 13:28:30 +00001783 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001784 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001785 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001786 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00001787 // util-linux 2.12a happily shows rootfs...
1788 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001789
1790 if (!fstype || !strcmp(mtpair->mnt_type, fstype))
1791 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1792 mtpair->mnt_dir, mtpair->mnt_type,
1793 mtpair->mnt_opts);
1794 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001795 if (ENABLE_FEATURE_CLEAN_UP)
1796 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001797 return EXIT_SUCCESS;
1798 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001799 storage_path = NULL;
1800 } else {
1801 // When we have two arguments, the second is the directory and we can
1802 // skip looking at fstab entirely. We can always abspath() the directory
1803 // argument when we get it.
1804 if (argv[1]) {
1805 if (nonroot)
1806 bb_error_msg_and_die(must_be_root);
1807 mtpair->mnt_fsname = argv[0];
1808 mtpair->mnt_dir = argv[1];
1809 mtpair->mnt_type = fstype;
1810 mtpair->mnt_opts = cmdopts;
1811 if (ENABLE_FEATURE_MOUNT_LABEL) {
1812 resolve_mount_spec(&mtpair->mnt_fsname);
1813 }
1814 rc = singlemount(mtpair, 0);
1815 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00001816 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001817 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001818 }
1819
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001820 // Past this point, we are handling either "mount -a [opts]"
1821 // or "mount [opts] single_param"
1822
Denis Vlasenkob4133682008-02-18 13:05:38 +00001823 i = parse_mount_options(cmdopts, 0); // FIXME: should be "long", not "int"
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001824 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
1825 bb_error_msg_and_die(must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00001826
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001827 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001828 if (ENABLE_FEATURE_MOUNT_FLAGS
1829 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1830 ) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001831 rc = verbose_mount(/*source:*/ "", /*target:*/ argv[0],
1832 /*type:*/ "", /*flags:*/ i, /*data:*/ "");
1833 if (rc)
1834 bb_simple_perror_msg_and_die(argv[0]);
1835 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001836 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001837
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001838 // Open either fstab or mtab
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001839 fstabname = "/etc/fstab";
1840 if (i & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001841 // WARNING. I am not sure this matches util-linux's
1842 // behavior. It's possible util-linux does not
1843 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001844 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001845 }
1846 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001847 if (!fstab)
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001848 bb_perror_msg_and_die("cannot read %s", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001849
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001850 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00001851 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001852 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001853 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001854
1855 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00001856 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001857 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001858 GETMNTENT_BUFSIZE/2)
1859 ) { // End of fstab/mtab is reached
1860 mtcur = mtother; // the thing we found last time
1861 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001862 }
1863
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001864 // If we're trying to mount something specific and this isn't it,
1865 // skip it. Note we must match the exact text in fstab (ala
1866 // "proc") or a full path from root
1867 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001868
1869 // Is this what we're looking for?
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001870 if (strcmp(argv[0], mtcur->mnt_fsname) &&
1871 strcmp(storage_path, mtcur->mnt_fsname) &&
1872 strcmp(argv[0], mtcur->mnt_dir) &&
1873 strcmp(storage_path, mtcur->mnt_dir)) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001874
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001875 // Remember this entry. Something later may have
1876 // overmounted it, and we want the _last_ match.
1877 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001878
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001879 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001880 } else {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001881 // Do we need to match a filesystem type?
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001882 if (fstype && match_fstype(mtcur, fstype))
1883 continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001884
1885 // Skip noauto and swap anyway.
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001886 if (parse_mount_options(mtcur->mnt_opts, 0) & (MOUNT_NOAUTO | MOUNT_SWAP))
1887 continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001888
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001889 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001890 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001891 if (nonroot)
1892 bb_error_msg_and_die(must_be_root);
1893
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001894 // Mount this thing
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001895 if (ENABLE_FEATURE_MOUNT_LABEL)
Denis Vlasenkode7684a2008-02-18 21:08:49 +00001896 resolve_mount_spec(&mtpair->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001897
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001898 // NFS mounts want this to be xrealloc-able
1899 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001900 if (singlemount(mtcur, 1)) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001901 // Count number of failed mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001902 rc++;
1903 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00001904 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001905 }
1906 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001907
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001908 // End of fstab/mtab is reached.
1909 // Were we looking for something specific?
1910 if (argv[0]) {
1911 // If we didn't find anything, complain
1912 if (!mtcur->mnt_fsname)
1913 bb_error_msg_and_die("can't find %s in %s",
1914 argv[0], fstabname);
1915 if (nonroot) {
1916 // fstab must have "users" or "user"
1917 if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS))
1918 bb_error_msg_and_die(must_be_root);
1919 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001920
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001921 // Mount the last thing we found
1922 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
1923 append_mount_options(&(mtcur->mnt_opts), cmdopts);
1924 if (ENABLE_FEATURE_MOUNT_LABEL) {
1925 resolve_mount_spec(&mtpair->mnt_fsname);
1926 }
1927 rc = singlemount(mtcur, 0);
1928 if (ENABLE_FEATURE_CLEAN_UP)
1929 free(mtcur->mnt_opts);
1930 }
1931
1932 if (ENABLE_FEATURE_CLEAN_UP)
1933 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001934 if (ENABLE_FEATURE_CLEAN_UP) {
1935 free(storage_path);
1936 free(cmdopts);
1937 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001938 return rc;
1939}