blob: 6a154e2b288268fc32db3cc063e4de4e33a02dd9 [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//
Pere Orga5bc8c002011-04-11 03:29:49 +020019
20//usage:#define mount_trivial_usage
21//usage: "[OPTIONS] [-o OPTS] DEVICE NODE"
22//usage:#define mount_full_usage "\n\n"
23//usage: "Mount a filesystem. Filesystem autodetection requires /proc.\n"
24//usage: "\nOptions:"
25//usage: "\n -a Mount all filesystems in fstab"
26//usage: IF_FEATURE_MOUNT_FAKE(
27//usage: IF_FEATURE_MTAB_SUPPORT(
28//usage: "\n -f Update /etc/mtab, but don't mount"
29//usage: )
30//usage: IF_NOT_FEATURE_MTAB_SUPPORT(
31//usage: "\n -f Dry run"
32//usage: )
33//usage: )
34//usage: IF_FEATURE_MOUNT_HELPERS(
35//usage: "\n -i Don't run mount helper"
36//usage: )
37//usage: IF_FEATURE_MTAB_SUPPORT(
38//usage: "\n -n Don't update /etc/mtab"
39//usage: )
40//usage: "\n -r Read-only mount"
41//usage: "\n -w Read-write mount (default)"
42//usage: "\n -t FSTYPE Filesystem type"
43//usage: "\n -O OPT Mount only filesystems with option OPT (-a only)"
44//usage: "\n-o OPT:"
45//usage: IF_FEATURE_MOUNT_LOOP(
46//usage: "\n loop Ignored (loop devices are autodetected)"
47//usage: )
48//usage: IF_FEATURE_MOUNT_FLAGS(
49//usage: "\n [a]sync Writes are [a]synchronous"
50//usage: "\n [no]atime Disable/enable updates to inode access times"
51//usage: "\n [no]diratime Disable/enable atime updates to directories"
52//usage: "\n [no]relatime Disable/enable atime updates relative to modification time"
53//usage: "\n [no]dev (Dis)allow use of special device files"
54//usage: "\n [no]exec (Dis)allow use of executable files"
55//usage: "\n [no]suid (Dis)allow set-user-id-root programs"
56//usage: "\n [r]shared Convert [recursively] to a shared subtree"
57//usage: "\n [r]slave Convert [recursively] to a slave subtree"
58//usage: "\n [r]private Convert [recursively] to a private subtree"
59//usage: "\n [un]bindable Make mount point [un]able to be bind mounted"
60//usage: "\n [r]bind Bind a file or directory [recursively] to another location"
61//usage: "\n move Relocate an existing mount point"
62//usage: )
63//usage: "\n remount Remount a mounted filesystem, changing flags"
64//usage: "\n ro/rw Same as -r/-w"
65//usage: "\n"
66//usage: "\nThere are filesystem-specific -o flags."
67//usage:
68//usage:#define mount_example_usage
69//usage: "$ mount\n"
70//usage: "/dev/hda3 on / type minix (rw)\n"
71//usage: "proc on /proc type proc (rw)\n"
72//usage: "devpts on /dev/pts type devpts (rw)\n"
73//usage: "$ mount /dev/fd0 /mnt -t msdos -o ro\n"
74//usage: "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
75//usage: "$ mount cd_image.iso mydir\n"
76//usage:#define mount_notes_usage
77//usage: "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
78
Eric Andersencc8ed391999-10-05 16:24:54 +000079#include <mntent.h>
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +000080#include <syslog.h>
Denys Vlasenkoda49f582009-07-08 02:58:38 +020081#include <sys/mount.h>
Denys Vlasenko102ff762009-11-21 17:14:08 +010082// Grab more as needed from util-linux's mount/mount_constants.h
83#ifndef MS_DIRSYNC
84# define MS_DIRSYNC (1 << 7) // Directory modifications are synchronous
85#endif
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +020086#ifndef MS_UNION
87# define MS_UNION (1 << 8)
88#endif
Denys Vlasenkoda49f582009-07-08 02:58:38 +020089#ifndef MS_BIND
90# define MS_BIND (1 << 12)
91#endif
92#ifndef MS_MOVE
93# define MS_MOVE (1 << 13)
94#endif
95#ifndef MS_RECURSIVE
96# define MS_RECURSIVE (1 << 14)
97#endif
98#ifndef MS_SILENT
99# define MS_SILENT (1 << 15)
100#endif
Denys Vlasenko102ff762009-11-21 17:14:08 +0100101// The shared subtree stuff, which went in around 2.6.15
Denys Vlasenkoda49f582009-07-08 02:58:38 +0200102#ifndef MS_UNBINDABLE
103# define MS_UNBINDABLE (1 << 17)
104#endif
105#ifndef MS_PRIVATE
106# define MS_PRIVATE (1 << 18)
107#endif
108#ifndef MS_SLAVE
109# define MS_SLAVE (1 << 19)
110#endif
111#ifndef MS_SHARED
112# define MS_SHARED (1 << 20)
113#endif
114#ifndef MS_RELATIME
115# define MS_RELATIME (1 << 21)
116#endif
Eric Andersenbd22ed82000-07-08 18:55:24 +0000117
Denys Vlasenko102ff762009-11-21 17:14:08 +0100118#include "libbb.h"
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000119#if ENABLE_FEATURE_MOUNT_LABEL
Natanael Copa9aff2992009-09-20 04:28:22 +0200120# include "volume_id.h"
121#else
122# define resolve_mount_spec(fsname) ((void)0)
Denis Vlasenko6aa76962008-03-18 01:44:52 +0000123#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000124
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000125// Needed for nfs support only
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000126#include <sys/utsname.h>
127#undef TRUE
128#undef FALSE
Denys Vlasenkocc428142009-12-16 02:06:56 +0100129#if ENABLE_FEATURE_MOUNT_NFS
130/* This is just a warning of a common mistake. Possibly this should be a
131 * uclibc faq entry rather than in busybox... */
132# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
133# error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
134# endif
135# include <rpc/rpc.h>
136# include <rpc/pmap_prot.h>
137# include <rpc/pmap_clnt.h>
138#endif
Denis Vlasenko30a64cd2006-09-15 15:12:00 +0000139
140
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000141#if defined(__dietlibc__)
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000142// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
143// dietlibc-0.30 does not have implementation of getmntent_r()
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +0000144static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000145 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000146{
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000147 struct mntent* ment = getmntent(stream);
Denis Vlasenkoa0e17f72008-05-26 01:19:53 +0000148 return memcpy(result, ment, sizeof(*ment));
Denis Vlasenko908d6b72006-12-18 23:07:42 +0000149}
150#endif
151
152
Rob Landleydc0955b2006-03-14 18:16:25 +0000153// Not real flags, but we want to be able to check for this.
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000154enum {
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000155 MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP,
156 MOUNT_NOAUTO = (1 << 29),
157 MOUNT_SWAP = (1 << 30),
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000158};
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000159
160
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000161#define OPTION_STR "o:t:rwanfvsiO:"
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000162enum {
163 OPT_o = (1 << 0),
164 OPT_t = (1 << 1),
165 OPT_r = (1 << 2),
166 OPT_w = (1 << 3),
167 OPT_a = (1 << 4),
168 OPT_n = (1 << 5),
169 OPT_f = (1 << 6),
170 OPT_v = (1 << 7),
171 OPT_s = (1 << 8),
172 OPT_i = (1 << 9),
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +0000173 OPT_O = (1 << 10),
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000174};
175
176#if ENABLE_FEATURE_MTAB_SUPPORT
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200177#define USE_MTAB (!(option_mask32 & OPT_n))
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000178#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200179#define USE_MTAB 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000180#endif
181
182#if ENABLE_FEATURE_MOUNT_FAKE
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200183#define FAKE_IT (option_mask32 & OPT_f)
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000184#else
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200185#define FAKE_IT 0
186#endif
187
188#if ENABLE_FEATURE_MOUNT_HELPERS
189#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
190#else
191#define HELPERS_ALLOWED 0
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +0000192#endif
193
194
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000195// TODO: more "user" flag compatibility.
196// "user" option (from mount manpage):
197// Only the user that mounted a filesystem can unmount it again.
198// If any user should be able to unmount, then use users instead of user
199// in the fstab line. The owner option is similar to the user option,
200// with the restriction that the user must be the owner of the special file.
201// This may be useful e.g. for /dev/fd if a login script makes
202// the console user owner of this device.
Rob Landley3ba7bd12006-08-09 19:51:13 +0000203
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000204// Standard mount options (from -o options or --options),
205// with corresponding flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000206static const int32_t mount_options[] = {
Rob Landleye3781b72006-08-08 01:39:49 +0000207 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
Rob Landleydc0955b2006-03-14 18:16:25 +0000208
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000209 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000210 /* "loop" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000211 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000212
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000213 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000214 /* "defaults" */ 0,
215 /* "quiet" 0 - do not filter out, vfat wants to see it */
216 /* "noauto" */ MOUNT_NOAUTO,
217 /* "sw" */ MOUNT_SWAP,
218 /* "swap" */ MOUNT_SWAP,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000219 IF_DESKTOP(/* "user" */ MOUNT_USERS,)
220 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
Denis Vlasenko8c638cb2008-01-29 09:31:09 +0000221 /* "_netdev" */ 0,
Rob Landleye3781b72006-08-08 01:39:49 +0000222 )
Rob Landleydc0955b2006-03-14 18:16:25 +0000223
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000224 IF_FEATURE_MOUNT_FLAGS(
Rob Landleye3781b72006-08-08 01:39:49 +0000225 // vfs flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000226 /* "nosuid" */ MS_NOSUID,
227 /* "suid" */ ~MS_NOSUID,
228 /* "dev" */ ~MS_NODEV,
229 /* "nodev" */ MS_NODEV,
230 /* "exec" */ ~MS_NOEXEC,
231 /* "noexec" */ MS_NOEXEC,
232 /* "sync" */ MS_SYNCHRONOUS,
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +0000233 /* "dirsync" */ MS_DIRSYNC,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000234 /* "async" */ ~MS_SYNCHRONOUS,
235 /* "atime" */ ~MS_NOATIME,
236 /* "noatime" */ MS_NOATIME,
237 /* "diratime" */ ~MS_NODIRATIME,
238 /* "nodiratime" */ MS_NODIRATIME,
Denis Vlasenko580ce2d2008-07-08 02:56:53 +0000239 /* "mand" */ MS_MANDLOCK,
240 /* "nomand" */ ~MS_MANDLOCK,
Bernhard Reutner-Fischerfb5902c2008-08-06 18:14:38 +0000241 /* "relatime" */ MS_RELATIME,
242 /* "norelatime" */ ~MS_RELATIME,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000243 /* "loud" */ ~MS_SILENT,
Roman Borisov19311bf2011-03-24 15:08:43 +0300244 /* "rbind" */ MS_BIND|MS_RECURSIVE,
Eric Andersen9601a1c2006-03-20 18:07:50 +0000245
Rob Landleye3781b72006-08-08 01:39:49 +0000246 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200247 /* "union" */ MS_UNION,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000248 /* "bind" */ MS_BIND,
249 /* "move" */ MS_MOVE,
250 /* "shared" */ MS_SHARED,
251 /* "slave" */ MS_SLAVE,
252 /* "private" */ MS_PRIVATE,
253 /* "unbindable" */ MS_UNBINDABLE,
254 /* "rshared" */ MS_SHARED|MS_RECURSIVE,
255 /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
Roman Borisovd3679d22011-03-23 11:20:25 +0300256 /* "rprivate" */ MS_PRIVATE|MS_RECURSIVE,
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000257 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
Rob Landleye3781b72006-08-08 01:39:49 +0000258 )
259
260 // Always understood.
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000261 /* "ro" */ MS_RDONLY, // vfs flag
262 /* "rw" */ ~MS_RDONLY, // vfs flag
263 /* "remount" */ MS_REMOUNT // action flag
Eric Andersencc8ed391999-10-05 16:24:54 +0000264};
265
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000266static const char mount_option_str[] =
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000267 IF_FEATURE_MOUNT_LOOP(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000268 "loop\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000269 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000270 IF_FEATURE_MOUNT_FSTAB(
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000271 "defaults\0"
272 // "quiet\0" - do not filter out, vfat wants to see it
273 "noauto\0"
274 "sw\0"
275 "swap\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000276 IF_DESKTOP("user\0")
277 IF_DESKTOP("users\0")
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000278 "_netdev\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000279 )
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000280 IF_FEATURE_MOUNT_FLAGS(
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000281 // vfs flags
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000282 "nosuid\0"
283 "suid\0"
284 "dev\0"
285 "nodev\0"
286 "exec\0"
287 "noexec\0"
288 "sync\0"
289 "dirsync\0"
290 "async\0"
291 "atime\0"
292 "noatime\0"
293 "diratime\0"
294 "nodiratime\0"
295 "mand\0"
296 "nomand\0"
297 "relatime\0"
298 "norelatime\0"
299 "loud\0"
Roman Borisov19311bf2011-03-24 15:08:43 +0300300 "rbind\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000301
302 // action flags
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200303 "union\0"
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000304 "bind\0"
305 "move\0"
Roman Borisov945fd172011-02-25 14:50:39 +0300306 "make-shared\0"
307 "make-slave\0"
308 "make-private\0"
309 "make-unbindable\0"
310 "make-rshared\0"
311 "make-rslave\0"
312 "make-rprivate\0"
313 "make-runbindable\0"
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000314 )
315
316 // Always understood.
Denis Vlasenko3f8f4b22008-12-10 11:28:30 +0000317 "ro\0" // vfs flag
318 "rw\0" // vfs flag
319 "remount\0" // action flag
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000320;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000321
Denis Vlasenkof732e962008-02-18 12:07:49 +0000322
323struct globals {
324#if ENABLE_FEATURE_MOUNT_NFS
325 smalluint nfs_mount_version;
326#endif
327#if ENABLE_FEATURE_MOUNT_VERBOSE
328 unsigned verbose;
329#endif
330 llist_t *fslist;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000331 char getmntent_buf[1];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100332} FIX_ALIASING;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000333enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
Denis Vlasenkof732e962008-02-18 12:07:49 +0000334#define G (*(struct globals*)&bb_common_bufsiz1)
335#define nfs_mount_version (G.nfs_mount_version)
Denis Vlasenkob4133682008-02-18 13:05:38 +0000336#if ENABLE_FEATURE_MOUNT_VERBOSE
Denis Vlasenkof732e962008-02-18 12:07:49 +0000337#define verbose (G.verbose )
Denis Vlasenkob4133682008-02-18 13:05:38 +0000338#else
339#define verbose 0
340#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000341#define fslist (G.fslist )
342#define getmntent_buf (G.getmntent_buf )
343
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100344#if ENABLE_FEATURE_MTAB_SUPPORT
345/*
346 * update_mtab_entry_on_move() is used to update entry in case of mount --move.
347 * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
348 * input mntent and replace it by new one.
349 */
350static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
351{
352 struct mntent *entries, *m;
353 int i, count;
354 FILE *mountTable;
355
356 mountTable = setmntent(bb_path_mtab_file, "r");
357 if (!mountTable) {
358 bb_perror_msg(bb_path_mtab_file);
359 return;
360 }
361
362 entries = NULL;
363 count = 0;
364 while ((m = getmntent(mountTable)) != NULL) {
365 entries = xrealloc_vector(entries, 3, count);
366 entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
367 entries[count].mnt_dir = xstrdup(m->mnt_dir);
368 entries[count].mnt_type = xstrdup(m->mnt_type);
369 entries[count].mnt_opts = xstrdup(m->mnt_opts);
370 entries[count].mnt_freq = m->mnt_freq;
371 entries[count].mnt_passno = m->mnt_passno;
372 count++;
373 }
374 endmntent(mountTable);
375
376 mountTable = setmntent(bb_path_mtab_file, "w");
377 if (mountTable) {
378 for (i = 0; i < count; i++) {
379 if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
380 addmntent(mountTable, &entries[i]);
381 else
382 addmntent(mountTable, mp);
383 }
384 endmntent(mountTable);
385 } else if (errno != EROFS)
386 bb_perror_msg(bb_path_mtab_file);
387
388 if (ENABLE_FEATURE_CLEAN_UP) {
389 for (i = 0; i < count; i++) {
390 free(entries[i].mnt_fsname);
391 free(entries[i].mnt_dir);
392 free(entries[i].mnt_type);
393 free(entries[i].mnt_opts);
394 }
395 free(entries);
396 }
397}
398#endif
Denis Vlasenkof732e962008-02-18 12:07:49 +0000399
400#if ENABLE_FEATURE_MOUNT_VERBOSE
401static int verbose_mount(const char *source, const char *target,
402 const char *filesystemtype,
403 unsigned long mountflags, const void *data)
404{
405 int rc;
406
407 errno = 0;
408 rc = mount(source, target, filesystemtype, mountflags, data);
409 if (verbose >= 2)
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000410 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
Denis Vlasenkob4133682008-02-18 13:05:38 +0000411 source, target, filesystemtype,
412 mountflags, (char*)data, rc);
Denis Vlasenkof732e962008-02-18 12:07:49 +0000413 return rc;
414}
415#else
416#define verbose_mount(...) mount(__VA_ARGS__)
417#endif
418
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000419// Append mount options to string
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000420static void append_mount_options(char **oldopts, const char *newopts)
Eric Andersencc8ed391999-10-05 16:24:54 +0000421{
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000422 if (*oldopts && **oldopts) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000423 // Do not insert options which are already there
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000424 while (newopts[0]) {
425 char *p;
426 int len = strlen(newopts);
427 p = strchr(newopts, ',');
428 if (p) len = p - newopts;
429 p = *oldopts;
430 while (1) {
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000431 if (!strncmp(p, newopts, len)
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000432 && (p[len] == ',' || p[len] == '\0'))
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000433 goto skip;
434 p = strchr(p,',');
Denis Vlasenko51742f42007-04-12 00:32:05 +0000435 if (!p) break;
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000436 p++;
437 }
438 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
439 free(*oldopts);
440 *oldopts = p;
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000441 skip:
Denis Vlasenkoc889d2b2006-09-17 15:08:12 +0000442 newopts += len;
443 while (newopts[0] == ',') newopts++;
444 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000445 } else {
446 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000447 *oldopts = xstrdup(newopts);
Rob Landleydc0955b2006-03-14 18:16:25 +0000448 }
449}
450
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000451// Use the mount_options list to parse options into flags.
Alexander Shishkin77650952010-10-28 06:10:03 +0200452// Also update list of unrecognized options if unrecognized != NULL
Denis Vlasenkob4133682008-02-18 13:05:38 +0000453static long parse_mount_options(char *options, char **unrecognized)
Rob Landleydc0955b2006-03-14 18:16:25 +0000454{
Denis Vlasenkob4133682008-02-18 13:05:38 +0000455 long flags = MS_SILENT;
Rob Landleydc0955b2006-03-14 18:16:25 +0000456
Rob Landley6a6798b2005-08-10 20:35:54 +0000457 // Loop through options
Rob Landleydc0955b2006-03-14 18:16:25 +0000458 for (;;) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000459 unsigned i;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000460 char *comma = strchr(options, ',');
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000461 const char *option_str = mount_option_str;
Eric Andersencc8ed391999-10-05 16:24:54 +0000462
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000463 if (comma) *comma = '\0';
Eric Andersen3ae0c781999-11-04 01:13:21 +0000464
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000465// FIXME: use hasmntopt()
Rob Landley6a6798b2005-08-10 20:35:54 +0000466 // Find this option in mount_options
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000467 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
Alexander Shishkin77650952010-10-28 06:10:03 +0200468 if (strcasecmp(option_str, options) == 0) {
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000469 long fl = mount_options[i];
Alexander Shishkin77650952010-10-28 06:10:03 +0200470 if (fl < 0)
471 flags &= fl;
472 else
473 flags |= fl;
474 goto found;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000475 }
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000476 option_str += strlen(option_str) + 1;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000477 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200478 // We did not recognize this option.
479 // If "unrecognized" is not NULL, append option there.
480 // Note that we should not append *empty* option -
481 // in this case we want to pass NULL, not "", to "data"
482 // parameter of mount(2) syscall.
483 // This is crucial for filesystems that don't accept
484 // any arbitrary mount options, like cgroup fs:
485 // "mount -t cgroup none /mnt"
486 if (options[0] && unrecognized) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000487 // Add it to strflags, to pass on to kernel
Alexander Shishkin77650952010-10-28 06:10:03 +0200488 char *p = *unrecognized;
489 unsigned len = p ? strlen(p) : 0;
490 *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
Eric Andersen9601a1c2006-03-20 18:07:50 +0000491
Rob Landley6a6798b2005-08-10 20:35:54 +0000492 // Comma separated if it's not the first one
Alexander Shishkin77650952010-10-28 06:10:03 +0200493 if (len) p[len++] = ',';
494 strcpy(p + len, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000495 }
Alexander Shishkin77650952010-10-28 06:10:03 +0200496 found:
Denis Vlasenko2535f122007-09-15 13:28:30 +0000497 if (!comma)
498 break;
499 // Advance to next option
500 *comma = ',';
501 options = ++comma;
Eric Andersencc8ed391999-10-05 16:24:54 +0000502 }
Eric Andersen9601a1c2006-03-20 18:07:50 +0000503
Rob Landleydc0955b2006-03-14 18:16:25 +0000504 return flags;
Eric Andersencc8ed391999-10-05 16:24:54 +0000505}
506
Rob Landleydc0955b2006-03-14 18:16:25 +0000507// Return a list of all block device backed filesystems
Rob Landleydc0955b2006-03-14 18:16:25 +0000508static llist_t *get_block_backed_filesystems(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000509{
Denis Vlasenko87468852007-04-13 23:22:00 +0000510 static const char filesystems[2][sizeof("/proc/filesystems")] = {
Denis Vlasenko372686b2006-10-12 22:42:33 +0000511 "/etc/filesystems",
512 "/proc/filesystems",
Denis Vlasenko372686b2006-10-12 22:42:33 +0000513 };
514 char *fs, *buf;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200515 llist_t *list = NULL;
Rob Landleydc0955b2006-03-14 18:16:25 +0000516 int i;
517 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000518
Denis Vlasenko87468852007-04-13 23:22:00 +0000519 for (i = 0; i < 2; i++) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000520 f = fopen_for_read(filesystems[i]);
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000521 if (!f) continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000522
Denis Vlasenko8ee649a2008-03-26 20:04:27 +0000523 while ((buf = xmalloc_fgetline(f)) != NULL) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200524 if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
Denis Vlasenko372686b2006-10-12 22:42:33 +0000525 continue;
Denis Vlasenkod18a3a22006-10-25 12:46:03 +0000526 fs = skip_whitespace(buf);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200527 if (*fs == '#' || *fs == '*' || !*fs)
528 continue;
Eric Andersen9601a1c2006-03-20 18:07:50 +0000529
Denis Vlasenko372686b2006-10-12 22:42:33 +0000530 llist_add_to_end(&list, xstrdup(fs));
531 free(buf);
Rob Landleydc0955b2006-03-14 18:16:25 +0000532 }
533 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
534 }
535
536 return list;
537}
538
Rob Landleydc0955b2006-03-14 18:16:25 +0000539#if ENABLE_FEATURE_CLEAN_UP
540static void delete_block_backed_filesystems(void)
541{
Rob Landleya6b5b602006-05-08 19:03:07 +0000542 llist_free(fslist, free);
Rob Landleydc0955b2006-03-14 18:16:25 +0000543}
Rob Landleyfe908fd2006-03-29 14:30:49 +0000544#else
545void delete_block_backed_filesystems(void);
Rob Landleydc0955b2006-03-14 18:16:25 +0000546#endif
547
Rob Landleydc0955b2006-03-14 18:16:25 +0000548// Perform actual mount of specific filesystem at specific location.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000549// NB: mp->xxx fields may be trashed on exit
Denis Vlasenkob4133682008-02-18 13:05:38 +0000550static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
Rob Landleydc0955b2006-03-14 18:16:25 +0000551{
Denis Vlasenkob1726782006-09-29 14:43:20 +0000552 int rc = 0;
Rob Landleyeaa34ca2006-03-18 02:58:11 +0000553
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200554 if (FAKE_IT) {
Denis Vlasenkob4133682008-02-18 13:05:38 +0000555 if (verbose >= 2)
556 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
557 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
558 vfsflags, filteropts);
559 goto mtab;
560 }
Eric Andersen19b5b8f2006-03-20 18:07:13 +0000561
Rob Landleydc0955b2006-03-14 18:16:25 +0000562 // Mount, with fallback to read-only if necessary.
Denis Vlasenko8d474b52006-09-17 15:00:58 +0000563 for (;;) {
Denis Vlasenkof732e962008-02-18 12:07:49 +0000564 errno = 0;
565 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
Rob Landleydc0955b2006-03-14 18:16:25 +0000566 vfsflags, filteropts);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000567
568 // If mount failed, try
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +0000569 // helper program mount.<mnt_type>
Denys Vlasenkoba986032009-09-15 23:00:09 +0200570 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200571 char *args[8];
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000572 int errno_save = errno;
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000573 args[0] = xasprintf("mount.%s", mp->mnt_type);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000574 rc = 1;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200575 if (FAKE_IT)
576 args[rc++] = (char *)"-f";
577 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
578 args[rc++] = (char *)"-n";
Denis Vlasenko5c329932009-04-12 12:16:21 +0000579 args[rc++] = mp->mnt_fsname;
580 args[rc++] = mp->mnt_dir;
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000581 if (filteropts) {
582 args[rc++] = (char *)"-o";
583 args[rc++] = filteropts;
584 }
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000585 args[rc] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +0100586 rc = spawn_and_wait(args);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +0000587 free(args[0]);
Denis Vlasenko32d49bc2008-02-03 23:52:41 +0000588 if (!rc)
589 break;
590 errno = errno_save;
591 }
592
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000593 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
Rob Landleydc0955b2006-03-14 18:16:25 +0000594 break;
Denis Vlasenko2535f122007-09-15 13:28:30 +0000595 if (!(vfsflags & MS_SILENT))
596 bb_error_msg("%s is write-protected, mounting read-only",
597 mp->mnt_fsname);
Rob Landleydc0955b2006-03-14 18:16:25 +0000598 vfsflags |= MS_RDONLY;
599 }
600
Rob Landleydc0955b2006-03-14 18:16:25 +0000601 // Abort entirely if permission denied.
602
603 if (rc && errno == EPERM)
604 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
605
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000606 // If the mount was successful, and we're maintaining an old-style
607 // mtab file by hand, add the new entry to it now.
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000608 mtab:
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +0200609 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000610 char *fsname;
Rob Landleydc0955b2006-03-14 18:16:25 +0000611 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000612 const char *option_str = mount_option_str;
Rob Landleydc0955b2006-03-14 18:16:25 +0000613 int i;
614
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000615 if (!mountTable) {
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100616 bb_perror_msg(bb_path_mtab_file);
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000617 goto ret;
618 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000619
620 // Add vfs string flags
Denis Vlasenko63430ae2007-10-29 19:18:39 +0000621 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
622 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
623 append_mount_options(&(mp->mnt_opts), option_str);
624 option_str += strlen(option_str) + 1;
625 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000626
627 // Remove trailing / (if any) from directory we mounted on
Denis Vlasenko727ef942006-09-14 13:19:19 +0000628 i = strlen(mp->mnt_dir) - 1;
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100629 while (i > 0 && mp->mnt_dir[i] == '/')
Denys Vlasenkoc6450c92011-02-28 11:09:49 +0100630 mp->mnt_dir[i--] = '\0';
Denis Vlasenko727ef942006-09-14 13:19:19 +0000631
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000632 // Convert to canonical pathnames as needed
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000633 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100634 fsname = NULL;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000635 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000636 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000637 mp->mnt_type = (char*)"bind";
Denis Vlasenko727ef942006-09-14 13:19:19 +0000638 }
Denis Vlasenko25098f72006-09-14 15:46:33 +0000639 mp->mnt_freq = mp->mnt_passno = 0;
Rob Landleydc0955b2006-03-14 18:16:25 +0000640
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100641 // Write and close
642#if ENABLE_FEATURE_MTAB_SUPPORT
643 if (vfsflags & MS_MOVE)
644 update_mtab_entry_on_move(mp);
645 else
646#endif
647 addmntent(mountTable, mp);
Rob Landleydc0955b2006-03-14 18:16:25 +0000648 endmntent(mountTable);
Roman Borisovc8dc01d2011-02-28 05:06:01 +0100649
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000650 if (ENABLE_FEATURE_CLEAN_UP) {
Denis Vlasenkoa52145a2006-09-17 15:09:48 +0000651 free(mp->mnt_dir);
Denis Vlasenkofc56dd22006-09-17 15:01:53 +0000652 free(fsname);
653 }
Rob Landleydc0955b2006-03-14 18:16:25 +0000654 }
Denis Vlasenko6a353c82006-11-19 17:34:57 +0000655 ret:
Rob Landleydc0955b2006-03-14 18:16:25 +0000656 return rc;
657}
658
Denis Vlasenko25098f72006-09-14 15:46:33 +0000659#if ENABLE_FEATURE_MOUNT_NFS
660
661/*
662 * Linux NFS mount
663 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
664 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +0200665 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenko25098f72006-09-14 15:46:33 +0000666 *
667 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
668 * numbers to be specified on the command line.
669 *
670 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
671 * Omit the call to connect() for Linux version 1.3.11 or later.
672 *
673 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
674 * Implemented the "bg", "fg" and "retry" mount options for NFS.
675 *
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000676 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
Denis Vlasenko25098f72006-09-14 15:46:33 +0000677 * - added Native Language Support
678 *
679 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
680 * plus NFSv3 stuff.
681 */
682
Denis Vlasenko25098f72006-09-14 15:46:33 +0000683#define MOUNTPORT 635
684#define MNTPATHLEN 1024
685#define MNTNAMLEN 255
686#define FHSIZE 32
687#define FHSIZE3 64
688
689typedef char fhandle[FHSIZE];
690
691typedef struct {
692 unsigned int fhandle3_len;
693 char *fhandle3_val;
694} fhandle3;
695
696enum mountstat3 {
697 MNT_OK = 0,
698 MNT3ERR_PERM = 1,
699 MNT3ERR_NOENT = 2,
700 MNT3ERR_IO = 5,
701 MNT3ERR_ACCES = 13,
702 MNT3ERR_NOTDIR = 20,
703 MNT3ERR_INVAL = 22,
704 MNT3ERR_NAMETOOLONG = 63,
705 MNT3ERR_NOTSUPP = 10004,
706 MNT3ERR_SERVERFAULT = 10006,
707};
708typedef enum mountstat3 mountstat3;
709
710struct fhstatus {
711 unsigned int fhs_status;
712 union {
713 fhandle fhs_fhandle;
714 } fhstatus_u;
715};
716typedef struct fhstatus fhstatus;
717
718struct mountres3_ok {
719 fhandle3 fhandle;
720 struct {
721 unsigned int auth_flavours_len;
722 char *auth_flavours_val;
723 } auth_flavours;
724};
725typedef struct mountres3_ok mountres3_ok;
726
727struct mountres3 {
728 mountstat3 fhs_status;
729 union {
730 mountres3_ok mountinfo;
731 } mountres3_u;
732};
733typedef struct mountres3 mountres3;
734
735typedef char *dirpath;
736
737typedef char *name;
738
739typedef struct mountbody *mountlist;
740
741struct mountbody {
742 name ml_hostname;
743 dirpath ml_directory;
744 mountlist ml_next;
745};
746typedef struct mountbody mountbody;
747
748typedef struct groupnode *groups;
749
750struct groupnode {
751 name gr_name;
752 groups gr_next;
753};
754typedef struct groupnode groupnode;
755
756typedef struct exportnode *exports;
757
758struct exportnode {
759 dirpath ex_dir;
760 groups ex_groups;
761 exports ex_next;
762};
763typedef struct exportnode exportnode;
764
765struct ppathcnf {
766 int pc_link_max;
767 short pc_max_canon;
768 short pc_max_input;
769 short pc_name_max;
770 short pc_path_max;
771 short pc_pipe_buf;
Denis Vlasenko28703012006-12-19 20:32:02 +0000772 uint8_t pc_vdisable;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000773 char pc_xxx;
774 short pc_mask[2];
775};
776typedef struct ppathcnf ppathcnf;
777
778#define MOUNTPROG 100005
779#define MOUNTVERS 1
780
781#define MOUNTPROC_NULL 0
782#define MOUNTPROC_MNT 1
783#define MOUNTPROC_DUMP 2
784#define MOUNTPROC_UMNT 3
785#define MOUNTPROC_UMNTALL 4
786#define MOUNTPROC_EXPORT 5
787#define MOUNTPROC_EXPORTALL 6
788
789#define MOUNTVERS_POSIX 2
790
791#define MOUNTPROC_PATHCONF 7
792
793#define MOUNT_V3 3
794
795#define MOUNTPROC3_NULL 0
796#define MOUNTPROC3_MNT 1
797#define MOUNTPROC3_DUMP 2
798#define MOUNTPROC3_UMNT 3
799#define MOUNTPROC3_UMNTALL 4
800#define MOUNTPROC3_EXPORT 5
801
802enum {
803#ifndef NFS_FHSIZE
804 NFS_FHSIZE = 32,
805#endif
806#ifndef NFS_PORT
807 NFS_PORT = 2049
808#endif
809};
810
Denis Vlasenko25098f72006-09-14 15:46:33 +0000811/*
812 * We want to be able to compile mount on old kernels in such a way
813 * that the binary will work well on more recent kernels.
814 * Thus, if necessary we teach nfsmount.c the structure of new fields
815 * that will come later.
816 *
817 * Moreover, the new kernel includes conflict with glibc includes
818 * so it is easiest to ignore the kernel altogether (at compile time).
819 */
820
821struct nfs2_fh {
822 char data[32];
823};
824struct nfs3_fh {
825 unsigned short size;
826 unsigned char data[64];
827};
828
829struct nfs_mount_data {
830 int version; /* 1 */
831 int fd; /* 1 */
832 struct nfs2_fh old_root; /* 1 */
833 int flags; /* 1 */
834 int rsize; /* 1 */
835 int wsize; /* 1 */
836 int timeo; /* 1 */
837 int retrans; /* 1 */
838 int acregmin; /* 1 */
839 int acregmax; /* 1 */
840 int acdirmin; /* 1 */
841 int acdirmax; /* 1 */
842 struct sockaddr_in addr; /* 1 */
843 char hostname[256]; /* 1 */
844 int namlen; /* 2 */
845 unsigned int bsize; /* 3 */
846 struct nfs3_fh root; /* 4 */
847};
848
849/* bits in the flags field */
850enum {
851 NFS_MOUNT_SOFT = 0x0001, /* 1 */
852 NFS_MOUNT_INTR = 0x0002, /* 1 */
853 NFS_MOUNT_SECURE = 0x0004, /* 1 */
854 NFS_MOUNT_POSIX = 0x0008, /* 1 */
855 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
856 NFS_MOUNT_NOAC = 0x0020, /* 1 */
857 NFS_MOUNT_TCP = 0x0040, /* 2 */
858 NFS_MOUNT_VER3 = 0x0080, /* 3 */
859 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +0000860 NFS_MOUNT_NONLM = 0x0200, /* 3 */
861 NFS_MOUNT_NORDIRPLUS = 0x4000
Denis Vlasenko25098f72006-09-14 15:46:33 +0000862};
863
864
865/*
866 * We need to translate between nfs status return values and
867 * the local errno values which may not be the same.
868 *
869 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
870 * "after #include <errno.h> the symbol errno is reserved for any use,
871 * it cannot even be used as a struct tag or field name".
872 */
Denis Vlasenko25098f72006-09-14 15:46:33 +0000873#ifndef EDQUOT
Denys Vlasenkocc428142009-12-16 02:06:56 +0100874# define EDQUOT ENOSPC
Denis Vlasenko25098f72006-09-14 15:46:33 +0000875#endif
Denis Vlasenko30e5cf82008-12-05 16:40:36 +0000876/* Convert each NFSERR_BLAH into EBLAH */
Denys Vlasenkocc428142009-12-16 02:06:56 +0100877static const uint8_t nfs_err_stat[] = {
878 1, 2, 5, 6, 13, 17,
879 19, 20, 21, 22, 27, 28,
880 30, 63, 66, 69, 70, 71
881};
Denys Vlasenkodc1fd2e2010-05-19 17:01:29 +0200882#if ( \
883 EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \
884 ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \
885 EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
886typedef uint8_t nfs_err_type;
887#else
888typedef uint16_t nfs_err_type;
889#endif
890static const nfs_err_type nfs_err_errnum[] = {
Denys Vlasenkocc428142009-12-16 02:06:56 +0100891 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
892 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
893 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
Denis Vlasenko25098f72006-09-14 15:46:33 +0000894};
Denis Vlasenko25098f72006-09-14 15:46:33 +0000895static char *nfs_strerror(int status)
896{
897 int i;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000898
Denys Vlasenkocc428142009-12-16 02:06:56 +0100899 for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
900 if (nfs_err_stat[i] == status)
901 return strerror(nfs_err_errnum[i]);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000902 }
Denis Vlasenkob9256052007-09-28 10:29:17 +0000903 return xasprintf("unknown nfs status return value: %d", status);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000904}
905
906static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
907{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200908 return xdr_opaque(xdrs, objp, FHSIZE);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000909}
910
911static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
912{
913 if (!xdr_u_int(xdrs, &objp->fhs_status))
914 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200915 if (objp->fhs_status == 0)
916 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000917 return TRUE;
918}
919
920static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
921{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200922 return xdr_string(xdrs, objp, MNTPATHLEN);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000923}
924
925static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
926{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200927 return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
928 (unsigned int *) &objp->fhandle3_len,
929 FHSIZE3);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000930}
931
932static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
933{
934 if (!xdr_fhandle3(xdrs, &objp->fhandle))
935 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200936 return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
937 &(objp->auth_flavours.auth_flavours_len),
938 ~0,
939 sizeof(int),
940 (xdrproc_t) xdr_int);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000941}
942
943static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
944{
Alexander Shishkin54779a42010-10-22 13:35:47 +0200945 return xdr_enum(xdrs, (enum_t *) objp);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000946}
947
948static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
949{
950 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
951 return FALSE;
Alexander Shishkin54779a42010-10-22 13:35:47 +0200952 if (objp->fhs_status == MNT_OK)
953 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
Denis Vlasenko25098f72006-09-14 15:46:33 +0000954 return TRUE;
955}
956
957#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
958
Denis Vlasenko25098f72006-09-14 15:46:33 +0000959/*
960 * Unfortunately, the kernel prints annoying console messages
961 * in case of an unexpected nfs mount version (instead of
962 * just returning some error). Therefore we'll have to try
963 * and figure out what version the kernel expects.
964 *
965 * Variables:
966 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
967 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
968 * nfs_mount_version: version this source and running kernel can handle
969 */
970static void
971find_kernel_nfs_mount_version(void)
972{
Denis Vlasenkob9256052007-09-28 10:29:17 +0000973 int kernel_version;
974
975 if (nfs_mount_version)
Denis Vlasenko25098f72006-09-14 15:46:33 +0000976 return;
977
978 nfs_mount_version = 4; /* default */
979
980 kernel_version = get_linux_version_code();
981 if (kernel_version) {
Denys Vlasenkocc428142009-12-16 02:06:56 +0100982 if (kernel_version < KERNEL_VERSION(2,2,18))
Denis Vlasenko25098f72006-09-14 15:46:33 +0000983 nfs_mount_version = 3;
984 /* else v4 since 2.3.99pre4 */
985 }
986}
987
Denis Vlasenko3f5fdc72007-10-14 04:55:59 +0000988static void
Denis Vlasenkob9256052007-09-28 10:29:17 +0000989get_mountport(struct pmap *pm_mnt,
990 struct sockaddr_in *server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +0000991 long unsigned prog,
992 long unsigned version,
993 long unsigned proto,
994 long unsigned port)
995{
996 struct pmaplist *pmap;
Denis Vlasenko25098f72006-09-14 15:46:33 +0000997
998 server_addr->sin_port = PMAPPORT;
Denis Vlasenko5870ad92007-02-04 02:39:55 +0000999/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1000 * I understand it like "IPv6 for this is not 100% ready" */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001001 pmap = pmap_getmaps(server_addr);
1002
1003 if (version > MAX_NFSPROT)
1004 version = MAX_NFSPROT;
1005 if (!prog)
1006 prog = MOUNTPROG;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001007 pm_mnt->pm_prog = prog;
1008 pm_mnt->pm_vers = version;
1009 pm_mnt->pm_prot = proto;
1010 pm_mnt->pm_port = port;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00001011
Denis Vlasenko25098f72006-09-14 15:46:33 +00001012 while (pmap) {
1013 if (pmap->pml_map.pm_prog != prog)
1014 goto next;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001015 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001016 goto next;
1017 if (version > 2 && pmap->pml_map.pm_vers != version)
1018 goto next;
1019 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1020 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001021 if (pmap->pml_map.pm_vers > MAX_NFSPROT
1022 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1023 || (port && pmap->pml_map.pm_port != port)
1024 ) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001025 goto next;
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001026 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001027 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1028 next:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001029 pmap = pmap->pml_next;
1030 }
Denis Vlasenkob9256052007-09-28 10:29:17 +00001031 if (!pm_mnt->pm_vers)
1032 pm_mnt->pm_vers = MOUNTVERS;
1033 if (!pm_mnt->pm_port)
1034 pm_mnt->pm_port = MOUNTPORT;
1035 if (!pm_mnt->pm_prot)
1036 pm_mnt->pm_prot = IPPROTO_TCP;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001037}
1038
Denis Vlasenkof0000652007-09-04 18:30:26 +00001039#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001040static int daemonize(void)
1041{
Denis Vlasenko25098f72006-09-14 15:46:33 +00001042 int pid = fork();
1043 if (pid < 0) /* error */
1044 return -errno;
1045 if (pid > 0) /* parent */
1046 return 0;
1047 /* child */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001048 close(0);
1049 xopen(bb_dev_null, O_RDWR);
1050 xdup2(0, 1);
1051 xdup2(0, 2);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001052 setsid();
Denis Vlasenko8f8f2682006-10-03 21:00:43 +00001053 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001054 logmode = LOGMODE_SYSLOG;
1055 return 1;
1056}
Denis Vlasenkof0000652007-09-04 18:30:26 +00001057#else
1058static inline int daemonize(void) { return -ENOSYS; }
1059#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001060
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001061/* TODO */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001062static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001063{
1064 return 0;
1065}
1066
1067/* RPC strerror analogs are terminally idiotic:
1068 * *mandatory* prefix and \n at end.
1069 * This hopefully helps. Usage:
1070 * error_msg_rpc(clnt_*error*(" ")) */
1071static void error_msg_rpc(const char *msg)
1072{
Denis Vlasenko23514fe2006-09-19 14:07:52 +00001073 int len;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001074 while (msg[0] == ' ' || msg[0] == ':') msg++;
1075 len = strlen(msg);
1076 while (len && msg[len-1] == '\n') len--;
1077 bb_error_msg("%.*s", len, msg);
1078}
1079
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001080/* NB: mp->xxx fields may be trashed on exit */
Denys Vlasenko810b7162009-05-13 23:48:59 +02001081static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001082{
1083 CLIENT *mclient;
1084 char *hostname;
1085 char *pathname;
1086 char *mounthost;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001087 /* prior to 2.6.23, kernel took NFS options in a form of this struct
1088 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1089 * then data pointer is interpreted as a string. */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001090 struct nfs_mount_data data;
1091 char *opt;
1092 struct hostent *hp;
1093 struct sockaddr_in server_addr;
1094 struct sockaddr_in mount_server_addr;
1095 int msock, fsock;
1096 union {
1097 struct fhstatus nfsv2;
1098 struct mountres3 nfsv3;
1099 } status;
1100 int daemonized;
1101 char *s;
1102 int port;
1103 int mountport;
1104 int proto;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001105#if BB_MMU
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001106 smallint bg = 0;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001107#else
1108 enum { bg = 0 };
1109#endif
Denis Vlasenko25098f72006-09-14 15:46:33 +00001110 int retry;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001111 int mountprog;
1112 int mountvers;
1113 int nfsprog;
1114 int nfsvers;
1115 int retval;
Denys Vlasenkoe71dd7c2009-05-13 16:32:32 +02001116 /* these all are one-bit really. gcc 4.3.1 likes this combination: */
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001117 smallint tcp;
1118 smallint soft;
1119 int intr;
1120 int posix;
1121 int nocto;
1122 int noac;
1123 int nordirplus;
1124 int nolock;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001125
1126 find_kernel_nfs_mount_version();
1127
1128 daemonized = 0;
1129 mounthost = NULL;
1130 retval = ETIMEDOUT;
1131 msock = fsock = -1;
1132 mclient = NULL;
1133
1134 /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1135
1136 filteropts = xstrdup(filteropts); /* going to trash it later... */
1137
1138 hostname = xstrdup(mp->mnt_fsname);
1139 /* mount_main() guarantees that ':' is there */
1140 s = strchr(hostname, ':');
1141 pathname = s + 1;
1142 *s = '\0';
1143 /* Ignore all but first hostname in replicated mounts
1144 until they can be fully supported. (mack@sgi.com) */
1145 s = strchr(hostname, ',');
1146 if (s) {
1147 *s = '\0';
1148 bb_error_msg("warning: multiple hostnames not supported");
1149 }
1150
1151 server_addr.sin_family = AF_INET;
1152 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1153 hp = gethostbyname(hostname);
1154 if (hp == NULL) {
1155 bb_herror_msg("%s", hostname);
1156 goto fail;
1157 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001158 if (hp->h_length != (int)sizeof(struct in_addr)) {
1159 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko25098f72006-09-14 15:46:33 +00001160 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001161 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001162 }
1163
1164 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1165
1166 /* add IP address to mtab options for use when unmounting */
1167
1168 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1169 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1170 } else {
1171 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1172 mp->mnt_opts[0] ? "," : "",
1173 inet_ntoa(server_addr.sin_addr));
1174 free(mp->mnt_opts);
1175 mp->mnt_opts = tmp;
1176 }
1177
1178 /* Set default options.
1179 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1180 * let the kernel decide.
1181 * timeo is filled in after we know whether it'll be TCP or UDP. */
1182 memset(&data, 0, sizeof(data));
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001183 data.retrans = 3;
1184 data.acregmin = 3;
1185 data.acregmax = 60;
1186 data.acdirmin = 30;
1187 data.acdirmax = 60;
1188 data.namlen = NAME_MAX;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001189
Denis Vlasenko25098f72006-09-14 15:46:33 +00001190 soft = 0;
1191 intr = 0;
1192 posix = 0;
1193 nocto = 0;
1194 nolock = 0;
1195 noac = 0;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001196 nordirplus = 0;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001197 retry = 10000; /* 10000 minutes ~ 1 week */
Bernhard Reutner-Fischer88206292011-05-04 19:03:30 +02001198 tcp = 1; /* nfs-utils uses tcp per default */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001199
1200 mountprog = MOUNTPROG;
1201 mountvers = 0;
1202 port = 0;
1203 mountport = 0;
1204 nfsprog = 100003;
1205 nfsvers = 0;
1206
1207 /* parse options */
Denis Vlasenko68de7202007-05-09 20:38:04 +00001208 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001209 char *opteq = strchr(opt, '=');
1210 if (opteq) {
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001211 int val, idx;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001212 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001213 /* 0 */ "rsize\0"
1214 /* 1 */ "wsize\0"
1215 /* 2 */ "timeo\0"
1216 /* 3 */ "retrans\0"
1217 /* 4 */ "acregmin\0"
1218 /* 5 */ "acregmax\0"
1219 /* 6 */ "acdirmin\0"
1220 /* 7 */ "acdirmax\0"
1221 /* 8 */ "actimeo\0"
1222 /* 9 */ "retry\0"
1223 /* 10 */ "port\0"
1224 /* 11 */ "mountport\0"
1225 /* 12 */ "mounthost\0"
1226 /* 13 */ "mountprog\0"
1227 /* 14 */ "mountvers\0"
1228 /* 15 */ "nfsprog\0"
1229 /* 16 */ "nfsvers\0"
1230 /* 17 */ "vers\0"
1231 /* 18 */ "proto\0"
1232 /* 19 */ "namlen\0"
1233 /* 20 */ "addr\0";
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001234
1235 *opteq++ = '\0';
1236 idx = index_in_strings(options, opt);
1237 switch (idx) {
1238 case 12: // "mounthost"
1239 mounthost = xstrndup(opteq,
1240 strcspn(opteq, " \t\n\r,"));
1241 continue;
1242 case 18: // "proto"
1243 if (!strncmp(opteq, "tcp", 3))
1244 tcp = 1;
1245 else if (!strncmp(opteq, "udp", 3))
1246 tcp = 0;
1247 else
1248 bb_error_msg("warning: unrecognized proto= option");
1249 continue;
1250 case 20: // "addr" - ignore
1251 continue;
Peter Korsgaard301fe502011-02-21 17:52:13 +01001252 case -1: // unknown
1253 if (vfsflags & MS_REMOUNT)
1254 continue;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001255 }
1256
Denys Vlasenko77832482010-08-12 14:14:45 +02001257 val = xatoi_positive(opteq);
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001258 switch (idx) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001259 case 0: // "rsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001260 data.rsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001261 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001262 case 1: // "wsize"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001263 data.wsize = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001264 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001265 case 2: // "timeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001266 data.timeo = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001267 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001268 case 3: // "retrans"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001269 data.retrans = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001270 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001271 case 4: // "acregmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001272 data.acregmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001273 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001274 case 5: // "acregmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001275 data.acregmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001276 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001277 case 6: // "acdirmin"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001278 data.acdirmin = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001279 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001280 case 7: // "acdirmax"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001281 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001282 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001283 case 8: // "actimeo"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001284 data.acregmin = val;
1285 data.acregmax = val;
1286 data.acdirmin = val;
1287 data.acdirmax = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001288 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001289 case 9: // "retry"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001290 retry = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001291 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001292 case 10: // "port"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001293 port = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001294 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001295 case 11: // "mountport"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001296 mountport = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001297 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001298 case 13: // "mountprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001299 mountprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001300 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001301 case 14: // "mountvers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001302 mountvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001303 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001304 case 15: // "nfsprog"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001305 nfsprog = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001306 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001307 case 16: // "nfsvers"
1308 case 17: // "vers"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001309 nfsvers = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001310 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001311 case 19: // "namlen"
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001312 //if (nfs_mount_version >= 2)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001313 data.namlen = val;
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001314 //else
1315 // bb_error_msg("warning: option namlen is not supported\n");
1316 continue;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001317 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001318 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1319 goto fail;
1320 }
1321 }
Denis Vlasenkof26e3d22008-06-24 21:39:32 +00001322 else { /* not of the form opt=val */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001323 static const char options[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001324 "bg\0"
1325 "fg\0"
1326 "soft\0"
1327 "hard\0"
1328 "intr\0"
1329 "posix\0"
1330 "cto\0"
1331 "ac\0"
1332 "tcp\0"
1333 "udp\0"
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001334 "lock\0"
1335 "rdirplus\0";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001336 int val = 1;
1337 if (!strncmp(opt, "no", 2)) {
1338 val = 0;
1339 opt += 2;
1340 }
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001341 switch (index_in_strings(options, opt)) {
Denis Vlasenko68f21872006-10-26 01:47:34 +00001342 case 0: // "bg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001343#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001344 bg = val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001345#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001346 break;
1347 case 1: // "fg"
Denis Vlasenkof0000652007-09-04 18:30:26 +00001348#if BB_MMU
Denis Vlasenko25098f72006-09-14 15:46:33 +00001349 bg = !val;
Denis Vlasenkof0000652007-09-04 18:30:26 +00001350#endif
Denis Vlasenko68f21872006-10-26 01:47:34 +00001351 break;
1352 case 2: // "soft"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001353 soft = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001354 break;
1355 case 3: // "hard"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001356 soft = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001357 break;
1358 case 4: // "intr"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001359 intr = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001360 break;
1361 case 5: // "posix"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001362 posix = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001363 break;
1364 case 6: // "cto"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001365 nocto = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001366 break;
1367 case 7: // "ac"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001368 noac = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001369 break;
1370 case 8: // "tcp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001371 tcp = val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001372 break;
1373 case 9: // "udp"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001374 tcp = !val;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001375 break;
1376 case 10: // "lock"
Denis Vlasenko25098f72006-09-14 15:46:33 +00001377 if (nfs_mount_version >= 3)
1378 nolock = !val;
1379 else
1380 bb_error_msg("warning: option nolock is not supported");
Denis Vlasenko68f21872006-10-26 01:47:34 +00001381 break;
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001382 case 11: //rdirplus
1383 nordirplus = !val;
1384 break;
Denis Vlasenko68f21872006-10-26 01:47:34 +00001385 default:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001386 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1387 goto fail;
1388 }
1389 }
1390 }
1391 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1392
1393 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1394 | (intr ? NFS_MOUNT_INTR : 0)
1395 | (posix ? NFS_MOUNT_POSIX : 0)
1396 | (nocto ? NFS_MOUNT_NOCTO : 0)
Denis Vlasenkoc29684a2008-07-19 22:40:30 +00001397 | (noac ? NFS_MOUNT_NOAC : 0)
1398 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001399 if (nfs_mount_version >= 2)
1400 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1401 if (nfs_mount_version >= 3)
1402 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1403 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1404 bb_error_msg("NFSv%d not supported", nfsvers);
1405 goto fail;
1406 }
1407 if (nfsvers && !mountvers)
1408 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1409 if (nfsvers && nfsvers < mountvers) {
1410 mountvers = nfsvers;
1411 }
1412
1413 /* Adjust options if none specified */
1414 if (!data.timeo)
1415 data.timeo = tcp ? 70 : 7;
1416
Denis Vlasenko25098f72006-09-14 15:46:33 +00001417 data.version = nfs_mount_version;
1418
1419 if (vfsflags & MS_REMOUNT)
1420 goto do_mount;
1421
1422 /*
1423 * If the previous mount operation on the same host was
1424 * backgrounded, and the "bg" for this mount is also set,
1425 * give up immediately, to avoid the initial timeout.
1426 */
1427 if (bg && we_saw_this_host_before(hostname)) {
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001428 daemonized = daemonize();
Denis Vlasenko25098f72006-09-14 15:46:33 +00001429 if (daemonized <= 0) { /* parent or error */
1430 retval = -daemonized;
1431 goto ret;
1432 }
1433 }
1434
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001435 /* Create mount daemon client */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001436 /* See if the nfs host = mount host. */
1437 if (mounthost) {
1438 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1439 mount_server_addr.sin_family = AF_INET;
1440 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1441 } else {
1442 hp = gethostbyname(mounthost);
1443 if (hp == NULL) {
1444 bb_herror_msg("%s", mounthost);
1445 goto fail;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001446 }
Denys Vlasenko99069332010-02-27 19:38:19 +01001447 if (hp->h_length != (int)sizeof(struct in_addr)) {
1448 bb_error_msg_and_die("only IPv4 is supported");
Denis Vlasenko77ad97f2008-05-13 02:27:31 +00001449 }
1450 mount_server_addr.sin_family = AF_INET;
Denys Vlasenko99069332010-02-27 19:38:19 +01001451 memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
Denis Vlasenko25098f72006-09-14 15:46:33 +00001452 }
1453 }
1454
1455 /*
1456 * The following loop implements the mount retries. When the mount
1457 * times out, and the "bg" option is set, we background ourself
1458 * and continue trying.
1459 *
1460 * The case where the mount point is not present and the "bg"
1461 * option is set, is treated as a timeout. This is done to
1462 * support nested mounts.
1463 *
1464 * The "retry" count specified by the user is the number of
1465 * minutes to retry before giving up.
1466 */
1467 {
1468 struct timeval total_timeout;
1469 struct timeval retry_timeout;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001470 struct pmap pm_mnt;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001471 time_t t;
1472 time_t prevt;
1473 time_t timeout;
1474
1475 retry_timeout.tv_sec = 3;
1476 retry_timeout.tv_usec = 0;
1477 total_timeout.tv_sec = 20;
1478 total_timeout.tv_usec = 0;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001479/* FIXME: use monotonic()? */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001480 timeout = time(NULL) + 60 * retry;
1481 prevt = 0;
1482 t = 30;
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001483 retry:
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001484 /* Be careful not to use too many CPU cycles */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001485 if (t - prevt < 30)
1486 sleep(30);
1487
Denis Vlasenkob9256052007-09-28 10:29:17 +00001488 get_mountport(&pm_mnt, &mount_server_addr,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001489 mountprog,
1490 mountvers,
1491 proto,
1492 mountport);
Denis Vlasenkob9256052007-09-28 10:29:17 +00001493 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001494
1495 /* contact the mount daemon via TCP */
Denis Vlasenkob9256052007-09-28 10:29:17 +00001496 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001497 msock = RPC_ANYSOCK;
1498
Denis Vlasenkob9256052007-09-28 10:29:17 +00001499 switch (pm_mnt.pm_prot) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001500 case IPPROTO_UDP:
1501 mclient = clntudp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001502 pm_mnt.pm_prog,
1503 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001504 retry_timeout,
1505 &msock);
1506 if (mclient)
1507 break;
Denis Vlasenkob9256052007-09-28 10:29:17 +00001508 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
Denis Vlasenko25098f72006-09-14 15:46:33 +00001509 msock = RPC_ANYSOCK;
1510 case IPPROTO_TCP:
1511 mclient = clnttcp_create(&mount_server_addr,
Denis Vlasenkob9256052007-09-28 10:29:17 +00001512 pm_mnt.pm_prog,
1513 pm_mnt.pm_vers,
Denis Vlasenko25098f72006-09-14 15:46:33 +00001514 &msock, 0, 0);
1515 break;
1516 default:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001517 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001518 }
1519 if (!mclient) {
1520 if (!daemonized && prevt == 0)
1521 error_msg_rpc(clnt_spcreateerror(" "));
1522 } else {
1523 enum clnt_stat clnt_stat;
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001524
1525 /* Try to mount hostname:pathname */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001526 mclient->cl_auth = authunix_create_default();
1527
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001528 /* Make pointers in xdr_mountres3 NULL so
Denis Vlasenko25098f72006-09-14 15:46:33 +00001529 * that xdr_array allocates memory for us
1530 */
1531 memset(&status, 0, sizeof(status));
1532
Denis Vlasenkob9256052007-09-28 10:29:17 +00001533 if (pm_mnt.pm_vers == 3)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001534 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1535 (xdrproc_t) xdr_dirpath,
1536 (caddr_t) &pathname,
1537 (xdrproc_t) xdr_mountres3,
1538 (caddr_t) &status,
1539 total_timeout);
1540 else
1541 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1542 (xdrproc_t) xdr_dirpath,
1543 (caddr_t) &pathname,
1544 (xdrproc_t) xdr_fhstatus,
1545 (caddr_t) &status,
1546 total_timeout);
1547
1548 if (clnt_stat == RPC_SUCCESS)
1549 goto prepare_kernel_data; /* we're done */
1550 if (errno != ECONNREFUSED) {
1551 error_msg_rpc(clnt_sperror(mclient, " "));
1552 goto fail; /* don't retry */
1553 }
1554 /* Connection refused */
1555 if (!daemonized && prevt == 0) /* print just once */
1556 error_msg_rpc(clnt_sperror(mclient, " "));
1557 auth_destroy(mclient->cl_auth);
1558 clnt_destroy(mclient);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001559 mclient = NULL;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001560 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001561 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001562 }
1563
1564 /* Timeout. We are going to retry... maybe */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001565 if (!bg)
1566 goto fail;
1567 if (!daemonized) {
1568 daemonized = daemonize();
1569 if (daemonized <= 0) { /* parent or error */
1570 retval = -daemonized;
1571 goto ret;
1572 }
1573 }
1574 prevt = t;
1575 t = time(NULL);
1576 if (t >= timeout)
1577 /* TODO error message */
1578 goto fail;
1579
1580 goto retry;
1581 }
1582
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001583 prepare_kernel_data:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001584
1585 if (nfsvers == 2) {
1586 if (status.nfsv2.fhs_status != 0) {
1587 bb_error_msg("%s:%s failed, reason given by server: %s",
1588 hostname, pathname,
1589 nfs_strerror(status.nfsv2.fhs_status));
1590 goto fail;
1591 }
1592 memcpy(data.root.data,
1593 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1594 NFS_FHSIZE);
1595 data.root.size = NFS_FHSIZE;
1596 memcpy(data.old_root.data,
1597 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1598 NFS_FHSIZE);
1599 } else {
1600 fhandle3 *my_fhandle;
1601 if (status.nfsv3.fhs_status != 0) {
1602 bb_error_msg("%s:%s failed, reason given by server: %s",
1603 hostname, pathname,
1604 nfs_strerror(status.nfsv3.fhs_status));
1605 goto fail;
1606 }
1607 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1608 memset(data.old_root.data, 0, NFS_FHSIZE);
1609 memset(&data.root, 0, sizeof(data.root));
1610 data.root.size = my_fhandle->fhandle3_len;
1611 memcpy(data.root.data,
1612 (char *) my_fhandle->fhandle3_val,
1613 my_fhandle->fhandle3_len);
1614
1615 data.flags |= NFS_MOUNT_VER3;
1616 }
1617
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001618 /* Create nfs socket for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001619 if (tcp) {
1620 if (nfs_mount_version < 3) {
1621 bb_error_msg("NFS over TCP is not supported");
1622 goto fail;
1623 }
1624 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1625 } else
1626 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1627 if (fsock < 0) {
1628 bb_perror_msg("nfs socket");
1629 goto fail;
1630 }
1631 if (bindresvport(fsock, 0) < 0) {
1632 bb_perror_msg("nfs bindresvport");
1633 goto fail;
1634 }
1635 if (port == 0) {
1636 server_addr.sin_port = PMAPPORT;
1637 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1638 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1639 if (port == 0)
1640 port = NFS_PORT;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001641 }
Denis Vlasenko25098f72006-09-14 15:46:33 +00001642 server_addr.sin_port = htons(port);
1643
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001644 /* Prepare data structure for kernel */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001645 data.fd = fsock;
1646 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1647 strncpy(data.hostname, hostname, sizeof(data.hostname));
1648
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001649 /* Clean up */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001650 auth_destroy(mclient->cl_auth);
1651 clnt_destroy(mclient);
1652 close(msock);
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001653 msock = -1;
Denis Vlasenko25098f72006-09-14 15:46:33 +00001654
1655 if (bg) {
1656 /* We must wait until mount directory is available */
1657 struct stat statbuf;
1658 int delay = 1;
1659 while (stat(mp->mnt_dir, &statbuf) == -1) {
1660 if (!daemonized) {
1661 daemonized = daemonize();
1662 if (daemonized <= 0) { /* parent or error */
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001663/* FIXME: parent doesn't close fsock - ??! */
Denis Vlasenko25098f72006-09-14 15:46:33 +00001664 retval = -daemonized;
1665 goto ret;
1666 }
1667 }
1668 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
1669 delay *= 2;
1670 if (delay > 30)
1671 delay = 30;
1672 }
1673 }
1674
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001675 /* Perform actual mount */
1676 do_mount:
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001677 mp->mnt_type = (char*)"nfs";
Denis Vlasenko25098f72006-09-14 15:46:33 +00001678 retval = mount_it_now(mp, vfsflags, (char*)&data);
1679 goto ret;
1680
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001681 /* Abort */
1682 fail:
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001683 if (msock >= 0) {
Denis Vlasenko25098f72006-09-14 15:46:33 +00001684 if (mclient) {
1685 auth_destroy(mclient->cl_auth);
1686 clnt_destroy(mclient);
1687 }
1688 close(msock);
1689 }
Denis Vlasenko7d8de4d2007-08-28 11:23:23 +00001690 if (fsock >= 0)
Denis Vlasenko25098f72006-09-14 15:46:33 +00001691 close(fsock);
1692
Denis Vlasenko63430ae2007-10-29 19:18:39 +00001693 ret:
Denis Vlasenko25098f72006-09-14 15:46:33 +00001694 free(hostname);
1695 free(mounthost);
1696 free(filteropts);
1697 return retval;
1698}
1699
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001700#else // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001701
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001702// Never called. Call should be optimized out.
Denis Vlasenkob4133682008-02-18 13:05:38 +00001703int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001704
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001705#endif // !ENABLE_FEATURE_MOUNT_NFS
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001706
1707// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
1708// type detection. Returns 0 for success, nonzero for failure.
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001709// NB: mp->xxx fields may be trashed on exit
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001710static int singlemount(struct mntent *mp, int ignore_busy)
1711{
Denis Vlasenkob4133682008-02-18 13:05:38 +00001712 int rc = -1;
1713 long vfsflags;
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001714 char *loopFile = NULL, *filteropts = NULL;
1715 llist_t *fl = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001716 struct stat st;
1717
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001718 errno = 0;
1719
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001720 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1721
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001722 // Treat fstype "auto" as unspecified
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001723 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1724 mp->mnt_type = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001725
Denis Vlasenko2535f122007-09-15 13:28:30 +00001726 // Might this be a virtual filesystem?
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001727 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1728 char *args[35];
1729 char *s;
1730 int n;
1731 // fsname: "cmd#arg1#arg2..."
1732 // WARNING: allows execution of arbitrary commands!
1733 // Try "mount 'sh#-c#sh' bogus_dir".
1734 // It is safe ONLY because non-root
1735 // cannot use two-argument mount command
1736 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1737 // "mount: can't find sh#-c#sh in /etc/fstab"
1738 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1739
1740 s = mp->mnt_fsname;
1741 n = 0;
1742 args[n++] = s;
1743 while (*s && n < 35 - 2) {
1744 if (*s++ == '#' && *s != '#') {
1745 s[-1] = '\0';
1746 args[n++] = s;
Denis Vlasenko2535f122007-09-15 13:28:30 +00001747 }
1748 }
Denis Vlasenko2535f122007-09-15 13:28:30 +00001749 args[n++] = mp->mnt_dir;
1750 args[n] = NULL;
Denys Vlasenko8531d762010-03-18 22:44:00 +01001751 rc = spawn_and_wait(args);
Denis Vlasenko2535f122007-09-15 13:28:30 +00001752 goto report_error;
1753 }
1754
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001755 // Might this be an CIFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001756 if (ENABLE_FEATURE_MOUNT_CIFS
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001757 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1758 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1759 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001760 ) {
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001761 int len;
1762 char c;
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001763 len_and_sockaddr *lsa;
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001764 char *hostname, *dotted, *ip;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001765
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001766 hostname = mp->mnt_fsname + 2;
1767 len = strcspn(hostname, "/\\");
1768 if (len == 0 || hostname[len] == '\0')
Denis Vlasenko5c329932009-04-12 12:16:21 +00001769 goto report_error;
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001770 c = hostname[len];
1771 hostname[len] = '\0';
1772 lsa = host2sockaddr(hostname, 0);
1773 hostname[len] = c;
Denis Vlasenko5c329932009-04-12 12:16:21 +00001774 if (!lsa)
1775 goto report_error;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001776
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001777 // Insert "ip=..." option into options
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +00001778 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001779 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001780 ip = xasprintf("ip=%s", dotted);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001781 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001782 parse_mount_options(ip, &filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001783 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001784
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001785 // "-o mand" is required [why?]
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001786 vfsflags |= MS_MANDLOCK;
Denis Vlasenko06c0a712007-01-29 22:51:44 +00001787 mp->mnt_type = (char*)"cifs";
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001788 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001789
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001790 goto report_error;
1791 }
1792
1793 // Might this be an NFS filesystem?
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001794 if (ENABLE_FEATURE_MOUNT_NFS
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001795 && (!mp->mnt_type || strcmp(mp->mnt_type, "nfs") == 0)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001796 && strchr(mp->mnt_fsname, ':') != NULL
1797 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001798 rc = nfsmount(mp, vfsflags, filteropts);
1799 goto report_error;
1800 }
1801
1802 // Look at the file. (Not found isn't a failure for remount, or for
1803 // a synthetic filesystem like proc or sysfs.)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001804 // (We use stat, not lstat, in order to allow
1805 // mount symlink_to_file_or_blkdev dir)
Denis Vlasenko38ec1472007-05-20 12:32:41 +00001806 if (!stat(mp->mnt_fsname, &st)
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001807 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1808 ) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001809 // Do we need to allocate a loopback device for it?
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001810 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1811 loopFile = bb_simplify_path(mp->mnt_fsname);
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001812 mp->mnt_fsname = NULL; // will receive malloced loop dev name
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001813 if (set_loop(&mp->mnt_fsname, loopFile, 0) < 0) {
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001814 if (errno == EPERM || errno == EACCES)
1815 bb_error_msg(bb_msg_perm_denied_are_you_root);
1816 else
Denys Vlasenko6331cf02009-11-13 09:08:27 +01001817 bb_perror_msg("can't setup loop device");
Denis Vlasenko13b49242006-09-17 15:04:35 +00001818 return errno;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001819 }
1820
1821 // Autodetect bind mounts
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001822 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1823 vfsflags |= MS_BIND;
1824 }
1825
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00001826 // If we know the fstype (or don't need to), jump straight
1827 // to the actual mount.
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001828 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001829 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001830 } else {
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00001831 // Loop through filesystem types until mount succeeds
1832 // or we run out
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001833
Denys Vlasenkob7a0e132009-12-15 16:36:14 +01001834 // Initialize list of block backed filesystems.
1835 // This has to be done here so that during "mount -a",
1836 // mounts after /proc shows up can autodetect.
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001837 if (!fslist) {
1838 fslist = get_block_backed_filesystems();
1839 if (ENABLE_FEATURE_CLEAN_UP && fslist)
1840 atexit(delete_block_backed_filesystems);
1841 }
1842
1843 for (fl = fslist; fl; fl = fl->link) {
1844 mp->mnt_type = fl->data;
Denis Vlasenko13b49242006-09-17 15:04:35 +00001845 rc = mount_it_now(mp, vfsflags, filteropts);
Denys Vlasenkoe2e4cc22009-06-19 11:48:29 +02001846 if (!rc)
1847 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001848 }
1849 }
1850
1851 // If mount failed, clean up loop file (if any).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001852 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1853 del_loop(mp->mnt_fsname);
1854 if (ENABLE_FEATURE_CLEAN_UP) {
1855 free(loopFile);
1856 free(mp->mnt_fsname);
1857 }
1858 }
1859
Denis Vlasenko5870ad92007-02-04 02:39:55 +00001860 report_error:
1861 if (ENABLE_FEATURE_CLEAN_UP)
1862 free(filteropts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001863
Denis Vlasenkoc8d4d2f2007-09-07 19:33:56 +00001864 if (errno == EBUSY && ignore_busy)
1865 return 0;
Denys Vlasenkofa1b3702010-06-27 16:47:40 +02001866 if (rc != 0)
Denis Vlasenko0e2c9fb2007-08-03 14:16:24 +00001867 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001868 return rc;
1869}
1870
Michael Abbott6b5accb2009-12-04 03:33:07 +01001871// -O support
1872// -O interprets a list of filter options which select whether a mount
1873// point will be mounted: only mounts with options matching *all* filtering
1874// options will be selected.
1875// By default each -O filter option must be present in the list of mount
1876// options, but if it is prefixed by "no" then it must be absent.
1877// For example,
1878// -O a,nob,c matches -o a,c but fails to match -o a,b,c
1879// (and also fails to match -o a because -o c is absent).
1880//
1881// It is different from -t in that each option is matched exactly; a leading
1882// "no" at the beginning of one option does not negate the rest.
1883static int match_opt(const char *fs_opt_in, const char *O_opt)
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001884{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001885 if (!O_opt)
Michael Abbott6b5accb2009-12-04 03:33:07 +01001886 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001887
Michael Abbott6b5accb2009-12-04 03:33:07 +01001888 while (*O_opt) {
1889 const char *fs_opt = fs_opt_in;
1890 int O_len;
1891 int match;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001892
Michael Abbott6b5accb2009-12-04 03:33:07 +01001893 // If option begins with "no" then treat as an inverted match:
1894 // matching is a failure
1895 match = 0;
1896 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1897 match = 1;
1898 O_opt += 2;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001899 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01001900 // Isolate the current O option
1901 O_len = strchrnul(O_opt, ',') - O_opt;
1902 // Check for a match against existing options
1903 while (1) {
1904 if (strncmp(fs_opt, O_opt, O_len) == 0
1905 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
1906 ) {
1907 if (match)
1908 return 0; // "no" prefix, but option found
1909 match = 1; // current O option found, go check next one
1910 break;
1911 }
1912 fs_opt = strchr(fs_opt, ',');
1913 if (!fs_opt)
1914 break;
1915 fs_opt++;
1916 }
1917 if (match == 0)
1918 return 0; // match wanted but not found
1919 if (O_opt[O_len] == '\0') // end?
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001920 break;
Michael Abbott6b5accb2009-12-04 03:33:07 +01001921 // Step to the next O option
1922 O_opt += O_len + 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001923 }
Michael Abbott6b5accb2009-12-04 03:33:07 +01001924 // If we get here then everything matched
1925 return 1;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001926}
1927
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001928// Parse options, if necessary parse fstab/mtab, and call singlemount for
1929// each directory to be mounted.
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001930int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001931int mount_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001932{
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001933 char *cmdopts = xzalloc(1);
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001934 char *fstype = NULL;
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001935 char *O_optmatch = NULL;
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001936 char *storage_path;
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001937 llist_t *lst_o = NULL;
Denis Vlasenko85f9e322006-09-19 14:14:12 +00001938 const char *fstabname;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001939 FILE *fstab;
Denys Vlasenkoa7329662009-12-05 04:25:19 +01001940 int i, j;
1941 int rc = EXIT_SUCCESS;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001942 unsigned opt;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001943 struct mntent mtpair[2], *mtcur = mtpair;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001944 IF_NOT_DESKTOP(const int nonroot = 0;)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001945
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001946 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
Denis Vlasenkoc9ca0a32008-02-18 11:08:33 +00001947
Denis Vlasenkof732e962008-02-18 12:07:49 +00001948 // Parse long options, like --bind and --move. Note that -o option
1949 // and --option are synonymous. Yes, this means --remount,rw works.
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001950 for (i = j = 1; argv[i]; i++) {
1951 if (argv[i][0] == '-' && argv[i][1] == '-')
1952 append_mount_options(&cmdopts, argv[i] + 2);
1953 else
1954 argv[j++] = argv[i];
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001955 }
Denis Vlasenkoa4522c52008-03-17 08:46:43 +00001956 argv[j] = NULL;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001957
1958 // Parse remaining options
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001959 // Max 2 params; -o is a list, -v is a counter
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001960 opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00001961 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001962 IF_FEATURE_MOUNT_VERBOSE(, &verbose));
Denis Vlasenkof9dde912008-10-18 19:15:57 +00001963 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
Denis Vlasenkob1d8e7d2008-02-16 23:28:42 +00001964 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
1965 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
Denis Vlasenko3bc59aa2006-09-17 15:04:01 +00001966 argv += optind;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001967
1968 // If we have no arguments, show currently mounted filesystems
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001969 if (!argv[0]) {
Denis Vlasenko397de612008-03-17 08:55:44 +00001970 if (!(opt & OPT_a)) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001971 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
1972
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001973 if (!mountTable)
1974 bb_error_msg_and_die("no %s", bb_path_mtab_file);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001975
Denis Vlasenko2535f122007-09-15 13:28:30 +00001976 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001977 GETMNTENT_BUFSIZE))
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001978 {
Denis Vlasenko13c5a682006-10-16 22:39:51 +00001979 // Don't show rootfs. FIXME: why??
Denis Vlasenko908d6b72006-12-18 23:07:42 +00001980 // util-linux 2.12a happily shows rootfs...
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001981 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001982
Denys Vlasenko4e60f302009-12-15 01:28:59 +01001983 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001984 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
1985 mtpair->mnt_dir, mtpair->mnt_type,
1986 mtpair->mnt_opts);
1987 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001988 if (ENABLE_FEATURE_CLEAN_UP)
1989 endmntent(mountTable);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00001990 return EXIT_SUCCESS;
1991 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00001992 storage_path = NULL;
1993 } else {
1994 // When we have two arguments, the second is the directory and we can
1995 // skip looking at fstab entirely. We can always abspath() the directory
1996 // argument when we get it.
1997 if (argv[1]) {
1998 if (nonroot)
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01001999 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002000 mtpair->mnt_fsname = argv[0];
2001 mtpair->mnt_dir = argv[1];
2002 mtpair->mnt_type = fstype;
2003 mtpair->mnt_opts = cmdopts;
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002004 resolve_mount_spec(&mtpair->mnt_fsname);
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002005 rc = singlemount(mtpair, /*ignore_busy:*/ 0);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002006 return rc;
Denis Vlasenkode7684a2008-02-18 21:08:49 +00002007 }
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002008 storage_path = bb_simplify_path(argv[0]); // malloced
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002009 }
2010
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002011 // Past this point, we are handling either "mount -a [opts]"
2012 // or "mount [opts] single_param"
2013
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002014 i = parse_mount_options(cmdopts, NULL); // FIXME: should be "long", not "int"
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002015 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002016 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko546cd182006-10-02 18:52:49 +00002017
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002018 // If we have a shared subtree flag, don't worry about fstab or mtab.
Denis Vlasenko6cd2d2b2007-01-22 14:06:03 +00002019 if (ENABLE_FEATURE_MOUNT_FLAGS
2020 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
2021 ) {
Denis Vlasenko30e5cf82008-12-05 16:40:36 +00002022 // verbose_mount(source, target, type, flags, data)
2023 rc = verbose_mount("", argv[0], "", i, "");
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002024 if (rc)
2025 bb_simple_perror_msg_and_die(argv[0]);
2026 return rc;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002027 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00002028
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002029 // Open either fstab or mtab
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002030 fstabname = "/etc/fstab";
2031 if (i & MS_REMOUNT) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002032 // WARNING. I am not sure this matches util-linux's
2033 // behavior. It's possible util-linux does not
2034 // take -o opts from mtab (takes only mount source).
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002035 fstabname = bb_path_mtab_file;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002036 }
2037 fstab = setmntent(fstabname, "r");
Denis Vlasenko8d474b52006-09-17 15:00:58 +00002038 if (!fstab)
Denys Vlasenko651a2692010-03-23 16:25:17 +01002039 bb_perror_msg_and_die("can't read '%s'", fstabname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002040
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002041 // Loop through entries until we find what we're looking for
Denis Vlasenko546cd182006-10-02 18:52:49 +00002042 memset(mtpair, 0, sizeof(mtpair));
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002043 for (;;) {
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002044 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002045
2046 // Get next fstab entry
Denis Vlasenko2535f122007-09-15 13:28:30 +00002047 if (!getmntent_r(fstab, mtcur, getmntent_buf
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00002048 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002049 GETMNTENT_BUFSIZE/2)
2050 ) { // End of fstab/mtab is reached
2051 mtcur = mtother; // the thing we found last time
2052 break;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002053 }
2054
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002055 // If we're trying to mount something specific and this isn't it,
2056 // skip it. Note we must match the exact text in fstab (ala
2057 // "proc") or a full path from root
2058 if (argv[0]) {
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002059
2060 // Is this what we're looking for?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002061 if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2062 && strcmp(storage_path, mtcur->mnt_fsname) != 0
2063 && strcmp(argv[0], mtcur->mnt_dir) != 0
2064 && strcmp(storage_path, mtcur->mnt_dir) != 0
2065 ) {
2066 continue; // no
2067 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002068
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002069 // Remember this entry. Something later may have
2070 // overmounted it, and we want the _last_ match.
2071 mtcur = mtother;
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002072
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002073 // If we're mounting all
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002074 } else {
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002075 struct mntent *mp;
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002076 // No, mount -a won't mount anything,
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002077 // even user mounts, for mere humans
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002078 if (nonroot)
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002079 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenko13c5a682006-10-16 22:39:51 +00002080
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002081 // Does type match? (NULL matches always)
2082 if (!match_fstype(mtcur, fstype))
2083 continue;
2084
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002085 // Skip noauto and swap anyway
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002086 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2087 // swap is bogus "fstype", parse_mount_options can't check fstypes
2088 || strcasecmp(mtcur->mnt_type, "swap") == 0
2089 ) {
2090 continue;
2091 }
2092
2093 // Does (at least one) option match?
2094 // (NULL matches always)
2095 if (!match_opt(mtcur->mnt_opts, O_optmatch))
2096 continue;
2097
2098 resolve_mount_spec(&mtcur->mnt_fsname);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002099
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002100 // NFS mounts want this to be xrealloc-able
2101 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
Denis Vlasenko6a2d0d92008-12-10 11:39:18 +00002102
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002103 // If nothing is mounted on this directory...
2104 // (otherwise repeated "mount -a" mounts everything again)
2105 mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2106 // We do not check fsname match of found mount point -
2107 // "/" may have fsname of "/dev/root" while fstab
2108 // says "/dev/something_else".
2109 if (mp) {
Denys Vlasenko86566762009-12-10 21:32:28 +01002110 if (verbose) {
2111 bb_error_msg("according to %s, "
2112 "%s is already mounted on %s",
2113 bb_path_mtab_file,
2114 mp->mnt_fsname, mp->mnt_dir);
2115 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002116 } else {
2117 // ...mount this thing
2118 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2119 // Count number of failed mounts
2120 rc++;
2121 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002122 }
Denis Vlasenko666da5e2006-12-26 18:17:42 +00002123 free(mtcur->mnt_opts);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002124 }
2125 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002126
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002127 // End of fstab/mtab is reached.
2128 // Were we looking for something specific?
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002129 if (argv[0]) { // yes
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002130 long l;
2131
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002132 // If we didn't find anything, complain
2133 if (!mtcur->mnt_fsname)
2134 bb_error_msg_and_die("can't find %s in %s",
2135 argv[0], fstabname);
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002136
2137 // What happens when we try to "mount swap_partition"?
2138 // (fstab containts "swap_partition swap swap defaults 0 0")
2139 // util-linux-ng 2.13.1 does this:
2140 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2141 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2142 // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory)
2143 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2144 // exit_group(32) = ?
2145#if 0
2146 // In case we want to simply skip swap partitions:
2147 l = parse_mount_options(mtcur->mnt_opts, NULL);
2148 if ((l & MOUNT_SWAP)
2149 // swap is bogus "fstype", parse_mount_options can't check fstypes
2150 || strcasecmp(mtcur->mnt_type, "swap") == 0
2151 ) {
2152 goto ret;
2153 }
2154#endif
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002155 if (nonroot) {
2156 // fstab must have "users" or "user"
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002157 l = parse_mount_options(mtcur->mnt_opts, NULL);
2158 if (!(l & MOUNT_USERS))
Denys Vlasenkob2e5fc32009-11-25 14:52:47 +01002159 bb_error_msg_and_die(bb_msg_you_must_be_root);
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002160 }
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002161
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002162 //util-linux-2.12 does not do this check.
2163 //// If nothing is mounted on this directory...
2164 //// (otherwise repeated "mount FOO" mounts FOO again)
2165 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2166 //if (mp) {
2167 // bb_error_msg("according to %s, "
2168 // "%s is already mounted on %s",
2169 // bb_path_mtab_file,
2170 // mp->mnt_fsname, mp->mnt_dir);
2171 //} else {
2172 // ...mount the last thing we found
2173 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2174 append_mount_options(&(mtcur->mnt_opts), cmdopts);
2175 resolve_mount_spec(&mtpair->mnt_fsname);
2176 rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2177 if (ENABLE_FEATURE_CLEAN_UP)
2178 free(mtcur->mnt_opts);
2179 //}
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002180 }
2181
Denis Vlasenko7aaedcf2009-03-14 22:57:20 +00002182 //ret:
Denis Vlasenkod0cc3f42008-06-24 18:59:59 +00002183 if (ENABLE_FEATURE_CLEAN_UP)
2184 endmntent(fstab);
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002185 if (ENABLE_FEATURE_CLEAN_UP) {
2186 free(storage_path);
2187 free(cmdopts);
2188 }
Denys Vlasenkoa7329662009-12-05 04:25:19 +01002189
2190//TODO: exitcode should be ORed mask of (from "man mount"):
2191// 0 success
2192// 1 incorrect invocation or permissions
2193// 2 system error (out of memory, cannot fork, no more loop devices)
2194// 4 internal mount bug or missing nfs support in mount
2195// 8 user interrupt
2196//16 problems writing or locking /etc/mtab
2197//32 mount failure
2198//64 some mount succeeded
Denis Vlasenko30a64cd2006-09-15 15:12:00 +00002199 return rc;
2200}