| lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame] | 1 | /* vi: set sw=4 ts=4: */ | 
|  | 2 | /* | 
|  | 3 | * Mini mount implementation for busybox | 
|  | 4 | * | 
|  | 5 | * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>. | 
|  | 6 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | 
|  | 7 | * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net> | 
|  | 8 | * | 
|  | 9 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 
|  | 10 | */ | 
|  | 11 | // 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 | // | 
|  | 19 |  | 
|  | 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:     "\n	-a		Mount all filesystems in fstab" | 
|  | 25 | //usage:	IF_FEATURE_MOUNT_FAKE( | 
|  | 26 | //usage:	IF_FEATURE_MTAB_SUPPORT( | 
|  | 27 | //usage:     "\n	-f		Update /etc/mtab, but don't mount" | 
|  | 28 | //usage:	) | 
|  | 29 | //usage:	IF_NOT_FEATURE_MTAB_SUPPORT( | 
|  | 30 | //usage:     "\n	-f		Dry run" | 
|  | 31 | //usage:	) | 
|  | 32 | //usage:	) | 
|  | 33 | //usage:	IF_FEATURE_MOUNT_HELPERS( | 
|  | 34 | //usage:     "\n	-i		Don't run mount helper" | 
|  | 35 | //usage:	) | 
|  | 36 | //usage:	IF_FEATURE_MTAB_SUPPORT( | 
|  | 37 | //usage:     "\n	-n		Don't update /etc/mtab" | 
|  | 38 | //usage:	) | 
|  | 39 | //usage:	IF_FEATURE_MOUNT_VERBOSE( | 
|  | 40 | //usage:     "\n	-v		Verbose" | 
|  | 41 | //usage:	) | 
|  | 42 | ////usage:   "\n	-s		Sloppy (ignored)" | 
|  | 43 | //usage:     "\n	-r		Read-only mount" | 
|  | 44 | //usage:     "\n	-w		Read-write mount (default)" | 
|  | 45 | //usage:     "\n	-t FSTYPE[,...]	Filesystem type(s)" | 
|  | 46 | //usage:     "\n	-O OPT		Mount only filesystems with option OPT (-a only)" | 
|  | 47 | //usage:     "\n-o OPT:" | 
|  | 48 | //usage:	IF_FEATURE_MOUNT_LOOP( | 
|  | 49 | //usage:     "\n	loop		Ignored (loop devices are autodetected)" | 
|  | 50 | //usage:	) | 
|  | 51 | //usage:	IF_FEATURE_MOUNT_FLAGS( | 
|  | 52 | //usage:     "\n	[a]sync		Writes are [a]synchronous" | 
|  | 53 | //usage:     "\n	[no]atime	Disable/enable updates to inode access times" | 
|  | 54 | //usage:     "\n	[no]diratime	Disable/enable atime updates to directories" | 
|  | 55 | //usage:     "\n	[no]relatime	Disable/enable atime updates relative to modification time" | 
|  | 56 | //usage:     "\n	[no]dev		(Dis)allow use of special device files" | 
|  | 57 | //usage:     "\n	[no]exec	(Dis)allow use of executable files" | 
|  | 58 | //usage:     "\n	[no]suid	(Dis)allow set-user-id-root programs" | 
|  | 59 | //usage:     "\n	[r]shared	Convert [recursively] to a shared subtree" | 
|  | 60 | //usage:     "\n	[r]slave	Convert [recursively] to a slave subtree" | 
|  | 61 | //usage:     "\n	[r]private	Convert [recursively] to a private subtree" | 
|  | 62 | //usage:     "\n	[un]bindable	Make mount point [un]able to be bind mounted" | 
|  | 63 | //usage:     "\n	[r]bind		Bind a file or directory [recursively] to another location" | 
|  | 64 | //usage:     "\n	move		Relocate an existing mount point" | 
|  | 65 | //usage:	) | 
|  | 66 | //usage:     "\n	remount		Remount a mounted filesystem, changing flags" | 
|  | 67 | //usage:     "\n	ro/rw		Same as -r/-w" | 
|  | 68 | //usage:     "\n" | 
|  | 69 | //usage:     "\nThere are filesystem-specific -o flags." | 
|  | 70 | //usage: | 
|  | 71 | //usage:#define mount_example_usage | 
|  | 72 | //usage:       "$ mount\n" | 
|  | 73 | //usage:       "/dev/hda3 on / type minix (rw)\n" | 
|  | 74 | //usage:       "proc on /proc type proc (rw)\n" | 
|  | 75 | //usage:       "devpts on /dev/pts type devpts (rw)\n" | 
|  | 76 | //usage:       "$ mount /dev/fd0 /mnt -t msdos -o ro\n" | 
|  | 77 | //usage:       "$ mount /tmp/diskimage /opt -t ext2 -o loop\n" | 
|  | 78 | //usage:       "$ mount cd_image.iso mydir\n" | 
|  | 79 | //usage:#define mount_notes_usage | 
|  | 80 | //usage:       "Returns 0 for success, number of failed mounts for -a, or errno for one mount." | 
|  | 81 |  | 
|  | 82 | #include <mntent.h> | 
|  | 83 | #include <syslog.h> | 
|  | 84 | #include <sys/mount.h> | 
|  | 85 | // Grab more as needed from util-linux's mount/mount_constants.h | 
|  | 86 | #ifndef MS_DIRSYNC | 
|  | 87 | # define MS_DIRSYNC     (1 << 7) // Directory modifications are synchronous | 
|  | 88 | #endif | 
|  | 89 | #ifndef MS_UNION | 
|  | 90 | # define MS_UNION       (1 << 8) | 
|  | 91 | #endif | 
|  | 92 | #ifndef MS_BIND | 
|  | 93 | # define MS_BIND        (1 << 12) | 
|  | 94 | #endif | 
|  | 95 | #ifndef MS_MOVE | 
|  | 96 | # define MS_MOVE        (1 << 13) | 
|  | 97 | #endif | 
|  | 98 | #ifndef MS_RECURSIVE | 
|  | 99 | # define MS_RECURSIVE   (1 << 14) | 
|  | 100 | #endif | 
|  | 101 | #ifndef MS_SILENT | 
|  | 102 | # define MS_SILENT      (1 << 15) | 
|  | 103 | #endif | 
|  | 104 | // The shared subtree stuff, which went in around 2.6.15 | 
|  | 105 | #ifndef MS_UNBINDABLE | 
|  | 106 | # define MS_UNBINDABLE  (1 << 17) | 
|  | 107 | #endif | 
|  | 108 | #ifndef MS_PRIVATE | 
|  | 109 | # define MS_PRIVATE     (1 << 18) | 
|  | 110 | #endif | 
|  | 111 | #ifndef MS_SLAVE | 
|  | 112 | # define MS_SLAVE       (1 << 19) | 
|  | 113 | #endif | 
|  | 114 | #ifndef MS_SHARED | 
|  | 115 | # define MS_SHARED      (1 << 20) | 
|  | 116 | #endif | 
|  | 117 | #ifndef MS_RELATIME | 
|  | 118 | # define MS_RELATIME    (1 << 21) | 
|  | 119 | #endif | 
|  | 120 | #ifndef MS_STRICTATIME | 
|  | 121 | # define MS_STRICTATIME (1 << 24) | 
|  | 122 | #endif | 
|  | 123 |  | 
|  | 124 | /* Any ~MS_FOO value has this bit set: */ | 
|  | 125 | #define BB_MS_INVERTED_VALUE (1u << 31) | 
|  | 126 |  | 
|  | 127 | #include "libbb.h" | 
|  | 128 | #if ENABLE_FEATURE_MOUNT_LABEL | 
|  | 129 | # include "volume_id.h" | 
|  | 130 | #else | 
|  | 131 | # define resolve_mount_spec(fsname) ((void)0) | 
|  | 132 | #endif | 
|  | 133 |  | 
|  | 134 | // Needed for nfs support only | 
|  | 135 | #include <sys/utsname.h> | 
|  | 136 | #undef TRUE | 
|  | 137 | #undef FALSE | 
|  | 138 | #if ENABLE_FEATURE_MOUNT_NFS | 
|  | 139 | /* This is just a warning of a common mistake.  Possibly this should be a | 
|  | 140 | * uclibc faq entry rather than in busybox... */ | 
|  | 141 | # if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) | 
|  | 142 | #  error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support" | 
|  | 143 | # endif | 
|  | 144 | # include <rpc/rpc.h> | 
|  | 145 | # include <rpc/pmap_prot.h> | 
|  | 146 | # include <rpc/pmap_clnt.h> | 
|  | 147 | #endif | 
|  | 148 | #ifndef MS_RELATIME | 
|  | 149 | #define	MS_RELATIME		(1 << 21)	/* Update atime relative to mtime/ctime */ | 
|  | 150 | #endif | 
|  | 151 |  | 
|  | 152 |  | 
|  | 153 | #if defined(__dietlibc__) | 
|  | 154 | // 16.12.2006, Sampo Kellomaki (sampo@iki.fi) | 
|  | 155 | // dietlibc-0.30 does not have implementation of getmntent_r() | 
|  | 156 | static struct mntent *getmntent_r(FILE* stream, struct mntent* result, | 
|  | 157 | char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM) | 
|  | 158 | { | 
|  | 159 | struct mntent* ment = getmntent(stream); | 
|  | 160 | return memcpy(result, ment, sizeof(*ment)); | 
|  | 161 | } | 
|  | 162 | #endif | 
|  | 163 |  | 
|  | 164 |  | 
|  | 165 | // Not real flags, but we want to be able to check for this. | 
|  | 166 | enum { | 
|  | 167 | MOUNT_USERS  = (1 << 28) * ENABLE_DESKTOP, | 
|  | 168 | MOUNT_NOAUTO = (1 << 29), | 
|  | 169 | MOUNT_SWAP   = (1 << 30), | 
|  | 170 | }; | 
|  | 171 |  | 
|  | 172 |  | 
|  | 173 | #define OPTION_STR "o:t:rwanfvsiO:" | 
|  | 174 | enum { | 
|  | 175 | OPT_o = (1 << 0), | 
|  | 176 | OPT_t = (1 << 1), | 
|  | 177 | OPT_r = (1 << 2), | 
|  | 178 | OPT_w = (1 << 3), | 
|  | 179 | OPT_a = (1 << 4), | 
|  | 180 | OPT_n = (1 << 5), | 
|  | 181 | OPT_f = (1 << 6), | 
|  | 182 | OPT_v = (1 << 7), | 
|  | 183 | OPT_s = (1 << 8), | 
|  | 184 | OPT_i = (1 << 9), | 
|  | 185 | OPT_O = (1 << 10), | 
|  | 186 | }; | 
|  | 187 |  | 
|  | 188 | #if ENABLE_FEATURE_MTAB_SUPPORT | 
|  | 189 | #define USE_MTAB (!(option_mask32 & OPT_n)) | 
|  | 190 | #else | 
|  | 191 | #define USE_MTAB 0 | 
|  | 192 | #endif | 
|  | 193 |  | 
|  | 194 | #if ENABLE_FEATURE_MOUNT_FAKE | 
|  | 195 | #define FAKE_IT (option_mask32 & OPT_f) | 
|  | 196 | #else | 
|  | 197 | #define FAKE_IT 0 | 
|  | 198 | #endif | 
|  | 199 |  | 
|  | 200 | #if ENABLE_FEATURE_MOUNT_HELPERS | 
|  | 201 | #define HELPERS_ALLOWED (!(option_mask32 & OPT_i)) | 
|  | 202 | #else | 
|  | 203 | #define HELPERS_ALLOWED 0 | 
|  | 204 | #endif | 
|  | 205 |  | 
|  | 206 |  | 
|  | 207 | // TODO: more "user" flag compatibility. | 
|  | 208 | // "user" option (from mount manpage): | 
|  | 209 | // Only the user that mounted a filesystem can unmount it again. | 
|  | 210 | // If any user should be able to unmount, then use users instead of user | 
|  | 211 | // in the fstab line.  The owner option is similar to the user option, | 
|  | 212 | // with the restriction that the user must be the owner of the special file. | 
|  | 213 | // This may be useful e.g. for /dev/fd if a login script makes | 
|  | 214 | // the console user owner of this device. | 
|  | 215 |  | 
|  | 216 | // Standard mount options (from -o options or --options), | 
|  | 217 | // with corresponding flags | 
|  | 218 | static const int32_t mount_options[] = { | 
|  | 219 | // MS_FLAGS set a bit.  ~MS_FLAGS disable that bit.  0 flags are NOPs. | 
|  | 220 |  | 
|  | 221 | IF_FEATURE_MOUNT_LOOP( | 
|  | 222 | /* "loop" */ 0, | 
|  | 223 | ) | 
|  | 224 |  | 
|  | 225 | IF_FEATURE_MOUNT_FSTAB( | 
|  | 226 | /* "defaults" */ 0, | 
|  | 227 | /* "quiet" 0 - do not filter out, vfat wants to see it */ | 
|  | 228 | /* "noauto" */ MOUNT_NOAUTO, | 
|  | 229 | /* "sw"     */ MOUNT_SWAP, | 
|  | 230 | /* "swap"   */ MOUNT_SWAP, | 
|  | 231 | IF_DESKTOP(/* "user"  */ MOUNT_USERS,) | 
|  | 232 | IF_DESKTOP(/* "users" */ MOUNT_USERS,) | 
|  | 233 | /* "_netdev" */ 0, | 
|  | 234 | IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */ | 
|  | 235 | ) | 
|  | 236 |  | 
|  | 237 | IF_FEATURE_MOUNT_FLAGS( | 
|  | 238 | // vfs flags | 
|  | 239 | /* "nosuid"      */ MS_NOSUID, | 
|  | 240 | /* "suid"        */ ~MS_NOSUID, | 
|  | 241 | /* "dev"         */ ~MS_NODEV, | 
|  | 242 | /* "nodev"       */ MS_NODEV, | 
|  | 243 | /* "exec"        */ ~MS_NOEXEC, | 
|  | 244 | /* "noexec"      */ MS_NOEXEC, | 
|  | 245 | /* "sync"        */ MS_SYNCHRONOUS, | 
|  | 246 | /* "dirsync"     */ MS_DIRSYNC, | 
|  | 247 | /* "async"       */ ~MS_SYNCHRONOUS, | 
|  | 248 | /* "atime"       */ ~MS_NOATIME, | 
|  | 249 | /* "noatime"     */ MS_NOATIME, | 
|  | 250 | /* "diratime"    */ ~MS_NODIRATIME, | 
|  | 251 | /* "nodiratime"  */ MS_NODIRATIME, | 
|  | 252 | /* "mand"        */ MS_MANDLOCK, | 
|  | 253 | /* "nomand"      */ ~MS_MANDLOCK, | 
|  | 254 | /* "relatime"    */ MS_RELATIME, | 
|  | 255 | /* "norelatime"  */ ~MS_RELATIME, | 
|  | 256 | /* "strictatime" */ MS_STRICTATIME, | 
|  | 257 | /* "loud"        */ ~MS_SILENT, | 
|  | 258 | /* "rbind"       */ MS_BIND|MS_RECURSIVE, | 
|  | 259 |  | 
|  | 260 | // action flags | 
|  | 261 | /* "union"       */ MS_UNION, | 
|  | 262 | /* "bind"        */ MS_BIND, | 
|  | 263 | /* "move"        */ MS_MOVE, | 
|  | 264 | /* "shared"      */ MS_SHARED, | 
|  | 265 | /* "slave"       */ MS_SLAVE, | 
|  | 266 | /* "private"     */ MS_PRIVATE, | 
|  | 267 | /* "unbindable"  */ MS_UNBINDABLE, | 
|  | 268 | /* "rshared"     */ MS_SHARED|MS_RECURSIVE, | 
|  | 269 | /* "rslave"      */ MS_SLAVE|MS_RECURSIVE, | 
|  | 270 | /* "rprivate"    */ MS_PRIVATE|MS_RECURSIVE, | 
|  | 271 | /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE, | 
|  | 272 | ) | 
|  | 273 |  | 
|  | 274 | // Always understood. | 
|  | 275 | /* "ro"      */ MS_RDONLY,  // vfs flag | 
|  | 276 | /* "rw"      */ ~MS_RDONLY, // vfs flag | 
|  | 277 | /* "remount" */ MS_REMOUNT  // action flag | 
|  | 278 | }; | 
|  | 279 |  | 
|  | 280 | static const char mount_option_str[] = | 
|  | 281 | IF_FEATURE_MOUNT_LOOP( | 
|  | 282 | "loop\0" | 
|  | 283 | ) | 
|  | 284 | IF_FEATURE_MOUNT_FSTAB( | 
|  | 285 | "defaults\0" | 
|  | 286 | // "quiet\0" - do not filter out, vfat wants to see it | 
|  | 287 | "noauto\0" | 
|  | 288 | "sw\0" | 
|  | 289 | "swap\0" | 
|  | 290 | IF_DESKTOP("user\0") | 
|  | 291 | IF_DESKTOP("users\0") | 
|  | 292 | "_netdev\0" | 
|  | 293 | IF_DESKTOP("comment=\0") /* systemd uses this in fstab */ | 
|  | 294 | ) | 
|  | 295 | IF_FEATURE_MOUNT_FLAGS( | 
|  | 296 | // vfs flags | 
|  | 297 | "nosuid\0" | 
|  | 298 | "suid\0" | 
|  | 299 | "dev\0" | 
|  | 300 | "nodev\0" | 
|  | 301 | "exec\0" | 
|  | 302 | "noexec\0" | 
|  | 303 | "sync\0" | 
|  | 304 | "dirsync\0" | 
|  | 305 | "async\0" | 
|  | 306 | "atime\0" | 
|  | 307 | "noatime\0" | 
|  | 308 | "diratime\0" | 
|  | 309 | "nodiratime\0" | 
|  | 310 | "mand\0" | 
|  | 311 | "nomand\0" | 
|  | 312 | "relatime\0" | 
|  | 313 | "norelatime\0" | 
|  | 314 | "strictatime\0" | 
|  | 315 | "loud\0" | 
|  | 316 | "rbind\0" | 
|  | 317 |  | 
|  | 318 | // action flags | 
|  | 319 | "union\0" | 
|  | 320 | "bind\0" | 
|  | 321 | "move\0" | 
|  | 322 | "make-shared\0" | 
|  | 323 | "make-slave\0" | 
|  | 324 | "make-private\0" | 
|  | 325 | "make-unbindable\0" | 
|  | 326 | "make-rshared\0" | 
|  | 327 | "make-rslave\0" | 
|  | 328 | "make-rprivate\0" | 
|  | 329 | "make-runbindable\0" | 
|  | 330 | ) | 
|  | 331 |  | 
|  | 332 | // Always understood. | 
|  | 333 | "ro\0"        // vfs flag | 
|  | 334 | "rw\0"        // vfs flag | 
|  | 335 | "remount\0"   // action flag | 
|  | 336 | ; | 
|  | 337 |  | 
|  | 338 |  | 
|  | 339 | struct globals { | 
|  | 340 | #if ENABLE_FEATURE_MOUNT_NFS | 
|  | 341 | smalluint nfs_mount_version; | 
|  | 342 | #endif | 
|  | 343 | #if ENABLE_FEATURE_MOUNT_VERBOSE | 
|  | 344 | unsigned verbose; | 
|  | 345 | #endif | 
|  | 346 | llist_t *fslist; | 
|  | 347 | char getmntent_buf[1]; | 
|  | 348 | } FIX_ALIASING; | 
|  | 349 | enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) }; | 
|  | 350 | #define G (*(struct globals*)&bb_common_bufsiz1) | 
|  | 351 | #define nfs_mount_version (G.nfs_mount_version) | 
|  | 352 | #if ENABLE_FEATURE_MOUNT_VERBOSE | 
|  | 353 | #define verbose           (G.verbose          ) | 
|  | 354 | #else | 
|  | 355 | #define verbose           0 | 
|  | 356 | #endif | 
|  | 357 | #define fslist            (G.fslist           ) | 
|  | 358 | #define getmntent_buf     (G.getmntent_buf    ) | 
|  | 359 | #define INIT_G() do { } while (0) | 
|  | 360 |  | 
|  | 361 | #if ENABLE_FEATURE_MTAB_SUPPORT | 
|  | 362 | /* | 
|  | 363 | * update_mtab_entry_on_move() is used to update entry in case of mount --move. | 
|  | 364 | * we are looking for existing entries mnt_dir which is equal to mnt_fsname of | 
|  | 365 | * input mntent and replace it by new one. | 
|  | 366 | */ | 
|  | 367 | static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp) | 
|  | 368 | { | 
|  | 369 | struct mntent *entries, *m; | 
|  | 370 | int i, count; | 
|  | 371 | FILE *mountTable; | 
|  | 372 |  | 
|  | 373 | mountTable = setmntent(bb_path_mtab_file, "r"); | 
|  | 374 | if (!mountTable) { | 
|  | 375 | bb_perror_msg(bb_path_mtab_file); | 
|  | 376 | return; | 
|  | 377 | } | 
|  | 378 |  | 
|  | 379 | entries = NULL; | 
|  | 380 | count = 0; | 
|  | 381 | while ((m = getmntent(mountTable)) != NULL) { | 
|  | 382 | entries = xrealloc_vector(entries, 3, count); | 
|  | 383 | entries[count].mnt_fsname = xstrdup(m->mnt_fsname); | 
|  | 384 | entries[count].mnt_dir = xstrdup(m->mnt_dir); | 
|  | 385 | entries[count].mnt_type = xstrdup(m->mnt_type); | 
|  | 386 | entries[count].mnt_opts = xstrdup(m->mnt_opts); | 
|  | 387 | entries[count].mnt_freq = m->mnt_freq; | 
|  | 388 | entries[count].mnt_passno = m->mnt_passno; | 
|  | 389 | count++; | 
|  | 390 | } | 
|  | 391 | endmntent(mountTable); | 
|  | 392 |  | 
|  | 393 | mountTable = setmntent(bb_path_mtab_file, "w"); | 
|  | 394 | if (mountTable) { | 
|  | 395 | for (i = 0; i < count; i++) { | 
|  | 396 | if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0) | 
|  | 397 | addmntent(mountTable, &entries[i]); | 
|  | 398 | else | 
|  | 399 | addmntent(mountTable, mp); | 
|  | 400 | } | 
|  | 401 | endmntent(mountTable); | 
|  | 402 | } else if (errno != EROFS) | 
|  | 403 | bb_perror_msg(bb_path_mtab_file); | 
|  | 404 |  | 
|  | 405 | if (ENABLE_FEATURE_CLEAN_UP) { | 
|  | 406 | for (i = 0; i < count; i++) { | 
|  | 407 | free(entries[i].mnt_fsname); | 
|  | 408 | free(entries[i].mnt_dir); | 
|  | 409 | free(entries[i].mnt_type); | 
|  | 410 | free(entries[i].mnt_opts); | 
|  | 411 | } | 
|  | 412 | free(entries); | 
|  | 413 | } | 
|  | 414 | } | 
|  | 415 | #endif | 
|  | 416 |  | 
|  | 417 | #if ENABLE_FEATURE_MOUNT_VERBOSE | 
|  | 418 | static int verbose_mount(const char *source, const char *target, | 
|  | 419 | const char *filesystemtype, | 
|  | 420 | unsigned long mountflags, const void *data) | 
|  | 421 | { | 
|  | 422 | int rc; | 
|  | 423 |  | 
|  | 424 | errno = 0; | 
|  | 425 | rc = mount(source, target, filesystemtype, mountflags, data); | 
|  | 426 | if (verbose >= 2) | 
|  | 427 | bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d", | 
|  | 428 | source, target, filesystemtype, | 
|  | 429 | mountflags, (char*)data, rc); | 
|  | 430 | return rc; | 
|  | 431 | } | 
|  | 432 | #else | 
|  | 433 | #define verbose_mount(...) mount(__VA_ARGS__) | 
|  | 434 | #endif | 
|  | 435 |  | 
|  | 436 | // Append mount options to string | 
|  | 437 | static void append_mount_options(char **oldopts, const char *newopts) | 
|  | 438 | { | 
|  | 439 | if (*oldopts && **oldopts) { | 
|  | 440 | // Do not insert options which are already there | 
|  | 441 | while (newopts[0]) { | 
|  | 442 | char *p; | 
|  | 443 | int len = strlen(newopts); | 
|  | 444 | p = strchr(newopts, ','); | 
|  | 445 | if (p) len = p - newopts; | 
|  | 446 | p = *oldopts; | 
|  | 447 | while (1) { | 
|  | 448 | if (!strncmp(p, newopts, len) | 
|  | 449 | && (p[len] == ',' || p[len] == '\0')) | 
|  | 450 | goto skip; | 
|  | 451 | p = strchr(p,','); | 
|  | 452 | if (!p) break; | 
|  | 453 | p++; | 
|  | 454 | } | 
|  | 455 | p = xasprintf("%s,%.*s", *oldopts, len, newopts); | 
|  | 456 | free(*oldopts); | 
|  | 457 | *oldopts = p; | 
|  | 458 | skip: | 
|  | 459 | newopts += len; | 
|  | 460 | while (newopts[0] == ',') newopts++; | 
|  | 461 | } | 
|  | 462 | } else { | 
|  | 463 | if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts); | 
|  | 464 | *oldopts = xstrdup(newopts); | 
|  | 465 | } | 
|  | 466 | } | 
|  | 467 |  | 
|  | 468 | // Use the mount_options list to parse options into flags. | 
|  | 469 | // Also update list of unrecognized options if unrecognized != NULL | 
|  | 470 | static unsigned long parse_mount_options(char *options, char **unrecognized) | 
|  | 471 | { | 
|  | 472 | unsigned long flags = MS_SILENT; | 
|  | 473 |  | 
|  | 474 | // Loop through options | 
|  | 475 | for (;;) { | 
|  | 476 | unsigned i; | 
|  | 477 | char *comma = strchr(options, ','); | 
|  | 478 | const char *option_str = mount_option_str; | 
|  | 479 |  | 
|  | 480 | if (comma) *comma = '\0'; | 
|  | 481 |  | 
|  | 482 | // FIXME: use hasmntopt() | 
|  | 483 | // Find this option in mount_options | 
|  | 484 | for (i = 0; i < ARRAY_SIZE(mount_options); i++) { | 
|  | 485 | unsigned opt_len = strlen(option_str); | 
|  | 486 |  | 
|  | 487 | if (strncasecmp(option_str, options, opt_len) == 0 | 
|  | 488 | && (options[opt_len] == '\0' | 
|  | 489 | /* or is it "comment=" thingy in fstab? */ | 
|  | 490 | IF_FEATURE_MOUNT_FSTAB(IF_DESKTOP( || option_str[opt_len-1] == '=' )) | 
|  | 491 | ) | 
|  | 492 | ) { | 
|  | 493 | unsigned long fl = mount_options[i]; | 
|  | 494 | if (fl & BB_MS_INVERTED_VALUE) | 
|  | 495 | flags &= fl; | 
|  | 496 | else | 
|  | 497 | flags |= fl; | 
|  | 498 | goto found; | 
|  | 499 | } | 
|  | 500 | option_str += opt_len + 1; | 
|  | 501 | } | 
|  | 502 | // We did not recognize this option. | 
|  | 503 | // If "unrecognized" is not NULL, append option there. | 
|  | 504 | // Note that we should not append *empty* option - | 
|  | 505 | // in this case we want to pass NULL, not "", to "data" | 
|  | 506 | // parameter of mount(2) syscall. | 
|  | 507 | // This is crucial for filesystems that don't accept | 
|  | 508 | // any arbitrary mount options, like cgroup fs: | 
|  | 509 | // "mount -t cgroup none /mnt" | 
|  | 510 | if (options[0] && unrecognized) { | 
|  | 511 | // Add it to strflags, to pass on to kernel | 
|  | 512 | char *p = *unrecognized; | 
|  | 513 | unsigned len = p ? strlen(p) : 0; | 
|  | 514 | *unrecognized = p = xrealloc(p, len + strlen(options) + 2); | 
|  | 515 |  | 
|  | 516 | // Comma separated if it's not the first one | 
|  | 517 | if (len) p[len++] = ','; | 
|  | 518 | strcpy(p + len, options); | 
|  | 519 | } | 
|  | 520 | found: | 
|  | 521 | if (!comma) | 
|  | 522 | break; | 
|  | 523 | // Advance to next option | 
|  | 524 | *comma = ','; | 
|  | 525 | options = ++comma; | 
|  | 526 | } | 
|  | 527 |  | 
|  | 528 | return flags; | 
|  | 529 | } | 
|  | 530 |  | 
|  | 531 | // Return a list of all block device backed filesystems | 
|  | 532 | static llist_t *get_block_backed_filesystems(void) | 
|  | 533 | { | 
|  | 534 | static const char filesystems[2][sizeof("/proc/filesystems")] = { | 
|  | 535 | "/etc/filesystems", | 
|  | 536 | "/proc/filesystems", | 
|  | 537 | }; | 
|  | 538 | char *fs, *buf; | 
|  | 539 | llist_t *list = NULL; | 
|  | 540 | int i; | 
|  | 541 | FILE *f; | 
|  | 542 |  | 
|  | 543 | for (i = 0; i < 2; i++) { | 
|  | 544 | f = fopen_for_read(filesystems[i]); | 
|  | 545 | if (!f) continue; | 
|  | 546 |  | 
|  | 547 | while ((buf = xmalloc_fgetline(f)) != NULL) { | 
|  | 548 | if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5])) | 
|  | 549 | goto next; | 
|  | 550 | fs = skip_whitespace(buf); | 
|  | 551 | if (*fs == '#' || *fs == '*' || !*fs) | 
|  | 552 | goto next; | 
|  | 553 |  | 
|  | 554 | llist_add_to_end(&list, xstrdup(fs)); | 
|  | 555 | next: | 
|  | 556 | free(buf); | 
|  | 557 | } | 
|  | 558 | if (ENABLE_FEATURE_CLEAN_UP) fclose(f); | 
|  | 559 | } | 
|  | 560 |  | 
|  | 561 | return list; | 
|  | 562 | } | 
|  | 563 |  | 
|  | 564 | #if ENABLE_FEATURE_CLEAN_UP | 
|  | 565 | static void delete_block_backed_filesystems(void) | 
|  | 566 | { | 
|  | 567 | llist_free(fslist, free); | 
|  | 568 | } | 
|  | 569 | #else | 
|  | 570 | void delete_block_backed_filesystems(void); | 
|  | 571 | #endif | 
|  | 572 |  | 
|  | 573 | // Perform actual mount of specific filesystem at specific location. | 
|  | 574 | // NB: mp->xxx fields may be trashed on exit | 
|  | 575 | static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts) | 
|  | 576 | { | 
|  | 577 | int rc = 0; | 
|  | 578 |  | 
|  | 579 | if (FAKE_IT) { | 
|  | 580 | if (verbose >= 2) | 
|  | 581 | bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')", | 
|  | 582 | mp->mnt_fsname, mp->mnt_dir, mp->mnt_type, | 
|  | 583 | vfsflags, filteropts); | 
|  | 584 | goto mtab; | 
|  | 585 | } | 
|  | 586 |  | 
|  | 587 | // Mount, with fallback to read-only if necessary. | 
|  | 588 | for (;;) { | 
|  | 589 | errno = 0; | 
|  | 590 | rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type, | 
|  | 591 | vfsflags, filteropts); | 
|  | 592 |  | 
|  | 593 | // If mount failed, try | 
|  | 594 | // helper program mount.<mnt_type> | 
|  | 595 | if (HELPERS_ALLOWED && rc && mp->mnt_type) { | 
|  | 596 | char *args[8]; | 
|  | 597 | int errno_save = errno; | 
|  | 598 | args[0] = xasprintf("mount.%s", mp->mnt_type); | 
|  | 599 | rc = 1; | 
|  | 600 | if (FAKE_IT) | 
|  | 601 | args[rc++] = (char *)"-f"; | 
|  | 602 | if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB) | 
|  | 603 | args[rc++] = (char *)"-n"; | 
|  | 604 | args[rc++] = mp->mnt_fsname; | 
|  | 605 | args[rc++] = mp->mnt_dir; | 
|  | 606 | if (filteropts) { | 
|  | 607 | args[rc++] = (char *)"-o"; | 
|  | 608 | args[rc++] = filteropts; | 
|  | 609 | } | 
|  | 610 | args[rc] = NULL; | 
|  | 611 | rc = spawn_and_wait(args); | 
|  | 612 | free(args[0]); | 
|  | 613 | if (!rc) | 
|  | 614 | break; | 
|  | 615 | errno = errno_save; | 
|  | 616 | } | 
|  | 617 |  | 
|  | 618 | if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS)) | 
|  | 619 | break; | 
|  | 620 | if (!(vfsflags & MS_SILENT)) | 
|  | 621 | bb_error_msg("%s is write-protected, mounting read-only", | 
|  | 622 | mp->mnt_fsname); | 
|  | 623 | vfsflags |= MS_RDONLY; | 
|  | 624 | } | 
|  | 625 |  | 
|  | 626 | // Abort entirely if permission denied. | 
|  | 627 |  | 
|  | 628 | if (rc && errno == EPERM) | 
|  | 629 | bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); | 
|  | 630 |  | 
|  | 631 | // If the mount was successful, and we're maintaining an old-style | 
|  | 632 | // mtab file by hand, add the new entry to it now. | 
|  | 633 | mtab: | 
|  | 634 | if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) { | 
|  | 635 | char *fsname; | 
|  | 636 | FILE *mountTable = setmntent(bb_path_mtab_file, "a+"); | 
|  | 637 | const char *option_str = mount_option_str; | 
|  | 638 | int i; | 
|  | 639 |  | 
|  | 640 | if (!mountTable) { | 
|  | 641 | bb_perror_msg(bb_path_mtab_file); | 
|  | 642 | goto ret; | 
|  | 643 | } | 
|  | 644 |  | 
|  | 645 | // Add vfs string flags | 
|  | 646 | for (i = 0; mount_options[i] != MS_REMOUNT; i++) { | 
|  | 647 | if (mount_options[i] > 0 && (mount_options[i] & vfsflags)) | 
|  | 648 | append_mount_options(&(mp->mnt_opts), option_str); | 
|  | 649 | option_str += strlen(option_str) + 1; | 
|  | 650 | } | 
|  | 651 |  | 
|  | 652 | // Remove trailing / (if any) from directory we mounted on | 
|  | 653 | i = strlen(mp->mnt_dir) - 1; | 
|  | 654 | while (i > 0 && mp->mnt_dir[i] == '/') | 
|  | 655 | mp->mnt_dir[i--] = '\0'; | 
|  | 656 |  | 
|  | 657 | // Convert to canonical pathnames as needed | 
|  | 658 | mp->mnt_dir = bb_simplify_path(mp->mnt_dir); | 
|  | 659 | fsname = NULL; | 
|  | 660 | if (!mp->mnt_type || !*mp->mnt_type) { // bind mount | 
|  | 661 | mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname); | 
|  | 662 | mp->mnt_type = (char*)"bind"; | 
|  | 663 | } | 
|  | 664 | mp->mnt_freq = mp->mnt_passno = 0; | 
|  | 665 |  | 
|  | 666 | // Write and close | 
|  | 667 | #if ENABLE_FEATURE_MTAB_SUPPORT | 
|  | 668 | if (vfsflags & MS_MOVE) | 
|  | 669 | update_mtab_entry_on_move(mp); | 
|  | 670 | else | 
|  | 671 | #endif | 
|  | 672 | addmntent(mountTable, mp); | 
|  | 673 | endmntent(mountTable); | 
|  | 674 |  | 
|  | 675 | if (ENABLE_FEATURE_CLEAN_UP) { | 
|  | 676 | free(mp->mnt_dir); | 
|  | 677 | free(fsname); | 
|  | 678 | } | 
|  | 679 | } | 
|  | 680 | ret: | 
|  | 681 | return rc; | 
|  | 682 | } | 
|  | 683 |  | 
|  | 684 | #if ENABLE_FEATURE_MOUNT_NFS | 
|  | 685 |  | 
|  | 686 | /* | 
|  | 687 | * Linux NFS mount | 
|  | 688 | * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com> | 
|  | 689 | * | 
|  | 690 | * Licensed under GPLv2, see file LICENSE in this source tree. | 
|  | 691 | * | 
|  | 692 | * Wed Feb  8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port | 
|  | 693 | * numbers to be specified on the command line. | 
|  | 694 | * | 
|  | 695 | * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>: | 
|  | 696 | * Omit the call to connect() for Linux version 1.3.11 or later. | 
|  | 697 | * | 
|  | 698 | * Wed Oct  1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com> | 
|  | 699 | * Implemented the "bg", "fg" and "retry" mount options for NFS. | 
|  | 700 | * | 
|  | 701 | * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org> | 
|  | 702 | * - added Native Language Support | 
|  | 703 | * | 
|  | 704 | * Modified by Olaf Kirch and Trond Myklebust for new NFS code, | 
|  | 705 | * plus NFSv3 stuff. | 
|  | 706 | */ | 
|  | 707 |  | 
|  | 708 | #define MOUNTPORT 635 | 
|  | 709 | #define MNTPATHLEN 1024 | 
|  | 710 | #define MNTNAMLEN 255 | 
|  | 711 | #define FHSIZE 32 | 
|  | 712 | #define FHSIZE3 64 | 
|  | 713 |  | 
|  | 714 | typedef char fhandle[FHSIZE]; | 
|  | 715 |  | 
|  | 716 | typedef struct { | 
|  | 717 | unsigned int fhandle3_len; | 
|  | 718 | char *fhandle3_val; | 
|  | 719 | } fhandle3; | 
|  | 720 |  | 
|  | 721 | enum mountstat3 { | 
|  | 722 | MNT_OK = 0, | 
|  | 723 | MNT3ERR_PERM = 1, | 
|  | 724 | MNT3ERR_NOENT = 2, | 
|  | 725 | MNT3ERR_IO = 5, | 
|  | 726 | MNT3ERR_ACCES = 13, | 
|  | 727 | MNT3ERR_NOTDIR = 20, | 
|  | 728 | MNT3ERR_INVAL = 22, | 
|  | 729 | MNT3ERR_NAMETOOLONG = 63, | 
|  | 730 | MNT3ERR_NOTSUPP = 10004, | 
|  | 731 | MNT3ERR_SERVERFAULT = 10006, | 
|  | 732 | }; | 
|  | 733 | typedef enum mountstat3 mountstat3; | 
|  | 734 |  | 
|  | 735 | struct fhstatus { | 
|  | 736 | unsigned int fhs_status; | 
|  | 737 | union { | 
|  | 738 | fhandle fhs_fhandle; | 
|  | 739 | } fhstatus_u; | 
|  | 740 | }; | 
|  | 741 | typedef struct fhstatus fhstatus; | 
|  | 742 |  | 
|  | 743 | struct mountres3_ok { | 
|  | 744 | fhandle3 fhandle; | 
|  | 745 | struct { | 
|  | 746 | unsigned int auth_flavours_len; | 
|  | 747 | char *auth_flavours_val; | 
|  | 748 | } auth_flavours; | 
|  | 749 | }; | 
|  | 750 | typedef struct mountres3_ok mountres3_ok; | 
|  | 751 |  | 
|  | 752 | struct mountres3 { | 
|  | 753 | mountstat3 fhs_status; | 
|  | 754 | union { | 
|  | 755 | mountres3_ok mountinfo; | 
|  | 756 | } mountres3_u; | 
|  | 757 | }; | 
|  | 758 | typedef struct mountres3 mountres3; | 
|  | 759 |  | 
|  | 760 | typedef char *dirpath; | 
|  | 761 |  | 
|  | 762 | typedef char *name; | 
|  | 763 |  | 
|  | 764 | typedef struct mountbody *mountlist; | 
|  | 765 |  | 
|  | 766 | struct mountbody { | 
|  | 767 | name ml_hostname; | 
|  | 768 | dirpath ml_directory; | 
|  | 769 | mountlist ml_next; | 
|  | 770 | }; | 
|  | 771 | typedef struct mountbody mountbody; | 
|  | 772 |  | 
|  | 773 | typedef struct groupnode *groups; | 
|  | 774 |  | 
|  | 775 | struct groupnode { | 
|  | 776 | name gr_name; | 
|  | 777 | groups gr_next; | 
|  | 778 | }; | 
|  | 779 | typedef struct groupnode groupnode; | 
|  | 780 |  | 
|  | 781 | typedef struct exportnode *exports; | 
|  | 782 |  | 
|  | 783 | struct exportnode { | 
|  | 784 | dirpath ex_dir; | 
|  | 785 | groups ex_groups; | 
|  | 786 | exports ex_next; | 
|  | 787 | }; | 
|  | 788 | typedef struct exportnode exportnode; | 
|  | 789 |  | 
|  | 790 | struct ppathcnf { | 
|  | 791 | int pc_link_max; | 
|  | 792 | short pc_max_canon; | 
|  | 793 | short pc_max_input; | 
|  | 794 | short pc_name_max; | 
|  | 795 | short pc_path_max; | 
|  | 796 | short pc_pipe_buf; | 
|  | 797 | uint8_t pc_vdisable; | 
|  | 798 | char pc_xxx; | 
|  | 799 | short pc_mask[2]; | 
|  | 800 | }; | 
|  | 801 | typedef struct ppathcnf ppathcnf; | 
|  | 802 |  | 
|  | 803 | #define MOUNTPROG 100005 | 
|  | 804 | #define MOUNTVERS 1 | 
|  | 805 |  | 
|  | 806 | #define MOUNTPROC_NULL 0 | 
|  | 807 | #define MOUNTPROC_MNT 1 | 
|  | 808 | #define MOUNTPROC_DUMP 2 | 
|  | 809 | #define MOUNTPROC_UMNT 3 | 
|  | 810 | #define MOUNTPROC_UMNTALL 4 | 
|  | 811 | #define MOUNTPROC_EXPORT 5 | 
|  | 812 | #define MOUNTPROC_EXPORTALL 6 | 
|  | 813 |  | 
|  | 814 | #define MOUNTVERS_POSIX 2 | 
|  | 815 |  | 
|  | 816 | #define MOUNTPROC_PATHCONF 7 | 
|  | 817 |  | 
|  | 818 | #define MOUNT_V3 3 | 
|  | 819 |  | 
|  | 820 | #define MOUNTPROC3_NULL 0 | 
|  | 821 | #define MOUNTPROC3_MNT 1 | 
|  | 822 | #define MOUNTPROC3_DUMP 2 | 
|  | 823 | #define MOUNTPROC3_UMNT 3 | 
|  | 824 | #define MOUNTPROC3_UMNTALL 4 | 
|  | 825 | #define MOUNTPROC3_EXPORT 5 | 
|  | 826 |  | 
|  | 827 | enum { | 
|  | 828 | #ifndef NFS_FHSIZE | 
|  | 829 | NFS_FHSIZE = 32, | 
|  | 830 | #endif | 
|  | 831 | #ifndef NFS_PORT | 
|  | 832 | NFS_PORT = 2049 | 
|  | 833 | #endif | 
|  | 834 | }; | 
|  | 835 |  | 
|  | 836 | /* | 
|  | 837 | * We want to be able to compile mount on old kernels in such a way | 
|  | 838 | * that the binary will work well on more recent kernels. | 
|  | 839 | * Thus, if necessary we teach nfsmount.c the structure of new fields | 
|  | 840 | * that will come later. | 
|  | 841 | * | 
|  | 842 | * Moreover, the new kernel includes conflict with glibc includes | 
|  | 843 | * so it is easiest to ignore the kernel altogether (at compile time). | 
|  | 844 | */ | 
|  | 845 |  | 
|  | 846 | struct nfs2_fh { | 
|  | 847 | char            data[32]; | 
|  | 848 | }; | 
|  | 849 | struct nfs3_fh { | 
|  | 850 | unsigned short  size; | 
|  | 851 | unsigned char   data[64]; | 
|  | 852 | }; | 
|  | 853 |  | 
|  | 854 | struct nfs_mount_data { | 
|  | 855 | int		version;	/* 1 */ | 
|  | 856 | int		fd;		/* 1 */ | 
|  | 857 | struct nfs2_fh	old_root;	/* 1 */ | 
|  | 858 | int		flags;		/* 1 */ | 
|  | 859 | int		rsize;		/* 1 */ | 
|  | 860 | int		wsize;		/* 1 */ | 
|  | 861 | int		timeo;		/* 1 */ | 
|  | 862 | int		retrans;	/* 1 */ | 
|  | 863 | int		acregmin;	/* 1 */ | 
|  | 864 | int		acregmax;	/* 1 */ | 
|  | 865 | int		acdirmin;	/* 1 */ | 
|  | 866 | int		acdirmax;	/* 1 */ | 
|  | 867 | struct sockaddr_in addr;	/* 1 */ | 
|  | 868 | char		hostname[256];	/* 1 */ | 
|  | 869 | int		namlen;		/* 2 */ | 
|  | 870 | unsigned int	bsize;		/* 3 */ | 
|  | 871 | struct nfs3_fh	root;		/* 4 */ | 
|  | 872 | }; | 
|  | 873 |  | 
|  | 874 | /* bits in the flags field */ | 
|  | 875 | enum { | 
|  | 876 | NFS_MOUNT_SOFT = 0x0001,	/* 1 */ | 
|  | 877 | NFS_MOUNT_INTR = 0x0002,	/* 1 */ | 
|  | 878 | NFS_MOUNT_SECURE = 0x0004,	/* 1 */ | 
|  | 879 | NFS_MOUNT_POSIX = 0x0008,	/* 1 */ | 
|  | 880 | NFS_MOUNT_NOCTO = 0x0010,	/* 1 */ | 
|  | 881 | NFS_MOUNT_NOAC = 0x0020,	/* 1 */ | 
|  | 882 | NFS_MOUNT_TCP = 0x0040,		/* 2 */ | 
|  | 883 | NFS_MOUNT_VER3 = 0x0080,	/* 3 */ | 
|  | 884 | NFS_MOUNT_KERBEROS = 0x0100,	/* 3 */ | 
|  | 885 | NFS_MOUNT_NONLM = 0x0200,	/* 3 */ | 
|  | 886 | NFS_MOUNT_NOACL = 0x0800,	/* 4 */ | 
|  | 887 | NFS_MOUNT_NORDIRPLUS = 0x4000 | 
|  | 888 | }; | 
|  | 889 |  | 
|  | 890 |  | 
|  | 891 | /* | 
|  | 892 | * We need to translate between nfs status return values and | 
|  | 893 | * the local errno values which may not be the same. | 
|  | 894 | * | 
|  | 895 | * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno: | 
|  | 896 | * "after #include <errno.h> the symbol errno is reserved for any use, | 
|  | 897 | *  it cannot even be used as a struct tag or field name". | 
|  | 898 | */ | 
|  | 899 | #ifndef EDQUOT | 
|  | 900 | # define EDQUOT ENOSPC | 
|  | 901 | #endif | 
|  | 902 | /* Convert each NFSERR_BLAH into EBLAH */ | 
|  | 903 | static const uint8_t nfs_err_stat[] = { | 
|  | 904 | 1,  2,  5,  6, 13, 17, | 
|  | 905 | 19, 20, 21, 22, 27, 28, | 
|  | 906 | 30, 63, 66, 69, 70, 71 | 
|  | 907 | }; | 
|  | 908 | #if ( \ | 
|  | 909 | EPERM | ENOENT      | EIO      | ENXIO | EACCES| EEXIST | \ | 
|  | 910 | ENODEV| ENOTDIR     | EISDIR   | EINVAL| EFBIG | ENOSPC | \ | 
|  | 911 | EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256 | 
|  | 912 | typedef uint8_t nfs_err_type; | 
|  | 913 | #else | 
|  | 914 | typedef uint16_t nfs_err_type; | 
|  | 915 | #endif | 
|  | 916 | static const nfs_err_type nfs_err_errnum[] = { | 
|  | 917 | EPERM , ENOENT      , EIO      , ENXIO , EACCES, EEXIST, | 
|  | 918 | ENODEV, ENOTDIR     , EISDIR   , EINVAL, EFBIG , ENOSPC, | 
|  | 919 | EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE | 
|  | 920 | }; | 
|  | 921 | static char *nfs_strerror(int status) | 
|  | 922 | { | 
|  | 923 | int i; | 
|  | 924 |  | 
|  | 925 | for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) { | 
|  | 926 | if (nfs_err_stat[i] == status) | 
|  | 927 | return strerror(nfs_err_errnum[i]); | 
|  | 928 | } | 
|  | 929 | return xasprintf("unknown nfs status return value: %d", status); | 
|  | 930 | } | 
|  | 931 |  | 
|  | 932 | static bool_t xdr_fhandle(XDR *xdrs, fhandle objp) | 
|  | 933 | { | 
|  | 934 | return xdr_opaque(xdrs, objp, FHSIZE); | 
|  | 935 | } | 
|  | 936 |  | 
|  | 937 | static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp) | 
|  | 938 | { | 
|  | 939 | if (!xdr_u_int(xdrs, &objp->fhs_status)) | 
|  | 940 | return FALSE; | 
|  | 941 | if (objp->fhs_status == 0) | 
|  | 942 | return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle); | 
|  | 943 | return TRUE; | 
|  | 944 | } | 
|  | 945 |  | 
|  | 946 | static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp) | 
|  | 947 | { | 
|  | 948 | return xdr_string(xdrs, objp, MNTPATHLEN); | 
|  | 949 | } | 
|  | 950 |  | 
|  | 951 | static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp) | 
|  | 952 | { | 
|  | 953 | return xdr_bytes(xdrs, (char **)&objp->fhandle3_val, | 
|  | 954 | (unsigned int *) &objp->fhandle3_len, | 
|  | 955 | FHSIZE3); | 
|  | 956 | } | 
|  | 957 |  | 
|  | 958 | static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp) | 
|  | 959 | { | 
|  | 960 | if (!xdr_fhandle3(xdrs, &objp->fhandle)) | 
|  | 961 | return FALSE; | 
|  | 962 | return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), | 
|  | 963 | &(objp->auth_flavours.auth_flavours_len), | 
|  | 964 | ~0, | 
|  | 965 | sizeof(int), | 
|  | 966 | (xdrproc_t) xdr_int); | 
|  | 967 | } | 
|  | 968 |  | 
|  | 969 | static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp) | 
|  | 970 | { | 
|  | 971 | return xdr_enum(xdrs, (enum_t *) objp); | 
|  | 972 | } | 
|  | 973 |  | 
|  | 974 | static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp) | 
|  | 975 | { | 
|  | 976 | if (!xdr_mountstat3(xdrs, &objp->fhs_status)) | 
|  | 977 | return FALSE; | 
|  | 978 | if (objp->fhs_status == MNT_OK) | 
|  | 979 | return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo); | 
|  | 980 | return TRUE; | 
|  | 981 | } | 
|  | 982 |  | 
|  | 983 | #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2) | 
|  | 984 |  | 
|  | 985 | /* | 
|  | 986 | * Unfortunately, the kernel prints annoying console messages | 
|  | 987 | * in case of an unexpected nfs mount version (instead of | 
|  | 988 | * just returning some error).  Therefore we'll have to try | 
|  | 989 | * and figure out what version the kernel expects. | 
|  | 990 | * | 
|  | 991 | * Variables: | 
|  | 992 | *	KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time | 
|  | 993 | *	NFS_MOUNT_VERSION: these nfsmount sources at compile time | 
|  | 994 | *	nfs_mount_version: version this source and running kernel can handle | 
|  | 995 | */ | 
|  | 996 | static void | 
|  | 997 | find_kernel_nfs_mount_version(void) | 
|  | 998 | { | 
|  | 999 | int kernel_version; | 
|  | 1000 |  | 
|  | 1001 | if (nfs_mount_version) | 
|  | 1002 | return; | 
|  | 1003 |  | 
|  | 1004 | nfs_mount_version = 4; /* default */ | 
|  | 1005 |  | 
|  | 1006 | kernel_version = get_linux_version_code(); | 
|  | 1007 | if (kernel_version) { | 
|  | 1008 | if (kernel_version < KERNEL_VERSION(2,2,18)) | 
|  | 1009 | nfs_mount_version = 3; | 
|  | 1010 | /* else v4 since 2.3.99pre4 */ | 
|  | 1011 | } | 
|  | 1012 | } | 
|  | 1013 |  | 
|  | 1014 | static void | 
|  | 1015 | get_mountport(struct pmap *pm_mnt, | 
|  | 1016 | struct sockaddr_in *server_addr, | 
|  | 1017 | long unsigned prog, | 
|  | 1018 | long unsigned version, | 
|  | 1019 | long unsigned proto, | 
|  | 1020 | long unsigned port) | 
|  | 1021 | { | 
|  | 1022 | struct pmaplist *pmap; | 
|  | 1023 |  | 
|  | 1024 | server_addr->sin_port = PMAPPORT; | 
|  | 1025 | /* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *). | 
|  | 1026 | * I understand it like "IPv6 for this is not 100% ready" */ | 
|  | 1027 | pmap = pmap_getmaps(server_addr); | 
|  | 1028 |  | 
|  | 1029 | if (version > MAX_NFSPROT) | 
|  | 1030 | version = MAX_NFSPROT; | 
|  | 1031 | if (!prog) | 
|  | 1032 | prog = MOUNTPROG; | 
|  | 1033 | pm_mnt->pm_prog = prog; | 
|  | 1034 | pm_mnt->pm_vers = version; | 
|  | 1035 | pm_mnt->pm_prot = proto; | 
|  | 1036 | pm_mnt->pm_port = port; | 
|  | 1037 |  | 
|  | 1038 | while (pmap) { | 
|  | 1039 | if (pmap->pml_map.pm_prog != prog) | 
|  | 1040 | goto next; | 
|  | 1041 | if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers) | 
|  | 1042 | goto next; | 
|  | 1043 | if (version > 2 && pmap->pml_map.pm_vers != version) | 
|  | 1044 | goto next; | 
|  | 1045 | if (version && version <= 2 && pmap->pml_map.pm_vers > 2) | 
|  | 1046 | goto next; | 
|  | 1047 | if (pmap->pml_map.pm_vers > MAX_NFSPROT | 
|  | 1048 | || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) | 
|  | 1049 | || (port && pmap->pml_map.pm_port != port) | 
|  | 1050 | ) { | 
|  | 1051 | goto next; | 
|  | 1052 | } | 
|  | 1053 | memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt)); | 
|  | 1054 | next: | 
|  | 1055 | pmap = pmap->pml_next; | 
|  | 1056 | } | 
|  | 1057 | if (!pm_mnt->pm_vers) | 
|  | 1058 | pm_mnt->pm_vers = MOUNTVERS; | 
|  | 1059 | if (!pm_mnt->pm_port) | 
|  | 1060 | pm_mnt->pm_port = MOUNTPORT; | 
|  | 1061 | if (!pm_mnt->pm_prot) | 
|  | 1062 | pm_mnt->pm_prot = IPPROTO_TCP; | 
|  | 1063 | } | 
|  | 1064 |  | 
|  | 1065 | #if BB_MMU | 
|  | 1066 | static int daemonize(void) | 
|  | 1067 | { | 
|  | 1068 | int pid = fork(); | 
|  | 1069 | if (pid < 0) /* error */ | 
|  | 1070 | return -errno; | 
|  | 1071 | if (pid > 0) /* parent */ | 
|  | 1072 | return 0; | 
|  | 1073 | /* child */ | 
|  | 1074 | close(0); | 
|  | 1075 | xopen(bb_dev_null, O_RDWR); | 
|  | 1076 | xdup2(0, 1); | 
|  | 1077 | xdup2(0, 2); | 
|  | 1078 | setsid(); | 
|  | 1079 | openlog(applet_name, LOG_PID, LOG_DAEMON); | 
|  | 1080 | logmode = LOGMODE_SYSLOG; | 
|  | 1081 | return 1; | 
|  | 1082 | } | 
|  | 1083 | #else | 
|  | 1084 | static inline int daemonize(void) { return -ENOSYS; } | 
|  | 1085 | #endif | 
|  | 1086 |  | 
|  | 1087 | /* TODO */ | 
|  | 1088 | static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM) | 
|  | 1089 | { | 
|  | 1090 | return 0; | 
|  | 1091 | } | 
|  | 1092 |  | 
|  | 1093 | /* RPC strerror analogs are terminally idiotic: | 
|  | 1094 | * *mandatory* prefix and \n at end. | 
|  | 1095 | * This hopefully helps. Usage: | 
|  | 1096 | * error_msg_rpc(clnt_*error*(" ")) */ | 
|  | 1097 | static void error_msg_rpc(const char *msg) | 
|  | 1098 | { | 
|  | 1099 | int len; | 
|  | 1100 | while (msg[0] == ' ' || msg[0] == ':') msg++; | 
|  | 1101 | len = strlen(msg); | 
|  | 1102 | while (len && msg[len-1] == '\n') len--; | 
|  | 1103 | bb_error_msg("%.*s", len, msg); | 
|  | 1104 | } | 
|  | 1105 |  | 
|  | 1106 | /* NB: mp->xxx fields may be trashed on exit */ | 
|  | 1107 | static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts) | 
|  | 1108 | { | 
|  | 1109 | CLIENT *mclient; | 
|  | 1110 | char *hostname; | 
|  | 1111 | char *pathname; | 
|  | 1112 | char *mounthost; | 
|  | 1113 | /* prior to 2.6.23, kernel took NFS options in a form of this struct | 
|  | 1114 | * only. 2.6.23+ looks at data->version, and if it's not 1..6, | 
|  | 1115 | * then data pointer is interpreted as a string. */ | 
|  | 1116 | struct nfs_mount_data data; | 
|  | 1117 | char *opt; | 
|  | 1118 | struct hostent *hp; | 
|  | 1119 | struct sockaddr_in server_addr; | 
|  | 1120 | struct sockaddr_in mount_server_addr; | 
|  | 1121 | int msock, fsock; | 
|  | 1122 | union { | 
|  | 1123 | struct fhstatus nfsv2; | 
|  | 1124 | struct mountres3 nfsv3; | 
|  | 1125 | } status; | 
|  | 1126 | int daemonized; | 
|  | 1127 | char *s; | 
|  | 1128 | int port; | 
|  | 1129 | int mountport; | 
|  | 1130 | int proto; | 
|  | 1131 | #if BB_MMU | 
|  | 1132 | smallint bg = 0; | 
|  | 1133 | #else | 
|  | 1134 | enum { bg = 0 }; | 
|  | 1135 | #endif | 
|  | 1136 | int retry; | 
|  | 1137 | int mountprog; | 
|  | 1138 | int mountvers; | 
|  | 1139 | int nfsprog; | 
|  | 1140 | int nfsvers; | 
|  | 1141 | int retval; | 
|  | 1142 | /* these all are one-bit really. gcc 4.3.1 likes this combination: */ | 
|  | 1143 | smallint tcp; | 
|  | 1144 | smallint soft; | 
|  | 1145 | int intr; | 
|  | 1146 | int posix; | 
|  | 1147 | int nocto; | 
|  | 1148 | int noac; | 
|  | 1149 | int nordirplus; | 
|  | 1150 | int nolock; | 
|  | 1151 | int noacl; | 
|  | 1152 |  | 
|  | 1153 | find_kernel_nfs_mount_version(); | 
|  | 1154 |  | 
|  | 1155 | daemonized = 0; | 
|  | 1156 | mounthost = NULL; | 
|  | 1157 | retval = ETIMEDOUT; | 
|  | 1158 | msock = fsock = -1; | 
|  | 1159 | mclient = NULL; | 
|  | 1160 |  | 
|  | 1161 | /* NB: hostname, mounthost, filteropts must be free()d prior to return */ | 
|  | 1162 |  | 
|  | 1163 | filteropts = xstrdup(filteropts); /* going to trash it later... */ | 
|  | 1164 |  | 
|  | 1165 | hostname = xstrdup(mp->mnt_fsname); | 
|  | 1166 | /* mount_main() guarantees that ':' is there */ | 
|  | 1167 | s = strchr(hostname, ':'); | 
|  | 1168 | pathname = s + 1; | 
|  | 1169 | *s = '\0'; | 
|  | 1170 | /* Ignore all but first hostname in replicated mounts | 
|  | 1171 | * until they can be fully supported. (mack@sgi.com) */ | 
|  | 1172 | s = strchr(hostname, ','); | 
|  | 1173 | if (s) { | 
|  | 1174 | *s = '\0'; | 
|  | 1175 | bb_error_msg("warning: multiple hostnames not supported"); | 
|  | 1176 | } | 
|  | 1177 |  | 
|  | 1178 | server_addr.sin_family = AF_INET; | 
|  | 1179 | if (!inet_aton(hostname, &server_addr.sin_addr)) { | 
|  | 1180 | hp = gethostbyname(hostname); | 
|  | 1181 | if (hp == NULL) { | 
|  | 1182 | bb_herror_msg("%s", hostname); | 
|  | 1183 | goto fail; | 
|  | 1184 | } | 
|  | 1185 | if (hp->h_length != (int)sizeof(struct in_addr)) { | 
|  | 1186 | bb_error_msg_and_die("only IPv4 is supported"); | 
|  | 1187 | } | 
|  | 1188 | memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr)); | 
|  | 1189 | } | 
|  | 1190 |  | 
|  | 1191 | memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr)); | 
|  | 1192 |  | 
|  | 1193 | /* add IP address to mtab options for use when unmounting */ | 
|  | 1194 |  | 
|  | 1195 | if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */ | 
|  | 1196 | mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr)); | 
|  | 1197 | } else { | 
|  | 1198 | char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts, | 
|  | 1199 | mp->mnt_opts[0] ? "," : "", | 
|  | 1200 | inet_ntoa(server_addr.sin_addr)); | 
|  | 1201 | free(mp->mnt_opts); | 
|  | 1202 | mp->mnt_opts = tmp; | 
|  | 1203 | } | 
|  | 1204 |  | 
|  | 1205 | /* Set default options. | 
|  | 1206 | * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to | 
|  | 1207 | * let the kernel decide. | 
|  | 1208 | * timeo is filled in after we know whether it'll be TCP or UDP. */ | 
|  | 1209 | memset(&data, 0, sizeof(data)); | 
|  | 1210 | data.retrans  = 3; | 
|  | 1211 | data.acregmin = 3; | 
|  | 1212 | data.acregmax = 60; | 
|  | 1213 | data.acdirmin = 30; | 
|  | 1214 | data.acdirmax = 60; | 
|  | 1215 | data.namlen   = NAME_MAX; | 
|  | 1216 |  | 
|  | 1217 | soft = 0; | 
|  | 1218 | intr = 0; | 
|  | 1219 | posix = 0; | 
|  | 1220 | nocto = 0; | 
|  | 1221 | nolock = 0; | 
|  | 1222 | noac = 0; | 
|  | 1223 | nordirplus = 0; | 
|  | 1224 | noacl = 0; | 
|  | 1225 | retry = 10000;		/* 10000 minutes ~ 1 week */ | 
|  | 1226 | tcp = 1;			/* nfs-utils uses tcp per default */ | 
|  | 1227 |  | 
|  | 1228 | mountprog = MOUNTPROG; | 
|  | 1229 | mountvers = 0; | 
|  | 1230 | port = 0; | 
|  | 1231 | mountport = 0; | 
|  | 1232 | nfsprog = 100003; | 
|  | 1233 | nfsvers = 0; | 
|  | 1234 |  | 
|  | 1235 | /* parse options */ | 
|  | 1236 | if (filteropts)	for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) { | 
|  | 1237 | char *opteq = strchr(opt, '='); | 
|  | 1238 | if (opteq) { | 
|  | 1239 | int val, idx; | 
|  | 1240 | static const char options[] ALIGN1 = | 
|  | 1241 | /* 0 */ "rsize\0" | 
|  | 1242 | /* 1 */ "wsize\0" | 
|  | 1243 | /* 2 */ "timeo\0" | 
|  | 1244 | /* 3 */ "retrans\0" | 
|  | 1245 | /* 4 */ "acregmin\0" | 
|  | 1246 | /* 5 */ "acregmax\0" | 
|  | 1247 | /* 6 */ "acdirmin\0" | 
|  | 1248 | /* 7 */ "acdirmax\0" | 
|  | 1249 | /* 8 */ "actimeo\0" | 
|  | 1250 | /* 9 */ "retry\0" | 
|  | 1251 | /* 10 */ "port\0" | 
|  | 1252 | /* 11 */ "mountport\0" | 
|  | 1253 | /* 12 */ "mounthost\0" | 
|  | 1254 | /* 13 */ "mountprog\0" | 
|  | 1255 | /* 14 */ "mountvers\0" | 
|  | 1256 | /* 15 */ "nfsprog\0" | 
|  | 1257 | /* 16 */ "nfsvers\0" | 
|  | 1258 | /* 17 */ "vers\0" | 
|  | 1259 | /* 18 */ "proto\0" | 
|  | 1260 | /* 19 */ "namlen\0" | 
|  | 1261 | /* 20 */ "addr\0"; | 
|  | 1262 |  | 
|  | 1263 | *opteq++ = '\0'; | 
|  | 1264 | idx = index_in_strings(options, opt); | 
|  | 1265 | switch (idx) { | 
|  | 1266 | case 12: // "mounthost" | 
|  | 1267 | mounthost = xstrndup(opteq, | 
|  | 1268 | strcspn(opteq, " \t\n\r,")); | 
|  | 1269 | continue; | 
|  | 1270 | case 18: // "proto" | 
|  | 1271 | if (!strncmp(opteq, "tcp", 3)) | 
|  | 1272 | tcp = 1; | 
|  | 1273 | else if (!strncmp(opteq, "udp", 3)) | 
|  | 1274 | tcp = 0; | 
|  | 1275 | else | 
|  | 1276 | bb_error_msg("warning: unrecognized proto= option"); | 
|  | 1277 | continue; | 
|  | 1278 | case 20: // "addr" - ignore | 
|  | 1279 | continue; | 
|  | 1280 | case -1: // unknown | 
|  | 1281 | if (vfsflags & MS_REMOUNT) | 
|  | 1282 | continue; | 
|  | 1283 | } | 
|  | 1284 |  | 
|  | 1285 | val = xatoi_positive(opteq); | 
|  | 1286 | switch (idx) { | 
|  | 1287 | case 0: // "rsize" | 
|  | 1288 | data.rsize = val; | 
|  | 1289 | continue; | 
|  | 1290 | case 1: // "wsize" | 
|  | 1291 | data.wsize = val; | 
|  | 1292 | continue; | 
|  | 1293 | case 2: // "timeo" | 
|  | 1294 | data.timeo = val; | 
|  | 1295 | continue; | 
|  | 1296 | case 3: // "retrans" | 
|  | 1297 | data.retrans = val; | 
|  | 1298 | continue; | 
|  | 1299 | case 4: // "acregmin" | 
|  | 1300 | data.acregmin = val; | 
|  | 1301 | continue; | 
|  | 1302 | case 5: // "acregmax" | 
|  | 1303 | data.acregmax = val; | 
|  | 1304 | continue; | 
|  | 1305 | case 6: // "acdirmin" | 
|  | 1306 | data.acdirmin = val; | 
|  | 1307 | continue; | 
|  | 1308 | case 7: // "acdirmax" | 
|  | 1309 | data.acdirmax = val; | 
|  | 1310 | continue; | 
|  | 1311 | case 8: // "actimeo" | 
|  | 1312 | data.acregmin = val; | 
|  | 1313 | data.acregmax = val; | 
|  | 1314 | data.acdirmin = val; | 
|  | 1315 | data.acdirmax = val; | 
|  | 1316 | continue; | 
|  | 1317 | case 9: // "retry" | 
|  | 1318 | retry = val; | 
|  | 1319 | continue; | 
|  | 1320 | case 10: // "port" | 
|  | 1321 | port = val; | 
|  | 1322 | continue; | 
|  | 1323 | case 11: // "mountport" | 
|  | 1324 | mountport = val; | 
|  | 1325 | continue; | 
|  | 1326 | case 13: // "mountprog" | 
|  | 1327 | mountprog = val; | 
|  | 1328 | continue; | 
|  | 1329 | case 14: // "mountvers" | 
|  | 1330 | mountvers = val; | 
|  | 1331 | continue; | 
|  | 1332 | case 15: // "nfsprog" | 
|  | 1333 | nfsprog = val; | 
|  | 1334 | continue; | 
|  | 1335 | case 16: // "nfsvers" | 
|  | 1336 | case 17: // "vers" | 
|  | 1337 | nfsvers = val; | 
|  | 1338 | continue; | 
|  | 1339 | case 19: // "namlen" | 
|  | 1340 | //if (nfs_mount_version >= 2) | 
|  | 1341 | data.namlen = val; | 
|  | 1342 | //else | 
|  | 1343 | //	bb_error_msg("warning: option namlen is not supported\n"); | 
|  | 1344 | continue; | 
|  | 1345 | default: | 
|  | 1346 | bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val); | 
|  | 1347 | goto fail; | 
|  | 1348 | } | 
|  | 1349 | } | 
|  | 1350 | else { /* not of the form opt=val */ | 
|  | 1351 | static const char options[] ALIGN1 = | 
|  | 1352 | "bg\0" | 
|  | 1353 | "fg\0" | 
|  | 1354 | "soft\0" | 
|  | 1355 | "hard\0" | 
|  | 1356 | "intr\0" | 
|  | 1357 | "posix\0" | 
|  | 1358 | "cto\0" | 
|  | 1359 | "ac\0" | 
|  | 1360 | "tcp\0" | 
|  | 1361 | "udp\0" | 
|  | 1362 | "lock\0" | 
|  | 1363 | "rdirplus\0" | 
|  | 1364 | "acl\0"; | 
|  | 1365 | int val = 1; | 
|  | 1366 | if (!strncmp(opt, "no", 2)) { | 
|  | 1367 | val = 0; | 
|  | 1368 | opt += 2; | 
|  | 1369 | } | 
|  | 1370 | switch (index_in_strings(options, opt)) { | 
|  | 1371 | case 0: // "bg" | 
|  | 1372 | #if BB_MMU | 
|  | 1373 | bg = val; | 
|  | 1374 | #endif | 
|  | 1375 | break; | 
|  | 1376 | case 1: // "fg" | 
|  | 1377 | #if BB_MMU | 
|  | 1378 | bg = !val; | 
|  | 1379 | #endif | 
|  | 1380 | break; | 
|  | 1381 | case 2: // "soft" | 
|  | 1382 | soft = val; | 
|  | 1383 | break; | 
|  | 1384 | case 3: // "hard" | 
|  | 1385 | soft = !val; | 
|  | 1386 | break; | 
|  | 1387 | case 4: // "intr" | 
|  | 1388 | intr = val; | 
|  | 1389 | break; | 
|  | 1390 | case 5: // "posix" | 
|  | 1391 | posix = val; | 
|  | 1392 | break; | 
|  | 1393 | case 6: // "cto" | 
|  | 1394 | nocto = !val; | 
|  | 1395 | break; | 
|  | 1396 | case 7: // "ac" | 
|  | 1397 | noac = !val; | 
|  | 1398 | break; | 
|  | 1399 | case 8: // "tcp" | 
|  | 1400 | tcp = val; | 
|  | 1401 | break; | 
|  | 1402 | case 9: // "udp" | 
|  | 1403 | tcp = !val; | 
|  | 1404 | break; | 
|  | 1405 | case 10: // "lock" | 
|  | 1406 | if (nfs_mount_version >= 3) | 
|  | 1407 | nolock = !val; | 
|  | 1408 | else | 
|  | 1409 | bb_error_msg("warning: option nolock is not supported"); | 
|  | 1410 | break; | 
|  | 1411 | case 11: //rdirplus | 
|  | 1412 | nordirplus = !val; | 
|  | 1413 | break; | 
|  | 1414 | case 12: // acl | 
|  | 1415 | noacl = !val; | 
|  | 1416 | break; | 
|  | 1417 | default: | 
|  | 1418 | bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt); | 
|  | 1419 | goto fail; | 
|  | 1420 | } | 
|  | 1421 | } | 
|  | 1422 | } | 
|  | 1423 | proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP; | 
|  | 1424 |  | 
|  | 1425 | data.flags = (soft ? NFS_MOUNT_SOFT : 0) | 
|  | 1426 | | (intr ? NFS_MOUNT_INTR : 0) | 
|  | 1427 | | (posix ? NFS_MOUNT_POSIX : 0) | 
|  | 1428 | | (nocto ? NFS_MOUNT_NOCTO : 0) | 
|  | 1429 | | (noac ? NFS_MOUNT_NOAC : 0) | 
|  | 1430 | | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0) | 
|  | 1431 | | (noacl ? NFS_MOUNT_NOACL : 0); | 
|  | 1432 | if (nfs_mount_version >= 2) | 
|  | 1433 | data.flags |= (tcp ? NFS_MOUNT_TCP : 0); | 
|  | 1434 | if (nfs_mount_version >= 3) | 
|  | 1435 | data.flags |= (nolock ? NFS_MOUNT_NONLM : 0); | 
|  | 1436 | if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) { | 
|  | 1437 | bb_error_msg("NFSv%d not supported", nfsvers); | 
|  | 1438 | goto fail; | 
|  | 1439 | } | 
|  | 1440 | if (nfsvers && !mountvers) | 
|  | 1441 | mountvers = (nfsvers < 3) ? 1 : nfsvers; | 
|  | 1442 | if (nfsvers && nfsvers < mountvers) { | 
|  | 1443 | mountvers = nfsvers; | 
|  | 1444 | } | 
|  | 1445 |  | 
|  | 1446 | /* Adjust options if none specified */ | 
|  | 1447 | if (!data.timeo) | 
|  | 1448 | data.timeo = tcp ? 70 : 7; | 
|  | 1449 |  | 
|  | 1450 | data.version = nfs_mount_version; | 
|  | 1451 |  | 
|  | 1452 | if (vfsflags & MS_REMOUNT) | 
|  | 1453 | goto do_mount; | 
|  | 1454 |  | 
|  | 1455 | /* | 
|  | 1456 | * If the previous mount operation on the same host was | 
|  | 1457 | * backgrounded, and the "bg" for this mount is also set, | 
|  | 1458 | * give up immediately, to avoid the initial timeout. | 
|  | 1459 | */ | 
|  | 1460 | if (bg && we_saw_this_host_before(hostname)) { | 
|  | 1461 | daemonized = daemonize(); | 
|  | 1462 | if (daemonized <= 0) { /* parent or error */ | 
|  | 1463 | retval = -daemonized; | 
|  | 1464 | goto ret; | 
|  | 1465 | } | 
|  | 1466 | } | 
|  | 1467 |  | 
|  | 1468 | /* Create mount daemon client */ | 
|  | 1469 | /* See if the nfs host = mount host. */ | 
|  | 1470 | if (mounthost) { | 
|  | 1471 | if (mounthost[0] >= '0' && mounthost[0] <= '9') { | 
|  | 1472 | mount_server_addr.sin_family = AF_INET; | 
|  | 1473 | mount_server_addr.sin_addr.s_addr = inet_addr(hostname); | 
|  | 1474 | } else { | 
|  | 1475 | hp = gethostbyname(mounthost); | 
|  | 1476 | if (hp == NULL) { | 
|  | 1477 | bb_herror_msg("%s", mounthost); | 
|  | 1478 | goto fail; | 
|  | 1479 | } | 
|  | 1480 | if (hp->h_length != (int)sizeof(struct in_addr)) { | 
|  | 1481 | bb_error_msg_and_die("only IPv4 is supported"); | 
|  | 1482 | } | 
|  | 1483 | mount_server_addr.sin_family = AF_INET; | 
|  | 1484 | memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr)); | 
|  | 1485 | } | 
|  | 1486 | } | 
|  | 1487 |  | 
|  | 1488 | /* | 
|  | 1489 | * The following loop implements the mount retries. When the mount | 
|  | 1490 | * times out, and the "bg" option is set, we background ourself | 
|  | 1491 | * and continue trying. | 
|  | 1492 | * | 
|  | 1493 | * The case where the mount point is not present and the "bg" | 
|  | 1494 | * option is set, is treated as a timeout. This is done to | 
|  | 1495 | * support nested mounts. | 
|  | 1496 | * | 
|  | 1497 | * The "retry" count specified by the user is the number of | 
|  | 1498 | * minutes to retry before giving up. | 
|  | 1499 | */ | 
|  | 1500 | { | 
|  | 1501 | struct timeval total_timeout; | 
|  | 1502 | struct timeval retry_timeout; | 
|  | 1503 | struct pmap pm_mnt; | 
|  | 1504 | time_t t; | 
|  | 1505 | time_t prevt; | 
|  | 1506 | time_t timeout; | 
|  | 1507 |  | 
|  | 1508 | retry_timeout.tv_sec = 3; | 
|  | 1509 | retry_timeout.tv_usec = 0; | 
|  | 1510 | total_timeout.tv_sec = 20; | 
|  | 1511 | total_timeout.tv_usec = 0; | 
|  | 1512 | /* FIXME: use monotonic()? */ | 
|  | 1513 | timeout = time(NULL) + 60 * retry; | 
|  | 1514 | prevt = 0; | 
|  | 1515 | t = 30; | 
|  | 1516 | retry: | 
|  | 1517 | /* Be careful not to use too many CPU cycles */ | 
|  | 1518 | if (t - prevt < 30) | 
|  | 1519 | sleep(30); | 
|  | 1520 |  | 
|  | 1521 | get_mountport(&pm_mnt, &mount_server_addr, | 
|  | 1522 | mountprog, | 
|  | 1523 | mountvers, | 
|  | 1524 | proto, | 
|  | 1525 | mountport); | 
|  | 1526 | nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers; | 
|  | 1527 |  | 
|  | 1528 | /* contact the mount daemon via TCP */ | 
|  | 1529 | mount_server_addr.sin_port = htons(pm_mnt.pm_port); | 
|  | 1530 | msock = RPC_ANYSOCK; | 
|  | 1531 |  | 
|  | 1532 | switch (pm_mnt.pm_prot) { | 
|  | 1533 | case IPPROTO_UDP: | 
|  | 1534 | mclient = clntudp_create(&mount_server_addr, | 
|  | 1535 | pm_mnt.pm_prog, | 
|  | 1536 | pm_mnt.pm_vers, | 
|  | 1537 | retry_timeout, | 
|  | 1538 | &msock); | 
|  | 1539 | if (mclient) | 
|  | 1540 | break; | 
|  | 1541 | mount_server_addr.sin_port = htons(pm_mnt.pm_port); | 
|  | 1542 | msock = RPC_ANYSOCK; | 
|  | 1543 | case IPPROTO_TCP: | 
|  | 1544 | mclient = clnttcp_create(&mount_server_addr, | 
|  | 1545 | pm_mnt.pm_prog, | 
|  | 1546 | pm_mnt.pm_vers, | 
|  | 1547 | &msock, 0, 0); | 
|  | 1548 | break; | 
|  | 1549 | default: | 
|  | 1550 | mclient = NULL; | 
|  | 1551 | } | 
|  | 1552 | if (!mclient) { | 
|  | 1553 | if (!daemonized && prevt == 0) | 
|  | 1554 | error_msg_rpc(clnt_spcreateerror(" ")); | 
|  | 1555 | } else { | 
|  | 1556 | enum clnt_stat clnt_stat; | 
|  | 1557 |  | 
|  | 1558 | /* Try to mount hostname:pathname */ | 
|  | 1559 | mclient->cl_auth = authunix_create_default(); | 
|  | 1560 |  | 
|  | 1561 | /* Make pointers in xdr_mountres3 NULL so | 
|  | 1562 | * that xdr_array allocates memory for us | 
|  | 1563 | */ | 
|  | 1564 | memset(&status, 0, sizeof(status)); | 
|  | 1565 |  | 
|  | 1566 | if (pm_mnt.pm_vers == 3) | 
|  | 1567 | clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT, | 
|  | 1568 | (xdrproc_t) xdr_dirpath, | 
|  | 1569 | (caddr_t) &pathname, | 
|  | 1570 | (xdrproc_t) xdr_mountres3, | 
|  | 1571 | (caddr_t) &status, | 
|  | 1572 | total_timeout); | 
|  | 1573 | else | 
|  | 1574 | clnt_stat = clnt_call(mclient, MOUNTPROC_MNT, | 
|  | 1575 | (xdrproc_t) xdr_dirpath, | 
|  | 1576 | (caddr_t) &pathname, | 
|  | 1577 | (xdrproc_t) xdr_fhstatus, | 
|  | 1578 | (caddr_t) &status, | 
|  | 1579 | total_timeout); | 
|  | 1580 |  | 
|  | 1581 | if (clnt_stat == RPC_SUCCESS) | 
|  | 1582 | goto prepare_kernel_data; /* we're done */ | 
|  | 1583 | if (errno != ECONNREFUSED) { | 
|  | 1584 | error_msg_rpc(clnt_sperror(mclient, " ")); | 
|  | 1585 | goto fail;	/* don't retry */ | 
|  | 1586 | } | 
|  | 1587 | /* Connection refused */ | 
|  | 1588 | if (!daemonized && prevt == 0) /* print just once */ | 
|  | 1589 | error_msg_rpc(clnt_sperror(mclient, " ")); | 
|  | 1590 | auth_destroy(mclient->cl_auth); | 
|  | 1591 | clnt_destroy(mclient); | 
|  | 1592 | mclient = NULL; | 
|  | 1593 | close(msock); | 
|  | 1594 | msock = -1; | 
|  | 1595 | } | 
|  | 1596 |  | 
|  | 1597 | /* Timeout. We are going to retry... maybe */ | 
|  | 1598 | if (!bg) | 
|  | 1599 | goto fail; | 
|  | 1600 | if (!daemonized) { | 
|  | 1601 | daemonized = daemonize(); | 
|  | 1602 | if (daemonized <= 0) { /* parent or error */ | 
|  | 1603 | retval = -daemonized; | 
|  | 1604 | goto ret; | 
|  | 1605 | } | 
|  | 1606 | } | 
|  | 1607 | prevt = t; | 
|  | 1608 | t = time(NULL); | 
|  | 1609 | if (t >= timeout) | 
|  | 1610 | /* TODO error message */ | 
|  | 1611 | goto fail; | 
|  | 1612 |  | 
|  | 1613 | goto retry; | 
|  | 1614 | } | 
|  | 1615 |  | 
|  | 1616 | prepare_kernel_data: | 
|  | 1617 |  | 
|  | 1618 | if (nfsvers == 2) { | 
|  | 1619 | if (status.nfsv2.fhs_status != 0) { | 
|  | 1620 | bb_error_msg("%s:%s failed, reason given by server: %s", | 
|  | 1621 | hostname, pathname, | 
|  | 1622 | nfs_strerror(status.nfsv2.fhs_status)); | 
|  | 1623 | goto fail; | 
|  | 1624 | } | 
|  | 1625 | memcpy(data.root.data, | 
|  | 1626 | (char *) status.nfsv2.fhstatus_u.fhs_fhandle, | 
|  | 1627 | NFS_FHSIZE); | 
|  | 1628 | data.root.size = NFS_FHSIZE; | 
|  | 1629 | memcpy(data.old_root.data, | 
|  | 1630 | (char *) status.nfsv2.fhstatus_u.fhs_fhandle, | 
|  | 1631 | NFS_FHSIZE); | 
|  | 1632 | } else { | 
|  | 1633 | fhandle3 *my_fhandle; | 
|  | 1634 | if (status.nfsv3.fhs_status != 0) { | 
|  | 1635 | bb_error_msg("%s:%s failed, reason given by server: %s", | 
|  | 1636 | hostname, pathname, | 
|  | 1637 | nfs_strerror(status.nfsv3.fhs_status)); | 
|  | 1638 | goto fail; | 
|  | 1639 | } | 
|  | 1640 | my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle; | 
|  | 1641 | memset(data.old_root.data, 0, NFS_FHSIZE); | 
|  | 1642 | memset(&data.root, 0, sizeof(data.root)); | 
|  | 1643 | data.root.size = my_fhandle->fhandle3_len; | 
|  | 1644 | memcpy(data.root.data, | 
|  | 1645 | (char *) my_fhandle->fhandle3_val, | 
|  | 1646 | my_fhandle->fhandle3_len); | 
|  | 1647 |  | 
|  | 1648 | data.flags |= NFS_MOUNT_VER3; | 
|  | 1649 | } | 
|  | 1650 |  | 
|  | 1651 | /* Create nfs socket for kernel */ | 
|  | 1652 | if (tcp) { | 
|  | 1653 | if (nfs_mount_version < 3) { | 
|  | 1654 | bb_error_msg("NFS over TCP is not supported"); | 
|  | 1655 | goto fail; | 
|  | 1656 | } | 
|  | 1657 | fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | 
|  | 1658 | } else | 
|  | 1659 | fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | 
|  | 1660 | if (fsock < 0) { | 
|  | 1661 | bb_perror_msg("nfs socket"); | 
|  | 1662 | goto fail; | 
|  | 1663 | } | 
|  | 1664 | if (bindresvport(fsock, 0) < 0) { | 
|  | 1665 | bb_perror_msg("nfs bindresvport"); | 
|  | 1666 | goto fail; | 
|  | 1667 | } | 
|  | 1668 | if (port == 0) { | 
|  | 1669 | server_addr.sin_port = PMAPPORT; | 
|  | 1670 | port = pmap_getport(&server_addr, nfsprog, nfsvers, | 
|  | 1671 | tcp ? IPPROTO_TCP : IPPROTO_UDP); | 
|  | 1672 | if (port == 0) | 
|  | 1673 | port = NFS_PORT; | 
|  | 1674 | } | 
|  | 1675 | server_addr.sin_port = htons(port); | 
|  | 1676 |  | 
|  | 1677 | /* Prepare data structure for kernel */ | 
|  | 1678 | data.fd = fsock; | 
|  | 1679 | memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr)); | 
|  | 1680 | strncpy(data.hostname, hostname, sizeof(data.hostname)); | 
|  | 1681 |  | 
|  | 1682 | /* Clean up */ | 
|  | 1683 | auth_destroy(mclient->cl_auth); | 
|  | 1684 | clnt_destroy(mclient); | 
|  | 1685 | close(msock); | 
|  | 1686 | msock = -1; | 
|  | 1687 |  | 
|  | 1688 | if (bg) { | 
|  | 1689 | /* We must wait until mount directory is available */ | 
|  | 1690 | struct stat statbuf; | 
|  | 1691 | int delay = 1; | 
|  | 1692 | while (stat(mp->mnt_dir, &statbuf) == -1) { | 
|  | 1693 | if (!daemonized) { | 
|  | 1694 | daemonized = daemonize(); | 
|  | 1695 | if (daemonized <= 0) { /* parent or error */ | 
|  | 1696 | /* FIXME: parent doesn't close fsock - ??! */ | 
|  | 1697 | retval = -daemonized; | 
|  | 1698 | goto ret; | 
|  | 1699 | } | 
|  | 1700 | } | 
|  | 1701 | sleep(delay);	/* 1, 2, 4, 8, 16, 30, ... */ | 
|  | 1702 | delay *= 2; | 
|  | 1703 | if (delay > 30) | 
|  | 1704 | delay = 30; | 
|  | 1705 | } | 
|  | 1706 | } | 
|  | 1707 |  | 
|  | 1708 | /* Perform actual mount */ | 
|  | 1709 | do_mount: | 
|  | 1710 | retval = mount_it_now(mp, vfsflags, (char*)&data); | 
|  | 1711 | goto ret; | 
|  | 1712 |  | 
|  | 1713 | /* Abort */ | 
|  | 1714 | fail: | 
|  | 1715 | if (msock >= 0) { | 
|  | 1716 | if (mclient) { | 
|  | 1717 | auth_destroy(mclient->cl_auth); | 
|  | 1718 | clnt_destroy(mclient); | 
|  | 1719 | } | 
|  | 1720 | close(msock); | 
|  | 1721 | } | 
|  | 1722 | if (fsock >= 0) | 
|  | 1723 | close(fsock); | 
|  | 1724 |  | 
|  | 1725 | ret: | 
|  | 1726 | free(hostname); | 
|  | 1727 | free(mounthost); | 
|  | 1728 | free(filteropts); | 
|  | 1729 | return retval; | 
|  | 1730 | } | 
|  | 1731 |  | 
|  | 1732 | #else // !ENABLE_FEATURE_MOUNT_NFS | 
|  | 1733 |  | 
|  | 1734 | /* Linux 2.6.23+ supports nfs mounts with options passed as a string. | 
|  | 1735 | * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS. | 
|  | 1736 | * (However, note that then you lose any chances that NFS over IPv6 would work). | 
|  | 1737 | */ | 
|  | 1738 | static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts) | 
|  | 1739 | { | 
|  | 1740 | len_and_sockaddr *lsa; | 
|  | 1741 | char *opts; | 
|  | 1742 | char *end; | 
|  | 1743 | char *dotted; | 
|  | 1744 | int ret; | 
|  | 1745 |  | 
|  | 1746 | # if ENABLE_FEATURE_IPV6 | 
|  | 1747 | end = strchr(mp->mnt_fsname, ']'); | 
|  | 1748 | if (end && end[1] == ':') | 
|  | 1749 | end++; | 
|  | 1750 | else | 
|  | 1751 | # endif | 
|  | 1752 | /* mount_main() guarantees that ':' is there */ | 
|  | 1753 | end = strchr(mp->mnt_fsname, ':'); | 
|  | 1754 |  | 
|  | 1755 | *end = '\0'; | 
|  | 1756 | lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0); | 
|  | 1757 | *end = ':'; | 
|  | 1758 | dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa); | 
|  | 1759 | if (ENABLE_FEATURE_CLEAN_UP) free(lsa); | 
|  | 1760 | opts = xasprintf("%s%saddr=%s", | 
|  | 1761 | filteropts ? filteropts : "", | 
|  | 1762 | filteropts ? "," : "", | 
|  | 1763 | dotted | 
|  | 1764 | ); | 
|  | 1765 | if (ENABLE_FEATURE_CLEAN_UP) free(dotted); | 
|  | 1766 | ret = mount_it_now(mp, vfsflags, opts); | 
|  | 1767 | if (ENABLE_FEATURE_CLEAN_UP) free(opts); | 
|  | 1768 |  | 
|  | 1769 | return ret; | 
|  | 1770 | } | 
|  | 1771 |  | 
|  | 1772 | #endif // !ENABLE_FEATURE_MOUNT_NFS | 
|  | 1773 |  | 
|  | 1774 | // Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem | 
|  | 1775 | // type detection.  Returns 0 for success, nonzero for failure. | 
|  | 1776 | // NB: mp->xxx fields may be trashed on exit | 
|  | 1777 | static int singlemount(struct mntent *mp, int ignore_busy) | 
|  | 1778 | { | 
|  | 1779 | int rc = -1; | 
|  | 1780 | unsigned long vfsflags; | 
|  | 1781 | char *loopFile = NULL, *filteropts = NULL; | 
|  | 1782 | llist_t *fl = NULL; | 
|  | 1783 | struct stat st; | 
|  | 1784 |  | 
|  | 1785 | errno = 0; | 
|  | 1786 |  | 
|  | 1787 | vfsflags = parse_mount_options(mp->mnt_opts, &filteropts); | 
|  | 1788 |  | 
|  | 1789 | // Treat fstype "auto" as unspecified | 
|  | 1790 | if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0) | 
|  | 1791 | mp->mnt_type = NULL; | 
|  | 1792 |  | 
|  | 1793 | // Might this be a virtual filesystem? | 
|  | 1794 | if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) { | 
|  | 1795 | char *args[35]; | 
|  | 1796 | char *s; | 
|  | 1797 | int n; | 
|  | 1798 | // fsname: "cmd#arg1#arg2..." | 
|  | 1799 | // WARNING: allows execution of arbitrary commands! | 
|  | 1800 | // Try "mount 'sh#-c#sh' bogus_dir". | 
|  | 1801 | // It is safe ONLY because non-root | 
|  | 1802 | // cannot use two-argument mount command | 
|  | 1803 | // and using one-argument "mount 'sh#-c#sh'" doesn't work: | 
|  | 1804 | // "mount: can't find sh#-c#sh in /etc/fstab" | 
|  | 1805 | // (if /etc/fstab has it, it's ok: root sets up /etc/fstab). | 
|  | 1806 |  | 
|  | 1807 | s = mp->mnt_fsname; | 
|  | 1808 | n = 0; | 
|  | 1809 | args[n++] = s; | 
|  | 1810 | while (*s && n < 35 - 2) { | 
|  | 1811 | if (*s++ == '#' && *s != '#') { | 
|  | 1812 | s[-1] = '\0'; | 
|  | 1813 | args[n++] = s; | 
|  | 1814 | } | 
|  | 1815 | } | 
|  | 1816 | args[n++] = mp->mnt_dir; | 
|  | 1817 | args[n] = NULL; | 
|  | 1818 | rc = spawn_and_wait(args); | 
|  | 1819 | goto report_error; | 
|  | 1820 | } | 
|  | 1821 |  | 
|  | 1822 | // Might this be an CIFS filesystem? | 
|  | 1823 | if (ENABLE_FEATURE_MOUNT_CIFS | 
|  | 1824 | && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0) | 
|  | 1825 | && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\') | 
|  | 1826 | && mp->mnt_fsname[0] == mp->mnt_fsname[1] | 
|  | 1827 | ) { | 
|  | 1828 | int len; | 
|  | 1829 | char c; | 
|  | 1830 | char *hostname, *share; | 
|  | 1831 | char *dotted, *ip; | 
|  | 1832 | len_and_sockaddr *lsa; | 
|  | 1833 |  | 
|  | 1834 | // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]" | 
|  | 1835 |  | 
|  | 1836 | hostname = mp->mnt_fsname + 2; | 
|  | 1837 | len = strcspn(hostname, "/\\"); | 
|  | 1838 | share = hostname + len + 1; | 
|  | 1839 | if (len == 0          // 3rd char is a [back]slash (IOW: empty hostname) | 
|  | 1840 | || share[-1] == '\0' // no [back]slash after hostname | 
|  | 1841 | || share[0] == '\0'  // empty share name | 
|  | 1842 | ) { | 
|  | 1843 | goto report_error; | 
|  | 1844 | } | 
|  | 1845 | c = share[-1]; | 
|  | 1846 | share[-1] = '\0'; | 
|  | 1847 | len = strcspn(share, "/\\"); | 
|  | 1848 |  | 
|  | 1849 | // "unc=\\hostname\share" option is mandatory | 
|  | 1850 | // after CIFS option parsing was rewritten in Linux 3.4. | 
|  | 1851 | // Must use backslashes. | 
|  | 1852 | // If /dir1/dir2 is present, also add "prefixpath=dir1/dir2" | 
|  | 1853 | { | 
|  | 1854 | char *unc = xasprintf( | 
|  | 1855 | share[len] != '\0'  /* "/dir1/dir2" exists? */ | 
|  | 1856 | ? "unc=\\\\%s\\%.*s,prefixpath=%s" | 
|  | 1857 | : "unc=\\\\%s\\%.*s", | 
|  | 1858 | hostname, | 
|  | 1859 | len, share, | 
|  | 1860 | share + len + 1  /* "dir1/dir2" */ | 
|  | 1861 | ); | 
|  | 1862 | parse_mount_options(unc, &filteropts); | 
|  | 1863 | if (ENABLE_FEATURE_CLEAN_UP) free(unc); | 
|  | 1864 | } | 
|  | 1865 |  | 
|  | 1866 | lsa = host2sockaddr(hostname, 0); | 
|  | 1867 | share[-1] = c; | 
|  | 1868 | if (!lsa) | 
|  | 1869 | goto report_error; | 
|  | 1870 |  | 
|  | 1871 | // Insert "ip=..." option into options | 
|  | 1872 | dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa); | 
|  | 1873 | if (ENABLE_FEATURE_CLEAN_UP) free(lsa); | 
|  | 1874 | ip = xasprintf("ip=%s", dotted); | 
|  | 1875 | if (ENABLE_FEATURE_CLEAN_UP) free(dotted); | 
|  | 1876 | parse_mount_options(ip, &filteropts); | 
|  | 1877 | if (ENABLE_FEATURE_CLEAN_UP) free(ip); | 
|  | 1878 |  | 
|  | 1879 | mp->mnt_type = (char*)"cifs"; | 
|  | 1880 | rc = mount_it_now(mp, vfsflags, filteropts); | 
|  | 1881 |  | 
|  | 1882 | goto report_error; | 
|  | 1883 | } | 
|  | 1884 |  | 
|  | 1885 | // Might this be an NFS filesystem? | 
|  | 1886 | if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0) | 
|  | 1887 | && strchr(mp->mnt_fsname, ':') != NULL | 
|  | 1888 | ) { | 
|  | 1889 | if (!mp->mnt_type) | 
|  | 1890 | mp->mnt_type = (char*)"nfs"; | 
|  | 1891 | rc = nfsmount(mp, vfsflags, filteropts); | 
|  | 1892 | goto report_error; | 
|  | 1893 | } | 
|  | 1894 |  | 
|  | 1895 | // Look at the file.  (Not found isn't a failure for remount, or for | 
|  | 1896 | // a synthetic filesystem like proc or sysfs.) | 
|  | 1897 | // (We use stat, not lstat, in order to allow | 
|  | 1898 | // mount symlink_to_file_or_blkdev dir) | 
|  | 1899 | if (!stat(mp->mnt_fsname, &st) | 
|  | 1900 | && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)) | 
|  | 1901 | ) { | 
|  | 1902 | // Do we need to allocate a loopback device for it? | 
|  | 1903 | if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) { | 
|  | 1904 | loopFile = bb_simplify_path(mp->mnt_fsname); | 
|  | 1905 | mp->mnt_fsname = NULL; // will receive malloced loop dev name | 
|  | 1906 | if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ (vfsflags & MS_RDONLY)) < 0) { | 
|  | 1907 | if (errno == EPERM || errno == EACCES) | 
|  | 1908 | bb_error_msg(bb_msg_perm_denied_are_you_root); | 
|  | 1909 | else | 
|  | 1910 | bb_perror_msg("can't setup loop device"); | 
|  | 1911 | return errno; | 
|  | 1912 | } | 
|  | 1913 |  | 
|  | 1914 | // Autodetect bind mounts | 
|  | 1915 | } else if (S_ISDIR(st.st_mode) && !mp->mnt_type) | 
|  | 1916 | vfsflags |= MS_BIND; | 
|  | 1917 | } | 
|  | 1918 |  | 
|  | 1919 | // If we know the fstype (or don't need to), jump straight | 
|  | 1920 | // to the actual mount. | 
|  | 1921 | if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) { | 
|  | 1922 | char *next; | 
|  | 1923 | for (;;) { | 
|  | 1924 | next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL; | 
|  | 1925 | if (next) | 
|  | 1926 | *next = '\0'; | 
|  | 1927 | rc = mount_it_now(mp, vfsflags, filteropts); | 
|  | 1928 | if (rc == 0 || !next) | 
|  | 1929 | break; | 
|  | 1930 | mp->mnt_type = next + 1; | 
|  | 1931 | } | 
|  | 1932 | } else { | 
|  | 1933 | // Loop through filesystem types until mount succeeds | 
|  | 1934 | // or we run out | 
|  | 1935 |  | 
|  | 1936 | // Initialize list of block backed filesystems. | 
|  | 1937 | // This has to be done here so that during "mount -a", | 
|  | 1938 | // mounts after /proc shows up can autodetect. | 
|  | 1939 | if (!fslist) { | 
|  | 1940 | fslist = get_block_backed_filesystems(); | 
|  | 1941 | if (ENABLE_FEATURE_CLEAN_UP && fslist) | 
|  | 1942 | atexit(delete_block_backed_filesystems); | 
|  | 1943 | } | 
|  | 1944 |  | 
|  | 1945 | for (fl = fslist; fl; fl = fl->link) { | 
|  | 1946 | mp->mnt_type = fl->data; | 
|  | 1947 | rc = mount_it_now(mp, vfsflags, filteropts); | 
|  | 1948 | if (rc == 0) | 
|  | 1949 | break; | 
|  | 1950 | } | 
|  | 1951 | } | 
|  | 1952 |  | 
|  | 1953 | // If mount failed, clean up loop file (if any). | 
|  | 1954 | if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) { | 
|  | 1955 | del_loop(mp->mnt_fsname); | 
|  | 1956 | if (ENABLE_FEATURE_CLEAN_UP) { | 
|  | 1957 | free(loopFile); | 
|  | 1958 | free(mp->mnt_fsname); | 
|  | 1959 | } | 
|  | 1960 | } | 
|  | 1961 |  | 
|  | 1962 | report_error: | 
|  | 1963 | if (ENABLE_FEATURE_CLEAN_UP) | 
|  | 1964 | free(filteropts); | 
|  | 1965 |  | 
|  | 1966 | if (errno == EBUSY && ignore_busy) | 
|  | 1967 | return 0; | 
|  | 1968 | if (rc != 0) | 
|  | 1969 | bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir); | 
|  | 1970 | return rc; | 
|  | 1971 | } | 
|  | 1972 |  | 
|  | 1973 | // -O support | 
|  | 1974 | //    -O interprets a list of filter options which select whether a mount | 
|  | 1975 | // point will be mounted: only mounts with options matching *all* filtering | 
|  | 1976 | // options will be selected. | 
|  | 1977 | //    By default each -O filter option must be present in the list of mount | 
|  | 1978 | // options, but if it is prefixed by "no" then it must be absent. | 
|  | 1979 | // For example, | 
|  | 1980 | //  -O a,nob,c  matches  -o a,c  but fails to match  -o a,b,c | 
|  | 1981 | //              (and also fails to match  -o a  because  -o c  is absent). | 
|  | 1982 | // | 
|  | 1983 | // It is different from -t in that each option is matched exactly; a leading | 
|  | 1984 | // "no" at the beginning of one option does not negate the rest. | 
|  | 1985 | static int match_opt(const char *fs_opt_in, const char *O_opt) | 
|  | 1986 | { | 
|  | 1987 | if (!O_opt) | 
|  | 1988 | return 1; | 
|  | 1989 |  | 
|  | 1990 | while (*O_opt) { | 
|  | 1991 | const char *fs_opt = fs_opt_in; | 
|  | 1992 | int O_len; | 
|  | 1993 | int match; | 
|  | 1994 |  | 
|  | 1995 | // If option begins with "no" then treat as an inverted match: | 
|  | 1996 | // matching is a failure | 
|  | 1997 | match = 0; | 
|  | 1998 | if (O_opt[0] == 'n' && O_opt[1] == 'o') { | 
|  | 1999 | match = 1; | 
|  | 2000 | O_opt += 2; | 
|  | 2001 | } | 
|  | 2002 | // Isolate the current O option | 
|  | 2003 | O_len = strchrnul(O_opt, ',') - O_opt; | 
|  | 2004 | // Check for a match against existing options | 
|  | 2005 | while (1) { | 
|  | 2006 | if (strncmp(fs_opt, O_opt, O_len) == 0 | 
|  | 2007 | && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',') | 
|  | 2008 | ) { | 
|  | 2009 | if (match) | 
|  | 2010 | return 0;  // "no" prefix, but option found | 
|  | 2011 | match = 1;  // current O option found, go check next one | 
|  | 2012 | break; | 
|  | 2013 | } | 
|  | 2014 | fs_opt = strchr(fs_opt, ','); | 
|  | 2015 | if (!fs_opt) | 
|  | 2016 | break; | 
|  | 2017 | fs_opt++; | 
|  | 2018 | } | 
|  | 2019 | if (match == 0) | 
|  | 2020 | return 0;     // match wanted but not found | 
|  | 2021 | if (O_opt[O_len] == '\0') // end? | 
|  | 2022 | break; | 
|  | 2023 | // Step to the next O option | 
|  | 2024 | O_opt += O_len + 1; | 
|  | 2025 | } | 
|  | 2026 | // If we get here then everything matched | 
|  | 2027 | return 1; | 
|  | 2028 | } | 
|  | 2029 |  | 
|  | 2030 | // Parse options, if necessary parse fstab/mtab, and call singlemount for | 
|  | 2031 | // each directory to be mounted. | 
|  | 2032 | int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 
|  | 2033 | int mount_main(int argc UNUSED_PARAM, char **argv) | 
|  | 2034 | { | 
|  | 2035 | char *cmdopts = xzalloc(1); | 
|  | 2036 | char *fstype = NULL; | 
|  | 2037 | char *O_optmatch = NULL; | 
|  | 2038 | char *storage_path; | 
|  | 2039 | llist_t *lst_o = NULL; | 
|  | 2040 | const char *fstabname; | 
|  | 2041 | FILE *fstab; | 
|  | 2042 | int i, j; | 
|  | 2043 | int rc = EXIT_SUCCESS; | 
|  | 2044 | unsigned long cmdopt_flags; | 
|  | 2045 | unsigned opt; | 
|  | 2046 | struct mntent mtpair[2], *mtcur = mtpair; | 
|  | 2047 | IF_NOT_DESKTOP(const int nonroot = 0;) | 
|  | 2048 |  | 
|  | 2049 | IF_DESKTOP(int nonroot = ) sanitize_env_if_suid(); | 
|  | 2050 |  | 
|  | 2051 | INIT_G(); | 
|  | 2052 |  | 
|  | 2053 | // Parse long options, like --bind and --move.  Note that -o option | 
|  | 2054 | // and --option are synonymous.  Yes, this means --remount,rw works. | 
|  | 2055 | for (i = j = 1; argv[i]; i++) { | 
|  | 2056 | if (argv[i][0] == '-' && argv[i][1] == '-') | 
|  | 2057 | append_mount_options(&cmdopts, argv[i] + 2); | 
|  | 2058 | else | 
|  | 2059 | argv[j++] = argv[i]; | 
|  | 2060 | } | 
|  | 2061 | argv[j] = NULL; | 
|  | 2062 |  | 
|  | 2063 | // Parse remaining options | 
|  | 2064 | // Max 2 params; -o is a list, -v is a counter | 
|  | 2065 | opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv"); | 
|  | 2066 | opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch | 
|  | 2067 | IF_FEATURE_MOUNT_VERBOSE(, &verbose)); | 
|  | 2068 | while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o | 
|  | 2069 | if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r | 
|  | 2070 | if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w | 
|  | 2071 | argv += optind; | 
|  | 2072 |  | 
|  | 2073 | // If we have no arguments, show currently mounted filesystems | 
|  | 2074 | if (!argv[0]) { | 
|  | 2075 | if (!(opt & OPT_a)) { | 
|  | 2076 | FILE *mountTable = setmntent(bb_path_mtab_file, "r"); | 
|  | 2077 |  | 
|  | 2078 | if (!mountTable) | 
|  | 2079 | bb_error_msg_and_die("no %s", bb_path_mtab_file); | 
|  | 2080 |  | 
|  | 2081 | while (getmntent_r(mountTable, &mtpair[0], getmntent_buf, | 
|  | 2082 | GETMNTENT_BUFSIZE)) | 
|  | 2083 | { | 
|  | 2084 | // Don't show rootfs. FIXME: why?? | 
|  | 2085 | // util-linux 2.12a happily shows rootfs... | 
|  | 2086 | //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue; | 
|  | 2087 |  | 
|  | 2088 | if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0) | 
|  | 2089 | printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname, | 
|  | 2090 | mtpair->mnt_dir, mtpair->mnt_type, | 
|  | 2091 | mtpair->mnt_opts); | 
|  | 2092 | } | 
|  | 2093 | if (ENABLE_FEATURE_CLEAN_UP) | 
|  | 2094 | endmntent(mountTable); | 
|  | 2095 | return EXIT_SUCCESS; | 
|  | 2096 | } | 
|  | 2097 | storage_path = NULL; | 
|  | 2098 | } else { | 
|  | 2099 | // When we have two arguments, the second is the directory and we can | 
|  | 2100 | // skip looking at fstab entirely.  We can always abspath() the directory | 
|  | 2101 | // argument when we get it. | 
|  | 2102 | if (argv[1]) { | 
|  | 2103 | if (nonroot) | 
|  | 2104 | bb_error_msg_and_die(bb_msg_you_must_be_root); | 
|  | 2105 | mtpair->mnt_fsname = argv[0]; | 
|  | 2106 | mtpair->mnt_dir = argv[1]; | 
|  | 2107 | mtpair->mnt_type = fstype; | 
|  | 2108 | mtpair->mnt_opts = cmdopts; | 
|  | 2109 | resolve_mount_spec(&mtpair->mnt_fsname); | 
|  | 2110 | rc = singlemount(mtpair, /*ignore_busy:*/ 0); | 
|  | 2111 | return rc; | 
|  | 2112 | } | 
|  | 2113 | storage_path = bb_simplify_path(argv[0]); // malloced | 
|  | 2114 | } | 
|  | 2115 |  | 
|  | 2116 | // Past this point, we are handling either "mount -a [opts]" | 
|  | 2117 | // or "mount [opts] single_param" | 
|  | 2118 |  | 
|  | 2119 | cmdopt_flags = parse_mount_options(cmdopts, NULL); | 
|  | 2120 | if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags | 
|  | 2121 | bb_error_msg_and_die(bb_msg_you_must_be_root); | 
|  | 2122 |  | 
|  | 2123 | // If we have a shared subtree flag, don't worry about fstab or mtab. | 
|  | 2124 | if (ENABLE_FEATURE_MOUNT_FLAGS | 
|  | 2125 | && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) | 
|  | 2126 | ) { | 
|  | 2127 | // verbose_mount(source, target, type, flags, data) | 
|  | 2128 | rc = verbose_mount("", argv[0], "", cmdopt_flags, ""); | 
|  | 2129 | if (rc) | 
|  | 2130 | bb_simple_perror_msg_and_die(argv[0]); | 
|  | 2131 | return rc; | 
|  | 2132 | } | 
|  | 2133 |  | 
|  | 2134 | // Open either fstab or mtab | 
|  | 2135 | fstabname = "/etc/fstab"; | 
|  | 2136 | if (cmdopt_flags & MS_REMOUNT) { | 
|  | 2137 | // WARNING. I am not sure this matches util-linux's | 
|  | 2138 | // behavior. It's possible util-linux does not | 
|  | 2139 | // take -o opts from mtab (takes only mount source). | 
|  | 2140 | fstabname = bb_path_mtab_file; | 
|  | 2141 | } | 
|  | 2142 | fstab = setmntent(fstabname, "r"); | 
|  | 2143 | if (!fstab) | 
|  | 2144 | bb_perror_msg_and_die("can't read '%s'", fstabname); | 
|  | 2145 |  | 
|  | 2146 | // Loop through entries until we find what we're looking for | 
|  | 2147 | memset(mtpair, 0, sizeof(mtpair)); | 
|  | 2148 | for (;;) { | 
|  | 2149 | struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair); | 
|  | 2150 |  | 
|  | 2151 | // Get next fstab entry | 
|  | 2152 | if (!getmntent_r(fstab, mtcur, getmntent_buf | 
|  | 2153 | + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0), | 
|  | 2154 | GETMNTENT_BUFSIZE/2) | 
|  | 2155 | ) { // End of fstab/mtab is reached | 
|  | 2156 | mtcur = mtother; // the thing we found last time | 
|  | 2157 | break; | 
|  | 2158 | } | 
|  | 2159 |  | 
|  | 2160 | // If we're trying to mount something specific and this isn't it, | 
|  | 2161 | // skip it.  Note we must match the exact text in fstab (ala | 
|  | 2162 | // "proc") or a full path from root | 
|  | 2163 | if (argv[0]) { | 
|  | 2164 |  | 
|  | 2165 | // Is this what we're looking for? | 
|  | 2166 | if (strcmp(argv[0], mtcur->mnt_fsname) != 0 | 
|  | 2167 | && strcmp(storage_path, mtcur->mnt_fsname) != 0 | 
|  | 2168 | && strcmp(argv[0], mtcur->mnt_dir) != 0 | 
|  | 2169 | && strcmp(storage_path, mtcur->mnt_dir) != 0 | 
|  | 2170 | ) { | 
|  | 2171 | continue; // no | 
|  | 2172 | } | 
|  | 2173 |  | 
|  | 2174 | // Remember this entry.  Something later may have | 
|  | 2175 | // overmounted it, and we want the _last_ match. | 
|  | 2176 | mtcur = mtother; | 
|  | 2177 |  | 
|  | 2178 | // If we're mounting all | 
|  | 2179 | } else { | 
|  | 2180 | struct mntent *mp; | 
|  | 2181 | // No, mount -a won't mount anything, | 
|  | 2182 | // even user mounts, for mere humans | 
|  | 2183 | if (nonroot) | 
|  | 2184 | bb_error_msg_and_die(bb_msg_you_must_be_root); | 
|  | 2185 |  | 
|  | 2186 | // Does type match? (NULL matches always) | 
|  | 2187 | if (!match_fstype(mtcur, fstype)) | 
|  | 2188 | continue; | 
|  | 2189 |  | 
|  | 2190 | // Skip noauto and swap anyway | 
|  | 2191 | if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP)) | 
|  | 2192 | // swap is bogus "fstype", parse_mount_options can't check fstypes | 
|  | 2193 | || strcasecmp(mtcur->mnt_type, "swap") == 0 | 
|  | 2194 | ) { | 
|  | 2195 | continue; | 
|  | 2196 | } | 
|  | 2197 |  | 
|  | 2198 | // Does (at least one) option match? | 
|  | 2199 | // (NULL matches always) | 
|  | 2200 | if (!match_opt(mtcur->mnt_opts, O_optmatch)) | 
|  | 2201 | continue; | 
|  | 2202 |  | 
|  | 2203 | resolve_mount_spec(&mtcur->mnt_fsname); | 
|  | 2204 |  | 
|  | 2205 | // NFS mounts want this to be xrealloc-able | 
|  | 2206 | mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); | 
|  | 2207 |  | 
|  | 2208 | // If nothing is mounted on this directory... | 
|  | 2209 | // (otherwise repeated "mount -a" mounts everything again) | 
|  | 2210 | mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0); | 
|  | 2211 | // We do not check fsname match of found mount point - | 
|  | 2212 | // "/" may have fsname of "/dev/root" while fstab | 
|  | 2213 | // says "/dev/something_else". | 
|  | 2214 | if (mp) { | 
|  | 2215 | if (verbose) { | 
|  | 2216 | bb_error_msg("according to %s, " | 
|  | 2217 | "%s is already mounted on %s", | 
|  | 2218 | bb_path_mtab_file, | 
|  | 2219 | mp->mnt_fsname, mp->mnt_dir); | 
|  | 2220 | } | 
|  | 2221 | } else { | 
|  | 2222 | // ...mount this thing | 
|  | 2223 | if (singlemount(mtcur, /*ignore_busy:*/ 1)) { | 
|  | 2224 | // Count number of failed mounts | 
|  | 2225 | rc++; | 
|  | 2226 | } | 
|  | 2227 | } | 
|  | 2228 | free(mtcur->mnt_opts); | 
|  | 2229 | } | 
|  | 2230 | } | 
|  | 2231 |  | 
|  | 2232 | // End of fstab/mtab is reached. | 
|  | 2233 | // Were we looking for something specific? | 
|  | 2234 | if (argv[0]) { // yes | 
|  | 2235 | unsigned long l; | 
|  | 2236 |  | 
|  | 2237 | // If we didn't find anything, complain | 
|  | 2238 | if (!mtcur->mnt_fsname) | 
|  | 2239 | bb_error_msg_and_die("can't find %s in %s", | 
|  | 2240 | argv[0], fstabname); | 
|  | 2241 |  | 
|  | 2242 | // What happens when we try to "mount swap_partition"? | 
|  | 2243 | // (fstab containts "swap_partition swap swap defaults 0 0") | 
|  | 2244 | // util-linux-ng 2.13.1 does this: | 
|  | 2245 | // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory) | 
|  | 2246 | // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory) | 
|  | 2247 | // lstat("swap", 0x7fff62a3a640)           = -1 ENOENT (No such file or directory) | 
|  | 2248 | // write(2, "mount: mount point swap does not exist\n", 39) = 39 | 
|  | 2249 | // exit_group(32)                          = ? | 
|  | 2250 | #if 0 | 
|  | 2251 | // In case we want to simply skip swap partitions: | 
|  | 2252 | l = parse_mount_options(mtcur->mnt_opts, NULL); | 
|  | 2253 | if ((l & MOUNT_SWAP) | 
|  | 2254 | // swap is bogus "fstype", parse_mount_options can't check fstypes | 
|  | 2255 | || strcasecmp(mtcur->mnt_type, "swap") == 0 | 
|  | 2256 | ) { | 
|  | 2257 | goto ret; | 
|  | 2258 | } | 
|  | 2259 | #endif | 
|  | 2260 | if (nonroot) { | 
|  | 2261 | // fstab must have "users" or "user" | 
|  | 2262 | l = parse_mount_options(mtcur->mnt_opts, NULL); | 
|  | 2263 | if (!(l & MOUNT_USERS)) | 
|  | 2264 | bb_error_msg_and_die(bb_msg_you_must_be_root); | 
|  | 2265 | } | 
|  | 2266 |  | 
|  | 2267 | //util-linux-2.12 does not do this check. | 
|  | 2268 | //// If nothing is mounted on this directory... | 
|  | 2269 | //// (otherwise repeated "mount FOO" mounts FOO again) | 
|  | 2270 | //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0); | 
|  | 2271 | //if (mp) { | 
|  | 2272 | //	bb_error_msg("according to %s, " | 
|  | 2273 | //		"%s is already mounted on %s", | 
|  | 2274 | //		bb_path_mtab_file, | 
|  | 2275 | //		mp->mnt_fsname, mp->mnt_dir); | 
|  | 2276 | //} else { | 
|  | 2277 | // ...mount the last thing we found | 
|  | 2278 | mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); | 
|  | 2279 | append_mount_options(&(mtcur->mnt_opts), cmdopts); | 
|  | 2280 | resolve_mount_spec(&mtpair->mnt_fsname); | 
|  | 2281 | rc = singlemount(mtcur, /*ignore_busy:*/ 0); | 
|  | 2282 | if (ENABLE_FEATURE_CLEAN_UP) | 
|  | 2283 | free(mtcur->mnt_opts); | 
|  | 2284 | //} | 
|  | 2285 | } | 
|  | 2286 |  | 
|  | 2287 | //ret: | 
|  | 2288 | if (ENABLE_FEATURE_CLEAN_UP) | 
|  | 2289 | endmntent(fstab); | 
|  | 2290 | if (ENABLE_FEATURE_CLEAN_UP) { | 
|  | 2291 | free(storage_path); | 
|  | 2292 | free(cmdopts); | 
|  | 2293 | } | 
|  | 2294 |  | 
|  | 2295 | //TODO: exitcode should be ORed mask of (from "man mount"): | 
|  | 2296 | // 0 success | 
|  | 2297 | // 1 incorrect invocation or permissions | 
|  | 2298 | // 2 system error (out of memory, cannot fork, no more loop devices) | 
|  | 2299 | // 4 internal mount bug or missing nfs support in mount | 
|  | 2300 | // 8 user interrupt | 
|  | 2301 | //16 problems writing or locking /etc/mtab | 
|  | 2302 | //32 mount failure | 
|  | 2303 | //64 some mount succeeded | 
|  | 2304 | return rc; | 
|  | 2305 | } |