xref: /titanic_52/usr/src/cmd/svr4pkg/libinst/mntinfo.c (revision 489b7c4ab76ae8df137fbfcc2214f7baa52883a0)
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