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