blob: 722d0be921d84fef7590297b5f645b45afb5488d [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 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02009 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Erik Andersenb7cc49d2000-01-13 06:38:14 +000010 */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000011// Design notes: There is no spec for mount. Remind me to write one.
12//
13// mount_main() calls singlemount() which calls mount_it_now().
14//
15// mount_main() can loop through /etc/fstab for mount -a
16// singlemount() can loop through /etc/filesystems for fstype detection.
17// mount_it_now() does the actual mount.
18//
Eric Andersencc8ed391999-10-05 16:24:54 +000019#include <mntent.h>
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +000020#include <syslog.h>
Denys Vlasenkoda49f582009-07-08 02:58:38 +020021#include <sys/mount.h>
Denys Vlasenko102ff762009-11-21 17:14:08 +010022// Grab more as needed from util-linux's mount/mount_constants.h
23#ifndef MS_DIRSYNC
24# define MS_DIRSYNC (1 << 7) // Directory modifications are synchronous
25#endif
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +020026#ifndef MS_UNION
27# define MS_UNION (1 << 8)
28#endif
Denys Vlasenkoda49f582009-07-08 02:58:38 +020029#ifndef MS_BIND
30# define MS_BIND (1 << 12)
31#endif
32#ifndef MS_MOVE
33# define MS_MOVE (1 << 13)
34#endif
35#ifndef MS_RECURSIVE
36# define MS_RECURSIVE (1 << 14)
37#endif
38#ifndef MS_SILENT
39# define MS_SILENT (1 << 15)
40#endif
Denys Vlasenko102ff762009-11-21 17:14:08 +010041// The shared subtree stuff, which went in around 2.6.15
Denys Vlasenkoda49f582009-07-08 02:58:38 +020042#ifndef MS_UNBINDABLE
43# define MS_UNBINDABLE (1 << 17)
44#endif
45#ifndef MS_PRIVATE
46# define MS_PRIVATE (1 << 18)
47#endif
48#ifndef MS_SLAVE
49# define MS_SLAVE (1 << 19)
50#endif
51#ifndef MS_SHARED
52# define MS_SHARED (1 << 20)
53#endif
54#ifndef MS_RELATIME
55# define MS_RELATIME (1 << 21)
56#endif
Eric Andersenbd22ed82000-07-08 18:55:24 +000057
Denys Vlasenko102ff762009-11-21 17:14:08 +010058#include "libbb.h"
Denis Vlasenko6aa76962008-03-18 01:44:52 +000059#if ENABLE_FEATURE_MOUNT_LABEL
Natanael Copa9aff2992009-09-20 04:28:22 +020060# include "volume_id.h"
61#else
62# define resolve_mount_spec(fsname) ((void)0)
Denis Vlasenko6aa76962008-03-18 01:44:52 +000063#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +000064
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000065// Needed for nfs support only
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000066#include <sys/utsname.h>
67#undef TRUE
68#undef FALSE
Denys Vlasenkocc428142009-12-16 02:06:56 +010069#if ENABLE_FEATURE_MOUNT_NFS
70/* This is just a warning of a common mistake. Possibly this should be a
71 * uclibc faq entry rather than in busybox... */
72# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
73# error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
74# endif
75# include <rpc/rpc.h>
76# include <rpc/pmap_prot.h>
77# include <rpc/pmap_clnt.h>
78#endif
Denis Vlasenko30a64cd2006-09-15 15:12:00 +000079
80
Denis Vlasenko908d6b72006-12-18 23:07:42 +000081#if defined(__dietlibc__)
Denis Vlasenko30e5cf82008-12-05 16:40:36 +000082// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
83// dietlibc-0.30 does not have implementation of getmntent_r()
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +000084static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000085 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
Denis Vlasenko908d6b72006-12-18 23:07:42 +000086{
Denis Vlasenko908d6b72006-12-18 23:07:42 +000087 struct mntent* ment = getmntent(stream);
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +000088 return memcpy(result, ment, sizeof(*ment));
Denis Vlasenko908d6b72006-12-18 23:07:42 +000089}
90#endif
91
92
Rob Landleydc0955b2006-03-14 18:16:25 +000093// Not real flags, but we want to be able to check for this.
Denis Vlasenko13c5a682006-10-16 22:39:51 +000094enum {
Denis Vlasenkoa4522c52008-03-17 08:46:43 +000095 MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP,
96 MOUNT_NOAUTO = (1 << 29),
97 MOUNT_SWAP = (1 << 30),
Denis Vlasenko13c5a682006-10-16 22:39:51 +000098};
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +000099
100
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000101#define OPTION_STR "o:t:rwanfvsiO:"
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000102enum {
103 OPT_o = (1 << 0),
104 OPT_t = (1 << 1),
105 OPT_r = (1 << 2),
106 OPT_w = (1 << 3),
107 OPT_a = (1 << 4),
108 OPT_n = (1 << 5),
109 OPT_f = (1 << 6),
110 OPT_v = (1 << 7),
111 OPT_s = (1 << 8),
112 OPT_i = (1 << 9),
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000113 OPT_O = (1 << 10),
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000114};
115
116#if ENABLE_FEATURE_MTAB_SUPPORT
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200117#define USE_MTAB (!(option_mask32 & OPT_n))
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000118#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200119#define USE_MTAB 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000120#endif
121
122#if ENABLE_FEATURE_MOUNT_FAKE
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200123#define FAKE_IT (option_mask32 & OPT_f)
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000124#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200125#define FAKE_IT 0
126#endif
127
128#if ENABLE_FEATURE_MOUNT_HELPERS
129#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
130#else
131#define HELPERS_ALLOWED 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000132#endif
133
134
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000135// TODO: more "user" flag compatibility.
136// "user" option (from mount manpage):
137// Only the user that mounted a filesystem can unmount it again.
138// If any user should be able to unmount, then use users instead of user
139// in the fstab line. The owner option is similar to the user option,
140// with the restriction that the user must be the owner of the special file.
141// This may be useful e.g. for /dev/fd if a login script makes
142// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +0000143
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000144// Standard mount options (from -o options or --options),
145// with corresponding flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000146static const int32_t mount_options[] = {
Rob Landleye3781b72006-08-08 01:39:49 +0000147 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +0000148
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000149 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000150 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000151 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000152
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000153 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000154 /* "defaults" */ 0,
155 /* "quiet" 0 - do not filter out, vfat wants to see it */
156 /* "noauto" */ MOUNT_NOAUTO,
157 /* "sw" */ MOUNT_SWAP,
158 /* "swap" */ MOUNT_SWAP,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000159 IF_DESKTOP(/* "user" */ MOUNT_USERS,)
160 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000161 /* "_netdev" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000162 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000163
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000164 IF_FEATURE_MOUNT_FLAGS(
Rob Landleye3781b72006-08-08 01:39:49 +0000165 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000166 /* "nosuid" */ MS_NOSUID,
167 /* "suid" */ ~MS_NOSUID,
168 /* "dev" */ ~MS_NODEV,
169 /* "nodev" */ MS_NODEV,
170 /* "exec" */ ~MS_NOEXEC,
171 /* "noexec" */ MS_NOEXEC,
172 /* "sync" */ MS_SYNCHRONOUS,
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000173 /* "dirsync" */ MS_DIRSYNC,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000174 /* "async" */ ~MS_SYNCHRONOUS,
175 /* "atime" */ ~MS_NOATIME,
176 /* "noatime" */ MS_NOATIME,
177 /* "diratime" */ ~MS_NODIRATIME,
178 /* "nodiratime" */ MS_NODIRATIME,
Denis Vlasenko580ce2d2008-07-08 02:56:53 +0000179 /* "mand" */ MS_MANDLOCK,
180 /* "nomand" */ ~MS_MANDLOCK,
Bernhard Reutner-Fischerfb5902c2008-08-06 18:14:38 +0000181 /* "relatime" */ MS_RELATIME,
182 /* "norelatime" */ ~MS_RELATIME,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000183 /* "loud" */ ~MS_SILENT,
Roman Borisov19311bf2011-03-24 15:08:43 +0300184 /* "rbind" */ MS_BIND|MS_RECURSIVE,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000185
Rob Landleye3781b72006-08-08 01:39:49 +0000186 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200187 /* "union" */ MS_UNION,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000188 /* "bind" */ MS_BIND,
189 /* "move" */ MS_MOVE,
190 /* "shared" */ MS_SHARED,
191 /* "slave" */ MS_SLAVE,
192 /* "private" */ MS_PRIVATE,
193 /* "unbindable" */ MS_UNBINDABLE,
194 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
195 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
Roman Borisovd3679d22011-03-23 11:20:25 +0300196 /* "rprivate" */ MS_PRIVATE|MS_RECURSIVE,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000197 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000198 )
199
200 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000201 /* "ro" */ MS_RDONLY, // vfs flag
202 /* "rw" */ ~MS_RDONLY, // vfs flag
203 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000204};
205
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000206static const char mount_option_str[] =
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000207 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000208 "loop\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000209 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000210 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000211 "defaults\0"
212 // "quiet\0" - do not filter out, vfat wants to see it
213 "noauto\0"
214 "sw\0"
215 "swap\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000216 IF_DESKTOP("user\0")
217 IF_DESKTOP("users\0")
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000218 "_netdev\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000219 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000220 IF_FEATURE_MOUNT_FLAGS(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000221 // vfs flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000222 "nosuid\0"
223 "suid\0"
224 "dev\0"
225 "nodev\0"
226 "exec\0"
227 "noexec\0"
228 "sync\0"
229 "dirsync\0"
230 "async\0"
231 "atime\0"
232 "noatime\0"
233 "diratime\0"
234 "nodiratime\0"
235 "mand\0"
236 "nomand\0"
237 "relatime\0"
238 "norelatime\0"
239 "loud\0"
Roman Borisov19311bf2011-03-24 15:08:43 +0300240 "rbind\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000241
242 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200243 "union\0"
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000244 "bind\0"
245 "move\0"
Roman Borisov945fd172011-02-25 14:50:39 +0300246 "make-shared\0"
247 "make-slave\0"
248 "make-private\0"
249 "make-unbindable\0"
250 "make-rshared\0"
251 "make-rslave\0"
252 "make-rprivate\0"
253 "make-runbindable\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000254 )
255
256 // Always understood.
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000257 "ro\0" // vfs flag
258 "rw\0" // vfs flag
259 "remount\0" // action flag
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000260;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000261
Denis Vlasenkof732e962008-02-18 12:07:49 +0000262
263struct globals {
264#if ENABLE_FEATURE_MOUNT_NFS
265 smalluint nfs_mount_version;
266#endif
267#if ENABLE_FEATURE_MOUNT_VERBOSE
268 unsigned verbose;
269#endif
270 llist_t *fslist;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000271 char getmntent_buf[1];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100272} FIX_ALIASING;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000273enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denis Vlasenkof732e962008-02-18 12:07:49 +0000274#define G (*(struct globals*)&bb_common_bufsiz1)
275#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000276#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000277#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000278#else
279#define verbose 0
280#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000281#define fslist (G.fslist )
282#define getmntent_buf (G.getmntent_buf )
283
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100284#if ENABLE_FEATURE_MTAB_SUPPORT
285/*
286 * update_mtab_entry_on_move() is used to update entry in case of mount --move.
287 * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
288 * input mntent and replace it by new one.
289 */
290static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
291{
292 struct mntent *entries, *m;
293 int i, count;
294 FILE *mountTable;
295
296 mountTable = setmntent(bb_path_mtab_file, "r");
297 if (!mountTable) {
298 bb_perror_msg(bb_path_mtab_file);
299 return;
300 }
301
302 entries = NULL;
303 count = 0;
304 while ((m = getmntent(mountTable)) != NULL) {
305 entries = xrealloc_vector(entries, 3, count);
306 entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
307 entries[count].mnt_dir = xstrdup(m->mnt_dir);
308 entries[count].mnt_type = xstrdup(m->mnt_type);
309 entries[count].mnt_opts = xstrdup(m->mnt_opts);
310 entries[count].mnt_freq = m->mnt_freq;
311 entries[count].mnt_passno = m->mnt_passno;
312 count++;
313 }
314 endmntent(mountTable);
315
316 mountTable = setmntent(bb_path_mtab_file, "w");
317 if (mountTable) {
318 for (i = 0; i < count; i++) {
319 if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
320 addmntent(mountTable, &entries[i]);
321 else
322 addmntent(mountTable, mp);
323 }
324 endmntent(mountTable);
325 } else if (errno != EROFS)
326 bb_perror_msg(bb_path_mtab_file);
327
328 if (ENABLE_FEATURE_CLEAN_UP) {
329 for (i = 0; i < count; i++) {
330 free(entries[i].mnt_fsname);
331 free(entries[i].mnt_dir);
332 free(entries[i].mnt_type);
333 free(entries[i].mnt_opts);
334 }
335 free(entries);
336 }
337}
338#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000339
340#if ENABLE_FEATURE_MOUNT_VERBOSE
341static int verbose_mount(const char *source, const char *target,
342 const char *filesystemtype,
343 unsigned long mountflags, const void *data)
344{
345 int rc;
346
347 errno = 0;
348 rc = mount(source, target, filesystemtype, mountflags, data);
349 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000350 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000351 source, target, filesystemtype,
352 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000353 return rc;
354}
355#else
356#define verbose_mount(...) mount(__VA_ARGS__)
357#endif
358
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000359// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000360static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000361{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000362 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000363 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000364 while (newopts[0]) {
365 char *p;
366 int len = strlen(newopts);
367 p = strchr(newopts, ',');
368 if (p) len = p - newopts;
369 p = *oldopts;
370 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000371 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000372 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000373 goto skip;
374 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000375 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000376 p++;
377 }
378 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
379 free(*oldopts);
380 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000381 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000382 newopts += len;
383 while (newopts[0] == ',') newopts++;
384 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000385 } else {
386 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000387 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000388 }
389}
390
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000391// Use the mount_options list to parse options into flags.
Alexander Shishkin77650952010-10-28 06:10:03 +0200392// Also update list of unrecognized options if unrecognized != NULL
Denis Vlasenkob4133682008-02-18 13:05:38 +0000393static long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000394{
Denis Vlasenkob4133682008-02-18 13:05:38 +0000395 long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000396
Rob Landley6a6798b2005-08-10 20:35:54 +0000397 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000398 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000399 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000400 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000401 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000402
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000403 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000404
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000405// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000406 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000407 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Alexander Shishkin77650952010-10-28 06:10:03 +0200408 if (strcasecmp(option_str, options) == 0) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000409 long fl = mount_options[i];
Alexander Shishkin77650952010-10-28 06:10:03 +0200410 if (fl < 0)
411 flags &= fl;
412 else
413 flags |= fl;
414 goto found;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000415 }
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000416 option_str += strlen(option_str) + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000417 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200418 // We did not recognize this option.
419 // If "unrecognized" is not NULL, append option there.
420 // Note that we should not append *empty* option -
421 // in this case we want to pass NULL, not "", to "data"
422 // parameter of mount(2) syscall.
423 // This is crucial for filesystems that don't accept
424 // any arbitrary mount options, like cgroup fs:
425 // "mount -t cgroup none /mnt"
426 if (options[0] && unrecognized) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000427 // Add it to strflags, to pass on to kernel
Alexander Shishkin77650952010-10-28 06:10:03 +0200428 char *p = *unrecognized;
429 unsigned len = p ? strlen(p) : 0;
430 *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000431
Rob Landley6a6798b2005-08-10 20:35:54 +0000432 // Comma separated if it's not the first one
Alexander Shishkin77650952010-10-28 06:10:03 +0200433 if (len) p[len++] = ',';
434 strcpy(p + len, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000435 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200436 found:
Denis Vlasenko2535f122007-09-15 13:28:30 +0000437 if (!comma)
438 break;
439 // Advance to next option
440 *comma = ',';
441 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000442 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000443
Rob Landleydc0955b2006-03-14 18:16:25 +0000444 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000445}
446
Rob Landleydc0955b2006-03-14 18:16:25 +0000447// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000448static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000449{
Denis Vlasenko87468852007-04-13 23:22:00 +0000450 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000451 "/etc/filesystems",
452 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000453 };
454 char *fs, *buf;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200455 llist_t *list = NULL;
Rob Landleydc0955b2006-03-14 18:16:25 +0000456 int i;
457 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000458
Denis Vlasenko87468852007-04-13 23:22:00 +0000459 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000460 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000461 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000462
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000463 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200464 if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
Denis Vlasenko372686b2006-10-12 22:42:33 +0000465 continue;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000466 fs = skip_whitespace(buf);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200467 if (*fs == '#' || *fs == '*' || !*fs)
468 continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000469
Denis Vlasenko372686b2006-10-12 22:42:33 +0000470 llist_add_to_end(&list, xstrdup(fs));
471 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000472 }
473 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
474 }
475
476 return list;
477}
478
Rob Landleydc0955b2006-03-14 18:16:25 +0000479#if ENABLE_FEATURE_CLEAN_UP
480static void delete_block_backed_filesystems(void)
481{
Rob Landleya6b5b602006-05-08 19:03:07 +0000482 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000483}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000484#else
485void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000486#endif
487
Rob Landleydc0955b2006-03-14 18:16:25 +0000488// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000489// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000490static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000491{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000492 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000493
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200494 if (FAKE_IT) {
Denis Vlasenkob4133682008-02-18 13:05:38 +0000495 if (verbose >= 2)
496 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
497 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
498 vfsflags, filteropts);
499 goto mtab;
500 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000501
Rob Landleydc0955b2006-03-14 18:16:25 +0000502 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000503 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000504 errno = 0;
505 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000506 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000507
508 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000509 // helper program mount.<mnt_type>
Denys Vlasenkoba986032009-09-15 23:00:09 +0200510 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200511 char *args[8];
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000512 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000513 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000514 rc = 1;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200515 if (FAKE_IT)
516 args[rc++] = (char *)"-f";
517 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
518 args[rc++] = (char *)"-n";
Denis Vlasenko5c329932009-04-12 12:16:21 +0000519 args[rc++] = mp->mnt_fsname;
520 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000521 if (filteropts) {
522 args[rc++] = (char *)"-o";
523 args[rc++] = filteropts;
524 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000525 args[rc] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +0100526 rc = spawn_and_wait(args);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000527 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000528 if (!rc)
529 break;
530 errno = errno_save;
531 }
532
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000533 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000534 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000535 if (!(vfsflags & MS_SILENT))
536 bb_error_msg("%s is write-protected, mounting read-only",
537 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000538 vfsflags |= MS_RDONLY;
539 }
540
Rob Landleydc0955b2006-03-14 18:16:25 +0000541 // Abort entirely if permission denied.
542
543 if (rc && errno == EPERM)
544 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
545
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000546 // If the mount was successful, and we're maintaining an old-style
547 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000548 mtab:
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200549 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000550 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000551 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000552 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000553 int i;
554
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000555 if (!mountTable) {
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100556 bb_perror_msg(bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000557 goto ret;
558 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000559
560 // Add vfs string flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000561 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
562 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
563 append_mount_options(&(mp->mnt_opts), option_str);
564 option_str += strlen(option_str) + 1;
565 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000566
567 // Remove trailing / (if any) from directory we mounted on
Denis Vlasenko727ef942006-09-14 13:19:19 +0000568 i = strlen(mp->mnt_dir) - 1;
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100569 while (i > 0 && mp->mnt_dir[i] == '/')
Denys Vlasenkoc6450c92011-02-28 11:09:49 +0100570 mp->mnt_dir[i--] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000571
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000572 // Convert to canonical pathnames as needed
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000573 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100574 fsname = NULL;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000575 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000576 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000577 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000578 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000579 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000580
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100581 // Write and close
582#if ENABLE_FEATURE_MTAB_SUPPORT
583 if (vfsflags & MS_MOVE)
584 update_mtab_entry_on_move(mp);
585 else
586#endif
587 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000588 endmntent(mountTable);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100589
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000590 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000591 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000592 free(fsname);
593 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000594 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000595 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000596 return rc;
597}
598
Denis Vlasenko25098f72006-09-14 15:46:33 +0000599#if ENABLE_FEATURE_MOUNT_NFS
600
601/*
602 * Linux NFS mount
603 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
604 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +0200605 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenko25098f72006-09-14 15:46:33 +0000606 *
607 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
608 * numbers to be specified on the command line.
609 *
610 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
611 * Omit the call to connect() for Linux version 1.3.11 or later.
612 *
613 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
614 * Implemented the "bg", "fg" and "retry" mount options for NFS.
615 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000616 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000617 * - added Native Language Support
618 *
619 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
620 * plus NFSv3 stuff.
621 */
622
Denis Vlasenko25098f72006-09-14 15:46:33 +0000623#define MOUNTPORT 635
624#define MNTPATHLEN 1024
625#define MNTNAMLEN 255
626#define FHSIZE 32
627#define FHSIZE3 64
628
629typedef char fhandle[FHSIZE];
630
631typedef struct {
632 unsigned int fhandle3_len;
633 char *fhandle3_val;
634} fhandle3;
635
636enum mountstat3 {
637 MNT_OK = 0,
638 MNT3ERR_PERM = 1,
639 MNT3ERR_NOENT = 2,
640 MNT3ERR_IO = 5,
641 MNT3ERR_ACCES = 13,
642 MNT3ERR_NOTDIR = 20,
643 MNT3ERR_INVAL = 22,
644 MNT3ERR_NAMETOOLONG = 63,
645 MNT3ERR_NOTSUPP = 10004,
646 MNT3ERR_SERVERFAULT = 10006,
647};
648typedef enum mountstat3 mountstat3;
649
650struct fhstatus {
651 unsigned int fhs_status;
652 union {
653 fhandle fhs_fhandle;
654 } fhstatus_u;
655};
656typedef struct fhstatus fhstatus;
657
658struct mountres3_ok {
659 fhandle3 fhandle;
660 struct {
661 unsigned int auth_flavours_len;
662 char *auth_flavours_val;
663 } auth_flavours;
664};
665typedef struct mountres3_ok mountres3_ok;
666
667struct mountres3 {
668 mountstat3 fhs_status;
669 union {
670 mountres3_ok mountinfo;
671 } mountres3_u;
672};
673typedef struct mountres3 mountres3;
674
675typedef char *dirpath;
676
677typedef char *name;
678
679typedef struct mountbody *mountlist;
680
681struct mountbody {
682 name ml_hostname;
683 dirpath ml_directory;
684 mountlist ml_next;
685};
686typedef struct mountbody mountbody;
687
688typedef struct groupnode *groups;
689
690struct groupnode {
691 name gr_name;
692 groups gr_next;
693};
694typedef struct groupnode groupnode;
695
696typedef struct exportnode *exports;
697
698struct exportnode {
699 dirpath ex_dir;
700 groups ex_groups;
701 exports ex_next;
702};
703typedef struct exportnode exportnode;
704
705struct ppathcnf {
706 int pc_link_max;
707 short pc_max_canon;
708 short pc_max_input;
709 short pc_name_max;
710 short pc_path_max;
711 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000712 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000713 char pc_xxx;
714 short pc_mask[2];
715};
716typedef struct ppathcnf ppathcnf;
717
718#define MOUNTPROG 100005
719#define MOUNTVERS 1
720
721#define MOUNTPROC_NULL 0
722#define MOUNTPROC_MNT 1
723#define MOUNTPROC_DUMP 2
724#define MOUNTPROC_UMNT 3
725#define MOUNTPROC_UMNTALL 4
726#define MOUNTPROC_EXPORT 5
727#define MOUNTPROC_EXPORTALL 6
728
729#define MOUNTVERS_POSIX 2
730
731#define MOUNTPROC_PATHCONF 7
732
733#define MOUNT_V3 3
734
735#define MOUNTPROC3_NULL 0
736#define MOUNTPROC3_MNT 1
737#define MOUNTPROC3_DUMP 2
738#define MOUNTPROC3_UMNT 3
739#define MOUNTPROC3_UMNTALL 4
740#define MOUNTPROC3_EXPORT 5
741
742enum {
743#ifndef NFS_FHSIZE
744 NFS_FHSIZE = 32,
745#endif
746#ifndef NFS_PORT
747 NFS_PORT = 2049
748#endif
749};
750
Denis Vlasenko25098f72006-09-14 15:46:33 +0000751/*
752 * We want to be able to compile mount on old kernels in such a way
753 * that the binary will work well on more recent kernels.
754 * Thus, if necessary we teach nfsmount.c the structure of new fields
755 * that will come later.
756 *
757 * Moreover, the new kernel includes conflict with glibc includes
758 * so it is easiest to ignore the kernel altogether (at compile time).
759 */
760
761struct nfs2_fh {
762 char data[32];
763};
764struct nfs3_fh {
765 unsigned short size;
766 unsigned char data[64];
767};
768
769struct nfs_mount_data {
770 int version; /* 1 */
771 int fd; /* 1 */
772 struct nfs2_fh old_root; /* 1 */
773 int flags; /* 1 */
774 int rsize; /* 1 */
775 int wsize; /* 1 */
776 int timeo; /* 1 */
777 int retrans; /* 1 */
778 int acregmin; /* 1 */
779 int acregmax; /* 1 */
780 int acdirmin; /* 1 */
781 int acdirmax; /* 1 */
782 struct sockaddr_in addr; /* 1 */
783 char hostname[256]; /* 1 */
784 int namlen; /* 2 */
785 unsigned int bsize; /* 3 */
786 struct nfs3_fh root; /* 4 */
787};
788
789/* bits in the flags field */
790enum {
791 NFS_MOUNT_SOFT = 0x0001, /* 1 */
792 NFS_MOUNT_INTR = 0x0002, /* 1 */
793 NFS_MOUNT_SECURE = 0x0004, /* 1 */
794 NFS_MOUNT_POSIX = 0x0008, /* 1 */
795 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
796 NFS_MOUNT_NOAC = 0x0020, /* 1 */
797 NFS_MOUNT_TCP = 0x0040, /* 2 */
798 NFS_MOUNT_VER3 = 0x0080, /* 3 */
799 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000800 NFS_MOUNT_NONLM = 0x0200, /* 3 */
801 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +0000802};
803
804
805/*
806 * We need to translate between nfs status return values and
807 * the local errno values which may not be the same.
808 *
809 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
810 * "after #include <errno.h> the symbol errno is reserved for any use,
811 * it cannot even be used as a struct tag or field name".
812 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000813#ifndef EDQUOT
Denys Vlasenkocc428142009-12-16 02:06:56 +0100814# define EDQUOT ENOSPC
Denis Vlasenko25098f72006-09-14 15:46:33 +0000815#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000816/* Convert each NFSERR_BLAH into EBLAH */
Denys Vlasenkocc428142009-12-16 02:06:56 +0100817static const uint8_t nfs_err_stat[] = {
818 1, 2, 5, 6, 13, 17,
819 19, 20, 21, 22, 27, 28,
820 30, 63, 66, 69, 70, 71
821};
Denys Vlasenkodc1fd2e2010-05-19 17:01:29 +0200822#if ( \
823 EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \
824 ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \
825 EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
826typedef uint8_t nfs_err_type;
827#else
828typedef uint16_t nfs_err_type;
829#endif
830static const nfs_err_type nfs_err_errnum[] = {
Denys Vlasenkocc428142009-12-16 02:06:56 +0100831 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
832 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
833 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
Denis Vlasenko25098f72006-09-14 15:46:33 +0000834};
Denis Vlasenko25098f72006-09-14 15:46:33 +0000835static char *nfs_strerror(int status)
836{
837 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000838
Denys Vlasenkocc428142009-12-16 02:06:56 +0100839 for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
840 if (nfs_err_stat[i] == status)
841 return strerror(nfs_err_errnum[i]);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000842 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000843 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000844}
845
846static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
847{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200848 return xdr_opaque(xdrs, objp, FHSIZE);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000849}
850
851static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
852{
853 if (!xdr_u_int(xdrs, &objp->fhs_status))
854 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200855 if (objp->fhs_status == 0)
856 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000857 return TRUE;
858}
859
860static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
861{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200862 return xdr_string(xdrs, objp, MNTPATHLEN);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000863}
864
865static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
866{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200867 return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
868 (unsigned int *) &objp->fhandle3_len,
869 FHSIZE3);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000870}
871
872static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
873{
874 if (!xdr_fhandle3(xdrs, &objp->fhandle))
875 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200876 return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
877 &(objp->auth_flavours.auth_flavours_len),
878 ~0,
879 sizeof(int),
880 (xdrproc_t) xdr_int);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000881}
882
883static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
884{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200885 return xdr_enum(xdrs, (enum_t *) objp);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000886}
887
888static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
889{
890 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
891 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200892 if (objp->fhs_status == MNT_OK)
893 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000894 return TRUE;
895}
896
897#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
898
Denis Vlasenko25098f72006-09-14 15:46:33 +0000899/*
900 * Unfortunately, the kernel prints annoying console messages
901 * in case of an unexpected nfs mount version (instead of
902 * just returning some error). Therefore we'll have to try
903 * and figure out what version the kernel expects.
904 *
905 * Variables:
906 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
907 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
908 * nfs_mount_version: version this source and running kernel can handle
909 */
910static void
911find_kernel_nfs_mount_version(void)
912{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000913 int kernel_version;
914
915 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000916 return;
917
918 nfs_mount_version = 4; /* default */
919
920 kernel_version = get_linux_version_code();
921 if (kernel_version) {
Denys Vlasenkocc428142009-12-16 02:06:56 +0100922 if (kernel_version < KERNEL_VERSION(2,2,18))
Denis Vlasenko25098f72006-09-14 15:46:33 +0000923 nfs_mount_version = 3;
924 /* else v4 since 2.3.99pre4 */
925 }
926}
927
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +0000928static void
Denis Vlasenkob9256052007-09-28 10:29:17 +0000929get_mountport(struct pmap *pm_mnt,
930 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +0000931 long unsigned prog,
932 long unsigned version,
933 long unsigned proto,
934 long unsigned port)
935{
936 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000937
938 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +0000939/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
940 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000941 pmap = pmap_getmaps(server_addr);
942
943 if (version > MAX_NFSPROT)
944 version = MAX_NFSPROT;
945 if (!prog)
946 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000947 pm_mnt->pm_prog = prog;
948 pm_mnt->pm_vers = version;
949 pm_mnt->pm_prot = proto;
950 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000951
Denis Vlasenko25098f72006-09-14 15:46:33 +0000952 while (pmap) {
953 if (pmap->pml_map.pm_prog != prog)
954 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000955 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000956 goto next;
957 if (version > 2 && pmap->pml_map.pm_vers != version)
958 goto next;
959 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
960 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +0100961 if (pmap->pml_map.pm_vers > MAX_NFSPROT
962 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
963 || (port && pmap->pml_map.pm_port != port)
964 ) {
Denis Vlasenko25098f72006-09-14 15:46:33 +0000965 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +0100966 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000967 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
968 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +0000969 pmap = pmap->pml_next;
970 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000971 if (!pm_mnt->pm_vers)
972 pm_mnt->pm_vers = MOUNTVERS;
973 if (!pm_mnt->pm_port)
974 pm_mnt->pm_port = MOUNTPORT;
975 if (!pm_mnt->pm_prot)
976 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000977}
978
Denis Vlasenkof0000652007-09-04 18:30:26 +0000979#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +0000980static int daemonize(void)
981{
Denis Vlasenko25098f72006-09-14 15:46:33 +0000982 int pid = fork();
983 if (pid < 0) /* error */
984 return -errno;
985 if (pid > 0) /* parent */
986 return 0;
987 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000988 close(0);
989 xopen(bb_dev_null, O_RDWR);
990 xdup2(0, 1);
991 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000992 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +0000993 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000994 logmode = LOGMODE_SYSLOG;
995 return 1;
996}
Denis Vlasenkof0000652007-09-04 18:30:26 +0000997#else
998static inline int daemonize(void) { return -ENOSYS; }
999#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001000
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001001/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001002static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001003{
1004 return 0;
1005}
1006
1007/* RPC strerror analogs are terminally idiotic:
1008 * *mandatory* prefix and \n at end.
1009 * This hopefully helps. Usage:
1010 * error_msg_rpc(clnt_*error*(" ")) */
1011static void error_msg_rpc(const char *msg)
1012{
Denis Vlasenko23514fe2006-09-19 14:07:52 +00001013 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001014 while (msg[0] == ' ' || msg[0] == ':') msg++;
1015 len = strlen(msg);
1016 while (len && msg[len-1] == '\n') len--;
1017 bb_error_msg("%.*s", len, msg);
1018}
1019
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001020/* NB: mp->xxx fields may be trashed on exit */
Denys Vlasenko810b7162009-05-13 23:48:59 +02001021static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001022{
1023 CLIENT *mclient;
1024 char *hostname;
1025 char *pathname;
1026 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001027 /* prior to 2.6.23, kernel took NFS options in a form of this struct
1028 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1029 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001030 struct nfs_mount_data data;
1031 char *opt;
1032 struct hostent *hp;
1033 struct sockaddr_in server_addr;
1034 struct sockaddr_in mount_server_addr;
1035 int msock, fsock;
1036 union {
1037 struct fhstatus nfsv2;
1038 struct mountres3 nfsv3;
1039 } status;
1040 int daemonized;
1041 char *s;
1042 int port;
1043 int mountport;
1044 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001045#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001046 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001047#else
1048 enum { bg = 0 };
1049#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001050 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001051 int mountprog;
1052 int mountvers;
1053 int nfsprog;
1054 int nfsvers;
1055 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001056 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001057 smallint tcp;
1058 smallint soft;
1059 int intr;
1060 int posix;
1061 int nocto;
1062 int noac;
1063 int nordirplus;
1064 int nolock;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001065
1066 find_kernel_nfs_mount_version();
1067
1068 daemonized = 0;
1069 mounthost = NULL;
1070 retval = ETIMEDOUT;
1071 msock = fsock = -1;
1072 mclient = NULL;
1073
1074 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1075
1076 filteropts = xstrdup(filteropts); /* going to trash it later... */
1077
1078 hostname = xstrdup(mp->mnt_fsname);
1079 /* mount_main() guarantees that ':' is there */
1080 s = strchr(hostname, ':');
1081 pathname = s + 1;
1082 *s = '\0';
1083 /* Ignore all but first hostname in replicated mounts
1084 until they can be fully supported. (mack@sgi.com) */
1085 s = strchr(hostname, ',');
1086 if (s) {
1087 *s = '\0';
1088 bb_error_msg("warning: multiple hostnames not supported");
1089 }
1090
1091 server_addr.sin_family = AF_INET;
1092 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1093 hp = gethostbyname(hostname);
1094 if (hp == NULL) {
1095 bb_herror_msg("%s", hostname);
1096 goto fail;
1097 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001098 if (hp->h_length != (int)sizeof(struct in_addr)) {
1099 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001100 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001101 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001102 }
1103
1104 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1105
1106 /* add IP address to mtab options for use when unmounting */
1107
1108 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1109 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1110 } else {
1111 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1112 mp->mnt_opts[0] ? "," : "",
1113 inet_ntoa(server_addr.sin_addr));
1114 free(mp->mnt_opts);
1115 mp->mnt_opts = tmp;
1116 }
1117
1118 /* Set default options.
1119 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1120 * let the kernel decide.
1121 * timeo is filled in after we know whether it'll be TCP or UDP. */
1122 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001123 data.retrans = 3;
1124 data.acregmin = 3;
1125 data.acregmax = 60;
1126 data.acdirmin = 30;
1127 data.acdirmax = 60;
1128 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001129
Denis Vlasenko25098f72006-09-14 15:46:33 +00001130 soft = 0;
1131 intr = 0;
1132 posix = 0;
1133 nocto = 0;
1134 nolock = 0;
1135 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001136 nordirplus = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001137 retry = 10000; /* 10000 minutes ~ 1 week */
1138 tcp = 0;
1139
1140 mountprog = MOUNTPROG;
1141 mountvers = 0;
1142 port = 0;
1143 mountport = 0;
1144 nfsprog = 100003;
1145 nfsvers = 0;
1146
1147 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001148 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001149 char *opteq = strchr(opt, '=');
1150 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001151 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001152 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001153 /* 0 */ "rsize\0"
1154 /* 1 */ "wsize\0"
1155 /* 2 */ "timeo\0"
1156 /* 3 */ "retrans\0"
1157 /* 4 */ "acregmin\0"
1158 /* 5 */ "acregmax\0"
1159 /* 6 */ "acdirmin\0"
1160 /* 7 */ "acdirmax\0"
1161 /* 8 */ "actimeo\0"
1162 /* 9 */ "retry\0"
1163 /* 10 */ "port\0"
1164 /* 11 */ "mountport\0"
1165 /* 12 */ "mounthost\0"
1166 /* 13 */ "mountprog\0"
1167 /* 14 */ "mountvers\0"
1168 /* 15 */ "nfsprog\0"
1169 /* 16 */ "nfsvers\0"
1170 /* 17 */ "vers\0"
1171 /* 18 */ "proto\0"
1172 /* 19 */ "namlen\0"
1173 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001174
1175 *opteq++ = '\0';
1176 idx = index_in_strings(options, opt);
1177 switch (idx) {
1178 case 12: // "mounthost"
1179 mounthost = xstrndup(opteq,
1180 strcspn(opteq, " \t\n\r,"));
1181 continue;
1182 case 18: // "proto"
1183 if (!strncmp(opteq, "tcp", 3))
1184 tcp = 1;
1185 else if (!strncmp(opteq, "udp", 3))
1186 tcp = 0;
1187 else
1188 bb_error_msg("warning: unrecognized proto= option");
1189 continue;
1190 case 20: // "addr" - ignore
1191 continue;
Peter Korsgaard301fe502011-02-21 17:52:13 +01001192 case -1: // unknown
1193 if (vfsflags & MS_REMOUNT)
1194 continue;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001195 }
1196
Denys Vlasenko77832482010-08-12 14:14:45 +02001197 val = xatoi_positive(opteq);
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001198 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001199 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001200 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001201 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001202 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001203 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001204 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001205 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001206 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001207 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001208 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001209 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001210 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001211 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001212 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001213 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001214 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001215 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001216 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001217 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001218 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001219 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001220 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001221 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001222 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001223 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001224 data.acregmin = val;
1225 data.acregmax = val;
1226 data.acdirmin = val;
1227 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001228 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001229 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001230 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001231 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001232 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001233 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001234 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001235 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001236 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001237 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001238 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001239 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001240 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001241 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001242 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001243 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001244 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001245 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001246 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001247 case 16: // "nfsvers"
1248 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001249 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001250 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001251 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001252 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001253 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001254 //else
1255 // bb_error_msg("warning: option namlen is not supported\n");
1256 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001257 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001258 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1259 goto fail;
1260 }
1261 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001262 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001263 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001264 "bg\0"
1265 "fg\0"
1266 "soft\0"
1267 "hard\0"
1268 "intr\0"
1269 "posix\0"
1270 "cto\0"
1271 "ac\0"
1272 "tcp\0"
1273 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001274 "lock\0"
1275 "rdirplus\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001276 int val = 1;
1277 if (!strncmp(opt, "no", 2)) {
1278 val = 0;
1279 opt += 2;
1280 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001281 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001282 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001283#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001284 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001285#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001286 break;
1287 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001288#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001289 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001290#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001291 break;
1292 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001293 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001294 break;
1295 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001296 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001297 break;
1298 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001299 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001300 break;
1301 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001302 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001303 break;
1304 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001305 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001306 break;
1307 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001308 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001309 break;
1310 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001311 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001312 break;
1313 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001314 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001315 break;
1316 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001317 if (nfs_mount_version >= 3)
1318 nolock = !val;
1319 else
1320 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001321 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001322 case 11: //rdirplus
1323 nordirplus = !val;
1324 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001325 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001326 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1327 goto fail;
1328 }
1329 }
1330 }
1331 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1332
1333 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1334 | (intr ? NFS_MOUNT_INTR : 0)
1335 | (posix ? NFS_MOUNT_POSIX : 0)
1336 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001337 | (noac ? NFS_MOUNT_NOAC : 0)
1338 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001339 if (nfs_mount_version >= 2)
1340 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1341 if (nfs_mount_version >= 3)
1342 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1343 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1344 bb_error_msg("NFSv%d not supported", nfsvers);
1345 goto fail;
1346 }
1347 if (nfsvers && !mountvers)
1348 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1349 if (nfsvers && nfsvers < mountvers) {
1350 mountvers = nfsvers;
1351 }
1352
1353 /* Adjust options if none specified */
1354 if (!data.timeo)
1355 data.timeo = tcp ? 70 : 7;
1356
Denis Vlasenko25098f72006-09-14 15:46:33 +00001357 data.version = nfs_mount_version;
1358
1359 if (vfsflags & MS_REMOUNT)
1360 goto do_mount;
1361
1362 /*
1363 * If the previous mount operation on the same host was
1364 * backgrounded, and the "bg" for this mount is also set,
1365 * give up immediately, to avoid the initial timeout.
1366 */
1367 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001368 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001369 if (daemonized <= 0) { /* parent or error */
1370 retval = -daemonized;
1371 goto ret;
1372 }
1373 }
1374
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001375 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001376 /* See if the nfs host = mount host. */
1377 if (mounthost) {
1378 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1379 mount_server_addr.sin_family = AF_INET;
1380 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1381 } else {
1382 hp = gethostbyname(mounthost);
1383 if (hp == NULL) {
1384 bb_herror_msg("%s", mounthost);
1385 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001386 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001387 if (hp->h_length != (int)sizeof(struct in_addr)) {
1388 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001389 }
1390 mount_server_addr.sin_family = AF_INET;
Denys Vlasenko99069332010-02-27 19:38:19 +01001391 memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001392 }
1393 }
1394
1395 /*
1396 * The following loop implements the mount retries. When the mount
1397 * times out, and the "bg" option is set, we background ourself
1398 * and continue trying.
1399 *
1400 * The case where the mount point is not present and the "bg"
1401 * option is set, is treated as a timeout. This is done to
1402 * support nested mounts.
1403 *
1404 * The "retry" count specified by the user is the number of
1405 * minutes to retry before giving up.
1406 */
1407 {
1408 struct timeval total_timeout;
1409 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001410 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001411 time_t t;
1412 time_t prevt;
1413 time_t timeout;
1414
1415 retry_timeout.tv_sec = 3;
1416 retry_timeout.tv_usec = 0;
1417 total_timeout.tv_sec = 20;
1418 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001419/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001420 timeout = time(NULL) + 60 * retry;
1421 prevt = 0;
1422 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001423 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001424 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001425 if (t - prevt < 30)
1426 sleep(30);
1427
Denis Vlasenkob9256052007-09-28 10:29:17 +00001428 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001429 mountprog,
1430 mountvers,
1431 proto,
1432 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001433 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001434
1435 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001436 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001437 msock = RPC_ANYSOCK;
1438
Denis Vlasenkob9256052007-09-28 10:29:17 +00001439 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001440 case IPPROTO_UDP:
1441 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001442 pm_mnt.pm_prog,
1443 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001444 retry_timeout,
1445 &msock);
1446 if (mclient)
1447 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001448 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001449 msock = RPC_ANYSOCK;
1450 case IPPROTO_TCP:
1451 mclient = clnttcp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001452 pm_mnt.pm_prog,
1453 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001454 &msock, 0, 0);
1455 break;
1456 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001457 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001458 }
1459 if (!mclient) {
1460 if (!daemonized && prevt == 0)
1461 error_msg_rpc(clnt_spcreateerror(" "));
1462 } else {
1463 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001464
1465 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001466 mclient->cl_auth = authunix_create_default();
1467
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001468 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001469 * that xdr_array allocates memory for us
1470 */
1471 memset(&status, 0, sizeof(status));
1472
Denis Vlasenkob9256052007-09-28 10:29:17 +00001473 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001474 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1475 (xdrproc_t) xdr_dirpath,
1476 (caddr_t) &pathname,
1477 (xdrproc_t) xdr_mountres3,
1478 (caddr_t) &status,
1479 total_timeout);
1480 else
1481 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1482 (xdrproc_t) xdr_dirpath,
1483 (caddr_t) &pathname,
1484 (xdrproc_t) xdr_fhstatus,
1485 (caddr_t) &status,
1486 total_timeout);
1487
1488 if (clnt_stat == RPC_SUCCESS)
1489 goto prepare_kernel_data; /* we're done */
1490 if (errno != ECONNREFUSED) {
1491 error_msg_rpc(clnt_sperror(mclient, " "));
1492 goto fail; /* don't retry */
1493 }
1494 /* Connection refused */
1495 if (!daemonized && prevt == 0) /* print just once */
1496 error_msg_rpc(clnt_sperror(mclient, " "));
1497 auth_destroy(mclient->cl_auth);
1498 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001499 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001500 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001501 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001502 }
1503
1504 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001505 if (!bg)
1506 goto fail;
1507 if (!daemonized) {
1508 daemonized = daemonize();
1509 if (daemonized <= 0) { /* parent or error */
1510 retval = -daemonized;
1511 goto ret;
1512 }
1513 }
1514 prevt = t;
1515 t = time(NULL);
1516 if (t >= timeout)
1517 /* TODO error message */
1518 goto fail;
1519
1520 goto retry;
1521 }
1522
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001523 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001524
1525 if (nfsvers == 2) {
1526 if (status.nfsv2.fhs_status != 0) {
1527 bb_error_msg("%s:%s failed, reason given by server: %s",
1528 hostname, pathname,
1529 nfs_strerror(status.nfsv2.fhs_status));
1530 goto fail;
1531 }
1532 memcpy(data.root.data,
1533 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1534 NFS_FHSIZE);
1535 data.root.size = NFS_FHSIZE;
1536 memcpy(data.old_root.data,
1537 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1538 NFS_FHSIZE);
1539 } else {
1540 fhandle3 *my_fhandle;
1541 if (status.nfsv3.fhs_status != 0) {
1542 bb_error_msg("%s:%s failed, reason given by server: %s",
1543 hostname, pathname,
1544 nfs_strerror(status.nfsv3.fhs_status));
1545 goto fail;
1546 }
1547 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1548 memset(data.old_root.data, 0, NFS_FHSIZE);
1549 memset(&data.root, 0, sizeof(data.root));
1550 data.root.size = my_fhandle->fhandle3_len;
1551 memcpy(data.root.data,
1552 (char *) my_fhandle->fhandle3_val,
1553 my_fhandle->fhandle3_len);
1554
1555 data.flags |= NFS_MOUNT_VER3;
1556 }
1557
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001558 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001559 if (tcp) {
1560 if (nfs_mount_version < 3) {
1561 bb_error_msg("NFS over TCP is not supported");
1562 goto fail;
1563 }
1564 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1565 } else
1566 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1567 if (fsock < 0) {
1568 bb_perror_msg("nfs socket");
1569 goto fail;
1570 }
1571 if (bindresvport(fsock, 0) < 0) {
1572 bb_perror_msg("nfs bindresvport");
1573 goto fail;
1574 }
1575 if (port == 0) {
1576 server_addr.sin_port = PMAPPORT;
1577 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1578 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1579 if (port == 0)
1580 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001581 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001582 server_addr.sin_port = htons(port);
1583
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001584 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001585 data.fd = fsock;
1586 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1587 strncpy(data.hostname, hostname, sizeof(data.hostname));
1588
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001589 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001590 auth_destroy(mclient->cl_auth);
1591 clnt_destroy(mclient);
1592 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001593 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001594
1595 if (bg) {
1596 /* We must wait until mount directory is available */
1597 struct stat statbuf;
1598 int delay = 1;
1599 while (stat(mp->mnt_dir, &statbuf) == -1) {
1600 if (!daemonized) {
1601 daemonized = daemonize();
1602 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001603/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001604 retval = -daemonized;
1605 goto ret;
1606 }
1607 }
1608 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1609 delay *= 2;
1610 if (delay > 30)
1611 delay = 30;
1612 }
1613 }
1614
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001615 /* Perform actual mount */
1616 do_mount:
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001617 mp->mnt_type = (char*)"nfs";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001618 retval = mount_it_now(mp, vfsflags, (char*)&data);
1619 goto ret;
1620
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001621 /* Abort */
1622 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001623 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001624 if (mclient) {
1625 auth_destroy(mclient->cl_auth);
1626 clnt_destroy(mclient);
1627 }
1628 close(msock);
1629 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001630 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001631 close(fsock);
1632
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001633 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001634 free(hostname);
1635 free(mounthost);
1636 free(filteropts);
1637 return retval;
1638}
1639
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001640#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001641
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001642// Never called. Call should be optimized out.
Denis Vlasenkob4133682008-02-18 13:05:38 +00001643int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001644
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001645#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001646
1647// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1648// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001649// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001650static int singlemount(struct mntent *mp, int ignore_busy)
1651{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001652 int rc = -1;
1653 long vfsflags;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001654 char *loopFile = NULL, *filteropts = NULL;
1655 llist_t *fl = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001656 struct stat st;
1657
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001658 errno = 0;
1659
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001660 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1661
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001662 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001663 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1664 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001665
Denis Vlasenko2535f122007-09-15 13:28:30 +00001666 // Might this be a virtual filesystem?
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001667 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1668 char *args[35];
1669 char *s;
1670 int n;
1671 // fsname: "cmd#arg1#arg2..."
1672 // WARNING: allows execution of arbitrary commands!
1673 // Try "mount 'sh#-c#sh' bogus_dir".
1674 // It is safe ONLY because non-root
1675 // cannot use two-argument mount command
1676 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1677 // "mount: can't find sh#-c#sh in /etc/fstab"
1678 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1679
1680 s = mp->mnt_fsname;
1681 n = 0;
1682 args[n++] = s;
1683 while (*s && n < 35 - 2) {
1684 if (*s++ == '#' && *s != '#') {
1685 s[-1] = '\0';
1686 args[n++] = s;
Denis Vlasenko2535f122007-09-15 13:28:30 +00001687 }
1688 }
Denis Vlasenko2535f122007-09-15 13:28:30 +00001689 args[n++] = mp->mnt_dir;
1690 args[n] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +01001691 rc = spawn_and_wait(args);
Denis Vlasenko2535f122007-09-15 13:28:30 +00001692 goto report_error;
1693 }
1694
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001695 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001696 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001697 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1698 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1699 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001700 ) {
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001701 int len;
1702 char c;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001703 len_and_sockaddr *lsa;
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001704 char *hostname, *dotted, *ip;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001705
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001706 hostname = mp->mnt_fsname + 2;
1707 len = strcspn(hostname, "/\\");
1708 if (len == 0 || hostname[len] == '\0')
Denis Vlasenko5c329932009-04-12 12:16:21 +00001709 goto report_error;
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001710 c = hostname[len];
1711 hostname[len] = '\0';
1712 lsa = host2sockaddr(hostname, 0);
1713 hostname[len] = c;
Denis Vlasenko5c329932009-04-12 12:16:21 +00001714 if (!lsa)
1715 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001716
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001717 // Insert "ip=..." option into options
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001718 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001719 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001720 ip = xasprintf("ip=%s", dotted);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001721 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001722 parse_mount_options(ip, &filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001723 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001724
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001725 // "-o mand" is required [why?]
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001726 vfsflags |= MS_MANDLOCK;
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001727 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001728 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001729
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001730 goto report_error;
1731 }
1732
1733 // Might this be an NFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001734 if (ENABLE_FEATURE_MOUNT_NFS
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001735 && (!mp->mnt_type || strcmp(mp->mnt_type, "nfs") == 0)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001736 && strchr(mp->mnt_fsname, ':') != NULL
1737 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001738 rc = nfsmount(mp, vfsflags, filteropts);
1739 goto report_error;
1740 }
1741
1742 // Look at the file. (Not found isn't a failure for remount, or for
1743 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001744 // (We use stat, not lstat, in order to allow
1745 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001746 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001747 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1748 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001749 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001750 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1751 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001752 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001753 if (set_loop(&mp->mnt_fsname, loopFile, 0) < 0) {
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001754 if (errno == EPERM || errno == EACCES)
1755 bb_error_msg(bb_msg_perm_denied_are_you_root);
1756 else
Denys Vlasenko6331cf02009-11-13 09:08:27 +01001757 bb_perror_msg("can't setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001758 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001759 }
1760
1761 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001762 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1763 vfsflags |= MS_BIND;
1764 }
1765
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001766 // If we know the fstype (or don't need to), jump straight
1767 // to the actual mount.
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001768 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001769 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001770 } else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001771 // Loop through filesystem types until mount succeeds
1772 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001773
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001774 // Initialize list of block backed filesystems.
1775 // This has to be done here so that during "mount -a",
1776 // mounts after /proc shows up can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001777 if (!fslist) {
1778 fslist = get_block_backed_filesystems();
1779 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1780 atexit(delete_block_backed_filesystems);
1781 }
1782
1783 for (fl = fslist; fl; fl = fl->link) {
1784 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001785 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001786 if (!rc)
1787 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001788 }
1789 }
1790
1791 // If mount failed, clean up loop file (if any).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001792 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1793 del_loop(mp->mnt_fsname);
1794 if (ENABLE_FEATURE_CLEAN_UP) {
1795 free(loopFile);
1796 free(mp->mnt_fsname);
1797 }
1798 }
1799
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001800 report_error:
1801 if (ENABLE_FEATURE_CLEAN_UP)
1802 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001803
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001804 if (errno == EBUSY && ignore_busy)
1805 return 0;
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001806 if (rc != 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001807 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001808 return rc;
1809}
1810
Michael Abbott6b5accb2009-12-04 03:33:07 +01001811// -O support
1812// -O interprets a list of filter options which select whether a mount
1813// point will be mounted: only mounts with options matching *all* filtering
1814// options will be selected.
1815// By default each -O filter option must be present in the list of mount
1816// options, but if it is prefixed by "no" then it must be absent.
1817// For example,
1818// -O a,nob,c matches -o a,c but fails to match -o a,b,c
1819// (and also fails to match -o a because -o c is absent).
1820//
1821// It is different from -t in that each option is matched exactly; a leading
1822// "no" at the beginning of one option does not negate the rest.
1823static int match_opt(const char *fs_opt_in, const char *O_opt)
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001824{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001825 if (!O_opt)
Michael Abbott6b5accb2009-12-04 03:33:07 +01001826 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001827
Michael Abbott6b5accb2009-12-04 03:33:07 +01001828 while (*O_opt) {
1829 const char *fs_opt = fs_opt_in;
1830 int O_len;
1831 int match;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001832
Michael Abbott6b5accb2009-12-04 03:33:07 +01001833 // If option begins with "no" then treat as an inverted match:
1834 // matching is a failure
1835 match = 0;
1836 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1837 match = 1;
1838 O_opt += 2;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001839 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01001840 // Isolate the current O option
1841 O_len = strchrnul(O_opt, ',') - O_opt;
1842 // Check for a match against existing options
1843 while (1) {
1844 if (strncmp(fs_opt, O_opt, O_len) == 0
1845 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
1846 ) {
1847 if (match)
1848 return 0; // "no" prefix, but option found
1849 match = 1; // current O option found, go check next one
1850 break;
1851 }
1852 fs_opt = strchr(fs_opt, ',');
1853 if (!fs_opt)
1854 break;
1855 fs_opt++;
1856 }
1857 if (match == 0)
1858 return 0; // match wanted but not found
1859 if (O_opt[O_len] == '\0') // end?
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001860 break;
Michael Abbott6b5accb2009-12-04 03:33:07 +01001861 // Step to the next O option
1862 O_opt += O_len + 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001863 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01001864 // If we get here then everything matched
1865 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001866}
1867
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001868// Parse options, if necessary parse fstab/mtab, and call singlemount for
1869// each directory to be mounted.
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001870int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001871int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001872{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001873 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001874 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001875 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001876 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001877 llist_t *lst_o = NULL;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001878 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001879 FILE *fstab;
Denys Vlasenkoa7329662009-12-05 04:25:19 +01001880 int i, j;
1881 int rc = EXIT_SUCCESS;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001882 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001883 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001884 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001885
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001886 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00001887
Denis Vlasenkof732e962008-02-18 12:07:49 +00001888 // Parse long options, like --bind and --move. Note that -o option
1889 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001890 for (i = j = 1; argv[i]; i++) {
1891 if (argv[i][0] == '-' && argv[i][1] == '-')
1892 append_mount_options(&cmdopts, argv[i] + 2);
1893 else
1894 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001895 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001896 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001897
1898 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001899 // Max 2 params; -o is a list, -v is a counter
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001900 opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001901 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001902 IF_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001903 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00001904 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1905 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001906 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001907
1908 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001909 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00001910 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001911 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1912
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001913 if (!mountTable)
1914 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001915
Denis Vlasenko2535f122007-09-15 13:28:30 +00001916 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001917 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001918 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001919 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00001920 // util-linux 2.12a happily shows rootfs...
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001921 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001922
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001923 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001924 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1925 mtpair->mnt_dir, mtpair->mnt_type,
1926 mtpair->mnt_opts);
1927 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001928 if (ENABLE_FEATURE_CLEAN_UP)
1929 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001930 return EXIT_SUCCESS;
1931 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001932 storage_path = NULL;
1933 } else {
1934 // When we have two arguments, the second is the directory and we can
1935 // skip looking at fstab entirely. We can always abspath() the directory
1936 // argument when we get it.
1937 if (argv[1]) {
1938 if (nonroot)
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01001939 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001940 mtpair->mnt_fsname = argv[0];
1941 mtpair->mnt_dir = argv[1];
1942 mtpair->mnt_type = fstype;
1943 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00001944 resolve_mount_spec(&mtpair->mnt_fsname);
Denys Vlasenkoa7329662009-12-05 04:25:19 +01001945 rc = singlemount(mtpair, /*ignore_busy:*/ 0);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001946 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00001947 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001948 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001949 }
1950
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001951 // Past this point, we are handling either "mount -a [opts]"
1952 // or "mount [opts] single_param"
1953
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001954 i = parse_mount_options(cmdopts, NULL); // FIXME: should be "long", not "int"
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001955 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01001956 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00001957
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001958 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001959 if (ENABLE_FEATURE_MOUNT_FLAGS
1960 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
1961 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001962 // verbose_mount(source, target, type, flags, data)
1963 rc = verbose_mount("", argv[0], "", i, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001964 if (rc)
1965 bb_simple_perror_msg_and_die(argv[0]);
1966 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001967 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001968
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001969 // Open either fstab or mtab
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001970 fstabname = "/etc/fstab";
1971 if (i & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001972 // WARNING. I am not sure this matches util-linux's
1973 // behavior. It's possible util-linux does not
1974 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001975 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001976 }
1977 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00001978 if (!fstab)
Denys Vlasenko651a2692010-03-23 16:25:17 +01001979 bb_perror_msg_and_die("can't read '%s'", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001980
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001981 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00001982 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001983 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001984 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001985
1986 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00001987 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001988 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001989 GETMNTENT_BUFSIZE/2)
1990 ) { // End of fstab/mtab is reached
1991 mtcur = mtother; // the thing we found last time
1992 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001993 }
1994
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001995 // If we're trying to mount something specific and this isn't it,
1996 // skip it. Note we must match the exact text in fstab (ala
1997 // "proc") or a full path from root
1998 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001999
2000 // Is this what we're looking for?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002001 if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2002 && strcmp(storage_path, mtcur->mnt_fsname) != 0
2003 && strcmp(argv[0], mtcur->mnt_dir) != 0
2004 && strcmp(storage_path, mtcur->mnt_dir) != 0
2005 ) {
2006 continue; // no
2007 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002008
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002009 // Remember this entry. Something later may have
2010 // overmounted it, and we want the _last_ match.
2011 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002012
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002013 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002014 } else {
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002015 struct mntent *mp;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002016 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002017 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002018 if (nonroot)
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002019 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002020
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002021 // Does type match? (NULL matches always)
2022 if (!match_fstype(mtcur, fstype))
2023 continue;
2024
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002025 // Skip noauto and swap anyway
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002026 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2027 // swap is bogus "fstype", parse_mount_options can't check fstypes
2028 || strcasecmp(mtcur->mnt_type, "swap") == 0
2029 ) {
2030 continue;
2031 }
2032
2033 // Does (at least one) option match?
2034 // (NULL matches always)
2035 if (!match_opt(mtcur->mnt_opts, O_optmatch))
2036 continue;
2037
2038 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002039
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002040 // NFS mounts want this to be xrealloc-able
2041 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002042
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002043 // If nothing is mounted on this directory...
2044 // (otherwise repeated "mount -a" mounts everything again)
2045 mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2046 // We do not check fsname match of found mount point -
2047 // "/" may have fsname of "/dev/root" while fstab
2048 // says "/dev/something_else".
2049 if (mp) {
Denys Vlasenko86566762009-12-10 21:32:28 +01002050 if (verbose) {
2051 bb_error_msg("according to %s, "
2052 "%s is already mounted on %s",
2053 bb_path_mtab_file,
2054 mp->mnt_fsname, mp->mnt_dir);
2055 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002056 } else {
2057 // ...mount this thing
2058 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2059 // Count number of failed mounts
2060 rc++;
2061 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002062 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002063 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002064 }
2065 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002066
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002067 // End of fstab/mtab is reached.
2068 // Were we looking for something specific?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002069 if (argv[0]) { // yes
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002070 long l;
2071
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002072 // If we didn't find anything, complain
2073 if (!mtcur->mnt_fsname)
2074 bb_error_msg_and_die("can't find %s in %s",
2075 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002076
2077 // What happens when we try to "mount swap_partition"?
2078 // (fstab containts "swap_partition swap swap defaults 0 0")
2079 // util-linux-ng 2.13.1 does this:
2080 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2081 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2082 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
2083 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2084 // exit_group(32) = ?
2085#if 0
2086 // In case we want to simply skip swap partitions:
2087 l = parse_mount_options(mtcur->mnt_opts, NULL);
2088 if ((l & MOUNT_SWAP)
2089 // swap is bogus "fstype", parse_mount_options can't check fstypes
2090 || strcasecmp(mtcur->mnt_type, "swap") == 0
2091 ) {
2092 goto ret;
2093 }
2094#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002095 if (nonroot) {
2096 // fstab must have "users" or "user"
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002097 l = parse_mount_options(mtcur->mnt_opts, NULL);
2098 if (!(l & MOUNT_USERS))
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002099 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002100 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002101
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002102 //util-linux-2.12 does not do this check.
2103 //// If nothing is mounted on this directory...
2104 //// (otherwise repeated "mount FOO" mounts FOO again)
2105 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2106 //if (mp) {
2107 // bb_error_msg("according to %s, "
2108 // "%s is already mounted on %s",
2109 // bb_path_mtab_file,
2110 // mp->mnt_fsname, mp->mnt_dir);
2111 //} else {
2112 // ...mount the last thing we found
2113 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2114 append_mount_options(&(mtcur->mnt_opts), cmdopts);
2115 resolve_mount_spec(&mtpair->mnt_fsname);
2116 rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2117 if (ENABLE_FEATURE_CLEAN_UP)
2118 free(mtcur->mnt_opts);
2119 //}
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002120 }
2121
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002122 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002123 if (ENABLE_FEATURE_CLEAN_UP)
2124 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002125 if (ENABLE_FEATURE_CLEAN_UP) {
2126 free(storage_path);
2127 free(cmdopts);
2128 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002129
2130//TODO: exitcode should be ORed mask of (from "man mount"):
2131// 0 success
2132// 1 incorrect invocation or permissions
2133// 2 system error (out of memory, cannot fork, no more loop devices)
2134// 4 internal mount bug or missing nfs support in mount
2135// 8 user interrupt
2136//16 problems writing or locking /etc/mtab
2137//32 mount failure
2138//64 some mount succeeded
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002139 return rc;
2140}