Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/util-linux/mount.c
- Timestamp:
- Nov 6, 2007, 11:01:53 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable/mindi-busybox/util-linux/mount.c
r902 r1770 10 10 */ 11 11 12 /* todo:13 * bb_getopt_ulflags();14 */15 16 12 /* Design notes: There is no spec for mount. Remind me to write one. 17 13 … … 23 19 */ 24 20 25 #include "busybox.h" 26 #include <unistd.h> 27 #include <errno.h> 28 #include <string.h> 21 #include "libbb.h" 29 22 #include <mntent.h> 30 #include <ctype.h> 31 #include <fcntl.h> // for CONFIG_FEATURE_MOUNT_LOOP 32 #include <sys/ioctl.h> // for CONFIG_FEATURE_MOUNT_LOOP 33 34 // These two aren't always defined in old headers 35 #ifndef MS_BIND 36 #define MS_BIND 4096 23 24 /* Needed for nfs support only... */ 25 #include <syslog.h> 26 #include <sys/utsname.h> 27 #undef TRUE 28 #undef FALSE 29 #include <rpc/rpc.h> 30 #include <rpc/pmap_prot.h> 31 #include <rpc/pmap_clnt.h> 32 33 34 #if defined(__dietlibc__) 35 /* 16.12.2006, Sampo Kellomaki (sampo@iki.fi) 36 * dietlibc-0.30 does not have implementation of getmntent_r() */ 37 /* OTOH: why we use getmntent_r instead of getmntent? TODO... */ 38 struct mntent *getmntent_r(FILE* stream, struct mntent* result, char* buffer, int bufsize) 39 { 40 /* *** XXX FIXME WARNING: This hack is NOT thread safe. --Sampo */ 41 struct mntent* ment = getmntent(stream); 42 memcpy(result, ment, sizeof(struct mntent)); 43 return result; 44 } 37 45 #endif 38 #ifndef MS_MOVE 39 #define MS_MOVE 8192 40 #endif 41 #ifndef MS_SILENT 42 #define MS_SILENT 32768 43 #endif 46 44 47 45 48 // Not real flags, but we want to be able to check for this. 46 #define MOUNT_NOAUTO (1<<29) 47 #define MOUNT_SWAP (1<<30) 49 enum { 50 MOUNT_USERS = (1<<28)*ENABLE_DESKTOP, 51 MOUNT_NOAUTO = (1<<29), 52 MOUNT_SWAP = (1<<30), 53 }; 54 // TODO: more "user" flag compatibility. 55 // "user" option (from mount manpage): 56 // Only the user that mounted a filesystem can unmount it again. 57 // If any user should be able to unmount, then use users instead of user 58 // in the fstab line. The owner option is similar to the user option, 59 // with the restriction that the user must be the owner of the special file. 60 // This may be useful e.g. for /dev/fd if a login script makes 61 // the console user owner of this device. 62 48 63 /* Standard mount options (from -o options or --options), with corresponding 49 64 * flags */ … … 52 67 const char *name; 53 68 long flags; 54 } static const mount_options[] = { 55 // NOP flags. 56 57 {"loop", 0}, 58 {"defaults", 0}, 59 {"quiet", 0}, 60 61 // vfs flags 62 63 {"ro", MS_RDONLY}, 64 {"rw", ~MS_RDONLY}, 65 {"nosuid", MS_NOSUID}, 66 {"suid", ~MS_NOSUID}, 67 {"dev", ~MS_NODEV}, 68 {"nodev", MS_NODEV}, 69 {"exec", ~MS_NOEXEC}, 70 {"noexec", MS_NOEXEC}, 71 {"sync", MS_SYNCHRONOUS}, 72 {"async", ~MS_SYNCHRONOUS}, 73 {"atime", ~MS_NOATIME}, 74 {"noatime", MS_NOATIME}, 75 {"diratime", ~MS_NODIRATIME}, 76 {"nodiratime", MS_NODIRATIME}, 77 {"loud", ~MS_SILENT}, 78 79 // action flags 80 81 {"remount", MS_REMOUNT}, 82 {"bind", MS_BIND}, 83 {"move", MS_MOVE}, 84 {"noauto",MOUNT_NOAUTO}, 85 {"swap",MOUNT_SWAP} 69 } static mount_options[] = { 70 // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs. 71 72 USE_FEATURE_MOUNT_LOOP( 73 {"loop", 0}, 74 ) 75 76 USE_FEATURE_MOUNT_FSTAB( 77 {"defaults", 0}, 78 /* {"quiet", 0}, - do not filter out, vfat wants to see it */ 79 {"noauto", MOUNT_NOAUTO}, 80 {"sw", MOUNT_SWAP}, 81 {"swap", MOUNT_SWAP}, 82 USE_DESKTOP({"user", MOUNT_USERS},) 83 USE_DESKTOP({"users", MOUNT_USERS},) 84 ) 85 86 USE_FEATURE_MOUNT_FLAGS( 87 // vfs flags 88 {"nosuid", MS_NOSUID}, 89 {"suid", ~MS_NOSUID}, 90 {"dev", ~MS_NODEV}, 91 {"nodev", MS_NODEV}, 92 {"exec", ~MS_NOEXEC}, 93 {"noexec", MS_NOEXEC}, 94 {"sync", MS_SYNCHRONOUS}, 95 {"async", ~MS_SYNCHRONOUS}, 96 {"atime", ~MS_NOATIME}, 97 {"noatime", MS_NOATIME}, 98 {"diratime", ~MS_NODIRATIME}, 99 {"nodiratime", MS_NODIRATIME}, 100 {"loud", ~MS_SILENT}, 101 102 // action flags 103 104 {"bind", MS_BIND}, 105 {"move", MS_MOVE}, 106 {"shared", MS_SHARED}, 107 {"slave", MS_SLAVE}, 108 {"private", MS_PRIVATE}, 109 {"unbindable", MS_UNBINDABLE}, 110 {"rshared", MS_SHARED|MS_RECURSIVE}, 111 {"rslave", MS_SLAVE|MS_RECURSIVE}, 112 {"rprivate", MS_SLAVE|MS_RECURSIVE}, 113 {"runbindable", MS_UNBINDABLE|MS_RECURSIVE}, 114 ) 115 116 // Always understood. 117 118 {"ro", MS_RDONLY}, // vfs flag 119 {"rw", ~MS_RDONLY}, // vfs flag 120 {"remount", MS_REMOUNT}, // action flag 86 121 }; 87 122 123 88 124 /* Append mount options to string */ 89 static void append_mount_options(char **oldopts, char *newopts) 90 { 91 if(*oldopts && **oldopts) { 92 char *temp=bb_xasprintf("%s,%s",*oldopts,newopts); 93 free(*oldopts); 94 *oldopts=temp; 125 static void append_mount_options(char **oldopts, const char *newopts) 126 { 127 if (*oldopts && **oldopts) { 128 /* do not insert options which are already there */ 129 while (newopts[0]) { 130 char *p; 131 int len = strlen(newopts); 132 p = strchr(newopts, ','); 133 if (p) len = p - newopts; 134 p = *oldopts; 135 while (1) { 136 if (!strncmp(p, newopts, len) 137 && (p[len]==',' || p[len]==0)) 138 goto skip; 139 p = strchr(p,','); 140 if (!p) break; 141 p++; 142 } 143 p = xasprintf("%s,%.*s", *oldopts, len, newopts); 144 free(*oldopts); 145 *oldopts = p; 146 skip: 147 newopts += len; 148 while (newopts[0] == ',') newopts++; 149 } 95 150 } else { 96 151 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts); 97 *oldopts = bb_xstrdup(newopts);152 *oldopts = xstrdup(newopts); 98 153 } 99 154 } 100 155 101 156 /* Use the mount_options list to parse options into flags. 102 * Return list of unrecognized options in *strflags if strflags!=NULL */157 * Also return list of unrecognized options if unrecognized!=NULL */ 103 158 static int parse_mount_options(char *options, char **unrecognized) 104 159 { … … 113 168 114 169 // Find this option in mount_options 115 for (i = 0; i < (sizeof(mount_options) / sizeof(*mount_options)); i++) {170 for (i = 0; i < ARRAY_SIZE(mount_options); i++) { 116 171 if (!strcasecmp(mount_options[i].name, options)) { 117 172 long fl = mount_options[i].flags; 118 if (fl < 0) flags &= fl;173 if (fl < 0) flags &= fl; 119 174 else flags |= fl; 120 175 break; … … 122 177 } 123 178 // If unrecognized not NULL, append unrecognized mount options */ 124 if (unrecognized 125 && i == (sizeof(mount_options) / sizeof(*mount_options))) 126 { 179 if (unrecognized && i == ARRAY_SIZE(mount_options)) { 127 180 // Add it to strflags, to pass on to kernel 128 181 i = *unrecognized ? strlen(*unrecognized) : 0; … … 135 188 136 189 // Advance to next option, or finish 137 if (comma) {190 if (comma) { 138 191 *comma = ','; 139 192 options = ++comma; … … 148 201 static llist_t *get_block_backed_filesystems(void) 149 202 { 150 char *fs, *buf, 151 *filesystems[] = {"/etc/filesystems", "/proc/filesystems", 0}; 203 static const char filesystems[2][sizeof("/proc/filesystems")] = { 204 "/etc/filesystems", 205 "/proc/filesystems", 206 }; 207 char *fs, *buf; 152 208 llist_t *list = 0; 153 209 int i; 154 210 FILE *f; 155 211 156 for(i = 0; filesystems[i]; i++) { 157 if(!(f = fopen(filesystems[i], "r"))) continue; 158 159 for(fs = buf = 0; (fs = buf = bb_get_chomped_line_from_file(f)); 160 free(buf)) 161 { 162 if(!strncmp(buf,"nodev",5) && isspace(buf[5])) continue; 163 164 while(isspace(*fs)) fs++; 165 if(*fs=='#' || *fs=='*') continue; 166 if(!*fs) continue; 167 168 llist_add_to_end(&list,bb_xstrdup(fs)); 212 for (i = 0; i < 2; i++) { 213 f = fopen(filesystems[i], "r"); 214 if (!f) continue; 215 216 while ((buf = xmalloc_getline(f)) != 0) { 217 if (!strncmp(buf, "nodev", 5) && isspace(buf[5])) 218 continue; 219 fs = skip_whitespace(buf); 220 if (*fs=='#' || *fs=='*' || !*fs) continue; 221 222 llist_add_to_end(&list, xstrdup(fs)); 223 free(buf); 169 224 } 170 225 if (ENABLE_FEATURE_CLEAN_UP) fclose(f); … … 186 241 187 242 #if ENABLE_FEATURE_MTAB_SUPPORT 188 static int useMtab ;243 static int useMtab = 1; 189 244 static int fakeIt; 190 245 #else … … 194 249 195 250 // Perform actual mount of specific filesystem at specific location. 196 251 // NB: mp->xxx fields may be trashed on exit 197 252 static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts) 198 253 { 199 int rc ;200 201 if (fakeIt) { return 0; }254 int rc = 0; 255 256 if (fakeIt) goto mtab; 202 257 203 258 // Mount, with fallback to read-only if necessary. 204 259 205 for (;;) {260 for (;;) { 206 261 rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type, 207 262 vfsflags, filteropts); 208 if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))263 if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS)) 209 264 break; 210 265 bb_error_msg("%s is write-protected, mounting read-only", … … 220 275 /* If the mount was successful, and we're maintaining an old-style 221 276 * mtab file by hand, add the new entry to it now. */ 222 223 if(ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc) { 277 mtab: 278 if (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) { 279 char *fsname; 224 280 FILE *mountTable = setmntent(bb_path_mtab_file, "a+"); 225 281 int i; 226 282 227 if(!mountTable) 228 bb_error_msg("No %s\n",bb_path_mtab_file); 283 if (!mountTable) { 284 bb_error_msg("no %s",bb_path_mtab_file); 285 goto ret; 286 } 229 287 230 288 // Add vfs string flags 231 289 232 for(i=0; mount_options[i].flags != MS_REMOUNT; i++) 233 if (mount_options[i].flags > 0) 234 append_mount_options(&(mp->mnt_opts), 235 // Shut up about the darn const. It's not important. I don't care. 236 (char *)mount_options[i].name); 290 for (i=0; mount_options[i].flags != MS_REMOUNT; i++) 291 if (mount_options[i].flags > 0 && (mount_options[i].flags & vfsflags)) 292 append_mount_options(&(mp->mnt_opts), mount_options[i].name); 237 293 238 294 // Remove trailing / (if any) from directory we mounted on 239 295 240 i = strlen(mp->mnt_dir); 241 if(i>1 && mp->mnt_dir[i-1] == '/') mp->mnt_dir[i-1] = 0; 296 i = strlen(mp->mnt_dir) - 1; 297 if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = 0; 298 299 // Convert to canonical pathnames as needed 300 301 mp->mnt_dir = bb_simplify_path(mp->mnt_dir); 302 fsname = 0; 303 if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */ 304 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname); 305 mp->mnt_type = (char*)"bind"; 306 } 307 mp->mnt_freq = mp->mnt_passno = 0; 242 308 243 309 // Write and close. 244 310 245 if(!mp->mnt_type || !*mp->mnt_type) mp->mnt_type="--bind";246 311 addmntent(mountTable, mp); 247 312 endmntent(mountTable); 248 if (ENABLE_FEATURE_CLEAN_UP) 249 if(strcmp(mp->mnt_type,"--bind")) mp->mnt_type = 0; 250 } 251 313 if (ENABLE_FEATURE_CLEAN_UP) { 314 free(mp->mnt_dir); 315 free(fsname); 316 } 317 } 318 ret: 252 319 return rc; 253 320 } 254 321 255 256 // Mount one directory. Handles NFS, loopback, autobind, and filesystem type 257 // detection. Returns 0 for success, nonzero for failure. 258 322 #if ENABLE_FEATURE_MOUNT_NFS 323 324 /* 325 * Linux NFS mount 326 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com> 327 * 328 * Licensed under GPLv2, see file LICENSE in this tarball for details. 329 * 330 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port 331 * numbers to be specified on the command line. 332 * 333 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>: 334 * Omit the call to connect() for Linux version 1.3.11 or later. 335 * 336 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com> 337 * Implemented the "bg", "fg" and "retry" mount options for NFS. 338 * 339 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org> 340 * - added Native Language Support 341 * 342 * Modified by Olaf Kirch and Trond Myklebust for new NFS code, 343 * plus NFSv3 stuff. 344 */ 345 346 /* This is just a warning of a common mistake. Possibly this should be a 347 * uclibc faq entry rather than in busybox... */ 348 #if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) 349 #error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support." 350 #endif 351 352 #define MOUNTPORT 635 353 #define MNTPATHLEN 1024 354 #define MNTNAMLEN 255 355 #define FHSIZE 32 356 #define FHSIZE3 64 357 358 typedef char fhandle[FHSIZE]; 359 360 typedef struct { 361 unsigned int fhandle3_len; 362 char *fhandle3_val; 363 } fhandle3; 364 365 enum mountstat3 { 366 MNT_OK = 0, 367 MNT3ERR_PERM = 1, 368 MNT3ERR_NOENT = 2, 369 MNT3ERR_IO = 5, 370 MNT3ERR_ACCES = 13, 371 MNT3ERR_NOTDIR = 20, 372 MNT3ERR_INVAL = 22, 373 MNT3ERR_NAMETOOLONG = 63, 374 MNT3ERR_NOTSUPP = 10004, 375 MNT3ERR_SERVERFAULT = 10006, 376 }; 377 typedef enum mountstat3 mountstat3; 378 379 struct fhstatus { 380 unsigned int fhs_status; 381 union { 382 fhandle fhs_fhandle; 383 } fhstatus_u; 384 }; 385 typedef struct fhstatus fhstatus; 386 387 struct mountres3_ok { 388 fhandle3 fhandle; 389 struct { 390 unsigned int auth_flavours_len; 391 char *auth_flavours_val; 392 } auth_flavours; 393 }; 394 typedef struct mountres3_ok mountres3_ok; 395 396 struct mountres3 { 397 mountstat3 fhs_status; 398 union { 399 mountres3_ok mountinfo; 400 } mountres3_u; 401 }; 402 typedef struct mountres3 mountres3; 403 404 typedef char *dirpath; 405 406 typedef char *name; 407 408 typedef struct mountbody *mountlist; 409 410 struct mountbody { 411 name ml_hostname; 412 dirpath ml_directory; 413 mountlist ml_next; 414 }; 415 typedef struct mountbody mountbody; 416 417 typedef struct groupnode *groups; 418 419 struct groupnode { 420 name gr_name; 421 groups gr_next; 422 }; 423 typedef struct groupnode groupnode; 424 425 typedef struct exportnode *exports; 426 427 struct exportnode { 428 dirpath ex_dir; 429 groups ex_groups; 430 exports ex_next; 431 }; 432 typedef struct exportnode exportnode; 433 434 struct ppathcnf { 435 int pc_link_max; 436 short pc_max_canon; 437 short pc_max_input; 438 short pc_name_max; 439 short pc_path_max; 440 short pc_pipe_buf; 441 uint8_t pc_vdisable; 442 char pc_xxx; 443 short pc_mask[2]; 444 }; 445 typedef struct ppathcnf ppathcnf; 446 447 #define MOUNTPROG 100005 448 #define MOUNTVERS 1 449 450 #define MOUNTPROC_NULL 0 451 #define MOUNTPROC_MNT 1 452 #define MOUNTPROC_DUMP 2 453 #define MOUNTPROC_UMNT 3 454 #define MOUNTPROC_UMNTALL 4 455 #define MOUNTPROC_EXPORT 5 456 #define MOUNTPROC_EXPORTALL 6 457 458 #define MOUNTVERS_POSIX 2 459 460 #define MOUNTPROC_PATHCONF 7 461 462 #define MOUNT_V3 3 463 464 #define MOUNTPROC3_NULL 0 465 #define MOUNTPROC3_MNT 1 466 #define MOUNTPROC3_DUMP 2 467 #define MOUNTPROC3_UMNT 3 468 #define MOUNTPROC3_UMNTALL 4 469 #define MOUNTPROC3_EXPORT 5 470 471 enum { 472 #ifndef NFS_FHSIZE 473 NFS_FHSIZE = 32, 474 #endif 475 #ifndef NFS_PORT 476 NFS_PORT = 2049 477 #endif 478 }; 479 480 /* 481 * We want to be able to compile mount on old kernels in such a way 482 * that the binary will work well on more recent kernels. 483 * Thus, if necessary we teach nfsmount.c the structure of new fields 484 * that will come later. 485 * 486 * Moreover, the new kernel includes conflict with glibc includes 487 * so it is easiest to ignore the kernel altogether (at compile time). 488 */ 489 490 struct nfs2_fh { 491 char data[32]; 492 }; 493 struct nfs3_fh { 494 unsigned short size; 495 unsigned char data[64]; 496 }; 497 498 struct nfs_mount_data { 499 int version; /* 1 */ 500 int fd; /* 1 */ 501 struct nfs2_fh old_root; /* 1 */ 502 int flags; /* 1 */ 503 int rsize; /* 1 */ 504 int wsize; /* 1 */ 505 int timeo; /* 1 */ 506 int retrans; /* 1 */ 507 int acregmin; /* 1 */ 508 int acregmax; /* 1 */ 509 int acdirmin; /* 1 */ 510 int acdirmax; /* 1 */ 511 struct sockaddr_in addr; /* 1 */ 512 char hostname[256]; /* 1 */ 513 int namlen; /* 2 */ 514 unsigned int bsize; /* 3 */ 515 struct nfs3_fh root; /* 4 */ 516 }; 517 518 /* bits in the flags field */ 519 enum { 520 NFS_MOUNT_SOFT = 0x0001, /* 1 */ 521 NFS_MOUNT_INTR = 0x0002, /* 1 */ 522 NFS_MOUNT_SECURE = 0x0004, /* 1 */ 523 NFS_MOUNT_POSIX = 0x0008, /* 1 */ 524 NFS_MOUNT_NOCTO = 0x0010, /* 1 */ 525 NFS_MOUNT_NOAC = 0x0020, /* 1 */ 526 NFS_MOUNT_TCP = 0x0040, /* 2 */ 527 NFS_MOUNT_VER3 = 0x0080, /* 3 */ 528 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */ 529 NFS_MOUNT_NONLM = 0x0200 /* 3 */ 530 }; 531 532 533 /* 534 * We need to translate between nfs status return values and 535 * the local errno values which may not be the same. 536 * 537 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno: 538 * "after #include <errno.h> the symbol errno is reserved for any use, 539 * it cannot even be used as a struct tag or field name". 540 */ 541 542 #ifndef EDQUOT 543 #define EDQUOT ENOSPC 544 #endif 545 546 // Convert each NFSERR_BLAH into EBLAH 547 548 static const struct { 549 int stat; 550 int errnum; 551 } nfs_errtbl[] = { 552 {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST}, 553 {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG}, 554 {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT}, 555 {70,ESTALE}, {71,EREMOTE}, {-1,EIO} 556 }; 557 558 static char *nfs_strerror(int status) 559 { 560 int i; 561 static char buf[sizeof("unknown nfs status return value: ") + sizeof(int)*3]; 562 563 for (i = 0; nfs_errtbl[i].stat != -1; i++) { 564 if (nfs_errtbl[i].stat == status) 565 return strerror(nfs_errtbl[i].errnum); 566 } 567 sprintf(buf, "unknown nfs status return value: %d", status); 568 return buf; 569 } 570 571 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp) 572 { 573 if (!xdr_opaque(xdrs, objp, FHSIZE)) 574 return FALSE; 575 return TRUE; 576 } 577 578 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp) 579 { 580 if (!xdr_u_int(xdrs, &objp->fhs_status)) 581 return FALSE; 582 switch (objp->fhs_status) { 583 case 0: 584 if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle)) 585 return FALSE; 586 break; 587 default: 588 break; 589 } 590 return TRUE; 591 } 592 593 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp) 594 { 595 if (!xdr_string(xdrs, objp, MNTPATHLEN)) 596 return FALSE; 597 return TRUE; 598 } 599 600 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp) 601 { 602 if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3)) 603 return FALSE; 604 return TRUE; 605 } 606 607 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp) 608 { 609 if (!xdr_fhandle3(xdrs, &objp->fhandle)) 610 return FALSE; 611 if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0, 612 sizeof (int), (xdrproc_t) xdr_int)) 613 return FALSE; 614 return TRUE; 615 } 616 617 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp) 618 { 619 if (!xdr_enum(xdrs, (enum_t *) objp)) 620 return FALSE; 621 return TRUE; 622 } 623 624 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp) 625 { 626 if (!xdr_mountstat3(xdrs, &objp->fhs_status)) 627 return FALSE; 628 switch (objp->fhs_status) { 629 case MNT_OK: 630 if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo)) 631 return FALSE; 632 break; 633 default: 634 break; 635 } 636 return TRUE; 637 } 638 639 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2) 640 641 /* 642 * nfs_mount_version according to the sources seen at compile time. 643 */ 644 static int nfs_mount_version; 645 static int kernel_version; 646 647 /* 648 * Unfortunately, the kernel prints annoying console messages 649 * in case of an unexpected nfs mount version (instead of 650 * just returning some error). Therefore we'll have to try 651 * and figure out what version the kernel expects. 652 * 653 * Variables: 654 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time 655 * NFS_MOUNT_VERSION: these nfsmount sources at compile time 656 * nfs_mount_version: version this source and running kernel can handle 657 */ 658 static void 659 find_kernel_nfs_mount_version(void) 660 { 661 if (kernel_version) 662 return; 663 664 nfs_mount_version = 4; /* default */ 665 666 kernel_version = get_linux_version_code(); 667 if (kernel_version) { 668 if (kernel_version < KERNEL_VERSION(2,1,32)) 669 nfs_mount_version = 1; 670 else if (kernel_version < KERNEL_VERSION(2,2,18) || 671 (kernel_version >= KERNEL_VERSION(2,3,0) && 672 kernel_version < KERNEL_VERSION(2,3,99))) 673 nfs_mount_version = 3; 674 /* else v4 since 2.3.99pre4 */ 675 } 676 } 677 678 static struct pmap * 679 get_mountport(struct sockaddr_in *server_addr, 680 long unsigned prog, 681 long unsigned version, 682 long unsigned proto, 683 long unsigned port) 684 { 685 struct pmaplist *pmap; 686 static struct pmap p = {0, 0, 0, 0}; 687 688 server_addr->sin_port = PMAPPORT; 689 /* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *). 690 * I understand it like "IPv6 for this is not 100% ready" */ 691 pmap = pmap_getmaps(server_addr); 692 693 if (version > MAX_NFSPROT) 694 version = MAX_NFSPROT; 695 if (!prog) 696 prog = MOUNTPROG; 697 p.pm_prog = prog; 698 p.pm_vers = version; 699 p.pm_prot = proto; 700 p.pm_port = port; 701 702 while (pmap) { 703 if (pmap->pml_map.pm_prog != prog) 704 goto next; 705 if (!version && p.pm_vers > pmap->pml_map.pm_vers) 706 goto next; 707 if (version > 2 && pmap->pml_map.pm_vers != version) 708 goto next; 709 if (version && version <= 2 && pmap->pml_map.pm_vers > 2) 710 goto next; 711 if (pmap->pml_map.pm_vers > MAX_NFSPROT || 712 (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) || 713 (port && pmap->pml_map.pm_port != port)) 714 goto next; 715 memcpy(&p, &pmap->pml_map, sizeof(p)); 716 next: 717 pmap = pmap->pml_next; 718 } 719 if (!p.pm_vers) 720 p.pm_vers = MOUNTVERS; 721 if (!p.pm_port) 722 p.pm_port = MOUNTPORT; 723 if (!p.pm_prot) 724 p.pm_prot = IPPROTO_TCP; 725 return &p; 726 } 727 728 static int daemonize(void) 729 { 730 int fd; 731 int pid = fork(); 732 if (pid < 0) /* error */ 733 return -errno; 734 if (pid > 0) /* parent */ 735 return 0; 736 /* child */ 737 fd = xopen(bb_dev_null, O_RDWR); 738 dup2(fd, 0); 739 dup2(fd, 1); 740 dup2(fd, 2); 741 while (fd > 2) close(fd--); 742 setsid(); 743 openlog(applet_name, LOG_PID, LOG_DAEMON); 744 logmode = LOGMODE_SYSLOG; 745 return 1; 746 } 747 748 // TODO 749 static inline int we_saw_this_host_before(const char *hostname) 750 { 751 return 0; 752 } 753 754 /* RPC strerror analogs are terminally idiotic: 755 * *mandatory* prefix and \n at end. 756 * This hopefully helps. Usage: 757 * error_msg_rpc(clnt_*error*(" ")) */ 758 static void error_msg_rpc(const char *msg) 759 { 760 int len; 761 while (msg[0] == ' ' || msg[0] == ':') msg++; 762 len = strlen(msg); 763 while (len && msg[len-1] == '\n') len--; 764 bb_error_msg("%.*s", len, msg); 765 } 766 767 // NB: mp->xxx fields may be trashed on exit 768 static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) 769 { 770 CLIENT *mclient; 771 char *hostname; 772 char *pathname; 773 char *mounthost; 774 struct nfs_mount_data data; 775 char *opt; 776 struct hostent *hp; 777 struct sockaddr_in server_addr; 778 struct sockaddr_in mount_server_addr; 779 int msock, fsock; 780 union { 781 struct fhstatus nfsv2; 782 struct mountres3 nfsv3; 783 } status; 784 int daemonized; 785 char *s; 786 int port; 787 int mountport; 788 int proto; 789 int bg; 790 int soft; 791 int intr; 792 int posix; 793 int nocto; 794 int noac; 795 int nolock; 796 int retry; 797 int tcp; 798 int mountprog; 799 int mountvers; 800 int nfsprog; 801 int nfsvers; 802 int retval; 803 804 find_kernel_nfs_mount_version(); 805 806 daemonized = 0; 807 mounthost = NULL; 808 retval = ETIMEDOUT; 809 msock = fsock = -1; 810 mclient = NULL; 811 812 /* NB: hostname, mounthost, filteropts must be free()d prior to return */ 813 814 filteropts = xstrdup(filteropts); /* going to trash it later... */ 815 816 hostname = xstrdup(mp->mnt_fsname); 817 /* mount_main() guarantees that ':' is there */ 818 s = strchr(hostname, ':'); 819 pathname = s + 1; 820 *s = '\0'; 821 /* Ignore all but first hostname in replicated mounts 822 until they can be fully supported. (mack@sgi.com) */ 823 s = strchr(hostname, ','); 824 if (s) { 825 *s = '\0'; 826 bb_error_msg("warning: multiple hostnames not supported"); 827 } 828 829 server_addr.sin_family = AF_INET; 830 if (!inet_aton(hostname, &server_addr.sin_addr)) { 831 hp = gethostbyname(hostname); 832 if (hp == NULL) { 833 bb_herror_msg("%s", hostname); 834 goto fail; 835 } 836 if (hp->h_length > sizeof(struct in_addr)) { 837 bb_error_msg("got bad hp->h_length"); 838 hp->h_length = sizeof(struct in_addr); 839 } 840 memcpy(&server_addr.sin_addr, 841 hp->h_addr, hp->h_length); 842 } 843 844 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr)); 845 846 /* add IP address to mtab options for use when unmounting */ 847 848 if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */ 849 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr)); 850 } else { 851 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts, 852 mp->mnt_opts[0] ? "," : "", 853 inet_ntoa(server_addr.sin_addr)); 854 free(mp->mnt_opts); 855 mp->mnt_opts = tmp; 856 } 857 858 /* Set default options. 859 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to 860 * let the kernel decide. 861 * timeo is filled in after we know whether it'll be TCP or UDP. */ 862 memset(&data, 0, sizeof(data)); 863 data.retrans = 3; 864 data.acregmin = 3; 865 data.acregmax = 60; 866 data.acdirmin = 30; 867 data.acdirmax = 60; 868 data.namlen = NAME_MAX; 869 870 bg = 0; 871 soft = 0; 872 intr = 0; 873 posix = 0; 874 nocto = 0; 875 nolock = 0; 876 noac = 0; 877 retry = 10000; /* 10000 minutes ~ 1 week */ 878 tcp = 0; 879 880 mountprog = MOUNTPROG; 881 mountvers = 0; 882 port = 0; 883 mountport = 0; 884 nfsprog = 100003; 885 nfsvers = 0; 886 887 /* parse options */ 888 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) { 889 char *opteq = strchr(opt, '='); 890 if (opteq) { 891 static const char options[] ALIGN1 = 892 /* 0 */ "rsize\0" 893 /* 1 */ "wsize\0" 894 /* 2 */ "timeo\0" 895 /* 3 */ "retrans\0" 896 /* 4 */ "acregmin\0" 897 /* 5 */ "acregmax\0" 898 /* 6 */ "acdirmin\0" 899 /* 7 */ "acdirmax\0" 900 /* 8 */ "actimeo\0" 901 /* 9 */ "retry\0" 902 /* 10 */ "port\0" 903 /* 11 */ "mountport\0" 904 /* 12 */ "mounthost\0" 905 /* 13 */ "mountprog\0" 906 /* 14 */ "mountvers\0" 907 /* 15 */ "nfsprog\0" 908 /* 16 */ "nfsvers\0" 909 /* 17 */ "vers\0" 910 /* 18 */ "proto\0" 911 /* 19 */ "namlen\0" 912 /* 20 */ "addr\0"; 913 int val = xatoi_u(opteq + 1); 914 *opteq = '\0'; 915 switch (index_in_strings(options, opt)) { 916 case 0: // "rsize" 917 data.rsize = val; 918 break; 919 case 1: // "wsize" 920 data.wsize = val; 921 break; 922 case 2: // "timeo" 923 data.timeo = val; 924 break; 925 case 3: // "retrans" 926 data.retrans = val; 927 break; 928 case 4: // "acregmin" 929 data.acregmin = val; 930 break; 931 case 5: // "acregmax" 932 data.acregmax = val; 933 break; 934 case 6: // "acdirmin" 935 data.acdirmin = val; 936 break; 937 case 7: // "acdirmax" 938 data.acdirmax = val; 939 break; 940 case 8: // "actimeo" 941 data.acregmin = val; 942 data.acregmax = val; 943 data.acdirmin = val; 944 data.acdirmax = val; 945 break; 946 case 9: // "retry" 947 retry = val; 948 break; 949 case 10: // "port" 950 port = val; 951 break; 952 case 11: // "mountport" 953 mountport = val; 954 break; 955 case 12: // "mounthost" 956 mounthost = xstrndup(opteq+1, 957 strcspn(opteq+1," \t\n\r,")); 958 break; 959 case 13: // "mountprog" 960 mountprog = val; 961 break; 962 case 14: // "mountvers" 963 mountvers = val; 964 break; 965 case 15: // "nfsprog" 966 nfsprog = val; 967 break; 968 case 16: // "nfsvers" 969 case 17: // "vers" 970 nfsvers = val; 971 break; 972 case 18: // "proto" 973 if (!strncmp(opteq+1, "tcp", 3)) 974 tcp = 1; 975 else if (!strncmp(opteq+1, "udp", 3)) 976 tcp = 0; 977 else 978 bb_error_msg("warning: unrecognized proto= option"); 979 break; 980 case 19: // "namlen" 981 if (nfs_mount_version >= 2) 982 data.namlen = val; 983 else 984 bb_error_msg("warning: option namlen is not supported\n"); 985 break; 986 case 20: // "addr" - ignore 987 break; 988 default: 989 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val); 990 goto fail; 991 } 992 } 993 else { 994 static const char options[] ALIGN1 = 995 "bg\0" 996 "fg\0" 997 "soft\0" 998 "hard\0" 999 "intr\0" 1000 "posix\0" 1001 "cto\0" 1002 "ac\0" 1003 "tcp\0" 1004 "udp\0" 1005 "lock\0"; 1006 int val = 1; 1007 if (!strncmp(opt, "no", 2)) { 1008 val = 0; 1009 opt += 2; 1010 } 1011 switch (index_in_strings(options, opt)) { 1012 case 0: // "bg" 1013 bg = val; 1014 break; 1015 case 1: // "fg" 1016 bg = !val; 1017 break; 1018 case 2: // "soft" 1019 soft = val; 1020 break; 1021 case 3: // "hard" 1022 soft = !val; 1023 break; 1024 case 4: // "intr" 1025 intr = val; 1026 break; 1027 case 5: // "posix" 1028 posix = val; 1029 break; 1030 case 6: // "cto" 1031 nocto = !val; 1032 break; 1033 case 7: // "ac" 1034 noac = !val; 1035 break; 1036 case 8: // "tcp" 1037 tcp = val; 1038 break; 1039 case 9: // "udp" 1040 tcp = !val; 1041 break; 1042 case 10: // "lock" 1043 if (nfs_mount_version >= 3) 1044 nolock = !val; 1045 else 1046 bb_error_msg("warning: option nolock is not supported"); 1047 break; 1048 default: 1049 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt); 1050 goto fail; 1051 } 1052 } 1053 } 1054 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP; 1055 1056 data.flags = (soft ? NFS_MOUNT_SOFT : 0) 1057 | (intr ? NFS_MOUNT_INTR : 0) 1058 | (posix ? NFS_MOUNT_POSIX : 0) 1059 | (nocto ? NFS_MOUNT_NOCTO : 0) 1060 | (noac ? NFS_MOUNT_NOAC : 0); 1061 if (nfs_mount_version >= 2) 1062 data.flags |= (tcp ? NFS_MOUNT_TCP : 0); 1063 if (nfs_mount_version >= 3) 1064 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0); 1065 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) { 1066 bb_error_msg("NFSv%d not supported", nfsvers); 1067 goto fail; 1068 } 1069 if (nfsvers && !mountvers) 1070 mountvers = (nfsvers < 3) ? 1 : nfsvers; 1071 if (nfsvers && nfsvers < mountvers) { 1072 mountvers = nfsvers; 1073 } 1074 1075 /* Adjust options if none specified */ 1076 if (!data.timeo) 1077 data.timeo = tcp ? 70 : 7; 1078 1079 data.version = nfs_mount_version; 1080 1081 if (vfsflags & MS_REMOUNT) 1082 goto do_mount; 1083 1084 /* 1085 * If the previous mount operation on the same host was 1086 * backgrounded, and the "bg" for this mount is also set, 1087 * give up immediately, to avoid the initial timeout. 1088 */ 1089 if (bg && we_saw_this_host_before(hostname)) { 1090 daemonized = daemonize(); /* parent or error */ 1091 if (daemonized <= 0) { /* parent or error */ 1092 retval = -daemonized; 1093 goto ret; 1094 } 1095 } 1096 1097 /* create mount daemon client */ 1098 /* See if the nfs host = mount host. */ 1099 if (mounthost) { 1100 if (mounthost[0] >= '0' && mounthost[0] <= '9') { 1101 mount_server_addr.sin_family = AF_INET; 1102 mount_server_addr.sin_addr.s_addr = inet_addr(hostname); 1103 } else { 1104 hp = gethostbyname(mounthost); 1105 if (hp == NULL) { 1106 bb_herror_msg("%s", mounthost); 1107 goto fail; 1108 } else { 1109 if (hp->h_length > sizeof(struct in_addr)) { 1110 bb_error_msg("got bad hp->h_length?"); 1111 hp->h_length = sizeof(struct in_addr); 1112 } 1113 mount_server_addr.sin_family = AF_INET; 1114 memcpy(&mount_server_addr.sin_addr, 1115 hp->h_addr, hp->h_length); 1116 } 1117 } 1118 } 1119 1120 /* 1121 * The following loop implements the mount retries. When the mount 1122 * times out, and the "bg" option is set, we background ourself 1123 * and continue trying. 1124 * 1125 * The case where the mount point is not present and the "bg" 1126 * option is set, is treated as a timeout. This is done to 1127 * support nested mounts. 1128 * 1129 * The "retry" count specified by the user is the number of 1130 * minutes to retry before giving up. 1131 */ 1132 { 1133 struct timeval total_timeout; 1134 struct timeval retry_timeout; 1135 struct pmap* pm_mnt; 1136 time_t t; 1137 time_t prevt; 1138 time_t timeout; 1139 1140 retry_timeout.tv_sec = 3; 1141 retry_timeout.tv_usec = 0; 1142 total_timeout.tv_sec = 20; 1143 total_timeout.tv_usec = 0; 1144 timeout = time(NULL) + 60 * retry; 1145 prevt = 0; 1146 t = 30; 1147 retry: 1148 /* be careful not to use too many CPU cycles */ 1149 if (t - prevt < 30) 1150 sleep(30); 1151 1152 pm_mnt = get_mountport(&mount_server_addr, 1153 mountprog, 1154 mountvers, 1155 proto, 1156 mountport); 1157 nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers; 1158 1159 /* contact the mount daemon via TCP */ 1160 mount_server_addr.sin_port = htons(pm_mnt->pm_port); 1161 msock = RPC_ANYSOCK; 1162 1163 switch (pm_mnt->pm_prot) { 1164 case IPPROTO_UDP: 1165 mclient = clntudp_create(&mount_server_addr, 1166 pm_mnt->pm_prog, 1167 pm_mnt->pm_vers, 1168 retry_timeout, 1169 &msock); 1170 if (mclient) 1171 break; 1172 mount_server_addr.sin_port = htons(pm_mnt->pm_port); 1173 msock = RPC_ANYSOCK; 1174 case IPPROTO_TCP: 1175 mclient = clnttcp_create(&mount_server_addr, 1176 pm_mnt->pm_prog, 1177 pm_mnt->pm_vers, 1178 &msock, 0, 0); 1179 break; 1180 default: 1181 mclient = 0; 1182 } 1183 if (!mclient) { 1184 if (!daemonized && prevt == 0) 1185 error_msg_rpc(clnt_spcreateerror(" ")); 1186 } else { 1187 enum clnt_stat clnt_stat; 1188 /* try to mount hostname:pathname */ 1189 mclient->cl_auth = authunix_create_default(); 1190 1191 /* make pointers in xdr_mountres3 NULL so 1192 * that xdr_array allocates memory for us 1193 */ 1194 memset(&status, 0, sizeof(status)); 1195 1196 if (pm_mnt->pm_vers == 3) 1197 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT, 1198 (xdrproc_t) xdr_dirpath, 1199 (caddr_t) &pathname, 1200 (xdrproc_t) xdr_mountres3, 1201 (caddr_t) &status, 1202 total_timeout); 1203 else 1204 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT, 1205 (xdrproc_t) xdr_dirpath, 1206 (caddr_t) &pathname, 1207 (xdrproc_t) xdr_fhstatus, 1208 (caddr_t) &status, 1209 total_timeout); 1210 1211 if (clnt_stat == RPC_SUCCESS) 1212 goto prepare_kernel_data; /* we're done */ 1213 if (errno != ECONNREFUSED) { 1214 error_msg_rpc(clnt_sperror(mclient, " ")); 1215 goto fail; /* don't retry */ 1216 } 1217 /* Connection refused */ 1218 if (!daemonized && prevt == 0) /* print just once */ 1219 error_msg_rpc(clnt_sperror(mclient, " ")); 1220 auth_destroy(mclient->cl_auth); 1221 clnt_destroy(mclient); 1222 mclient = 0; 1223 close(msock); 1224 } 1225 1226 /* Timeout. We are going to retry... maybe */ 1227 1228 if (!bg) 1229 goto fail; 1230 if (!daemonized) { 1231 daemonized = daemonize(); 1232 if (daemonized <= 0) { /* parent or error */ 1233 retval = -daemonized; 1234 goto ret; 1235 } 1236 } 1237 prevt = t; 1238 t = time(NULL); 1239 if (t >= timeout) 1240 /* TODO error message */ 1241 goto fail; 1242 1243 goto retry; 1244 } 1245 1246 prepare_kernel_data: 1247 1248 if (nfsvers == 2) { 1249 if (status.nfsv2.fhs_status != 0) { 1250 bb_error_msg("%s:%s failed, reason given by server: %s", 1251 hostname, pathname, 1252 nfs_strerror(status.nfsv2.fhs_status)); 1253 goto fail; 1254 } 1255 memcpy(data.root.data, 1256 (char *) status.nfsv2.fhstatus_u.fhs_fhandle, 1257 NFS_FHSIZE); 1258 data.root.size = NFS_FHSIZE; 1259 memcpy(data.old_root.data, 1260 (char *) status.nfsv2.fhstatus_u.fhs_fhandle, 1261 NFS_FHSIZE); 1262 } else { 1263 fhandle3 *my_fhandle; 1264 if (status.nfsv3.fhs_status != 0) { 1265 bb_error_msg("%s:%s failed, reason given by server: %s", 1266 hostname, pathname, 1267 nfs_strerror(status.nfsv3.fhs_status)); 1268 goto fail; 1269 } 1270 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle; 1271 memset(data.old_root.data, 0, NFS_FHSIZE); 1272 memset(&data.root, 0, sizeof(data.root)); 1273 data.root.size = my_fhandle->fhandle3_len; 1274 memcpy(data.root.data, 1275 (char *) my_fhandle->fhandle3_val, 1276 my_fhandle->fhandle3_len); 1277 1278 data.flags |= NFS_MOUNT_VER3; 1279 } 1280 1281 /* create nfs socket for kernel */ 1282 1283 if (tcp) { 1284 if (nfs_mount_version < 3) { 1285 bb_error_msg("NFS over TCP is not supported"); 1286 goto fail; 1287 } 1288 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 1289 } else 1290 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 1291 if (fsock < 0) { 1292 bb_perror_msg("nfs socket"); 1293 goto fail; 1294 } 1295 if (bindresvport(fsock, 0) < 0) { 1296 bb_perror_msg("nfs bindresvport"); 1297 goto fail; 1298 } 1299 if (port == 0) { 1300 server_addr.sin_port = PMAPPORT; 1301 port = pmap_getport(&server_addr, nfsprog, nfsvers, 1302 tcp ? IPPROTO_TCP : IPPROTO_UDP); 1303 if (port == 0) 1304 port = NFS_PORT; 1305 } 1306 server_addr.sin_port = htons(port); 1307 1308 /* prepare data structure for kernel */ 1309 1310 data.fd = fsock; 1311 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr)); 1312 strncpy(data.hostname, hostname, sizeof(data.hostname)); 1313 1314 /* clean up */ 1315 1316 auth_destroy(mclient->cl_auth); 1317 clnt_destroy(mclient); 1318 close(msock); 1319 1320 if (bg) { 1321 /* We must wait until mount directory is available */ 1322 struct stat statbuf; 1323 int delay = 1; 1324 while (stat(mp->mnt_dir, &statbuf) == -1) { 1325 if (!daemonized) { 1326 daemonized = daemonize(); 1327 if (daemonized <= 0) { /* parent or error */ 1328 retval = -daemonized; 1329 goto ret; 1330 } 1331 } 1332 sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */ 1333 delay *= 2; 1334 if (delay > 30) 1335 delay = 30; 1336 } 1337 } 1338 1339 do_mount: /* perform actual mount */ 1340 1341 mp->mnt_type = (char*)"nfs"; 1342 retval = mount_it_now(mp, vfsflags, (char*)&data); 1343 goto ret; 1344 1345 fail: /* abort */ 1346 1347 if (msock != -1) { 1348 if (mclient) { 1349 auth_destroy(mclient->cl_auth); 1350 clnt_destroy(mclient); 1351 } 1352 close(msock); 1353 } 1354 if (fsock != -1) 1355 close(fsock); 1356 1357 ret: 1358 free(hostname); 1359 free(mounthost); 1360 free(filteropts); 1361 return retval; 1362 } 1363 1364 #else /* !ENABLE_FEATURE_MOUNT_NFS */ 1365 1366 /* Never called. Call should be optimized out. */ 1367 int nfsmount(struct mntent *mp, int vfsflags, char *filteropts); 1368 1369 #endif /* !ENABLE_FEATURE_MOUNT_NFS */ 1370 1371 // Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem 1372 // type detection. Returns 0 for success, nonzero for failure. 1373 // NB: mp->xxx fields may be trashed on exit 259 1374 static int singlemount(struct mntent *mp, int ignore_busy) 260 1375 { … … 268 1383 // Treat fstype "auto" as unspecified. 269 1384 270 if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0; 1385 if (mp->mnt_type && strcmp(mp->mnt_type,"auto") == 0) 1386 mp->mnt_type = 0; 1387 1388 // Might this be an CIFS filesystem? 1389 1390 if (ENABLE_FEATURE_MOUNT_CIFS 1391 && (!mp->mnt_type || strcmp(mp->mnt_type,"cifs") == 0) 1392 && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\') 1393 && mp->mnt_fsname[0]==mp->mnt_fsname[1] 1394 ) { 1395 len_and_sockaddr *lsa; 1396 char *ip, *dotted; 1397 char *s; 1398 1399 rc = 1; 1400 // Replace '/' with '\' and verify that unc points to "//server/share". 1401 1402 for (s = mp->mnt_fsname; *s; ++s) 1403 if (*s == '/') *s = '\\'; 1404 1405 // get server IP 1406 1407 s = strrchr(mp->mnt_fsname, '\\'); 1408 if (s <= mp->mnt_fsname+1) goto report_error; 1409 *s = '\0'; 1410 lsa = host2sockaddr(mp->mnt_fsname+2, 0); 1411 *s = '\\'; 1412 if (!lsa) goto report_error; 1413 1414 // insert ip=... option into string flags. 1415 1416 dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa); 1417 ip = xasprintf("ip=%s", dotted); 1418 parse_mount_options(ip, &filteropts); 1419 1420 // compose new unc '\\server-ip\share' 1421 // (s => slash after hostname) 1422 1423 mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s); 1424 1425 // lock is required 1426 vfsflags |= MS_MANDLOCK; 1427 1428 mp->mnt_type = (char*)"cifs"; 1429 rc = mount_it_now(mp, vfsflags, filteropts); 1430 if (ENABLE_FEATURE_CLEAN_UP) { 1431 free(mp->mnt_fsname); 1432 free(ip); 1433 free(dotted); 1434 free(lsa); 1435 } 1436 goto report_error; 1437 } 271 1438 272 1439 // Might this be an NFS filesystem? 273 1440 274 if (ENABLE_FEATURE_MOUNT_NFS && 275 (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) && 276 strchr(mp->mnt_fsname, ':') != NULL) 277 { 278 if (nfsmount(mp->mnt_fsname, mp->mnt_dir, &vfsflags, &filteropts, 1)) { 279 bb_perror_msg("nfsmount failed"); 280 goto report_error; 281 } else { 282 // Strangely enough, nfsmount() doesn't actually mount() anything. 283 mp->mnt_type = "nfs"; 284 rc = mount_it_now(mp, vfsflags, filteropts); 285 if (ENABLE_FEATURE_CLEAN_UP) free(filteropts); 286 287 goto report_error; 288 } 1441 if (ENABLE_FEATURE_MOUNT_NFS 1442 && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs")) 1443 && strchr(mp->mnt_fsname, ':') != NULL 1444 ) { 1445 rc = nfsmount(mp, vfsflags, filteropts); 1446 goto report_error; 289 1447 } 290 1448 291 1449 // Look at the file. (Not found isn't a failure for remount, or for 292 1450 // a synthetic filesystem like proc or sysfs.) 293 294 if (stat(mp->mnt_fsname, &st)); 295 else if (!(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) { 1451 // (We use stat, not lstat, in order to allow 1452 // mount symlink_to_file_or_blkdev dir) 1453 1454 if (!stat(mp->mnt_fsname, &st) 1455 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)) 1456 ) { 296 1457 // Do we need to allocate a loopback device for it? 297 1458 298 1459 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) { 299 1460 loopFile = bb_simplify_path(mp->mnt_fsname); 300 mp->mnt_fsname = 0; 301 switch(set_loop(&(mp->mnt_fsname), loopFile, 0)) { 302 case 0: 303 case 1: 304 break; 305 default: 306 bb_error_msg( errno == EPERM || errno == EACCES 307 ? bb_msg_perm_denied_are_you_root 308 : "Couldn't setup loop device"); 309 return errno; 1461 mp->mnt_fsname = NULL; /* will receive malloced loop dev name */ 1462 if (set_loop(&(mp->mnt_fsname), loopFile, 0) < 0) { 1463 if (errno == EPERM || errno == EACCES) 1464 bb_error_msg(bb_msg_perm_denied_are_you_root); 1465 else 1466 bb_perror_msg("cannot setup loop device"); 1467 return errno; 310 1468 } 311 1469 312 1470 // Autodetect bind mounts 313 1471 314 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type) vfsflags |= MS_BIND; 1472 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type) 1473 vfsflags |= MS_BIND; 315 1474 } 316 1475 … … 320 1479 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) 321 1480 rc = mount_it_now(mp, vfsflags, filteropts); 322 323 // Loop through filesystem types until mount succeeds or we run out324 325 1481 else { 1482 // Loop through filesystem types until mount succeeds 1483 // or we run out 326 1484 327 1485 /* Initialize list of block backed filesystems. This has to be … … 337 1495 for (fl = fslist; fl; fl = fl->link) { 338 1496 mp->mnt_type = fl->data; 339 340 if (!(rc = mount_it_now(mp,vfsflags, filteropts))) break; 341 342 mp->mnt_type = 0; 343 } 344 } 345 346 if (ENABLE_FEATURE_CLEAN_UP) free(filteropts); 1497 rc = mount_it_now(mp, vfsflags, filteropts); 1498 if (!rc) break; 1499 } 1500 } 347 1501 348 1502 // If mount failed, clean up loop file (if any). … … 355 1509 } 356 1510 } 357 report_error: 358 if (rc && errno == EBUSY && ignore_busy) rc = 0; 1511 1512 report_error: 1513 if (ENABLE_FEATURE_CLEAN_UP) 1514 free(filteropts); 1515 1516 if (rc && errno == EBUSY && ignore_busy) 1517 rc = 0; 359 1518 if (rc < 0) 360 bb_perror_msg(" Mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);1519 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir); 361 1520 362 1521 return rc; … … 366 1525 // each directory to be mounted. 367 1526 1527 static const char must_be_root[] ALIGN1 = "you must be root"; 1528 1529 int mount_main(int argc, char **argv); 368 1530 int mount_main(int argc, char **argv) 369 1531 { 370 char *cmdopts = bb_xstrdup(""), *fstabname, *fstype=0, *storage_path=0; 1532 enum { OPT_ALL = 0x10 }; 1533 1534 char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0; 1535 char *opt_o; 1536 const char *fstabname; 371 1537 FILE *fstab; 372 int i, opt, all = FALSE, rc = 0; 1538 int i, j, rc = 0; 1539 unsigned opt; 373 1540 struct mntent mtpair[2], *mtcur = mtpair; 1541 SKIP_DESKTOP(const int nonroot = 0;) 1542 USE_DESKTOP( int nonroot = (getuid() != 0);) 374 1543 375 1544 /* parse long options, like --bind and --move. Note that -o option 376 1545 * and --option are synonymous. Yes, this means --remount,rw works. */ 377 1546 378 for (i = opt= 0; i < argc; i++) {1547 for (i = j = 0; i < argc; i++) { 379 1548 if (argv[i][0] == '-' && argv[i][1] == '-') { 380 append_mount_options(&cmdopts,argv[i]+2); 381 } else argv[opt++] = argv[i]; 382 } 383 argc = opt; 1549 append_mount_options(&cmdopts, argv[i]+2); 1550 } else argv[j++] = argv[i]; 1551 } 1552 argv[j] = 0; 1553 argc = j; 384 1554 385 1555 // Parse remaining options 386 1556 387 while ((opt = getopt(argc, argv, "o:t:rwavnf")) > 0) { 388 switch (opt) { 389 case 'o': 390 append_mount_options(&cmdopts, optarg); 391 break; 392 case 't': 393 fstype = optarg; 394 break; 395 case 'r': 396 append_mount_options(&cmdopts, "ro"); 397 break; 398 case 'w': 399 append_mount_options(&cmdopts, "rw"); 400 break; 401 case 'a': 402 all = TRUE; 403 break; 404 case 'n': 405 USE_FEATURE_MTAB_SUPPORT(useMtab = FALSE;) 406 break; 407 case 'f': 408 USE_FEATURE_MTAB_SUPPORT(fakeIt = FALSE;) 409 break; 410 case 'v': 411 break; // ignore -v 412 default: 413 bb_show_usage(); 414 } 415 } 1557 opt = getopt32(argv, "o:t:rwanfvs", &opt_o, &fstype); 1558 if (opt & 0x1) append_mount_options(&cmdopts, opt_o); // -o 1559 //if (opt & 0x2) // -t 1560 if (opt & 0x4) append_mount_options(&cmdopts, "ro"); // -r 1561 if (opt & 0x8) append_mount_options(&cmdopts, "rw"); // -w 1562 //if (opt & 0x10) // -a 1563 if (opt & 0x20) USE_FEATURE_MTAB_SUPPORT(useMtab = 0); // -n 1564 if (opt & 0x40) USE_FEATURE_MTAB_SUPPORT(fakeIt = 1); // -f 1565 //if (opt & 0x80) // -v: verbose (ignore) 1566 //if (opt & 0x100) // -s: sloppy (ignore) 1567 argv += optind; 1568 argc -= optind; 416 1569 417 1570 // Three or more non-option arguments? Die with a usage message. 418 1571 419 if ( optind-argc>2) bb_show_usage();1572 if (argc > 2) bb_show_usage(); 420 1573 421 1574 // If we have no arguments, show currently mounted filesystems 422 1575 423 if ( optind ==argc) {424 if (! all) {1576 if (!argc) { 1577 if (!(opt & OPT_ALL)) { 425 1578 FILE *mountTable = setmntent(bb_path_mtab_file, "r"); 426 1579 427 if (!mountTable) bb_error_msg_and_die("No %s",bb_path_mtab_file);428 429 while (getmntent_r(mountTable, mtpair,bb_common_bufsiz1,1580 if (!mountTable) bb_error_msg_and_die("no %s", bb_path_mtab_file); 1581 1582 while (getmntent_r(mountTable, mtpair, bb_common_bufsiz1, 430 1583 sizeof(bb_common_bufsiz1))) 431 1584 { 432 // Don't show rootfs. 433 if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue; 1585 // Don't show rootfs. FIXME: why?? 1586 // util-linux 2.12a happily shows rootfs... 1587 //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue; 434 1588 435 1589 if (!fstype || !strcmp(mtpair->mnt_type, fstype)) … … 441 1595 return EXIT_SUCCESS; 442 1596 } 443 } 1597 } else storage_path = bb_simplify_path(argv[0]); 444 1598 445 1599 // When we have two arguments, the second is the directory and we can … … 447 1601 // argument when we get it. 448 1602 449 if (optind+2 == argc) { 450 mtpair->mnt_fsname = argv[optind]; 451 mtpair->mnt_dir = argv[optind+1]; 1603 if (argc == 2) { 1604 if (nonroot) 1605 bb_error_msg_and_die(must_be_root); 1606 mtpair->mnt_fsname = argv[0]; 1607 mtpair->mnt_dir = argv[1]; 452 1608 mtpair->mnt_type = fstype; 453 1609 mtpair->mnt_opts = cmdopts; … … 456 1612 } 457 1613 458 // If we have at least one argument, it's the storage location 459 460 if (optind < argc) storage_path = bb_simplify_path(argv[optind]); 1614 i = parse_mount_options(cmdopts, 0); 1615 if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags 1616 bb_error_msg_and_die(must_be_root); 1617 1618 // If we have a shared subtree flag, don't worry about fstab or mtab. 1619 1620 if (ENABLE_FEATURE_MOUNT_FLAGS 1621 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) 1622 ) { 1623 rc = mount("", argv[0], "", i, ""); 1624 if (rc) bb_perror_msg_and_die("%s", argv[0]); 1625 goto clean_up; 1626 } 461 1627 462 1628 // Open either fstab or mtab 463 1629 464 if (parse_mount_options(cmdopts,0) & MS_REMOUNT) 465 fstabname = (char *)bb_path_mtab_file; // Again with the evil const. 466 else fstabname="/etc/fstab"; 467 468 if (!(fstab=setmntent(fstabname,"r"))) 469 bb_perror_msg_and_die("Cannot read %s",fstabname); 1630 fstabname = "/etc/fstab"; 1631 if (i & MS_REMOUNT) { 1632 fstabname = bb_path_mtab_file; 1633 } 1634 fstab = setmntent(fstabname, "r"); 1635 if (!fstab) 1636 bb_perror_msg_and_die("cannot read %s", fstabname); 470 1637 471 1638 // Loop through entries until we find what we're looking for. 472 1639 473 memset(mtpair, 0,sizeof(mtpair));1640 memset(mtpair, 0, sizeof(mtpair)); 474 1641 for (;;) { 475 struct mntent *mtnext = mtpair + (mtcur==mtpair ? 1 : 0);1642 struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair); 476 1643 477 1644 // Get next fstab entry … … 483 1650 // Were we looking for something specific? 484 1651 485 if ( optind !=argc) {1652 if (argc) { 486 1653 487 1654 // If we didn't find anything, complain. 488 1655 489 1656 if (!mtnext->mnt_fsname) 490 bb_error_msg_and_die("Can't find %s in %s", 491 argv[optind], fstabname); 1657 bb_error_msg_and_die("can't find %s in %s", 1658 argv[0], fstabname); 1659 1660 mtcur = mtnext; 1661 if (nonroot) { 1662 // fstab must have "users" or "user" 1663 if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS)) 1664 bb_error_msg_and_die(must_be_root); 1665 } 492 1666 493 1667 // Mount the last thing we found. 494 1668 495 mtcur = mtnext; 496 mtcur->mnt_opts=bb_xstrdup(mtcur->mnt_opts); 497 append_mount_options(&(mtcur->mnt_opts),cmdopts); 1669 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); 1670 append_mount_options(&(mtcur->mnt_opts), cmdopts); 498 1671 rc = singlemount(mtcur, 0); 499 1672 free(mtcur->mnt_opts); … … 506 1679 * "proc") or a full path from root */ 507 1680 508 if ( optind !=argc) {1681 if (argc) { 509 1682 510 1683 // Is this what we're looking for? 511 1684 512 if (strcmp(argv[optind],mtcur->mnt_fsname) &&513 strcmp(storage_path, mtcur->mnt_fsname) &&514 strcmp(argv[ optind],mtcur->mnt_dir) &&515 strcmp(storage_path, mtcur->mnt_dir)) continue;1685 if (strcmp(argv[0], mtcur->mnt_fsname) && 1686 strcmp(storage_path, mtcur->mnt_fsname) && 1687 strcmp(argv[0], mtcur->mnt_dir) && 1688 strcmp(storage_path, mtcur->mnt_dir)) continue; 516 1689 517 1690 // Remember this entry. Something later may have overmounted … … 523 1696 524 1697 } else { 525 526 1698 // Do we need to match a filesystem type? 527 if (fstype && strcmp(mtcur->mnt_type,fstype)) continue;1699 if (fstype && match_fstype(mtcur, fstype)) continue; 528 1700 529 1701 // Skip noauto and swap anyway. 530 1702 531 if (parse_mount_options(mtcur->mnt_opts, 0)1703 if (parse_mount_options(mtcur->mnt_opts, 0) 532 1704 & (MOUNT_NOAUTO | MOUNT_SWAP)) continue; 533 1705 1706 // No, mount -a won't mount anything, 1707 // even user mounts, for mere humans. 1708 1709 if (nonroot) 1710 bb_error_msg_and_die(must_be_root); 1711 534 1712 // Mount this thing. 535 1713 1714 // NFS mounts want this to be xrealloc-able 1715 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); 536 1716 if (singlemount(mtcur, 1)) { 537 1717 /* Count number of failed mounts */ 538 1718 rc++; 539 1719 } 1720 free(mtcur->mnt_opts); 540 1721 } 541 1722 }
Note:
See TracChangeset
for help on using the changeset viewer.