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