1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 /* 29 * System includes 30 */ 31 32 #include <stdio.h> 33 #include <limits.h> 34 #include <errno.h> 35 #include <stdlib.h> 36 #include <unistd.h> 37 #include <string.h> 38 #include <wait.h> 39 #include <signal.h> 40 #include <malloc.h> 41 #include <sys/types.h> 42 #include <sys/mount.h> 43 #include <sys/stat.h> 44 #include <fcntl.h> 45 #include <sys/systeminfo.h> 46 #include <pkgstrct.h> 47 #include <pkginfo.h> 48 #include <locale.h> 49 #include <libintl.h> 50 51 #include <sys/mnttab.h> 52 #include <sys/mntent.h> 53 #include <sys/vfstab.h> 54 55 /* 56 * consolidation pkg command library includes 57 */ 58 59 #include <pkglib.h> 60 61 /* 62 * local pkg command library includes 63 */ 64 65 #include "install.h" 66 #include "libinst.h" 67 #include "libadm.h" 68 #include "messages.h" 69 70 extern char **environ; 71 72 static int match_mount; /* This holds the mount of interest. */ 73 74 int fs_tab_used = 0; 75 int fs_tab_alloc = 0; 76 static int fs_list = -1; 77 78 struct fstable **fs_tab = NULL; 79 80 #define PKGDBROOT "/var/sadm" 81 #define MOUNT "/sbin/mount" 82 #define UMOUNT "/sbin/umount" 83 84 #define setmntent fopen 85 #define endmntent fclose 86 #define MOUNT_TABLE MNTTAB 87 88 /* returned by already_mounted() */ 89 #define MNT_NOT 0 90 #define MNT_EXACT 1 91 #define MNT_AVAIL 2 92 93 /* used with is_remote_src() */ 94 #define NOT_REMOTE 0 95 #define REAL_REMOTE 1 96 #define SELF_SERVE 2 97 98 /* 99 * Due to /etc/mnttab files containing entries for multiple nfs hosts 100 * HOST_NM_LN needs to be accommodating. The recommended value in the sysinfo 101 * man page of 257 needs to be expanded. See bugid 4076513. 102 * 1024 chars is defined in the mnttab.h header as the max size of an entry. 103 */ 104 105 #define HOST_NM_LN MNT_LINE_MAX 106 107 /* These cachefs definitions should be in mntent.h. Maybe some day. */ 108 #define MNTTYPE_CFS "cachefs" 109 #define MNTOPT_BACKFSTYPE "backfstype" 110 #define MNTTYPE_AUTO "autofs" 111 112 /* 113 * Utilities for getting filesystem information from the mount table. 114 * 115 * Note: vanilla SVr4 code (pkginstall/dockspace.c) used the output from 116 * popen() on the "/etc/mount" command. However, we need to get more 117 * information about mounted filesystems, so we use the C interfaces to 118 * the mount table, which also happens to be much faster than running 119 * another process. Since several of the pkg commands need access to the 120 * the code has been placed here, to be included in the libinst library. 121 */ 122 123 #define ALLOC_CHUNK 30 124 125 /* 126 * fs_tab_ent_comp - compare fstable entries first by length in reverse 127 * order, then alphabetically. 128 */ 129 static int 130 fs_tab_ent_comp(const void *e1, const void *e2) 131 { 132 struct fstable *fs1 = *((struct fstable **)e1); 133 struct fstable *fs2 = *((struct fstable **)e2); 134 135 if (fs1->namlen == fs2->namlen) 136 return (strcmp(fs1->name, fs2->name)); 137 else 138 return (fs2->namlen - fs1->namlen); 139 } 140 141 /* 142 * This determines if the source of the mount is from another host. If it's 143 * from this host, then it might be writable. This returns NOT_REMOTE if it's 144 * pure local, REAL_REMOTE if it's being served from another host and 145 * SELF_SERVE if it's being served by the current host. 146 */ 147 static int 148 is_remote_src(char *source) 149 { 150 static char host_name[HOST_NM_LN]; 151 char source_host[HOST_NM_LN], *src_ptr, *src_host_ptr; 152 static int hn_len; 153 154 if (hn_len == 0) { 155 /* Find out what host this is. */ 156 (void) sysinfo(SI_HOSTNAME, host_name, HOST_NM_LN); 157 hn_len = strlen(host_name); 158 } 159 160 if (source[0] == '/') 161 return (NOT_REMOTE); /* No server name, so it's local. */ 162 163 if (strchr(source, ':') == NULL) 164 return (NOT_REMOTE); /* it's a floppy disk or something */ 165 166 src_ptr = source; 167 src_host_ptr = source_host; 168 169 /* Scan to the end of the hostname (find the ":"). */ 170 while (*src_ptr != ':') 171 *src_host_ptr++ = *src_ptr++; 172 *src_host_ptr = '\0'; 173 174 if (strncmp(source, host_name, hn_len) == 0 && 175 *(source+hn_len) == ':' || is_local_host(source_host)) 176 return (SELF_SERVE); /* Exporting from itself, it's local. */ 177 178 return (REAL_REMOTE); 179 } 180 181 /* 182 * This determines if an apparently writeable filesystem is really writeable 183 * or if it's been shared over the network with root-restrictive options. 184 */ 185 static int 186 really_write(char *mountpt) 187 { 188 char testfile[PATH_MAX]; 189 int fd, retval = 0; 190 struct stat status; 191 192 (void) snprintf(testfile, sizeof (testfile), "%s/testXXXXXX", mountpt); 193 194 if (mktemp(testfile) == NULL) 195 return (0); /* may as well be read-only */ 196 /* LINTED do not use creat(); use open(path,... */ 197 else if ((fd = creat(testfile, 0777)) == -1) 198 return (0); /* can't write */ 199 else if (fstat(fd, &status) == -1) 200 retval = 0; /* may as well be read-only */ 201 else if (status.st_uid != 0) 202 retval = 0; /* too many restrictions */ 203 else 204 retval = 1; 205 206 (void) close(fd); 207 (void) unlink(testfile); 208 209 return (retval); 210 } 211 212 /* This returns the hostname portion of a remote path. */ 213 char * 214 get_server_host(short n) 215 { 216 static char hostname[HOST_NM_LN], *host_end; 217 218 if (fs_tab_used == 0) { 219 return ("unknown source"); 220 } 221 222 if (n >= 0 && n < fs_tab_used) { 223 (void) strcpy(hostname, fs_tab[n]->remote_name); 224 if ((host_end = strchr(hostname, ':')) == NULL) { 225 if ((strcmp(fs_tab[n]->fstype, MNTTYPE_AUTO)) == NULL) 226 return ("automounter"); 227 else 228 return (fs_tab[n]->fstype); 229 } else { 230 *host_end = '\0'; 231 return (hostname); 232 } 233 } 234 235 return ("unknown source"); 236 } 237 238 /* 239 * This pulls the path out of a hostpath which may be of the form host:path 240 * where path is an absolute path. NOTE: If path turns out to be relative, 241 * this returns NULL. 242 */ 243 static char * 244 path_part(char *hostpath) 245 { 246 char *host_end; 247 248 if ((host_end = strchr(hostpath, ':')) == NULL && hostpath[0] == '/') 249 return (hostpath); /* It's already legit. */ 250 251 if (*(host_end+1) == '/') 252 return (host_end+1); /* Here's the path part. */ 253 254 return (NULL); 255 } 256 257 /* 258 * This scans the filesystems already mounted to see if this remote mount is 259 * already in place on the server. This scans the fs_tab for a remote_name 260 * exactly matching the client's. It stores the current entry number 261 * corresponding to this mount in the static match_mount. 262 * 263 * Returns: 264 * MNT_NOT Couldn't find it. 265 * MNT_EXACT This has actually been manually mounted for us 266 * MNT_AVAIL This is mounted for the server, but needs to be 267 * loopback mounted from the client's perspective. 268 */ 269 static int 270 already_mounted(struct vfstab *vfs, int is_local_host, char *client_path, 271 char *host_path) 272 { 273 int i; 274 275 match_mount = -1; 276 277 if (fs_tab_used == 0) { 278 return (MNT_NOT); 279 } 280 281 for (i = 0; i < fs_tab_used; i++) { 282 /* 283 * Determine if this has been manually mounted exactly as we 284 * require. Begin by finding a mount on our current 285 * mountpoint. 286 */ 287 if (strcmp(fs_tab[i]->name, client_path) == 0) { 288 /* 289 * Now see if it is really the same mount. This isn't 290 * smart enough to find mounts on top of mounts, but 291 * assuming there is no conspiracy to fool this 292 * function, it will be good enough. 293 */ 294 if (is_local_host && 295 strcmp(fs_tab[i]->remote_name, host_path) == 0) { 296 match_mount = i; 297 return (MNT_EXACT); 298 } 299 } 300 301 /* Determine if this mount is available to the server. */ 302 if (strcmp(fs_tab[i]->remote_name, vfs->vfs_special) == 0) { 303 match_mount = i; 304 return (MNT_AVAIL); 305 } 306 } 307 return (MNT_NOT); 308 } 309 310 /* 311 * This function unmounts all of the loopback mounts created for the client. 312 * If no client stuff is mounted, this is completely benign, it finds that 313 * nothing is mounted up and returns. It returns "1" for unmounted everything 314 * OK and "0" for failure. 315 */ 316 int 317 unmount_client() 318 { 319 int errcode; 320 int exit_no; 321 int n; 322 int retcode = 1; 323 int status; 324 pid_t pid; 325 pid_t pid_return; 326 327 if (fs_tab_used == 0) { 328 return (1); 329 } 330 331 for (n = 0; n < fs_tab_used-1; n++) { 332 /* If the filesystem is mounted and this utility did it ... */ 333 if (fs_tab[n]->cl_mounted && fs_tab[n]->srvr_map) { 334 char *arg[3]; 335 336 /* create arglist for umount command */ 337 338 arg[0] = UMOUNT; 339 arg[1] = fs_tab[n]->name; 340 arg[2] = (char *)NULL; 341 342 /* flush standard i/o before creating new process */ 343 344 (void) fflush(stderr); 345 (void) fflush(stdout); 346 347 /* 348 * create new process to execute command in; 349 * vfork is being used to avoid duplicating the parents 350 * memory space - this means that the child process may 351 * not modify any of the parents memory including the 352 * standard i/o descriptors - all the child can do is 353 * adjust interrupts and open files as a prelude to a 354 * call to exec(). 355 */ 356 357 pid = vfork(); 358 if (pid < 0) { 359 /* fork failed! */ 360 361 logerr(WRN_BAD_FORK, errno, strerror(errno)); 362 retcode = 0; 363 } else if (pid > 0) { 364 /* 365 * this is the parent process 366 */ 367 368 status = 0; 369 pid_return = waitpid(pid, &status, 0); 370 371 if (pid_return != pid) { 372 logerr(WRN_BAD_WAIT, pid, pid_return, 373 (unsigned long)status, errno, 374 strerror(errno)); 375 retcode = 0; 376 } 377 378 /* 379 * If the child was stopped or killed by a 380 * signal or exied with any code but 0, we 381 * assume the mount has failed. 382 */ 383 384 if (!WIFEXITED(status) || 385 (errcode = WEXITSTATUS(status))) { 386 retcode = 0; 387 logerr(WRN_FSTAB_UMOUNT, 388 fs_tab[n]->name, errcode); 389 } else { 390 fs_tab[n]->cl_mounted = 0; 391 } 392 } else { 393 /* 394 * this is the child process 395 */ 396 397 int i; 398 399 /* reset any signals to default */ 400 401 for (i = 0; i < NSIG; i++) { 402 (void) sigset(i, SIG_DFL); 403 } 404 405 /* 406 * Redirect output to /dev/null because the 407 * umount error message may be confusing to 408 * the user. 409 */ 410 411 i = open("/dev/null", O_WRONLY); 412 if (i >= 0) { 413 dup2(2, STDERR_FILENO); 414 } 415 416 /* close all file descriptors except stdio */ 417 418 closefrom(3); 419 420 exit_no = execve(arg[0], arg, environ); 421 _exit(exit_no); 422 } 423 } 424 } 425 426 return (retcode); 427 } 428 429 /* 430 * This function creates the necessary loopback mounts to emulate the client 431 * configuration with respect to the server. If this is being run on a 432 * standalone or the installation is actually to the local system, this call 433 * is benign since srvr_map won't be set anywhere. It returns "1" for mounted 434 * everything OK and "0" for failure. 435 */ 436 int 437 mount_client() 438 { 439 int errcode; 440 int exit_no; 441 int n; 442 int retcode = 1; 443 int status; 444 pid_t pid; 445 pid_t pid_return; 446 447 if (fs_tab_used == 0) { 448 return (1); 449 } 450 451 for (n = fs_tab_used-1; n >= 0; n--) { 452 /* 453 * If the filesystem is mounted (meaning available) and the 454 * apparent filesystem can be mapped to a local filesystem 455 * AND the local filesystem is not the same as the target 456 * filesystem, mount it. 457 */ 458 if (fs_tab[n]->mounted && fs_tab[n]->srvr_map) { 459 char *arg[6]; 460 461 /* create arglist for mount command */ 462 463 arg[0] = MOUNT; 464 arg[1] = "-F"; 465 arg[2] = "lofs"; 466 arg[3] = fs_tab[n]->remote_name; 467 arg[4] = fs_tab[n]->name; 468 arg[5] = (char *)NULL; 469 470 /* flush standard i/o before creating new process */ 471 472 (void) fflush(stderr); 473 (void) fflush(stdout); 474 475 /* 476 * create new process to execute command in; 477 * vfork is being used to avoid duplicating the parents 478 * memory space - this means that the child process may 479 * not modify any of the parents memory including the 480 * standard i/o descriptors - all the child can do is 481 * adjust interrupts and open files as a prelude to a 482 * call to exec(). 483 */ 484 485 pid = vfork(); 486 if (pid < 0) { 487 /* fork failed! */ 488 489 logerr(WRN_BAD_FORK, errno, strerror(errno)); 490 retcode = 0; 491 } else if (pid > 0) { 492 /* 493 * this is the parent process 494 */ 495 496 pid_return = waitpid(pid, &status, 0); 497 498 if (pid_return != pid) { 499 logerr(WRN_BAD_WAIT, pid, pid_return, 500 (unsigned long)status, errno, 501 strerror(errno)); 502 retcode = 0; 503 } 504 505 /* 506 * If the child was stopped or killed by a 507 * signal or exied with any code but 0, we 508 * assume the mount has failed. 509 */ 510 511 if (!WIFEXITED(status) || 512 (errcode = WEXITSTATUS(status))) { 513 retcode = 0; 514 fs_tab[n]->mnt_failed = 1; 515 logerr(WRN_FSTAB_MOUNT, 516 fs_tab[n]->name, errcode); 517 } else { 518 fs_tab[n]->cl_mounted = 1; 519 } 520 } else { 521 /* 522 * this is the child process 523 */ 524 525 int i; 526 527 /* reset all signals to default */ 528 529 for (i = 0; i < NSIG; i++) { 530 (void) sigset(i, SIG_DFL); 531 } 532 533 /* 534 * Redirect output to /dev/null because the 535 * mount error message may be confusing to 536 * the user. 537 */ 538 539 i = open("/dev/null", O_WRONLY); 540 if (i >= 0) { 541 dup2(i, STDERR_FILENO); 542 } 543 544 /* close all file descriptors except stdio */ 545 546 closefrom(3); 547 548 exit_no = execve(arg[0], arg, environ); 549 _exit(exit_no); 550 /*NOTREACHED*/ 551 } 552 } 553 } 554 return (retcode); 555 } 556 557 /* 558 * This function maps path, on a loopback filesystem, back to the real server 559 * filesystem. fsys_value is the fs_tab[] entry to which the loopback'd path is 560 * mapped. This returns a pointer to a static area. If the result is needed 561 * for further processing, it should be strdup()'d or something. 562 */ 563 char * 564 server_map(char *path, short fsys_value) 565 { 566 static char server_construction[PATH_MAX]; 567 568 if (fs_tab_used == 0) { 569 (void) strcpy(server_construction, path); 570 } else if (fsys_value >= 0 && fsys_value < fs_tab_used) { 571 (void) snprintf(server_construction, 572 sizeof (server_construction), 573 "%s%s", fs_tab[fsys_value]->remote_name, 574 path+strlen(fs_tab[fsys_value]->name)); 575 } else { 576 (void) strcpy(server_construction, path); 577 } 578 579 return (server_construction); 580 } 581 582 /* This function sets up the standard parts of the fs_tab. */ 583 static struct fstable * 584 fs_tab_init(char *mountp, char *fstype) 585 { 586 struct fstable *nfte; 587 588 /* Create the array if necessary. */ 589 if (fs_list == -1) { 590 fs_list = ar_create(ALLOC_CHUNK, 591 (unsigned)sizeof (struct fstable), 592 "filesystem mount data"); 593 if (fs_list == -1) { 594 progerr(ERR_MALLOC, "fs_list", errno, strerror(errno)); 595 return (NULL); 596 } 597 } 598 599 /* 600 * Allocate an fstable entry for this mnttab entry. 601 */ 602 if ((nfte = *(struct fstable **)ar_next_avail(fs_list)) 603 == NULL) { 604 progerr(ERR_MALLOC, "nfte", errno, strerror(errno)); 605 return (NULL); 606 } 607 608 /* 609 * Point fs_tab at the head of the array again, since it may have 610 * moved due to realloc in ar_next_avail(). If ar_next_avail() realizes 611 * that there is no more room to grow the array, it reallocates the 612 * array. Because we stored pointer to that array in fs_tab, we need 613 * to make sure that it is updated as well. 614 */ 615 if ((fs_tab = (struct fstable **)ar_get_head(fs_list)) == NULL) { 616 progerr(ERR_NOTABLE, "mount", MOUNT_TABLE, strerror(errno)); 617 return (NULL); 618 } 619 620 /* 621 * Get the length of the 'mount point' name. 622 */ 623 nfte->namlen = strlen(mountp); 624 /* 625 * Allocate space for the 'mount point' name. 626 */ 627 if ((nfte->name = malloc(nfte->namlen+1)) == NULL) { 628 progerr(ERR_MALLOC, "name", errno, strerror(errno)); 629 return (NULL); 630 } 631 (void) strcpy(nfte->name, mountp); 632 633 if ((nfte->fstype = malloc(strlen(fstype)+1)) == NULL) { 634 progerr(ERR_MALLOC, "fstype", errno, strerror(errno)); 635 return (NULL); 636 } 637 (void) strcpy(nfte->fstype, fstype); 638 639 fs_tab_used++; 640 641 return (nfte); 642 } 643 644 /* This function frees all memory associated with the filesystem table. */ 645 void 646 fs_tab_free(void) 647 { 648 int n; 649 650 if (fs_tab_used == 0) { 651 return; 652 } 653 654 for (n = 0; n < fs_tab_used; n++) { 655 free(fs_tab[n]->fstype); 656 free(fs_tab[n]->name); 657 free(fs_tab[n]->remote_name); 658 } 659 660 ar_free(fs_list); 661 } 662 663 /* This function scans a string of mount options for a specific keyword. */ 664 static int 665 hasopt(char *options, char *keyword) 666 { 667 char vfs_options[VFS_LINE_MAX], *optptr; 668 669 if (!options) { 670 (void) strcpy(vfs_options, "ro"); 671 } else { 672 (void) strcpy(vfs_options, options); 673 } 674 675 while (optptr = strrchr(vfs_options, ',')) { 676 *optptr++ = '\0'; 677 678 if (strcmp(optptr, keyword) == 0) 679 return (1); 680 } 681 682 /* Now deal with the remainder. */ 683 if (strcmp(vfs_options, keyword) == 0) 684 return (1); 685 686 return (0); 687 } 688 689 /* 690 * This function constructs a new filesystem table (fs_tab[]) entry based on 691 * an /etc/mnttab entry. When it returns, the new entry has been inserted 692 * into fs_tab[]. 693 */ 694 static int 695 construct_mt(struct mnttab *mt) 696 { 697 struct fstable *nfte; 698 699 /* 700 * Initialize fstable structure and make the standard entries. 701 */ 702 if ((nfte = fs_tab_init(mt->mnt_mountp, mt->mnt_fstype)) == NULL) 703 return (1); 704 705 /* See if this is served from another host. */ 706 if (is_remote_src(mt->mnt_special) == REAL_REMOTE || 707 strcmp(mt->mnt_fstype, MNTTYPE_AUTO) == 0) 708 nfte->remote = 1; 709 else 710 nfte->remote = 0; 711 712 /* It's mounted now (by definition), so we don't have to remap it. */ 713 nfte->srvr_map = 0; 714 nfte->mounted = 1; 715 716 nfte->remote_name = strdup(mt->mnt_special); 717 718 /* 719 * This checks the mount commands which establish the most 720 * basic level of access. Later further tests may be 721 * necessary to fully qualify this. We set this bit 722 * preliminarily because we have access to the mount data 723 * now. 724 */ 725 nfte->writeable = 0; /* Assume read-only. */ 726 if (hasmntopt(mt, MNTOPT_RO) == NULL) { 727 nfte->writeable = 1; 728 if (!(nfte->remote)) 729 /* 730 * There's no network involved, so this 731 * assessment is confirmed. 732 */ 733 nfte->write_tested = 1; 734 } else 735 /* read-only is read-only */ 736 nfte->write_tested = 1; 737 738 /* Is this coming to us from a server? */ 739 if (nfte->remote && !(nfte->writeable)) 740 nfte->served = 1; 741 742 return (0); 743 } 744 745 /* 746 * This function modifies an existing fs_tab[] entry. It was found mounted up 747 * exactly the way we would have mounted it in mount_client() only at the 748 * time we didn't know it was for the client. Now we do, so we're setting the 749 * various permissions to conform to the client view. 750 */ 751 static void 752 mod_existing(struct vfstab *vfsent, int fstab_entry, int is_remote) 753 { 754 /* 755 * Establish whether the client will see this as served. 756 */ 757 if (is_remote && hasopt(vfsent->vfs_mntopts, MNTOPT_RO)) 758 fs_tab[fstab_entry]->served = 1; 759 760 fs_tab[fstab_entry]->cl_mounted = 1; 761 } 762 763 /* 764 * This function constructs a new fs_tab[] entry based on 765 * an /etc/vfstab entry. When it returns, the new entry has been inserted 766 * into fstab[]. 767 */ 768 static int 769 construct_vfs(struct vfstab *vfsent, char *client_path, char *link_name, 770 int is_remote, int mnt_stat) 771 { 772 int use_link; 773 struct fstable *nfte; 774 775 if ((nfte = fs_tab_init(client_path, vfsent->vfs_fstype)) == NULL) 776 return (1); 777 778 nfte->remote = (is_remote == REAL_REMOTE); 779 780 /* 781 * The file system mounted on the client may or may not be writeable. 782 * So we hand it over to fsys() to evaluate. This will have the same 783 * read/write attributes as the corresponding mounted filesystem. 784 */ 785 use_link = 0; 786 if (nfte->remote) { 787 /* 788 * Deal here with mount points actually on a system remote 789 * from the server. 790 */ 791 if (mnt_stat == MNT_NOT) { 792 /* 793 * This filesystem isn't in the current mount table 794 * meaning it isn't mounted, the current host can't 795 * write to it and there's no point to mapping it for 796 * the server. 797 */ 798 link_name = NULL; 799 nfte->mounted = 0; 800 nfte->srvr_map = 0; 801 nfte->writeable = 0; 802 } else { /* It's MNT_AVAIL. */ 803 /* 804 * This filesystem is associated with a current 805 * mountpoint. Since it's mounted, it needs to be 806 * remapped and it is writable if the real mounted 807 * filesystem is writeable. 808 */ 809 use_link = 1; 810 link_name = strdup(fs_tab[match_mount]->name); 811 nfte->mounted = 1; 812 nfte->srvr_map = 1; 813 nfte->writeable = fs_tab[match_mount]->writeable; 814 nfte->write_tested = fs_tab[match_mount]->write_tested; 815 } 816 } else { /* local filesystem */ 817 use_link = 1; 818 nfte->mounted = 1; 819 nfte->srvr_map = 1; 820 nfte->writeable = fs_tab[fsys(link_name)]->writeable; 821 nfte->write_tested = 1; 822 } 823 824 /* 825 * Now we establish whether the client will see this as served. 826 */ 827 if (is_remote && hasopt(vfsent->vfs_mntopts, MNTOPT_RO)) 828 nfte->served = 1; 829 830 if (use_link) { 831 nfte->remote_name = link_name; 832 } else { 833 nfte->remote_name = strdup(vfsent->vfs_special); 834 } 835 836 return (0); 837 } 838 839 /* 840 * get_mntinfo - get the mount table, now dynamically allocated. Returns 0 if 841 * no problem and 1 if there's a fatal error. 842 */ 843 int 844 get_mntinfo(int map_client, char *vfstab_file) 845 { 846 static char *rn = "/"; 847 FILE *pp; 848 struct mnttab mtbuf; 849 struct mnttab *mt = &mtbuf; 850 char *install_root; 851 int is_remote; 852 853 /* 854 * Open the mount table for the current host and establish a global 855 * table that holds data about current mount status. 856 */ 857 if ((pp = setmntent(MOUNT_TABLE, "r")) == NULL) { 858 progerr(ERR_NOTABLE, "mount", MOUNT_TABLE, strerror(errno)); 859 return (1); 860 } 861 862 /* 863 * First, review the mounted filesystems on the managing host. This 864 * may also be the target host but we haven't decided that for sure 865 * yet. 866 */ 867 while (!getmntent(pp, mt)) 868 if (construct_mt(mt)) 869 return (1); 870 871 (void) endmntent(pp); 872 873 /* 874 * Now, we see if this installation is to a client. If it is, we scan 875 * the client's vfstab to determine what filesystems are 876 * inappropriate to write to. This simply adds the vfstab entries 877 * representing what will be remote file systems for the client. 878 * Everything that isn't remote to the client is already accounted 879 * for in the fs_tab[] so far. If the remote filesystem is really on 880 * this server, we will write through to the server from this client. 881 */ 882 install_root = get_inst_root(); 883 if (install_root && strcmp(install_root, "/") != 0 && map_client) { 884 /* OK, this is a legitimate remote client. */ 885 struct vfstab vfsbuf; 886 struct vfstab *vfs = &vfsbuf; 887 char VFS_TABLE[PATH_MAX]; 888 889 /* 890 * Since we use the fsys() function later, and it depends on 891 * an ordered list, we have to sort the list here. 892 */ 893 qsort(fs_tab, fs_tab_used, 894 sizeof (struct fstable *), fs_tab_ent_comp); 895 896 /* 897 * Here's where the vfstab for the target is. If we can get 898 * to it, we'll scan it for what the client will see as 899 * remote filesystems, otherwise, we'll just skip this. 900 */ 901 if (vfstab_file) { 902 (void) snprintf(VFS_TABLE, sizeof (VFS_TABLE), "%s", 903 vfstab_file); 904 } else { 905 (void) snprintf(VFS_TABLE, sizeof (VFS_TABLE), "%s%s", 906 install_root, VFSTAB); 907 } 908 909 if (access(VFS_TABLE, R_OK) == 0) { 910 char *link_name; 911 912 /* 913 * Open the vfs table for the target host. 914 */ 915 if ((pp = setmntent(VFS_TABLE, "r")) == NULL) { 916 progerr(ERR_NOTABLE, "vfs", VFS_TABLE, 917 strerror(errno)); 918 return (1); 919 } 920 921 /* Do this for each entry in the vfstab. */ 922 while (!getvfsent(pp, vfs)) { 923 char client_mountp[PATH_MAX]; 924 int mnt_stat; 925 926 /* 927 * We put it into the fs table if it's 928 * remote mounted (even from this server) or 929 * loopback mounted from the client's point 930 * of view. 931 */ 932 if (!(is_remote = 933 is_remote_src(vfs->vfs_special)) && 934 strcmp(vfs->vfs_fstype, MNTTYPE_LOFS) != 935 0) 936 continue; /* not interesting */ 937 938 /* 939 * Construct client_mountp by prepending the 940 * install_root to the 'mount point' name. 941 */ 942 if (strcmp(vfs->vfs_mountp, "/") == 0) { 943 (void) strcpy(client_mountp, 944 install_root); 945 } else { 946 (void) snprintf(client_mountp, 947 sizeof (client_mountp), "%s%s", 948 install_root, vfs->vfs_mountp); 949 } 950 951 /* 952 * We also skip the entry if the vfs_special 953 * path and the client_path are the same. 954 * There's no need to mount it, it's just a 955 * cachefs optimization that mounts a 956 * directory over itself from this server. 957 */ 958 if ((is_remote == SELF_SERVE) && 959 strcmp(path_part(vfs->vfs_special), 960 client_mountp) == 0) 961 continue; 962 963 /* Determine if this is already mounted. */ 964 link_name = strdup(path_part(vfs->vfs_special)); 965 mnt_stat = already_mounted(vfs, 966 (is_remote != REAL_REMOTE), client_mountp, 967 link_name); 968 969 if (mnt_stat == MNT_EXACT) { 970 mod_existing(vfs, match_mount, 971 is_remote); 972 } else { /* MNT_NOT */ 973 if (construct_vfs(vfs, client_mountp, 974 link_name, is_remote, mnt_stat)) 975 return (1); 976 } 977 } 978 (void) endmntent(pp); 979 } /* end of if(access()) */ 980 } /* end of if(install_root) */ 981 982 /* This next one may look stupid, but it can really happen. */ 983 if (fs_tab_used <= 0) { 984 progerr(ERR_MNT_NOMOUNTS); 985 return (1); 986 } 987 988 /* 989 * Now that we have the complete list of mounted (or virtually 990 * mounted) filesystems, we sort the mountpoints in reverse order 991 * based on the length of the 'mount point' name. 992 */ 993 qsort(fs_tab, fs_tab_used, sizeof (struct fstable *), fs_tab_ent_comp); 994 if (strcmp(fs_tab[fs_tab_used-1]->name, rn) != 0) { 995 progerr(ERR_MNT_NOROOT, fs_tab[fs_tab_used-1]->name, rn, errno, 996 strerror(errno)); 997 return (1); 998 } else { 999 return (0); 1000 } 1001 } 1002 1003 /* 1004 * This function supports dryrun mode by allowing the filesystem table to be 1005 * directly loaded from the continuation file. 1006 */ 1007 int 1008 load_fsentry(struct fstable *fs_entry, char *name, char *fstype, 1009 char *remote_name) 1010 { 1011 struct fstable *nfte; 1012 1013 if ((nfte = fs_tab_init(name, fstype)) == NULL) 1014 return (1); 1015 1016 /* Grab the name and fstype from the new structure. */ 1017 fs_entry->name = nfte->name; 1018 fs_entry->fstype = nfte->fstype; 1019 1020 /* Copy the basic structure into place. */ 1021 (void) memcpy(nfte, fs_entry, sizeof (struct fstable)); 1022 1023 /* 1024 * Allocate space for the 'special' name. 1025 */ 1026 if ((nfte->remote_name = malloc(strlen(remote_name)+1)) == NULL) { 1027 progerr(ERR_MALLOC, "remote_name", errno, strerror(errno)); 1028 return (1); 1029 } 1030 1031 (void) strcpy(nfte->remote_name, remote_name); 1032 1033 return (0); 1034 } 1035 1036 /* 1037 * Given a path, return the table index of the filesystem the file apparently 1038 * resides on. This doesn't put any time into resolving filesystems that 1039 * refer to other filesystems. It just returns the entry containing this 1040 * path. 1041 */ 1042 short 1043 fsys(char *path) 1044 { 1045 register int i; 1046 char real_path[PATH_MAX]; 1047 char *path2use = NULL; 1048 char *cp = NULL; 1049 int pathlen; 1050 1051 path2use = path; 1052 1053 real_path[0] = '\0'; 1054 1055 (void) realpath(path, real_path); 1056 1057 if (real_path[0]) { 1058 cp = strstr(path, real_path); 1059 if (cp != path || cp == NULL) 1060 path2use = real_path; 1061 } 1062 1063 pathlen = strlen(path2use); 1064 1065 /* 1066 * The following algorithm scans the list of attached file systems 1067 * for the one containing path. At this point the file names in 1068 * fs_tab[] are sorted by decreasing length to facilitate the scan. 1069 * The first for() scans past all the file system names too short to 1070 * contain path. The second for() does the actual string comparison. 1071 * It tests first to assure that the comparison is against a complete 1072 * token by assuring that the end of the filesystem name aligns with 1073 * the end of a token in path2use (ie: '/' or NULL) then it does a 1074 * string compare. -- JST 1075 */ 1076 1077 if (fs_tab_used == 0) { 1078 return (-1); 1079 } 1080 1081 for (i = 0; i < fs_tab_used; i++) 1082 if (fs_tab[i] == NULL) 1083 continue; 1084 else if (fs_tab[i]->namlen <= pathlen) 1085 break; 1086 for (; i < fs_tab_used; i++) { 1087 int fs_namelen; 1088 char term_char; 1089 1090 if (fs_tab[i] == NULL) 1091 continue; 1092 1093 fs_namelen = fs_tab[i]->namlen; 1094 term_char = path2use[fs_namelen]; 1095 1096 /* 1097 * If we're putting the file "/a/kernel" into the filesystem 1098 * "/a", then fs_namelen == 2 and term_char == '/'. If, we're 1099 * putting "/etc/termcap" into "/", fs_namelen == 1 and 1100 * term_char (unfortunately) == 'e'. In the case of 1101 * fs_namelen == 1, we check to make sure the filesystem is 1102 * "/" and if it is, we have a guaranteed fit, otherwise we 1103 * do the string compare. -- JST 1104 */ 1105 if ((fs_namelen == 1 && *(fs_tab[i]->name) == '/') || 1106 ((term_char == '/' || term_char == NULL) && 1107 strncmp(fs_tab[i]->name, path2use, fs_namelen) == 0)) 1108 return (i); 1109 } 1110 1111 /* 1112 * It only gets here if the root filesystem is fundamentally corrupt. 1113 * (This can happen!) 1114 */ 1115 progerr(ERR_FSYS_FELLOUT, path2use); 1116 1117 return (-1); 1118 } 1119 1120 /* 1121 * This function returns the entry in the fs_tab[] corresponding to the 1122 * actual filesystem of record. It won't return a loopback filesystem entry, 1123 * it will return the filesystem that the loopback filesystem is mounted 1124 * over. 1125 */ 1126 short 1127 resolved_fsys(char *path) 1128 { 1129 int i = -1; 1130 char path2use[PATH_MAX]; 1131 1132 (void) strcpy(path2use, path); 1133 1134 /* If this isn't a "real" filesystem, resolve the map. */ 1135 do { 1136 (void) strcpy(path2use, server_map(path2use, i)); 1137 i = fsys(path2use); 1138 } while (fs_tab[i]->srvr_map); 1139 1140 return (i); 1141 } 1142 1143 /* 1144 * This function returns the srvr_map status based upon the fs_tab entry 1145 * number. This tells us if the server path constructed from the package 1146 * install root is really the target filesystem. 1147 */ 1148 int 1149 use_srvr_map_n(short n) 1150 { 1151 return ((int)fs_tab[n]->srvr_map); 1152 } 1153 1154 /* 1155 * This function returns the mount status based upon the fs_tab entry 1156 * number. This tells us if there is any hope of gaining access 1157 * to this file system. 1158 */ 1159 int 1160 is_mounted_n(short n) 1161 { 1162 return ((int)fs_tab[n]->mounted); 1163 } 1164 1165 /* 1166 * is_fs_writeable_n - given an fstab index, return 1 1167 * if it's writeable, 0 if read-only. 1168 */ 1169 int 1170 is_fs_writeable_n(short n) 1171 { 1172 /* 1173 * If the write access permissions haven't been confirmed, do that 1174 * now. Note that the only reason we need to do the special check is 1175 * in the case of an NFS mount (remote) because we can't determine if 1176 * root has access in any other way. 1177 */ 1178 if (fs_tab[n]->remote && fs_tab[n]->mounted && 1179 !fs_tab[n]->write_tested) { 1180 if (fs_tab[n]->writeable && !really_write(fs_tab[n]->name)) 1181 fs_tab[n]->writeable = 0; /* not really */ 1182 1183 fs_tab[n]->write_tested = 1; /* confirmed */ 1184 } 1185 1186 return ((int)fs_tab[n]->writeable); 1187 } 1188 1189 /* 1190 * is_remote_fs_n - given an fstab index, return 1 1191 * if it's a remote filesystem, 0 if local. 1192 * 1193 * Note: Upon entry, a valid fsys() is required. 1194 */ 1195 int 1196 is_remote_fs_n(short n) 1197 { 1198 return ((int)fs_tab[n]->remote); 1199 } 1200 1201 /* index-driven is_served() */ 1202 int 1203 is_served_n(short n) 1204 { 1205 return ((int)fs_tab[n]->served); 1206 } 1207 1208 /* 1209 * This returns the number of blocks available on the indicated filesystem. 1210 * 1211 * Note: Upon entry, a valid fsys() is required. 1212 */ 1213 fsblkcnt_t 1214 get_blk_free_n(short n) 1215 { 1216 return (fs_tab[n]->bfree); 1217 } 1218 1219 /* 1220 * This returns the number of blocks being used on the indicated filesystem. 1221 * 1222 * Note: Upon entry, a valid fsys() is required. 1223 */ 1224 fsblkcnt_t 1225 get_blk_used_n(short n) 1226 { 1227 return (fs_tab[n]->bused); 1228 } 1229 1230 /* 1231 * This returns the number of inodes available on the indicated filesystem. 1232 * 1233 * Note: Upon entry, a valid fsys() is required. 1234 */ 1235 fsblkcnt_t 1236 get_inode_free_n(short n) 1237 { 1238 return (fs_tab[n]->ffree); 1239 } 1240 1241 /* 1242 * This returns the number of inodes being used on the indicated filesystem. 1243 * 1244 * Note: Upon entry, a valid fsys() is required. 1245 */ 1246 fsblkcnt_t 1247 get_inode_used_n(short n) 1248 { 1249 return (fs_tab[n]->fused); 1250 } 1251 1252 /* 1253 * Sets the number of blocks being used on the indicated filesystem. 1254 * 1255 * Note: Upon entry, a valid fsys() is required. 1256 */ 1257 void 1258 set_blk_used_n(short n, fsblkcnt_t value) 1259 { 1260 fs_tab[n]->bused = value; 1261 } 1262 1263 /* Get the filesystem block size. */ 1264 fsblkcnt_t 1265 get_blk_size_n(short n) 1266 { 1267 return (fs_tab[n]->bsize); 1268 } 1269 1270 /* Get the filesystem fragment size. */ 1271 fsblkcnt_t 1272 get_frag_size_n(short n) 1273 { 1274 return (fs_tab[n]->bsize); 1275 } 1276 1277 /* 1278 * This returns the name of the indicated filesystem. 1279 */ 1280 char * 1281 get_fs_name_n(short n) 1282 { 1283 if (fs_tab_used == 0) { 1284 return (NULL); 1285 } else if (n >= fs_tab_used) { 1286 return (NULL); 1287 } else { 1288 return (fs_tab[n]->name); 1289 } 1290 } 1291 1292 /* 1293 * This returns the remote name of the indicated filesystem. 1294 * 1295 * Note: Upon entry, a valid fsys() is required. 1296 */ 1297 char * 1298 get_source_name_n(short n) 1299 { 1300 return (fs_tab[n]->remote_name); 1301 } 1302 1303 /* 1304 * This function returns the srvr_map status based upon the path. 1305 */ 1306 int 1307 use_srvr_map(char *path, short *fsys_value) 1308 { 1309 if (*fsys_value == BADFSYS) 1310 *fsys_value = fsys(path); 1311 1312 return (use_srvr_map_n(*fsys_value)); 1313 } 1314 1315 /* 1316 * This function returns the mount status based upon the path. 1317 */ 1318 int 1319 is_mounted(char *path, short *fsys_value) 1320 { 1321 if (*fsys_value == BADFSYS) 1322 *fsys_value = fsys(path); 1323 1324 return (is_mounted_n(*fsys_value)); 1325 } 1326 1327 /* 1328 * is_fs_writeable - given a cfent entry, return 1 1329 * if it's writeable, 0 if read-only. 1330 * 1331 * Note: Upon exit, a valid fsys() is guaranteed. This is 1332 * an interface requirement. 1333 */ 1334 int 1335 is_fs_writeable(char *path, short *fsys_value) 1336 { 1337 if (*fsys_value == BADFSYS) 1338 *fsys_value = fsys(path); 1339 1340 return (is_fs_writeable_n(*fsys_value)); 1341 } 1342 1343 /* 1344 * is_remote_fs - given a cfent entry, return 1 1345 * if it's a remote filesystem, 0 if local. 1346 * 1347 * Also Note: Upon exit, a valid fsys() is guaranteed. This is 1348 * an interface requirement. 1349 */ 1350 int 1351 is_remote_fs(char *path, short *fsys_value) 1352 { 1353 if (*fsys_value == BADFSYS) 1354 *fsys_value = fsys(path); 1355 1356 return (is_remote_fs_n(*fsys_value)); 1357 } 1358 1359 /* 1360 * This function returns the served status of the filesystem. Served means a 1361 * client is getting this file from a server and it is not writeable by the 1362 * client. It has nothing to do with whether or not this particular operation 1363 * (eg: pkgadd or pkgrm) will be writing to it. 1364 */ 1365 int 1366 is_served(char *path, short *fsys_value) 1367 { 1368 if (*fsys_value == BADFSYS) 1369 *fsys_value = fsys(path); 1370 1371 return (is_served_n(*fsys_value)); 1372 } 1373 1374 /* 1375 * get_remote_path - given a filesystem table index, return the 1376 * path of the filesystem on the remote system. Otherwise, 1377 * return NULL if it's a local filesystem. 1378 */ 1379 char * 1380 get_remote_path(short n) 1381 { 1382 char *p; 1383 1384 if (!is_remote_fs_n(n)) 1385 return (NULL); /* local */ 1386 p = strchr(fs_tab[n]->remote_name, ':'); 1387 if (!p) 1388 p = fs_tab[n]->remote_name; /* Loopback */ 1389 else 1390 p++; /* remote */ 1391 return (p); 1392 } 1393 1394 /* 1395 * get_mount_point - given a filesystem table index, return the 1396 * path of the mount point. Otherwise, 1397 * return NULL if it's a local filesystem. 1398 */ 1399 char * 1400 get_mount_point(short n) 1401 { 1402 if (!is_remote_fs_n(n)) 1403 return (NULL); /* local */ 1404 return (fs_tab[n]->name); 1405 } 1406 1407 struct fstable * 1408 get_fs_entry(short n) 1409 { 1410 if (fs_tab_used == 0) { 1411 return (NULL); 1412 } else if (n >= fs_tab_used) { 1413 return (NULL); 1414 } else { 1415 return (fs_tab[n]); 1416 } 1417 } 1418