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