8 |
* |
* |
9 |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
10 |
*/ |
*/ |
11 |
|
// Design notes: There is no spec for mount. Remind me to write one. |
12 |
/* Design notes: There is no spec for mount. Remind me to write one. |
// |
13 |
|
// mount_main() calls singlemount() which calls mount_it_now(). |
14 |
mount_main() calls singlemount() which calls mount_it_now(). |
// |
15 |
|
// mount_main() can loop through /etc/fstab for mount -a |
16 |
mount_main() can loop through /etc/fstab for mount -a |
// singlemount() can loop through /etc/filesystems for fstype detection. |
17 |
singlemount() can loop through /etc/filesystems for fstype detection. |
// mount_it_now() does the actual mount. |
18 |
mount_it_now() does the actual mount. |
// |
|
*/ |
|
|
|
|
19 |
#include <mntent.h> |
#include <mntent.h> |
20 |
#include <syslog.h> |
#include <syslog.h> |
21 |
#include "libbb.h" |
#include <sys/mount.h> |
22 |
|
// Grab more as needed from util-linux's mount/mount_constants.h |
23 |
|
#ifndef MS_DIRSYNC |
24 |
|
# define MS_DIRSYNC (1 << 7) // Directory modifications are synchronous |
25 |
|
#endif |
26 |
|
#ifndef MS_UNION |
27 |
|
# define MS_UNION (1 << 8) |
28 |
|
#endif |
29 |
|
#ifndef MS_BIND |
30 |
|
# define MS_BIND (1 << 12) |
31 |
|
#endif |
32 |
|
#ifndef MS_MOVE |
33 |
|
# define MS_MOVE (1 << 13) |
34 |
|
#endif |
35 |
|
#ifndef MS_RECURSIVE |
36 |
|
# define MS_RECURSIVE (1 << 14) |
37 |
|
#endif |
38 |
|
#ifndef MS_SILENT |
39 |
|
# define MS_SILENT (1 << 15) |
40 |
|
#endif |
41 |
|
// The shared subtree stuff, which went in around 2.6.15 |
42 |
|
#ifndef MS_UNBINDABLE |
43 |
|
# define MS_UNBINDABLE (1 << 17) |
44 |
|
#endif |
45 |
|
#ifndef MS_PRIVATE |
46 |
|
# define MS_PRIVATE (1 << 18) |
47 |
|
#endif |
48 |
|
#ifndef MS_SLAVE |
49 |
|
# define MS_SLAVE (1 << 19) |
50 |
|
#endif |
51 |
|
#ifndef MS_SHARED |
52 |
|
# define MS_SHARED (1 << 20) |
53 |
|
#endif |
54 |
|
#ifndef MS_RELATIME |
55 |
|
# define MS_RELATIME (1 << 21) |
56 |
|
#endif |
57 |
|
|
58 |
|
#include "libbb.h" |
59 |
#if ENABLE_FEATURE_MOUNT_LABEL |
#if ENABLE_FEATURE_MOUNT_LABEL |
60 |
#include "volume_id.h" |
# include "volume_id.h" |
61 |
|
#else |
62 |
|
# define resolve_mount_spec(fsname) ((void)0) |
63 |
#endif |
#endif |
64 |
|
|
65 |
/* Needed for nfs support only */ |
// Needed for nfs support only |
66 |
#include <sys/utsname.h> |
#include <sys/utsname.h> |
67 |
#undef TRUE |
#undef TRUE |
68 |
#undef FALSE |
#undef FALSE |
69 |
#include <rpc/rpc.h> |
#if ENABLE_FEATURE_MOUNT_NFS |
70 |
#include <rpc/pmap_prot.h> |
/* This is just a warning of a common mistake. Possibly this should be a |
71 |
#include <rpc/pmap_clnt.h> |
* uclibc faq entry rather than in busybox... */ |
72 |
|
# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) |
73 |
#ifndef MS_SILENT |
# error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support" |
74 |
#define MS_SILENT (1 << 15) |
# endif |
75 |
#endif |
# include <rpc/rpc.h> |
76 |
/* Grab more as needed from util-linux's mount/mount_constants.h */ |
# include <rpc/pmap_prot.h> |
77 |
#ifndef MS_DIRSYNC |
# include <rpc/pmap_clnt.h> |
|
#define MS_DIRSYNC 128 /* Directory modifications are synchronous */ |
|
78 |
#endif |
#endif |
79 |
|
|
80 |
|
|
81 |
#if defined(__dietlibc__) |
#if defined(__dietlibc__) |
82 |
/* 16.12.2006, Sampo Kellomaki (sampo@iki.fi) |
// 16.12.2006, Sampo Kellomaki (sampo@iki.fi) |
83 |
* dietlibc-0.30 does not have implementation of getmntent_r() */ |
// dietlibc-0.30 does not have implementation of getmntent_r() |
84 |
static struct mntent *getmntent_r(FILE* stream, struct mntent* result, |
static struct mntent *getmntent_r(FILE* stream, struct mntent* result, |
85 |
char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM) |
char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM) |
86 |
{ |
{ |
98 |
}; |
}; |
99 |
|
|
100 |
|
|
101 |
#define OPTION_STR "o:t:rwanfvsi" |
#define OPTION_STR "o:t:rwanfvsiO:" |
102 |
enum { |
enum { |
103 |
OPT_o = (1 << 0), |
OPT_o = (1 << 0), |
104 |
OPT_t = (1 << 1), |
OPT_t = (1 << 1), |
110 |
OPT_v = (1 << 7), |
OPT_v = (1 << 7), |
111 |
OPT_s = (1 << 8), |
OPT_s = (1 << 8), |
112 |
OPT_i = (1 << 9), |
OPT_i = (1 << 9), |
113 |
|
OPT_O = (1 << 10), |
114 |
}; |
}; |
115 |
|
|
116 |
#if ENABLE_FEATURE_MTAB_SUPPORT |
#if ENABLE_FEATURE_MTAB_SUPPORT |
117 |
#define useMtab (!(option_mask32 & OPT_n)) |
#define USE_MTAB (!(option_mask32 & OPT_n)) |
118 |
#else |
#else |
119 |
#define useMtab 0 |
#define USE_MTAB 0 |
120 |
#endif |
#endif |
121 |
|
|
122 |
#if ENABLE_FEATURE_MOUNT_FAKE |
#if ENABLE_FEATURE_MOUNT_FAKE |
123 |
#define fakeIt (option_mask32 & OPT_f) |
#define FAKE_IT (option_mask32 & OPT_f) |
124 |
#else |
#else |
125 |
#define fakeIt 0 |
#define FAKE_IT 0 |
126 |
|
#endif |
127 |
|
|
128 |
|
#if ENABLE_FEATURE_MOUNT_HELPERS |
129 |
|
#define HELPERS_ALLOWED (!(option_mask32 & OPT_i)) |
130 |
|
#else |
131 |
|
#define HELPERS_ALLOWED 0 |
132 |
#endif |
#endif |
133 |
|
|
134 |
|
|
141 |
// This may be useful e.g. for /dev/fd if a login script makes |
// This may be useful e.g. for /dev/fd if a login script makes |
142 |
// the console user owner of this device. |
// the console user owner of this device. |
143 |
|
|
144 |
/* Standard mount options (from -o options or --options), with corresponding |
// Standard mount options (from -o options or --options), |
145 |
* flags */ |
// with corresponding flags |
|
|
|
146 |
static const int32_t mount_options[] = { |
static const int32_t mount_options[] = { |
147 |
// MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs. |
// MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs. |
148 |
|
|
149 |
USE_FEATURE_MOUNT_LOOP( |
IF_FEATURE_MOUNT_LOOP( |
150 |
/* "loop" */ 0, |
/* "loop" */ 0, |
151 |
) |
) |
152 |
|
|
153 |
USE_FEATURE_MOUNT_FSTAB( |
IF_FEATURE_MOUNT_FSTAB( |
154 |
/* "defaults" */ 0, |
/* "defaults" */ 0, |
155 |
/* "quiet" 0 - do not filter out, vfat wants to see it */ |
/* "quiet" 0 - do not filter out, vfat wants to see it */ |
156 |
/* "noauto" */ MOUNT_NOAUTO, |
/* "noauto" */ MOUNT_NOAUTO, |
157 |
/* "sw" */ MOUNT_SWAP, |
/* "sw" */ MOUNT_SWAP, |
158 |
/* "swap" */ MOUNT_SWAP, |
/* "swap" */ MOUNT_SWAP, |
159 |
USE_DESKTOP(/* "user" */ MOUNT_USERS,) |
IF_DESKTOP(/* "user" */ MOUNT_USERS,) |
160 |
USE_DESKTOP(/* "users" */ MOUNT_USERS,) |
IF_DESKTOP(/* "users" */ MOUNT_USERS,) |
161 |
/* "_netdev" */ 0, |
/* "_netdev" */ 0, |
162 |
) |
) |
163 |
|
|
164 |
USE_FEATURE_MOUNT_FLAGS( |
IF_FEATURE_MOUNT_FLAGS( |
165 |
// vfs flags |
// vfs flags |
166 |
/* "nosuid" */ MS_NOSUID, |
/* "nosuid" */ MS_NOSUID, |
167 |
/* "suid" */ ~MS_NOSUID, |
/* "suid" */ ~MS_NOSUID, |
183 |
/* "loud" */ ~MS_SILENT, |
/* "loud" */ ~MS_SILENT, |
184 |
|
|
185 |
// action flags |
// action flags |
186 |
|
/* "union" */ MS_UNION, |
187 |
/* "bind" */ MS_BIND, |
/* "bind" */ MS_BIND, |
188 |
/* "move" */ MS_MOVE, |
/* "move" */ MS_MOVE, |
189 |
/* "shared" */ MS_SHARED, |
/* "shared" */ MS_SHARED, |
203 |
}; |
}; |
204 |
|
|
205 |
static const char mount_option_str[] = |
static const char mount_option_str[] = |
206 |
USE_FEATURE_MOUNT_LOOP( |
IF_FEATURE_MOUNT_LOOP( |
207 |
"loop" "\0" |
"loop\0" |
208 |
) |
) |
209 |
USE_FEATURE_MOUNT_FSTAB( |
IF_FEATURE_MOUNT_FSTAB( |
210 |
"defaults" "\0" |
"defaults\0" |
211 |
/* "quiet" "\0" - do not filter out, vfat wants to see it */ |
// "quiet\0" - do not filter out, vfat wants to see it |
212 |
"noauto" "\0" |
"noauto\0" |
213 |
"sw" "\0" |
"sw\0" |
214 |
"swap" "\0" |
"swap\0" |
215 |
USE_DESKTOP("user" "\0") |
IF_DESKTOP("user\0") |
216 |
USE_DESKTOP("users" "\0") |
IF_DESKTOP("users\0") |
217 |
"_netdev" "\0" |
"_netdev\0" |
218 |
) |
) |
219 |
USE_FEATURE_MOUNT_FLAGS( |
IF_FEATURE_MOUNT_FLAGS( |
220 |
// vfs flags |
// vfs flags |
221 |
"nosuid" "\0" |
"nosuid\0" |
222 |
"suid" "\0" |
"suid\0" |
223 |
"dev" "\0" |
"dev\0" |
224 |
"nodev" "\0" |
"nodev\0" |
225 |
"exec" "\0" |
"exec\0" |
226 |
"noexec" "\0" |
"noexec\0" |
227 |
"sync" "\0" |
"sync\0" |
228 |
"dirsync" "\0" |
"dirsync\0" |
229 |
"async" "\0" |
"async\0" |
230 |
"atime" "\0" |
"atime\0" |
231 |
"noatime" "\0" |
"noatime\0" |
232 |
"diratime" "\0" |
"diratime\0" |
233 |
"nodiratime" "\0" |
"nodiratime\0" |
234 |
"mand" "\0" |
"mand\0" |
235 |
"nomand" "\0" |
"nomand\0" |
236 |
"relatime" "\0" |
"relatime\0" |
237 |
"norelatime" "\0" |
"norelatime\0" |
238 |
"loud" "\0" |
"loud\0" |
239 |
|
|
240 |
// action flags |
// action flags |
241 |
"bind" "\0" |
"union\0" |
242 |
"move" "\0" |
"bind\0" |
243 |
"shared" "\0" |
"move\0" |
244 |
"slave" "\0" |
"shared\0" |
245 |
"private" "\0" |
"slave\0" |
246 |
"unbindable" "\0" |
"private\0" |
247 |
"rshared" "\0" |
"unbindable\0" |
248 |
"rslave" "\0" |
"rshared\0" |
249 |
"rprivate" "\0" |
"rslave\0" |
250 |
"runbindable" "\0" |
"rprivate\0" |
251 |
|
"runbindable\0" |
252 |
) |
) |
253 |
|
|
254 |
// Always understood. |
// Always understood. |
255 |
"ro" "\0" // vfs flag |
"ro\0" // vfs flag |
256 |
"rw" "\0" // vfs flag |
"rw\0" // vfs flag |
257 |
"remount" "\0" // action flag |
"remount\0" // action flag |
258 |
; |
; |
259 |
|
|
260 |
|
|
300 |
#define verbose_mount(...) mount(__VA_ARGS__) |
#define verbose_mount(...) mount(__VA_ARGS__) |
301 |
#endif |
#endif |
302 |
|
|
303 |
static int resolve_mount_spec(char **fsname) |
// Append mount options to string |
|
{ |
|
|
char *tmp = NULL; |
|
|
|
|
|
#if ENABLE_FEATURE_MOUNT_LABEL |
|
|
if (!strncmp(*fsname, "UUID=", 5)) |
|
|
tmp = get_devname_from_uuid(*fsname + 5); |
|
|
else if (!strncmp(*fsname, "LABEL=", 6)) |
|
|
tmp = get_devname_from_label(*fsname + 6); |
|
|
#endif |
|
|
|
|
|
if (tmp) { |
|
|
*fsname = tmp; |
|
|
return 1; |
|
|
} |
|
|
return 0; |
|
|
} |
|
|
|
|
|
/* Append mount options to string */ |
|
304 |
static void append_mount_options(char **oldopts, const char *newopts) |
static void append_mount_options(char **oldopts, const char *newopts) |
305 |
{ |
{ |
306 |
if (*oldopts && **oldopts) { |
if (*oldopts && **oldopts) { |
307 |
/* do not insert options which are already there */ |
// Do not insert options which are already there |
308 |
while (newopts[0]) { |
while (newopts[0]) { |
309 |
char *p; |
char *p; |
310 |
int len = strlen(newopts); |
int len = strlen(newopts); |
332 |
} |
} |
333 |
} |
} |
334 |
|
|
335 |
/* Use the mount_options list to parse options into flags. |
// Use the mount_options list to parse options into flags. |
336 |
* Also return list of unrecognized options if unrecognized!=NULL */ |
// Also return list of unrecognized options if unrecognized != NULL |
337 |
static long parse_mount_options(char *options, char **unrecognized) |
static long parse_mount_options(char *options, char **unrecognized) |
338 |
{ |
{ |
339 |
long flags = MS_SILENT; |
long flags = MS_SILENT; |
346 |
|
|
347 |
if (comma) *comma = '\0'; |
if (comma) *comma = '\0'; |
348 |
|
|
349 |
/* FIXME: use hasmntopt() */ |
// FIXME: use hasmntopt() |
350 |
// Find this option in mount_options |
// Find this option in mount_options |
351 |
for (i = 0; i < ARRAY_SIZE(mount_options); i++) { |
for (i = 0; i < ARRAY_SIZE(mount_options); i++) { |
352 |
if (!strcasecmp(option_str, options)) { |
if (!strcasecmp(option_str, options)) { |
357 |
} |
} |
358 |
option_str += strlen(option_str) + 1; |
option_str += strlen(option_str) + 1; |
359 |
} |
} |
360 |
// If unrecognized not NULL, append unrecognized mount options */ |
// If unrecognized not NULL, append unrecognized mount options |
361 |
if (unrecognized && i == ARRAY_SIZE(mount_options)) { |
if (unrecognized && i == ARRAY_SIZE(mount_options)) { |
362 |
// Add it to strflags, to pass on to kernel |
// Add it to strflags, to pass on to kernel |
363 |
i = *unrecognized ? strlen(*unrecognized) : 0; |
i = *unrecognized ? strlen(*unrecognized) : 0; |
379 |
} |
} |
380 |
|
|
381 |
// Return a list of all block device backed filesystems |
// Return a list of all block device backed filesystems |
|
|
|
382 |
static llist_t *get_block_backed_filesystems(void) |
static llist_t *get_block_backed_filesystems(void) |
383 |
{ |
{ |
384 |
static const char filesystems[2][sizeof("/proc/filesystems")] = { |
static const char filesystems[2][sizeof("/proc/filesystems")] = { |
386 |
"/proc/filesystems", |
"/proc/filesystems", |
387 |
}; |
}; |
388 |
char *fs, *buf; |
char *fs, *buf; |
389 |
llist_t *list = 0; |
llist_t *list = NULL; |
390 |
int i; |
int i; |
391 |
FILE *f; |
FILE *f; |
392 |
|
|
395 |
if (!f) continue; |
if (!f) continue; |
396 |
|
|
397 |
while ((buf = xmalloc_fgetline(f)) != NULL) { |
while ((buf = xmalloc_fgetline(f)) != NULL) { |
398 |
if (!strncmp(buf, "nodev", 5) && isspace(buf[5])) |
if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5])) |
399 |
continue; |
continue; |
400 |
fs = skip_whitespace(buf); |
fs = skip_whitespace(buf); |
401 |
if (*fs=='#' || *fs=='*' || !*fs) continue; |
if (*fs == '#' || *fs == '*' || !*fs) |
402 |
|
continue; |
403 |
|
|
404 |
llist_add_to_end(&list, xstrdup(fs)); |
llist_add_to_end(&list, xstrdup(fs)); |
405 |
free(buf); |
free(buf); |
425 |
{ |
{ |
426 |
int rc = 0; |
int rc = 0; |
427 |
|
|
428 |
if (fakeIt) { |
if (FAKE_IT) { |
429 |
if (verbose >= 2) |
if (verbose >= 2) |
430 |
bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')", |
bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')", |
431 |
mp->mnt_fsname, mp->mnt_dir, mp->mnt_type, |
mp->mnt_fsname, mp->mnt_dir, mp->mnt_type, |
441 |
|
|
442 |
// If mount failed, try |
// If mount failed, try |
443 |
// helper program mount.<mnt_type> |
// helper program mount.<mnt_type> |
444 |
if (ENABLE_FEATURE_MOUNT_HELPERS && rc) { |
if (HELPERS_ALLOWED && rc && mp->mnt_type) { |
445 |
char *args[6]; |
char *args[8]; |
446 |
int errno_save = errno; |
int errno_save = errno; |
447 |
args[0] = xasprintf("mount.%s", mp->mnt_type); |
args[0] = xasprintf("mount.%s", mp->mnt_type); |
448 |
rc = 1; |
rc = 1; |
449 |
|
if (FAKE_IT) |
450 |
|
args[rc++] = (char *)"-f"; |
451 |
|
if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB) |
452 |
|
args[rc++] = (char *)"-n"; |
453 |
|
args[rc++] = mp->mnt_fsname; |
454 |
|
args[rc++] = mp->mnt_dir; |
455 |
if (filteropts) { |
if (filteropts) { |
456 |
args[rc++] = (char *)"-o"; |
args[rc++] = (char *)"-o"; |
457 |
args[rc++] = filteropts; |
args[rc++] = filteropts; |
458 |
} |
} |
|
args[rc++] = mp->mnt_fsname; |
|
|
args[rc++] = mp->mnt_dir; |
|
459 |
args[rc] = NULL; |
args[rc] = NULL; |
460 |
rc = wait4pid(spawn(args)); |
rc = wait4pid(spawn(args)); |
461 |
free(args[0]); |
free(args[0]); |
477 |
if (rc && errno == EPERM) |
if (rc && errno == EPERM) |
478 |
bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); |
bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); |
479 |
|
|
480 |
/* If the mount was successful, and we're maintaining an old-style |
// If the mount was successful, and we're maintaining an old-style |
481 |
* mtab file by hand, add the new entry to it now. */ |
// mtab file by hand, add the new entry to it now. |
482 |
mtab: |
mtab: |
483 |
if (useMtab && !rc && !(vfsflags & MS_REMOUNT)) { |
if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) { |
484 |
char *fsname; |
char *fsname; |
485 |
FILE *mountTable = setmntent(bb_path_mtab_file, "a+"); |
FILE *mountTable = setmntent(bb_path_mtab_file, "a+"); |
486 |
const char *option_str = mount_option_str; |
const char *option_str = mount_option_str; |
508 |
|
|
509 |
mp->mnt_dir = bb_simplify_path(mp->mnt_dir); |
mp->mnt_dir = bb_simplify_path(mp->mnt_dir); |
510 |
fsname = 0; |
fsname = 0; |
511 |
if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */ |
if (!mp->mnt_type || !*mp->mnt_type) { // bind mount |
512 |
mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname); |
mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname); |
513 |
mp->mnt_type = (char*)"bind"; |
mp->mnt_type = (char*)"bind"; |
514 |
} |
} |
551 |
* plus NFSv3 stuff. |
* plus NFSv3 stuff. |
552 |
*/ |
*/ |
553 |
|
|
|
/* This is just a warning of a common mistake. Possibly this should be a |
|
|
* uclibc faq entry rather than in busybox... */ |
|
|
#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) |
|
|
#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support." |
|
|
#endif |
|
|
|
|
554 |
#define MOUNTPORT 635 |
#define MOUNTPORT 635 |
555 |
#define MNTPATHLEN 1024 |
#define MNTPATHLEN 1024 |
556 |
#define MNTNAMLEN 255 |
#define MNTNAMLEN 255 |
741 |
* "after #include <errno.h> the symbol errno is reserved for any use, |
* "after #include <errno.h> the symbol errno is reserved for any use, |
742 |
* it cannot even be used as a struct tag or field name". |
* it cannot even be used as a struct tag or field name". |
743 |
*/ |
*/ |
|
|
|
744 |
#ifndef EDQUOT |
#ifndef EDQUOT |
745 |
#define EDQUOT ENOSPC |
# define EDQUOT ENOSPC |
746 |
#endif |
#endif |
747 |
|
/* Convert each NFSERR_BLAH into EBLAH */ |
748 |
// Convert each NFSERR_BLAH into EBLAH |
static const uint8_t nfs_err_stat[] = { |
749 |
|
1, 2, 5, 6, 13, 17, |
750 |
static const struct { |
19, 20, 21, 22, 27, 28, |
751 |
short stat; |
30, 63, 66, 69, 70, 71 |
752 |
short errnum; |
}; |
753 |
} nfs_errtbl[] = { |
static const uint8_t nfs_err_errnum[] = { |
754 |
{0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST}, |
EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST, |
755 |
{19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG}, |
ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC, |
756 |
{28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT}, |
EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE |
|
{70,ESTALE}, {71,EREMOTE}, {-1,EIO} |
|
757 |
}; |
}; |
|
|
|
758 |
static char *nfs_strerror(int status) |
static char *nfs_strerror(int status) |
759 |
{ |
{ |
760 |
int i; |
int i; |
761 |
|
|
762 |
for (i = 0; nfs_errtbl[i].stat != -1; i++) { |
for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) { |
763 |
if (nfs_errtbl[i].stat == status) |
if (nfs_err_stat[i] == status) |
764 |
return strerror(nfs_errtbl[i].errnum); |
return strerror(nfs_err_errnum[i]); |
765 |
} |
} |
766 |
return xasprintf("unknown nfs status return value: %d", status); |
return xasprintf("unknown nfs status return value: %d", status); |
767 |
} |
} |
797 |
|
|
798 |
static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp) |
static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp) |
799 |
{ |
{ |
800 |
if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3)) |
if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, |
801 |
|
(unsigned int *) &objp->fhandle3_len, |
802 |
|
FHSIZE3) |
803 |
|
) { |
804 |
return FALSE; |
return FALSE; |
805 |
|
} |
806 |
return TRUE; |
return TRUE; |
807 |
} |
} |
808 |
|
|
810 |
{ |
{ |
811 |
if (!xdr_fhandle3(xdrs, &objp->fhandle)) |
if (!xdr_fhandle3(xdrs, &objp->fhandle)) |
812 |
return FALSE; |
return FALSE; |
813 |
if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0, |
if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), |
814 |
sizeof (int), (xdrproc_t) xdr_int)) |
&(objp->auth_flavours.auth_flavours_len), |
815 |
|
~0, |
816 |
|
sizeof(int), |
817 |
|
(xdrproc_t) xdr_int) |
818 |
|
) { |
819 |
return FALSE; |
return FALSE; |
820 |
|
} |
821 |
return TRUE; |
return TRUE; |
822 |
} |
} |
823 |
|
|
868 |
|
|
869 |
kernel_version = get_linux_version_code(); |
kernel_version = get_linux_version_code(); |
870 |
if (kernel_version) { |
if (kernel_version) { |
871 |
if (kernel_version < KERNEL_VERSION(2,1,32)) |
if (kernel_version < KERNEL_VERSION(2,2,18)) |
|
nfs_mount_version = 1; |
|
|
else if (kernel_version < KERNEL_VERSION(2,2,18) || |
|
|
(kernel_version >= KERNEL_VERSION(2,3,0) && |
|
|
kernel_version < KERNEL_VERSION(2,3,99))) |
|
872 |
nfs_mount_version = 3; |
nfs_mount_version = 3; |
873 |
/* else v4 since 2.3.99pre4 */ |
/* else v4 since 2.3.99pre4 */ |
874 |
} |
} |
945 |
static inline int daemonize(void) { return -ENOSYS; } |
static inline int daemonize(void) { return -ENOSYS; } |
946 |
#endif |
#endif |
947 |
|
|
948 |
// TODO |
/* TODO */ |
949 |
static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM) |
static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM) |
950 |
{ |
{ |
951 |
return 0; |
return 0; |
964 |
bb_error_msg("%.*s", len, msg); |
bb_error_msg("%.*s", len, msg); |
965 |
} |
} |
966 |
|
|
967 |
// NB: mp->xxx fields may be trashed on exit |
/* NB: mp->xxx fields may be trashed on exit */ |
968 |
static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts) |
static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts) |
969 |
{ |
{ |
970 |
CLIENT *mclient; |
CLIENT *mclient; |
971 |
char *hostname; |
char *hostname; |
972 |
char *pathname; |
char *pathname; |
973 |
char *mounthost; |
char *mounthost; |
974 |
|
/* prior to 2.6.23, kernel took NFS options in a form of this struct |
975 |
|
* only. 2.6.23+ looks at data->version, and if it's not 1..6, |
976 |
|
* then data pointer is interpreted as a string. */ |
977 |
struct nfs_mount_data data; |
struct nfs_mount_data data; |
978 |
char *opt; |
char *opt; |
979 |
struct hostent *hp; |
struct hostent *hp; |
1000 |
int nfsprog; |
int nfsprog; |
1001 |
int nfsvers; |
int nfsvers; |
1002 |
int retval; |
int retval; |
1003 |
/* these all are one-bit really. 4.3.1 likes this combination: */ |
/* these all are one-bit really. gcc 4.3.1 likes this combination: */ |
1004 |
smallint tcp; |
smallint tcp; |
1005 |
smallint soft; |
smallint soft; |
1006 |
int intr; |
int intr; |
1318 |
} |
} |
1319 |
} |
} |
1320 |
|
|
1321 |
/* create mount daemon client */ |
/* Create mount daemon client */ |
1322 |
/* See if the nfs host = mount host. */ |
/* See if the nfs host = mount host. */ |
1323 |
if (mounthost) { |
if (mounthost) { |
1324 |
if (mounthost[0] >= '0' && mounthost[0] <= '9') { |
if (mounthost[0] >= '0' && mounthost[0] <= '9') { |
1364 |
retry_timeout.tv_usec = 0; |
retry_timeout.tv_usec = 0; |
1365 |
total_timeout.tv_sec = 20; |
total_timeout.tv_sec = 20; |
1366 |
total_timeout.tv_usec = 0; |
total_timeout.tv_usec = 0; |
1367 |
//FIXME: use monotonic()? |
/* FIXME: use monotonic()? */ |
1368 |
timeout = time(NULL) + 60 * retry; |
timeout = time(NULL) + 60 * retry; |
1369 |
prevt = 0; |
prevt = 0; |
1370 |
t = 30; |
t = 30; |
1371 |
retry: |
retry: |
1372 |
/* be careful not to use too many CPU cycles */ |
/* Be careful not to use too many CPU cycles */ |
1373 |
if (t - prevt < 30) |
if (t - prevt < 30) |
1374 |
sleep(30); |
sleep(30); |
1375 |
|
|
1409 |
error_msg_rpc(clnt_spcreateerror(" ")); |
error_msg_rpc(clnt_spcreateerror(" ")); |
1410 |
} else { |
} else { |
1411 |
enum clnt_stat clnt_stat; |
enum clnt_stat clnt_stat; |
1412 |
/* try to mount hostname:pathname */ |
|
1413 |
|
/* Try to mount hostname:pathname */ |
1414 |
mclient->cl_auth = authunix_create_default(); |
mclient->cl_auth = authunix_create_default(); |
1415 |
|
|
1416 |
/* make pointers in xdr_mountres3 NULL so |
/* Make pointers in xdr_mountres3 NULL so |
1417 |
* that xdr_array allocates memory for us |
* that xdr_array allocates memory for us |
1418 |
*/ |
*/ |
1419 |
memset(&status, 0, sizeof(status)); |
memset(&status, 0, sizeof(status)); |
1450 |
} |
} |
1451 |
|
|
1452 |
/* Timeout. We are going to retry... maybe */ |
/* Timeout. We are going to retry... maybe */ |
|
|
|
1453 |
if (!bg) |
if (!bg) |
1454 |
goto fail; |
goto fail; |
1455 |
if (!daemonized) { |
if (!daemonized) { |
1503 |
data.flags |= NFS_MOUNT_VER3; |
data.flags |= NFS_MOUNT_VER3; |
1504 |
} |
} |
1505 |
|
|
1506 |
/* create nfs socket for kernel */ |
/* Create nfs socket for kernel */ |
|
|
|
1507 |
if (tcp) { |
if (tcp) { |
1508 |
if (nfs_mount_version < 3) { |
if (nfs_mount_version < 3) { |
1509 |
bb_error_msg("NFS over TCP is not supported"); |
bb_error_msg("NFS over TCP is not supported"); |
1529 |
} |
} |
1530 |
server_addr.sin_port = htons(port); |
server_addr.sin_port = htons(port); |
1531 |
|
|
1532 |
/* prepare data structure for kernel */ |
/* Prepare data structure for kernel */ |
|
|
|
1533 |
data.fd = fsock; |
data.fd = fsock; |
1534 |
memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr)); |
memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr)); |
1535 |
strncpy(data.hostname, hostname, sizeof(data.hostname)); |
strncpy(data.hostname, hostname, sizeof(data.hostname)); |
1536 |
|
|
1537 |
/* clean up */ |
/* Clean up */ |
|
|
|
1538 |
auth_destroy(mclient->cl_auth); |
auth_destroy(mclient->cl_auth); |
1539 |
clnt_destroy(mclient); |
clnt_destroy(mclient); |
1540 |
close(msock); |
close(msock); |
1548 |
if (!daemonized) { |
if (!daemonized) { |
1549 |
daemonized = daemonize(); |
daemonized = daemonize(); |
1550 |
if (daemonized <= 0) { /* parent or error */ |
if (daemonized <= 0) { /* parent or error */ |
1551 |
// FIXME: parent doesn't close fsock - ??! |
/* FIXME: parent doesn't close fsock - ??! */ |
1552 |
retval = -daemonized; |
retval = -daemonized; |
1553 |
goto ret; |
goto ret; |
1554 |
} |
} |
1560 |
} |
} |
1561 |
} |
} |
1562 |
|
|
1563 |
do_mount: /* perform actual mount */ |
/* Perform actual mount */ |
1564 |
|
do_mount: |
1565 |
mp->mnt_type = (char*)"nfs"; |
mp->mnt_type = (char*)"nfs"; |
1566 |
retval = mount_it_now(mp, vfsflags, (char*)&data); |
retval = mount_it_now(mp, vfsflags, (char*)&data); |
1567 |
goto ret; |
goto ret; |
1568 |
|
|
1569 |
fail: /* abort */ |
/* Abort */ |
1570 |
|
fail: |
1571 |
if (msock >= 0) { |
if (msock >= 0) { |
1572 |
if (mclient) { |
if (mclient) { |
1573 |
auth_destroy(mclient->cl_auth); |
auth_destroy(mclient->cl_auth); |
1585 |
return retval; |
return retval; |
1586 |
} |
} |
1587 |
|
|
1588 |
#else /* !ENABLE_FEATURE_MOUNT_NFS */ |
#else // !ENABLE_FEATURE_MOUNT_NFS |
1589 |
|
|
1590 |
/* Never called. Call should be optimized out. */ |
// Never called. Call should be optimized out. |
1591 |
int nfsmount(struct mntent *mp, long vfsflags, char *filteropts); |
int nfsmount(struct mntent *mp, long vfsflags, char *filteropts); |
1592 |
|
|
1593 |
#endif /* !ENABLE_FEATURE_MOUNT_NFS */ |
#endif // !ENABLE_FEATURE_MOUNT_NFS |
1594 |
|
|
1595 |
// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem |
// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem |
1596 |
// type detection. Returns 0 for success, nonzero for failure. |
// type detection. Returns 0 for success, nonzero for failure. |
1599 |
{ |
{ |
1600 |
int rc = -1; |
int rc = -1; |
1601 |
long vfsflags; |
long vfsflags; |
1602 |
char *loopFile = 0, *filteropts = 0; |
char *loopFile = NULL, *filteropts = NULL; |
1603 |
llist_t *fl = 0; |
llist_t *fl = NULL; |
1604 |
struct stat st; |
struct stat st; |
1605 |
|
|
1606 |
vfsflags = parse_mount_options(mp->mnt_opts, &filteropts); |
errno = 0; |
1607 |
|
|
1608 |
// Treat fstype "auto" as unspecified. |
vfsflags = parse_mount_options(mp->mnt_opts, &filteropts); |
1609 |
|
|
1610 |
|
// Treat fstype "auto" as unspecified |
1611 |
if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0) |
if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0) |
1612 |
mp->mnt_type = NULL; |
mp->mnt_type = NULL; |
1613 |
|
|
1614 |
// Might this be a virtual filesystem? |
// Might this be a virtual filesystem? |
1615 |
|
if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) { |
1616 |
if (ENABLE_FEATURE_MOUNT_HELPERS |
char *args[35]; |
1617 |
&& (strchr(mp->mnt_fsname, '#')) |
char *s; |
1618 |
) { |
int n; |
1619 |
char *s, *p, *args[35]; |
// fsname: "cmd#arg1#arg2..." |
1620 |
int n = 0; |
// WARNING: allows execution of arbitrary commands! |
1621 |
// FIXME: does it allow execution of arbitrary commands?! |
// Try "mount 'sh#-c#sh' bogus_dir". |
1622 |
// What args[0] can end up with? |
// It is safe ONLY because non-root |
1623 |
for (s = p = mp->mnt_fsname; *s && n < 35-3; ++s) { |
// cannot use two-argument mount command |
1624 |
if (s[0] == '#' && s[1] != '#') { |
// and using one-argument "mount 'sh#-c#sh'" doesn't work: |
1625 |
*s = '\0'; |
// "mount: can't find sh#-c#sh in /etc/fstab" |
1626 |
args[n++] = p; |
// (if /etc/fstab has it, it's ok: root sets up /etc/fstab). |
1627 |
p = s + 1; |
|
1628 |
|
s = mp->mnt_fsname; |
1629 |
|
n = 0; |
1630 |
|
args[n++] = s; |
1631 |
|
while (*s && n < 35 - 2) { |
1632 |
|
if (*s++ == '#' && *s != '#') { |
1633 |
|
s[-1] = '\0'; |
1634 |
|
args[n++] = s; |
1635 |
} |
} |
1636 |
} |
} |
|
args[n++] = p; |
|
1637 |
args[n++] = mp->mnt_dir; |
args[n++] = mp->mnt_dir; |
1638 |
args[n] = NULL; |
args[n] = NULL; |
1639 |
rc = wait4pid(xspawn(args)); |
rc = wait4pid(xspawn(args)); |
1641 |
} |
} |
1642 |
|
|
1643 |
// Might this be an CIFS filesystem? |
// Might this be an CIFS filesystem? |
|
|
|
1644 |
if (ENABLE_FEATURE_MOUNT_CIFS |
if (ENABLE_FEATURE_MOUNT_CIFS |
1645 |
&& (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0) |
&& (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0) |
1646 |
&& (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\') |
&& (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\') |
1647 |
&& mp->mnt_fsname[0] == mp->mnt_fsname[1] |
&& mp->mnt_fsname[0] == mp->mnt_fsname[1] |
1648 |
) { |
) { |
1649 |
|
int len; |
1650 |
|
char c; |
1651 |
len_and_sockaddr *lsa; |
len_and_sockaddr *lsa; |
1652 |
char *ip, *dotted; |
char *hostname, *dotted, *ip; |
|
char *s; |
|
|
|
|
|
rc = 1; |
|
|
// Replace '/' with '\' and verify that unc points to "//server/share". |
|
|
|
|
|
for (s = mp->mnt_fsname; *s; ++s) |
|
|
if (*s == '/') *s = '\\'; |
|
1653 |
|
|
1654 |
// get server IP |
hostname = mp->mnt_fsname + 2; |
1655 |
|
len = strcspn(hostname, "/\\"); |
1656 |
s = strrchr(mp->mnt_fsname, '\\'); |
if (len == 0 || hostname[len] == '\0') |
1657 |
if (s <= mp->mnt_fsname+1) goto report_error; |
goto report_error; |
1658 |
*s = '\0'; |
c = hostname[len]; |
1659 |
lsa = host2sockaddr(mp->mnt_fsname+2, 0); |
hostname[len] = '\0'; |
1660 |
*s = '\\'; |
lsa = host2sockaddr(hostname, 0); |
1661 |
if (!lsa) goto report_error; |
hostname[len] = c; |
1662 |
|
if (!lsa) |
1663 |
// insert ip=... option into string flags. |
goto report_error; |
1664 |
|
|
1665 |
|
// Insert "ip=..." option into options |
1666 |
dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa); |
dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa); |
1667 |
|
if (ENABLE_FEATURE_CLEAN_UP) free(lsa); |
1668 |
ip = xasprintf("ip=%s", dotted); |
ip = xasprintf("ip=%s", dotted); |
1669 |
|
if (ENABLE_FEATURE_CLEAN_UP) free(dotted); |
1670 |
parse_mount_options(ip, &filteropts); |
parse_mount_options(ip, &filteropts); |
1671 |
|
if (ENABLE_FEATURE_CLEAN_UP) free(ip); |
1672 |
|
|
1673 |
// compose new unc '\\server-ip\share' |
// "-o mand" is required [why?] |
|
// (s => slash after hostname) |
|
|
|
|
|
mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s); |
|
|
|
|
|
// lock is required |
|
1674 |
vfsflags |= MS_MANDLOCK; |
vfsflags |= MS_MANDLOCK; |
|
|
|
1675 |
mp->mnt_type = (char*)"cifs"; |
mp->mnt_type = (char*)"cifs"; |
1676 |
rc = mount_it_now(mp, vfsflags, filteropts); |
rc = mount_it_now(mp, vfsflags, filteropts); |
1677 |
if (ENABLE_FEATURE_CLEAN_UP) { |
|
|
free(mp->mnt_fsname); |
|
|
free(ip); |
|
|
free(dotted); |
|
|
free(lsa); |
|
|
} |
|
1678 |
goto report_error; |
goto report_error; |
1679 |
} |
} |
1680 |
|
|
1681 |
// Might this be an NFS filesystem? |
// Might this be an NFS filesystem? |
|
|
|
1682 |
if (ENABLE_FEATURE_MOUNT_NFS |
if (ENABLE_FEATURE_MOUNT_NFS |
1683 |
&& (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs")) |
&& (!mp->mnt_type || strcmp(mp->mnt_type, "nfs") == 0) |
1684 |
&& strchr(mp->mnt_fsname, ':') != NULL |
&& strchr(mp->mnt_fsname, ':') != NULL |
1685 |
) { |
) { |
1686 |
rc = nfsmount(mp, vfsflags, filteropts); |
rc = nfsmount(mp, vfsflags, filteropts); |
1691 |
// a synthetic filesystem like proc or sysfs.) |
// a synthetic filesystem like proc or sysfs.) |
1692 |
// (We use stat, not lstat, in order to allow |
// (We use stat, not lstat, in order to allow |
1693 |
// mount symlink_to_file_or_blkdev dir) |
// mount symlink_to_file_or_blkdev dir) |
|
|
|
1694 |
if (!stat(mp->mnt_fsname, &st) |
if (!stat(mp->mnt_fsname, &st) |
1695 |
&& !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)) |
&& !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)) |
1696 |
) { |
) { |
1697 |
// Do we need to allocate a loopback device for it? |
// Do we need to allocate a loopback device for it? |
|
|
|
1698 |
if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) { |
if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) { |
1699 |
loopFile = bb_simplify_path(mp->mnt_fsname); |
loopFile = bb_simplify_path(mp->mnt_fsname); |
1700 |
mp->mnt_fsname = NULL; /* will receive malloced loop dev name */ |
mp->mnt_fsname = NULL; // will receive malloced loop dev name |
1701 |
if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) { |
if (set_loop(&mp->mnt_fsname, loopFile, 0) < 0) { |
1702 |
if (errno == EPERM || errno == EACCES) |
if (errno == EPERM || errno == EACCES) |
1703 |
bb_error_msg(bb_msg_perm_denied_are_you_root); |
bb_error_msg(bb_msg_perm_denied_are_you_root); |
1704 |
else |
else |
1705 |
bb_perror_msg("cannot setup loop device"); |
bb_perror_msg("can't setup loop device"); |
1706 |
return errno; |
return errno; |
1707 |
} |
} |
1708 |
|
|
1709 |
// Autodetect bind mounts |
// Autodetect bind mounts |
|
|
|
1710 |
} else if (S_ISDIR(st.st_mode) && !mp->mnt_type) |
} else if (S_ISDIR(st.st_mode) && !mp->mnt_type) |
1711 |
vfsflags |= MS_BIND; |
vfsflags |= MS_BIND; |
1712 |
} |
} |
1713 |
|
|
1714 |
/* If we know the fstype (or don't need to), jump straight |
// If we know the fstype (or don't need to), jump straight |
1715 |
* to the actual mount. */ |
// to the actual mount. |
|
|
|
1716 |
if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) |
if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) |
1717 |
rc = mount_it_now(mp, vfsflags, filteropts); |
rc = mount_it_now(mp, vfsflags, filteropts); |
1718 |
else { |
else { |
1719 |
// Loop through filesystem types until mount succeeds |
// Loop through filesystem types until mount succeeds |
1720 |
// or we run out |
// or we run out |
1721 |
|
|
1722 |
/* Initialize list of block backed filesystems. This has to be |
// Initialize list of block backed filesystems. |
1723 |
* done here so that during "mount -a", mounts after /proc shows up |
// This has to be done here so that during "mount -a", |
1724 |
* can autodetect. */ |
// mounts after /proc shows up can autodetect. |
|
|
|
1725 |
if (!fslist) { |
if (!fslist) { |
1726 |
fslist = get_block_backed_filesystems(); |
fslist = get_block_backed_filesystems(); |
1727 |
if (ENABLE_FEATURE_CLEAN_UP && fslist) |
if (ENABLE_FEATURE_CLEAN_UP && fslist) |
1731 |
for (fl = fslist; fl; fl = fl->link) { |
for (fl = fslist; fl; fl = fl->link) { |
1732 |
mp->mnt_type = fl->data; |
mp->mnt_type = fl->data; |
1733 |
rc = mount_it_now(mp, vfsflags, filteropts); |
rc = mount_it_now(mp, vfsflags, filteropts); |
1734 |
if (!rc) break; |
if (!rc) |
1735 |
|
break; |
1736 |
} |
} |
1737 |
} |
} |
1738 |
|
|
1739 |
// If mount failed, clean up loop file (if any). |
// If mount failed, clean up loop file (if any). |
|
|
|
1740 |
if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) { |
if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) { |
1741 |
del_loop(mp->mnt_fsname); |
del_loop(mp->mnt_fsname); |
1742 |
if (ENABLE_FEATURE_CLEAN_UP) { |
if (ENABLE_FEATURE_CLEAN_UP) { |
1756 |
return rc; |
return rc; |
1757 |
} |
} |
1758 |
|
|
1759 |
// Parse options, if necessary parse fstab/mtab, and call singlemount for |
// -O support |
1760 |
// each directory to be mounted. |
// -O interprets a list of filter options which select whether a mount |
1761 |
|
// point will be mounted: only mounts with options matching *all* filtering |
1762 |
|
// options will be selected. |
1763 |
|
// By default each -O filter option must be present in the list of mount |
1764 |
|
// options, but if it is prefixed by "no" then it must be absent. |
1765 |
|
// For example, |
1766 |
|
// -O a,nob,c matches -o a,c but fails to match -o a,b,c |
1767 |
|
// (and also fails to match -o a because -o c is absent). |
1768 |
|
// |
1769 |
|
// It is different from -t in that each option is matched exactly; a leading |
1770 |
|
// "no" at the beginning of one option does not negate the rest. |
1771 |
|
static int match_opt(const char *fs_opt_in, const char *O_opt) |
1772 |
|
{ |
1773 |
|
if (!O_opt) |
1774 |
|
return 1; |
1775 |
|
|
1776 |
static const char must_be_root[] ALIGN1 = "you must be root"; |
while (*O_opt) { |
1777 |
|
const char *fs_opt = fs_opt_in; |
1778 |
|
int O_len; |
1779 |
|
int match; |
1780 |
|
|
1781 |
|
// If option begins with "no" then treat as an inverted match: |
1782 |
|
// matching is a failure |
1783 |
|
match = 0; |
1784 |
|
if (O_opt[0] == 'n' && O_opt[1] == 'o') { |
1785 |
|
match = 1; |
1786 |
|
O_opt += 2; |
1787 |
|
} |
1788 |
|
// Isolate the current O option |
1789 |
|
O_len = strchrnul(O_opt, ',') - O_opt; |
1790 |
|
// Check for a match against existing options |
1791 |
|
while (1) { |
1792 |
|
if (strncmp(fs_opt, O_opt, O_len) == 0 |
1793 |
|
&& (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',') |
1794 |
|
) { |
1795 |
|
if (match) |
1796 |
|
return 0; // "no" prefix, but option found |
1797 |
|
match = 1; // current O option found, go check next one |
1798 |
|
break; |
1799 |
|
} |
1800 |
|
fs_opt = strchr(fs_opt, ','); |
1801 |
|
if (!fs_opt) |
1802 |
|
break; |
1803 |
|
fs_opt++; |
1804 |
|
} |
1805 |
|
if (match == 0) |
1806 |
|
return 0; // match wanted but not found |
1807 |
|
if (O_opt[O_len] == '\0') // end? |
1808 |
|
break; |
1809 |
|
// Step to the next O option |
1810 |
|
O_opt += O_len + 1; |
1811 |
|
} |
1812 |
|
// If we get here then everything matched |
1813 |
|
return 1; |
1814 |
|
} |
1815 |
|
|
1816 |
|
// Parse options, if necessary parse fstab/mtab, and call singlemount for |
1817 |
|
// each directory to be mounted. |
1818 |
int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1819 |
int mount_main(int argc UNUSED_PARAM, char **argv) |
int mount_main(int argc UNUSED_PARAM, char **argv) |
1820 |
{ |
{ |
1821 |
char *cmdopts = xstrdup(""); |
char *cmdopts = xzalloc(1); |
1822 |
char *fstype = NULL; |
char *fstype = NULL; |
1823 |
|
char *O_optmatch = NULL; |
1824 |
char *storage_path; |
char *storage_path; |
1825 |
llist_t *lst_o = NULL; |
llist_t *lst_o = NULL; |
1826 |
const char *fstabname; |
const char *fstabname; |
1827 |
FILE *fstab; |
FILE *fstab; |
1828 |
int i, j, rc = 0; |
int i, j; |
1829 |
|
int rc = EXIT_SUCCESS; |
1830 |
unsigned opt; |
unsigned opt; |
1831 |
struct mntent mtpair[2], *mtcur = mtpair; |
struct mntent mtpair[2], *mtcur = mtpair; |
1832 |
SKIP_DESKTOP(const int nonroot = 0;) |
IF_NOT_DESKTOP(const int nonroot = 0;) |
1833 |
|
|
1834 |
USE_DESKTOP(int nonroot = ) sanitize_env_if_suid(); |
IF_DESKTOP(int nonroot = ) sanitize_env_if_suid(); |
1835 |
|
|
1836 |
// Parse long options, like --bind and --move. Note that -o option |
// Parse long options, like --bind and --move. Note that -o option |
1837 |
// and --option are synonymous. Yes, this means --remount,rw works. |
// and --option are synonymous. Yes, this means --remount,rw works. |
1844 |
argv[j] = NULL; |
argv[j] = NULL; |
1845 |
|
|
1846 |
// Parse remaining options |
// Parse remaining options |
1847 |
// Max 2 params; -v is a counter |
// Max 2 params; -o is a list, -v is a counter |
1848 |
opt_complementary = "?2o::" USE_FEATURE_MOUNT_VERBOSE(":vv"); |
opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv"); |
1849 |
opt = getopt32(argv, OPTION_STR, &lst_o, &fstype |
opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch |
1850 |
USE_FEATURE_MOUNT_VERBOSE(, &verbose)); |
IF_FEATURE_MOUNT_VERBOSE(, &verbose)); |
1851 |
while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o |
while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o |
1852 |
if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r |
if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r |
1853 |
if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w |
if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w |
1866 |
{ |
{ |
1867 |
// Don't show rootfs. FIXME: why?? |
// Don't show rootfs. FIXME: why?? |
1868 |
// util-linux 2.12a happily shows rootfs... |
// util-linux 2.12a happily shows rootfs... |
1869 |
//if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue; |
//if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue; |
1870 |
|
|
1871 |
if (!fstype || !strcmp(mtpair->mnt_type, fstype)) |
if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0) |
1872 |
printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname, |
printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname, |
1873 |
mtpair->mnt_dir, mtpair->mnt_type, |
mtpair->mnt_dir, mtpair->mnt_type, |
1874 |
mtpair->mnt_opts); |
mtpair->mnt_opts); |
1884 |
// argument when we get it. |
// argument when we get it. |
1885 |
if (argv[1]) { |
if (argv[1]) { |
1886 |
if (nonroot) |
if (nonroot) |
1887 |
bb_error_msg_and_die(must_be_root); |
bb_error_msg_and_die(bb_msg_you_must_be_root); |
1888 |
mtpair->mnt_fsname = argv[0]; |
mtpair->mnt_fsname = argv[0]; |
1889 |
mtpair->mnt_dir = argv[1]; |
mtpair->mnt_dir = argv[1]; |
1890 |
mtpair->mnt_type = fstype; |
mtpair->mnt_type = fstype; |
1891 |
mtpair->mnt_opts = cmdopts; |
mtpair->mnt_opts = cmdopts; |
1892 |
if (ENABLE_FEATURE_MOUNT_LABEL) { |
resolve_mount_spec(&mtpair->mnt_fsname); |
1893 |
resolve_mount_spec(&mtpair->mnt_fsname); |
rc = singlemount(mtpair, /*ignore_busy:*/ 0); |
|
} |
|
|
rc = singlemount(mtpair, 0); |
|
1894 |
return rc; |
return rc; |
1895 |
} |
} |
1896 |
storage_path = bb_simplify_path(argv[0]); // malloced |
storage_path = bb_simplify_path(argv[0]); // malloced |
1899 |
// Past this point, we are handling either "mount -a [opts]" |
// Past this point, we are handling either "mount -a [opts]" |
1900 |
// or "mount [opts] single_param" |
// or "mount [opts] single_param" |
1901 |
|
|
1902 |
i = parse_mount_options(cmdopts, 0); // FIXME: should be "long", not "int" |
i = parse_mount_options(cmdopts, NULL); // FIXME: should be "long", not "int" |
1903 |
if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags |
if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags |
1904 |
bb_error_msg_and_die(must_be_root); |
bb_error_msg_and_die(bb_msg_you_must_be_root); |
1905 |
|
|
1906 |
// If we have a shared subtree flag, don't worry about fstab or mtab. |
// If we have a shared subtree flag, don't worry about fstab or mtab. |
1907 |
if (ENABLE_FEATURE_MOUNT_FLAGS |
if (ENABLE_FEATURE_MOUNT_FLAGS |
1908 |
&& (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) |
&& (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) |
1909 |
) { |
) { |
1910 |
rc = verbose_mount(/*source:*/ "", /*target:*/ argv[0], |
// verbose_mount(source, target, type, flags, data) |
1911 |
/*type:*/ "", /*flags:*/ i, /*data:*/ ""); |
rc = verbose_mount("", argv[0], "", i, ""); |
1912 |
if (rc) |
if (rc) |
1913 |
bb_simple_perror_msg_and_die(argv[0]); |
bb_simple_perror_msg_and_die(argv[0]); |
1914 |
return rc; |
return rc; |
1924 |
} |
} |
1925 |
fstab = setmntent(fstabname, "r"); |
fstab = setmntent(fstabname, "r"); |
1926 |
if (!fstab) |
if (!fstab) |
1927 |
bb_perror_msg_and_die("cannot read %s", fstabname); |
bb_perror_msg_and_die("can't read %s", fstabname); |
1928 |
|
|
1929 |
// Loop through entries until we find what we're looking for |
// Loop through entries until we find what we're looking for |
1930 |
memset(mtpair, 0, sizeof(mtpair)); |
memset(mtpair, 0, sizeof(mtpair)); |
1946 |
if (argv[0]) { |
if (argv[0]) { |
1947 |
|
|
1948 |
// Is this what we're looking for? |
// Is this what we're looking for? |
1949 |
if (strcmp(argv[0], mtcur->mnt_fsname) && |
if (strcmp(argv[0], mtcur->mnt_fsname) != 0 |
1950 |
strcmp(storage_path, mtcur->mnt_fsname) && |
&& strcmp(storage_path, mtcur->mnt_fsname) != 0 |
1951 |
strcmp(argv[0], mtcur->mnt_dir) && |
&& strcmp(argv[0], mtcur->mnt_dir) != 0 |
1952 |
strcmp(storage_path, mtcur->mnt_dir)) continue; |
&& strcmp(storage_path, mtcur->mnt_dir) != 0 |
1953 |
|
) { |
1954 |
|
continue; // no |
1955 |
|
} |
1956 |
|
|
1957 |
// Remember this entry. Something later may have |
// Remember this entry. Something later may have |
1958 |
// overmounted it, and we want the _last_ match. |
// overmounted it, and we want the _last_ match. |
1960 |
|
|
1961 |
// If we're mounting all |
// If we're mounting all |
1962 |
} else { |
} else { |
1963 |
// Do we need to match a filesystem type? |
struct mntent *mp; |
1964 |
if (fstype && match_fstype(mtcur, fstype)) |
// No, mount -a won't mount anything, |
1965 |
|
// even user mounts, for mere humans |
1966 |
|
if (nonroot) |
1967 |
|
bb_error_msg_and_die(bb_msg_you_must_be_root); |
1968 |
|
|
1969 |
|
// Does type match? (NULL matches always) |
1970 |
|
if (!match_fstype(mtcur, fstype)) |
1971 |
continue; |
continue; |
1972 |
|
|
1973 |
// Skip noauto and swap anyway. |
// Skip noauto and swap anyway |
1974 |
if (parse_mount_options(mtcur->mnt_opts, 0) & (MOUNT_NOAUTO | MOUNT_SWAP)) |
if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP)) |
1975 |
|
// swap is bogus "fstype", parse_mount_options can't check fstypes |
1976 |
|
|| strcasecmp(mtcur->mnt_type, "swap") == 0 |
1977 |
|
) { |
1978 |
continue; |
continue; |
1979 |
|
} |
1980 |
|
|
1981 |
// No, mount -a won't mount anything, |
// Does (at least one) option match? |
1982 |
// even user mounts, for mere humans |
// (NULL matches always) |
1983 |
if (nonroot) |
if (!match_opt(mtcur->mnt_opts, O_optmatch)) |
1984 |
bb_error_msg_and_die(must_be_root); |
continue; |
1985 |
|
|
1986 |
// Mount this thing |
resolve_mount_spec(&mtcur->mnt_fsname); |
|
if (ENABLE_FEATURE_MOUNT_LABEL) |
|
|
resolve_mount_spec(&mtpair->mnt_fsname); |
|
1987 |
|
|
1988 |
// NFS mounts want this to be xrealloc-able |
// NFS mounts want this to be xrealloc-able |
1989 |
mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); |
mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); |
1990 |
if (singlemount(mtcur, 1)) { |
|
1991 |
// Count number of failed mounts |
// If nothing is mounted on this directory... |
1992 |
rc++; |
// (otherwise repeated "mount -a" mounts everything again) |
1993 |
|
mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0); |
1994 |
|
// We do not check fsname match of found mount point - |
1995 |
|
// "/" may have fsname of "/dev/root" while fstab |
1996 |
|
// says "/dev/something_else". |
1997 |
|
if (mp) { |
1998 |
|
if (verbose) { |
1999 |
|
bb_error_msg("according to %s, " |
2000 |
|
"%s is already mounted on %s", |
2001 |
|
bb_path_mtab_file, |
2002 |
|
mp->mnt_fsname, mp->mnt_dir); |
2003 |
|
} |
2004 |
|
} else { |
2005 |
|
// ...mount this thing |
2006 |
|
if (singlemount(mtcur, /*ignore_busy:*/ 1)) { |
2007 |
|
// Count number of failed mounts |
2008 |
|
rc++; |
2009 |
|
} |
2010 |
} |
} |
2011 |
free(mtcur->mnt_opts); |
free(mtcur->mnt_opts); |
2012 |
} |
} |
2014 |
|
|
2015 |
// End of fstab/mtab is reached. |
// End of fstab/mtab is reached. |
2016 |
// Were we looking for something specific? |
// Were we looking for something specific? |
2017 |
if (argv[0]) { |
if (argv[0]) { // yes |
2018 |
|
long l; |
2019 |
|
|
2020 |
// If we didn't find anything, complain |
// If we didn't find anything, complain |
2021 |
if (!mtcur->mnt_fsname) |
if (!mtcur->mnt_fsname) |
2022 |
bb_error_msg_and_die("can't find %s in %s", |
bb_error_msg_and_die("can't find %s in %s", |
2023 |
argv[0], fstabname); |
argv[0], fstabname); |
2024 |
|
|
2025 |
|
// What happens when we try to "mount swap_partition"? |
2026 |
|
// (fstab containts "swap_partition swap swap defaults 0 0") |
2027 |
|
// util-linux-ng 2.13.1 does this: |
2028 |
|
// stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory) |
2029 |
|
// mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory) |
2030 |
|
// lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory) |
2031 |
|
// write(2, "mount: mount point swap does not exist\n", 39) = 39 |
2032 |
|
// exit_group(32) = ? |
2033 |
|
#if 0 |
2034 |
|
// In case we want to simply skip swap partitions: |
2035 |
|
l = parse_mount_options(mtcur->mnt_opts, NULL); |
2036 |
|
if ((l & MOUNT_SWAP) |
2037 |
|
// swap is bogus "fstype", parse_mount_options can't check fstypes |
2038 |
|
|| strcasecmp(mtcur->mnt_type, "swap") == 0 |
2039 |
|
) { |
2040 |
|
goto ret; |
2041 |
|
} |
2042 |
|
#endif |
2043 |
if (nonroot) { |
if (nonroot) { |
2044 |
// fstab must have "users" or "user" |
// fstab must have "users" or "user" |
2045 |
if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS)) |
l = parse_mount_options(mtcur->mnt_opts, NULL); |
2046 |
bb_error_msg_and_die(must_be_root); |
if (!(l & MOUNT_USERS)) |
2047 |
} |
bb_error_msg_and_die(bb_msg_you_must_be_root); |
2048 |
|
} |
2049 |
// Mount the last thing we found |
|
2050 |
mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); |
//util-linux-2.12 does not do this check. |
2051 |
append_mount_options(&(mtcur->mnt_opts), cmdopts); |
//// If nothing is mounted on this directory... |
2052 |
if (ENABLE_FEATURE_MOUNT_LABEL) { |
//// (otherwise repeated "mount FOO" mounts FOO again) |
2053 |
|
//mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0); |
2054 |
|
//if (mp) { |
2055 |
|
// bb_error_msg("according to %s, " |
2056 |
|
// "%s is already mounted on %s", |
2057 |
|
// bb_path_mtab_file, |
2058 |
|
// mp->mnt_fsname, mp->mnt_dir); |
2059 |
|
//} else { |
2060 |
|
// ...mount the last thing we found |
2061 |
|
mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); |
2062 |
|
append_mount_options(&(mtcur->mnt_opts), cmdopts); |
2063 |
resolve_mount_spec(&mtpair->mnt_fsname); |
resolve_mount_spec(&mtpair->mnt_fsname); |
2064 |
} |
rc = singlemount(mtcur, /*ignore_busy:*/ 0); |
2065 |
rc = singlemount(mtcur, 0); |
if (ENABLE_FEATURE_CLEAN_UP) |
2066 |
if (ENABLE_FEATURE_CLEAN_UP) |
free(mtcur->mnt_opts); |
2067 |
free(mtcur->mnt_opts); |
//} |
2068 |
} |
} |
2069 |
|
|
2070 |
|
//ret: |
2071 |
if (ENABLE_FEATURE_CLEAN_UP) |
if (ENABLE_FEATURE_CLEAN_UP) |
2072 |
endmntent(fstab); |
endmntent(fstab); |
2073 |
if (ENABLE_FEATURE_CLEAN_UP) { |
if (ENABLE_FEATURE_CLEAN_UP) { |
2074 |
free(storage_path); |
free(storage_path); |
2075 |
free(cmdopts); |
free(cmdopts); |
2076 |
} |
} |
2077 |
|
|
2078 |
|
//TODO: exitcode should be ORed mask of (from "man mount"): |
2079 |
|
// 0 success |
2080 |
|
// 1 incorrect invocation or permissions |
2081 |
|
// 2 system error (out of memory, cannot fork, no more loop devices) |
2082 |
|
// 4 internal mount bug or missing nfs support in mount |
2083 |
|
// 8 user interrupt |
2084 |
|
//16 problems writing or locking /etc/mtab |
2085 |
|
//32 mount failure |
2086 |
|
//64 some mount succeeded |
2087 |
return rc; |
return rc; |
2088 |
} |
} |