mntinfo.c revision 9781:ccf49524d5dc
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
70extern char **environ;
71
72static int match_mount;		/* This holds the mount of interest. */
73
74int	fs_tab_used  = 0;
75int	fs_tab_alloc = 0;
76static int	fs_list = -1;
77
78struct	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 */
129static int
130fs_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 */
147static int
148is_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 */
185static int
186really_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. */
213char *
214get_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 */
243static char *
244path_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 */
269static int
270already_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 */
316int
317unmount_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 */
436int
437mount_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 */
563char *
564server_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. */
583static struct fstable *
584fs_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. */
645void
646fs_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. */
664static int
665hasopt(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 */
694static int
695construct_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 */
751static void
752mod_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 */
768static int
769construct_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 */
843int
844get_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 */
1007int
1008load_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 */
1042short
1043fsys(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 */
1126short
1127resolved_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 */
1148int
1149use_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 */
1159int
1160is_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 */
1169int
1170is_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 */
1195int
1196is_remote_fs_n(short n)
1197{
1198	return ((int)fs_tab[n]->remote);
1199}
1200
1201/* index-driven is_served() */
1202int
1203is_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 */
1213fsblkcnt_t
1214get_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 */
1224fsblkcnt_t
1225get_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 */
1235fsblkcnt_t
1236get_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 */
1246fsblkcnt_t
1247get_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 */
1257void
1258set_blk_used_n(short n, fsblkcnt_t value)
1259{
1260	fs_tab[n]->bused = value;
1261}
1262
1263/* Get the filesystem block size. */
1264fsblkcnt_t
1265get_blk_size_n(short n)
1266{
1267	return (fs_tab[n]->bsize);
1268}
1269
1270/* Get the filesystem fragment size. */
1271fsblkcnt_t
1272get_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 */
1280char *
1281get_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 */
1297char *
1298get_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 */
1306int
1307use_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 */
1318int
1319is_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 */
1334int
1335is_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 */
1350int
1351is_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 */
1365int
1366is_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 */
1379char *
1380get_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 */
1399char *
1400get_mount_point(short n)
1401{
1402	if (!is_remote_fs_n(n))
1403		return (NULL); 	/* local */
1404	return (fs_tab[n]->name);
1405}
1406
1407struct fstable *
1408get_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