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