xref: /titanic_41/usr/src/uts/common/fs/mntfs/mntvnops.c (revision 7535ae1914017b0e648abd7a139aca709fa82be3)
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  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <sys/file.h>
26 #include <sys/stat.h>
27 #include <sys/atomic.h>
28 #include <sys/mntio.h>
29 #include <sys/mnttab.h>
30 #include <sys/mount.h>
31 #include <sys/sunddi.h>
32 #include <sys/sysmacros.h>
33 #include <sys/systm.h>
34 #include <sys/vfs.h>
35 #include <sys/vfs_opreg.h>
36 #include <sys/fs/mntdata.h>
37 #include <fs/fs_subr.h>
38 #include <sys/vmsystm.h>
39 #include <vm/seg_vn.h>
40 #include <sys/time.h>
41 #include <sys/ksynch.h>
42 #include <sys/sdt.h>
43 
44 #define	MNTROOTINO	2
45 
46 static mntnode_t *mntgetnode(vnode_t *);
47 
48 vnodeops_t *mntvnodeops;
49 extern void vfs_mnttab_readop(void);
50 
51 /*
52  * Design of kernel mnttab accounting.
53  *
54  * mntfs provides two methods of reading the in-kernel mnttab, i.e. the state of
55  * the mounted resources: the read-only file /etc/mnttab, and a collection of
56  * ioctl() commands. Most of these interfaces are public and are described in
57  * mnttab(4). Three private ioctl() commands, MNTIOC_GETMNTENT,
58  * MNTIOC_GETEXTMNTENT and MNTIOC_GETMNTANY, provide for the getmntent(3C)
59  * family of functions, allowing them to support white space in mount names.
60  *
61  * A significant feature of mntfs is that it provides a file descriptor with a
62  * snapshot once it begins to consume mnttab data. Thus, as the process
63  * continues to consume data, its view of the in-kernel mnttab does not change
64  * even if resources are mounted or unmounted. The intent is to ensure that
65  * processes are guaranteed to read self-consistent data even as the system
66  * changes.
67  *
68  * The snapshot is implemented by a "database", unique to each zone, that
69  * comprises a linked list of mntelem_ts. The database is identified by
70  * zone_mntfs_db and is protected by zone_mntfs_db_lock. Each element contains
71  * the text entry in /etc/mnttab for a mounted resource, i.e. a vfs_t, and is
72  * marked with its time of "birth", i.e. creation. An element is "killed", and
73  * marked with its time of death, when it is found to be out of date, e.g. when
74  * the corresponding resource has been unmounted.
75  *
76  * When a process performs the first read() or ioctl() for a file descriptor for
77  * /etc/mnttab, the database is updated by a call to mntfs_snapshot() to ensure
78  * that an element exists for each currently mounted resource. Following this,
79  * the current time is written into a snapshot structure, a mntsnap_t, embedded
80  * in the descriptor's mntnode_t.
81  *
82  * mntfs is able to enumerate the /etc/mnttab entries corresponding to a
83  * particular file descriptor by searching the database for entries that were
84  * born before the appropriate snapshot and that either are still alive or died
85  * after the snapshot was created. Consumers use the iterator function
86  * mntfs_get_next_elem() to identify the next suitable element in the database.
87  *
88  * Each snapshot has a hold on its corresponding database elements, effected by
89  * a per-element reference count. At last close(), a snapshot is destroyed in
90  * mntfs_freesnap() by releasing all of its holds; an element is destroyed if
91  * its reference count becomes zero. Therefore the database never exists unless
92  * there is at least one active consumer of /etc/mnttab.
93  *
94  * getmntent(3C) et al. "do not open, close or rewind the file." This implies
95  * that getmntent() and read() must be able to operate without interaction on
96  * the same file descriptor; this is accomplished by the use of separate
97  * mntsnap_ts for both read() and ioctl().
98  *
99  * mntfs observes the following lock-ordering:
100  *
101  *	mnp->mnt_contents -> vfslist -> zonep->zone_mntfs_db_lock
102  *
103  * NOTE: The following variable enables the generation of the "dev=xxx"
104  * in the option string for a mounted file system.  Really this should
105  * be gotten rid of altogether, but for the sake of backwards compatibility
106  * we had to leave it in.  It is defined as a 32-bit device number.  This
107  * means that when 64-bit device numbers are in use, if either the major or
108  * minor part of the device number will not fit in a 16 bit quantity, the
109  * "dev=" will be set to NODEV (0x7fffffff).  See PSARC 1999/566 and
110  * 1999/131 for details.  The cmpldev() function used to generate the 32-bit
111  * device number handles this check and assigns the proper value.
112  */
113 int mntfs_enabledev = 1;	/* enable old "dev=xxx" option */
114 
115 extern void vfs_mono_time(timespec_t *);
116 enum { MNTFS_FIRST, MNTFS_SECOND, MNTFS_NEITHER };
117 
118 /*
119  * Determine whether a field within a line from /etc/mnttab contains actual
120  * content or simply the marker string "-". This never applies to the time,
121  * therefore the delimiter must be a tab.
122  */
123 #define	MNTFS_REAL_FIELD(x)	(*(x) != '-' || *((x) + 1) != '\t')
124 
125 static int
126 mntfs_devsize(struct vfs *vfsp)
127 {
128 	dev32_t odev;
129 
130 	(void) cmpldev(&odev, vfsp->vfs_dev);
131 	return (snprintf(NULL, 0, "dev=%x", odev));
132 }
133 
134 static int
135 mntfs_devprint(struct vfs *vfsp, char *buf)
136 {
137 	dev32_t odev;
138 
139 	(void) cmpldev(&odev, vfsp->vfs_dev);
140 	return (snprintf(buf, MAX_MNTOPT_STR, "dev=%x", odev));
141 }
142 
143 /* Identify which, if either, of two supplied timespec structs is newer. */
144 static int
145 mntfs_newest(timespec_t *a, timespec_t *b)
146 {
147 	if (a->tv_sec == b->tv_sec &&
148 	    a->tv_nsec == b->tv_nsec) {
149 		return (MNTFS_NEITHER);
150 	} else if (b->tv_sec > a->tv_sec ||
151 	    (b->tv_sec == a->tv_sec &&
152 	    b->tv_nsec > a->tv_nsec)) {
153 		return (MNTFS_SECOND);
154 	} else {
155 		return (MNTFS_FIRST);
156 	}
157 }
158 
159 static int
160 mntfs_optsize(struct vfs *vfsp)
161 {
162 	int i, size = 0;
163 	mntopt_t *mop;
164 
165 	for (i = 0; i < vfsp->vfs_mntopts.mo_count; i++) {
166 		mop = &vfsp->vfs_mntopts.mo_list[i];
167 		if (mop->mo_flags & MO_NODISPLAY)
168 			continue;
169 		if (mop->mo_flags & MO_SET) {
170 			if (size)
171 				size++; /* space for comma */
172 			size += strlen(mop->mo_name);
173 			/*
174 			 * count option value if there is one
175 			 */
176 			if (mop->mo_arg != NULL) {
177 				size += strlen(mop->mo_arg) + 1;
178 			}
179 		}
180 	}
181 	if (vfsp->vfs_zone != NULL && vfsp->vfs_zone != global_zone) {
182 		/*
183 		 * Add space for "zone=<zone_name>" if required.
184 		 */
185 		if (size)
186 			size++;	/* space for comma */
187 		size += sizeof ("zone=") - 1;
188 		size += strlen(vfsp->vfs_zone->zone_name);
189 	}
190 	if (mntfs_enabledev) {
191 		if (size != 0)
192 			size++; /* space for comma */
193 		size += mntfs_devsize(vfsp);
194 	}
195 	if (size == 0)
196 		size = strlen("-");
197 	return (size);
198 }
199 
200 static int
201 mntfs_optprint(struct vfs *vfsp, char *buf)
202 {
203 	int i, optinbuf = 0;
204 	mntopt_t *mop;
205 	char *origbuf = buf;
206 
207 	for (i = 0; i < vfsp->vfs_mntopts.mo_count; i++) {
208 		mop = &vfsp->vfs_mntopts.mo_list[i];
209 		if (mop->mo_flags & MO_NODISPLAY)
210 			continue;
211 		if (mop->mo_flags & MO_SET) {
212 			if (optinbuf)
213 				*buf++ = ',';
214 			else
215 				optinbuf = 1;
216 			buf += snprintf(buf, MAX_MNTOPT_STR,
217 			    "%s", mop->mo_name);
218 			/*
219 			 * print option value if there is one
220 			 */
221 			if (mop->mo_arg != NULL) {
222 				buf += snprintf(buf, MAX_MNTOPT_STR, "=%s",
223 				    mop->mo_arg);
224 			}
225 		}
226 	}
227 	if (vfsp->vfs_zone != NULL && vfsp->vfs_zone != global_zone) {
228 		if (optinbuf)
229 			*buf++ = ',';
230 		else
231 			optinbuf = 1;
232 		buf += snprintf(buf, MAX_MNTOPT_STR, "zone=%s",
233 		    vfsp->vfs_zone->zone_name);
234 	}
235 	if (mntfs_enabledev) {
236 		if (optinbuf++)
237 			*buf++ = ',';
238 		buf += mntfs_devprint(vfsp, buf);
239 	}
240 	if (!optinbuf) {
241 		buf += snprintf(buf, MAX_MNTOPT_STR, "-");
242 	}
243 	return (buf - origbuf);
244 }
245 
246 void
247 mntfs_populate_text(vfs_t *vfsp, zone_t *zonep, mntelem_t *elemp)
248 {
249 	struct extmnttab *tabp = &elemp->mnte_tab;
250 	const char *resource, *mntpt;
251 	char *cp = elemp->mnte_text;
252 	mntpt = refstr_value(vfsp->vfs_mntpt);
253 	resource = refstr_value(vfsp->vfs_resource);
254 
255 	tabp->mnt_special = 0;
256 	if (resource != NULL && resource[0] != '\0') {
257 		if (resource[0] != '/') {
258 			cp += snprintf(cp, MAXPATHLEN, "%s\t", resource);
259 		} else if (!ZONE_PATH_VISIBLE(resource, zonep)) {
260 			/*
261 			 * Use the mount point as the resource.
262 			 */
263 			cp += snprintf(cp, MAXPATHLEN, "%s\t",
264 			    ZONE_PATH_TRANSLATE(mntpt, zonep));
265 		} else {
266 			cp += snprintf(cp, MAXPATHLEN, "%s\t",
267 			    ZONE_PATH_TRANSLATE(resource, zonep));
268 		}
269 	} else {
270 		cp += snprintf(cp, MAXPATHLEN, "-\t");
271 	}
272 
273 	tabp->mnt_mountp = (char *)(cp - elemp->mnte_text);
274 	if (mntpt != NULL && mntpt[0] != '\0') {
275 		/*
276 		 * We know the mount point is visible from within the zone,
277 		 * otherwise it wouldn't be on the zone's vfs list.
278 		 */
279 		cp += snprintf(cp, MAXPATHLEN, "%s\t",
280 		    ZONE_PATH_TRANSLATE(mntpt, zonep));
281 	} else {
282 		cp += snprintf(cp, MAXPATHLEN, "-\t");
283 	}
284 
285 	tabp->mnt_fstype = (char *)(cp - elemp->mnte_text);
286 	cp += snprintf(cp, MAXPATHLEN, "%s\t",
287 	    vfssw[vfsp->vfs_fstype].vsw_name);
288 
289 	tabp->mnt_mntopts = (char *)(cp - elemp->mnte_text);
290 	cp += mntfs_optprint(vfsp, cp);
291 	*cp++ = '\t';
292 
293 	tabp->mnt_time = (char *)(cp - elemp->mnte_text);
294 	cp += snprintf(cp, MAX_MNTOPT_STR, "%ld", vfsp->vfs_mtime);
295 	*cp++ = '\n'; /* over-write snprintf's trailing null-byte */
296 
297 	tabp->mnt_major = getmajor(vfsp->vfs_dev);
298 	tabp->mnt_minor = getminor(vfsp->vfs_dev);
299 
300 	elemp->mnte_text_size = cp - elemp->mnte_text;
301 	elemp->mnte_vfs_ctime = vfsp->vfs_hrctime;
302 	elemp->mnte_hidden = vfsp->vfs_flag & VFS_NOMNTTAB;
303 }
304 
305 /* Determine the length of the /etc/mnttab entry for this vfs_t. */
306 static size_t
307 mntfs_text_len(vfs_t *vfsp, zone_t *zone)
308 {
309 	size_t size = 0;
310 	const char *resource, *mntpt;
311 	size_t mntsize;
312 
313 	mntpt = refstr_value(vfsp->vfs_mntpt);
314 	if (mntpt != NULL && mntpt[0] != '\0') {
315 		mntsize = strlen(ZONE_PATH_TRANSLATE(mntpt, zone)) + 1;
316 	} else {
317 		mntsize = 2;	/* "-\t" */
318 	}
319 	size += mntsize;
320 
321 	resource = refstr_value(vfsp->vfs_resource);
322 	if (resource != NULL && resource[0] != '\0') {
323 		if (resource[0] != '/') {
324 			size += strlen(resource) + 1;
325 		} else if (!ZONE_PATH_VISIBLE(resource, zone)) {
326 			/*
327 			 * Same as the zone's view of the mount point.
328 			 */
329 			size += mntsize;
330 		} else {
331 			size += strlen(ZONE_PATH_TRANSLATE(resource, zone)) + 1;
332 		}
333 	} else {
334 		size += 2;	/* "-\t" */
335 	}
336 	size += strlen(vfssw[vfsp->vfs_fstype].vsw_name) + 1;
337 	size += mntfs_optsize(vfsp);
338 	size += snprintf(NULL, 0, "\t%ld\n", vfsp->vfs_mtime);
339 	return (size);
340 }
341 
342 /* Destroy the resources associated with a snapshot element. */
343 static void
344 mntfs_destroy_elem(mntelem_t *elemp)
345 {
346 	kmem_free(elemp->mnte_text, elemp->mnte_text_size);
347 	kmem_free(elemp, sizeof (mntelem_t));
348 }
349 
350 /*
351  * Return 1 if the given snapshot is in the range of the given element; return
352  * 0 otherwise.
353  */
354 static int
355 mntfs_elem_in_range(mntsnap_t *snapp, mntelem_t *elemp)
356 {
357 	timespec_t	*stimep = &snapp->mnts_time;
358 	timespec_t	*btimep = &elemp->mnte_birth;
359 	timespec_t	*dtimep = &elemp->mnte_death;
360 
361 	/*
362 	 * If a snapshot is in range of an element then the snapshot must have
363 	 * been created after the birth of the element, and either the element
364 	 * is still alive or it died after the snapshot was created.
365 	 */
366 	if (mntfs_newest(btimep, stimep) == MNTFS_SECOND &&
367 	    (MNTFS_ELEM_IS_ALIVE(elemp) ||
368 	    mntfs_newest(stimep, dtimep) == MNTFS_SECOND))
369 		return (1);
370 	else
371 		return (0);
372 }
373 
374 /*
375  * Return the next valid database element, after the one provided, for a given
376  * snapshot; return NULL if none exists. The caller must hold the zone's
377  * database lock as a reader before calling this function.
378  */
379 static mntelem_t *
380 mntfs_get_next_elem(mntsnap_t *snapp, mntelem_t *elemp)
381 {
382 	int show_hidden = snapp->mnts_flags & MNTS_SHOWHIDDEN;
383 
384 	do {
385 		elemp = elemp->mnte_next;
386 	} while (elemp &&
387 	    (!mntfs_elem_in_range(snapp, elemp) ||
388 	    (!show_hidden && elemp->mnte_hidden)));
389 	return (elemp);
390 }
391 
392 /*
393  * This function frees the resources associated with a mntsnap_t. It walks
394  * through the database, decrementing the reference count of any element that
395  * satisfies the snapshot. If the reference count of an element becomes zero
396  * then it is removed from the database.
397  */
398 static void
399 mntfs_freesnap(mntnode_t *mnp, mntsnap_t *snapp)
400 {
401 	zone_t *zonep = MTOD(mnp)->mnt_zone_ref.zref_zone;
402 	krwlock_t *dblockp = &zonep->zone_mntfs_db_lock;
403 	mntelem_t **elempp = &zonep->zone_mntfs_db;
404 	mntelem_t *elemp;
405 	int show_hidden = snapp->mnts_flags & MNTS_SHOWHIDDEN;
406 	size_t number_decremented = 0;
407 
408 	ASSERT(RW_WRITE_HELD(&mnp->mnt_contents));
409 
410 	/* Ignore an uninitialised snapshot. */
411 	if (snapp->mnts_nmnts == 0)
412 		return;
413 
414 	/* Drop the holds on any matching database elements. */
415 	rw_enter(dblockp, RW_WRITER);
416 	while ((elemp = *elempp) != NULL) {
417 		if (mntfs_elem_in_range(snapp, elemp) &&
418 		    (!elemp->mnte_hidden || show_hidden) &&
419 		    ++number_decremented && --elemp->mnte_refcnt == 0) {
420 			if ((*elempp = elemp->mnte_next) != NULL)
421 				(*elempp)->mnte_prev = elemp->mnte_prev;
422 			mntfs_destroy_elem(elemp);
423 		} else {
424 			elempp = &elemp->mnte_next;
425 		}
426 	}
427 	rw_exit(dblockp);
428 	ASSERT(number_decremented == snapp->mnts_nmnts);
429 
430 	/* Clear the snapshot data. */
431 	bzero(snapp, sizeof (mntsnap_t));
432 }
433 
434 /* Insert the new database element newp after the existing element prevp. */
435 static void
436 mntfs_insert_after(mntelem_t *newp, mntelem_t *prevp)
437 {
438 	newp->mnte_prev = prevp;
439 	newp->mnte_next = prevp->mnte_next;
440 	prevp->mnte_next = newp;
441 	if (newp->mnte_next != NULL)
442 		newp->mnte_next->mnte_prev = newp;
443 }
444 
445 /* Create and return a copy of a given database element. */
446 static mntelem_t *
447 mntfs_copy(mntelem_t *origp)
448 {
449 	mntelem_t *copyp;
450 
451 	copyp = kmem_zalloc(sizeof (mntelem_t), KM_SLEEP);
452 	copyp->mnte_vfs_ctime = origp->mnte_vfs_ctime;
453 	copyp->mnte_text_size = origp->mnte_text_size;
454 	copyp->mnte_text = kmem_alloc(copyp->mnte_text_size, KM_SLEEP);
455 	bcopy(origp->mnte_text, copyp->mnte_text, copyp->mnte_text_size);
456 	copyp->mnte_tab = origp->mnte_tab;
457 	copyp->mnte_hidden = origp->mnte_hidden;
458 
459 	return (copyp);
460 }
461 
462 /*
463  * Compare two database elements and determine whether or not the vfs_t payload
464  * data of each are the same. Return 1 if so and 0 otherwise.
465  */
466 static int
467 mntfs_is_same_element(mntelem_t *a, mntelem_t *b)
468 {
469 	if (a->mnte_hidden == b->mnte_hidden &&
470 	    a->mnte_text_size == b->mnte_text_size &&
471 	    bcmp(a->mnte_text, b->mnte_text, a->mnte_text_size) == 0 &&
472 	    bcmp(&a->mnte_tab, &b->mnte_tab, sizeof (struct extmnttab)) == 0)
473 		return (1);
474 	else
475 		return (0);
476 }
477 
478 /*
479  * mntfs_snapshot() updates the database, creating it if necessary, so that it
480  * accurately reflects the state of the in-kernel mnttab. It also increments
481  * the reference count on all database elements that correspond to currently-
482  * mounted resources. Finally, it initialises the appropriate snapshot
483  * structure.
484  *
485  * Each vfs_t is given a high-resolution time stamp, for the benefit of mntfs,
486  * when it is inserted into the in-kernel mnttab. This time stamp is copied into
487  * the corresponding database element when it is created, allowing the element
488  * and the vfs_t to be identified as a pair. It is possible that some file
489  * systems may make unadvertised changes to, for example, a resource's mount
490  * options. Therefore, in order to determine whether a database element is an
491  * up-to-date representation of a given vfs_t, it is compared with a temporary
492  * element generated for this purpose. Although less efficient, this is safer
493  * than implementing an mtime for a vfs_t.
494  *
495  * Some mounted resources are marked as "hidden" with a VFS_NOMNTTAB flag. These
496  * are considered invisible unless the user has already set the MNT_SHOWHIDDEN
497  * flag in the vnode using the MNTIOC_SHOWHIDDEN ioctl.
498  */
499 static void
500 mntfs_snapshot(mntnode_t *mnp, mntsnap_t *snapp)
501 {
502 	mntdata_t	*mnd = MTOD(mnp);
503 	zone_t		*zonep = mnd->mnt_zone_ref.zref_zone;
504 	int		is_global_zone = (zonep == global_zone);
505 	int		show_hidden = mnp->mnt_flags & MNT_SHOWHIDDEN;
506 	vfs_t		*vfsp, *firstvfsp, *lastvfsp;
507 	vfs_t		dummyvfs;
508 	vfs_t		*dummyvfsp = NULL;
509 	krwlock_t	*dblockp = &zonep->zone_mntfs_db_lock;
510 	mntelem_t	**headpp = &zonep->zone_mntfs_db;
511 	mntelem_t	*elemp;
512 	mntelem_t	*prevp = NULL;
513 	int		order;
514 	mntelem_t	*tempelemp;
515 	mntelem_t	*newp;
516 	mntelem_t	*firstp = NULL;
517 	size_t		nmnts = 0;
518 	size_t		total_text_size = 0;
519 	size_t		normal_text_size = 0;
520 	int		insert_before;
521 	timespec_t	last_mtime;
522 	size_t		entry_length, new_entry_length;
523 
524 
525 	ASSERT(RW_WRITE_HELD(&mnp->mnt_contents));
526 	vfs_list_read_lock();
527 	vfs_mnttab_modtime(&last_mtime);
528 
529 	/*
530 	 * If this snapshot already exists then we must have been asked to
531 	 * rewind the file, i.e. discard the snapshot and create a new one in
532 	 * its place. In this case we first see if the in-kernel mnttab has
533 	 * advertised a change; if not then we simply reinitialise the metadata.
534 	 */
535 	if (snapp->mnts_nmnts) {
536 		if (mntfs_newest(&last_mtime, &snapp->mnts_last_mtime) ==
537 		    MNTFS_NEITHER) {
538 			/*
539 			 * An unchanged mtime is no guarantee that the
540 			 * in-kernel mnttab is unchanged; for example, a
541 			 * concurrent remount may be between calls to
542 			 * vfs_setmntopt_nolock() and vfs_mnttab_modtimeupd().
543 			 * It follows that the database may have changed, and
544 			 * in particular that some elements in this snapshot
545 			 * may have been killed by another call to
546 			 * mntfs_snapshot(). It is therefore not merely
547 			 * unnecessary to update the snapshot's time but in
548 			 * fact dangerous; it needs to be left alone.
549 			 */
550 			snapp->mnts_next = snapp->mnts_first;
551 			snapp->mnts_flags &= ~MNTS_REWIND;
552 			snapp->mnts_foffset = snapp->mnts_ieoffset = 0;
553 			vfs_list_unlock();
554 			return;
555 		} else {
556 			mntfs_freesnap(mnp, snapp);
557 		}
558 	}
559 
560 	/*
561 	 * Create a temporary database element. For each vfs_t, the temporary
562 	 * element will be populated with the corresponding text. If the vfs_t
563 	 * does not have a corresponding element within the database, or if
564 	 * there is such an element but it is stale, a copy of the temporary
565 	 * element is inserted into the database at the appropriate location.
566 	 */
567 	tempelemp = kmem_alloc(sizeof (mntelem_t), KM_SLEEP);
568 	entry_length = MNT_LINE_MAX;
569 	tempelemp->mnte_text = kmem_alloc(entry_length, KM_SLEEP);
570 
571 	/* Find the first and last vfs_t for the given zone. */
572 	if (is_global_zone) {
573 		firstvfsp = rootvfs;
574 		lastvfsp = firstvfsp->vfs_prev;
575 	} else {
576 		firstvfsp = zonep->zone_vfslist;
577 		/*
578 		 * If there isn't already a vfs_t for root then we create a
579 		 * dummy which will be used as the head of the list (which will
580 		 * therefore no longer be circular).
581 		 */
582 		if (firstvfsp == NULL ||
583 		    strcmp(refstr_value(firstvfsp->vfs_mntpt),
584 		    zonep->zone_rootpath) != 0) {
585 			/*
586 			 * The zone's vfs_ts will have mount points relative to
587 			 * the zone's root path. The vfs_t for the zone's
588 			 * root file system would therefore have a mount point
589 			 * equal to the zone's root path. Since the zone's root
590 			 * path isn't a mount point, we copy the vfs_t of the
591 			 * zone's root vnode, and provide it with a fake mount
592 			 * and resource. However, if the zone's root is a
593 			 * zfs dataset, use the dataset name as the resource.
594 			 *
595 			 * Note that by cloning another vfs_t we also acquire
596 			 * its high-resolution ctime. This might appear to
597 			 * violate the requirement that the ctimes in the list
598 			 * of vfs_ts are unique and monotonically increasing;
599 			 * this is not the case. The dummy vfs_t appears in only
600 			 * a non-global zone's vfs_t list, where the cloned
601 			 * vfs_t would not ordinarily be visible; the ctimes are
602 			 * therefore unique. The zone's root path must be
603 			 * available before the zone boots, and so its root
604 			 * vnode's vfs_t's ctime must be lower than those of any
605 			 * resources subsequently mounted by the zone. The
606 			 * ctimes are therefore monotonically increasing.
607 			 */
608 			dummyvfs = *zonep->zone_rootvp->v_vfsp;
609 			dummyvfs.vfs_mntpt = refstr_alloc(zonep->zone_rootpath);
610 			if (strcmp(vfssw[dummyvfs.vfs_fstype].vsw_name, "zfs")
611 			    != 0)
612 				dummyvfs.vfs_resource = dummyvfs.vfs_mntpt;
613 			dummyvfsp = &dummyvfs;
614 			if (firstvfsp == NULL) {
615 				lastvfsp = dummyvfsp;
616 			} else {
617 				lastvfsp = firstvfsp->vfs_zone_prev;
618 				dummyvfsp->vfs_zone_next = firstvfsp;
619 			}
620 			firstvfsp = dummyvfsp;
621 		} else {
622 			lastvfsp = firstvfsp->vfs_zone_prev;
623 		}
624 	}
625 
626 	/*
627 	 * Now walk through all the vfs_ts for this zone. For each one, find the
628 	 * corresponding database element, creating it first if necessary, and
629 	 * increment its reference count.
630 	 */
631 	rw_enter(dblockp, RW_WRITER);
632 	elemp = zonep->zone_mntfs_db;
633 	/* CSTYLED */
634 	for (vfsp = firstvfsp;;
635 	    vfsp = is_global_zone ? vfsp->vfs_next : vfsp->vfs_zone_next) {
636 		DTRACE_PROBE1(new__vfs, vfs_t *, vfsp);
637 		/* Consider only visible entries. */
638 		if ((vfsp->vfs_flag & VFS_NOMNTTAB) == 0 || show_hidden) {
639 			/*
640 			 * Walk through the existing database looking for either
641 			 * an element that matches the current vfs_t, or for the
642 			 * correct place in which to insert a new element.
643 			 */
644 			insert_before = 0;
645 			for (; elemp; prevp = elemp, elemp = elemp->mnte_next) {
646 				DTRACE_PROBE1(considering__elem, mntelem_t *,
647 				    elemp);
648 
649 				/* Compare the vfs_t with the element. */
650 				order = mntfs_newest(&elemp->mnte_vfs_ctime,
651 				    &vfsp->vfs_hrctime);
652 
653 				/*
654 				 * If we encounter a database element newer than
655 				 * this vfs_t then we've stepped over a gap
656 				 * where the element for this vfs_t must be
657 				 * inserted.
658 				 */
659 				if (order == MNTFS_FIRST) {
660 					insert_before = 1;
661 					break;
662 				}
663 
664 				/* Dead elements no longer interest us. */
665 				if (MNTFS_ELEM_IS_DEAD(elemp))
666 					continue;
667 
668 				/*
669 				 * If the time stamps are the same then the
670 				 * element is potential match for the vfs_t,
671 				 * although it may later prove to be stale.
672 				 */
673 				if (order == MNTFS_NEITHER)
674 					break;
675 
676 				/*
677 				 * This element must be older than the vfs_t.
678 				 * It must, therefore, correspond to a vfs_t
679 				 * that has been unmounted. Since the element is
680 				 * still alive, we kill it if it is visible.
681 				 */
682 				if (!elemp->mnte_hidden || show_hidden)
683 					vfs_mono_time(&elemp->mnte_death);
684 			}
685 			DTRACE_PROBE2(possible__match, vfs_t *, vfsp,
686 			    mntelem_t *, elemp);
687 
688 			/* Create a new database element if required. */
689 			new_entry_length = mntfs_text_len(vfsp, zonep);
690 			if (new_entry_length > entry_length) {
691 				kmem_free(tempelemp->mnte_text, entry_length);
692 				tempelemp->mnte_text =
693 				    kmem_alloc(new_entry_length, KM_SLEEP);
694 				entry_length = new_entry_length;
695 			}
696 			mntfs_populate_text(vfsp, zonep, tempelemp);
697 			ASSERT(tempelemp->mnte_text_size == new_entry_length);
698 			if (elemp == NULL) {
699 				/*
700 				 * We ran off the end of the database. Insert a
701 				 * new element at the end.
702 				 */
703 				newp = mntfs_copy(tempelemp);
704 				vfs_mono_time(&newp->mnte_birth);
705 				if (prevp) {
706 					mntfs_insert_after(newp, prevp);
707 				} else {
708 					newp->mnte_next = NULL;
709 					newp->mnte_prev = NULL;
710 					ASSERT(*headpp == NULL);
711 					*headpp = newp;
712 				}
713 				elemp = newp;
714 			} else if (insert_before) {
715 				/*
716 				 * Insert a new element before the current one.
717 				 */
718 				newp = mntfs_copy(tempelemp);
719 				vfs_mono_time(&newp->mnte_birth);
720 				if (prevp) {
721 					mntfs_insert_after(newp, prevp);
722 				} else {
723 					newp->mnte_next = elemp;
724 					newp->mnte_prev = NULL;
725 					elemp->mnte_prev = newp;
726 					ASSERT(*headpp == elemp);
727 					*headpp = newp;
728 				}
729 				elemp = newp;
730 			} else if (!mntfs_is_same_element(elemp, tempelemp)) {
731 				/*
732 				 * The element corresponds to the vfs_t, but the
733 				 * vfs_t has changed; it must have been
734 				 * remounted. Kill the old element and insert a
735 				 * new one after it.
736 				 */
737 				vfs_mono_time(&elemp->mnte_death);
738 				newp = mntfs_copy(tempelemp);
739 				vfs_mono_time(&newp->mnte_birth);
740 				mntfs_insert_after(newp, elemp);
741 				elemp = newp;
742 			}
743 
744 			/* We've found the corresponding element. Hold it. */
745 			DTRACE_PROBE1(incrementing, mntelem_t *, elemp);
746 			elemp->mnte_refcnt++;
747 
748 			/*
749 			 * Update the parameters used to initialise the
750 			 * snapshot.
751 			 */
752 			nmnts++;
753 			total_text_size += elemp->mnte_text_size;
754 			if (!elemp->mnte_hidden)
755 				normal_text_size += elemp->mnte_text_size;
756 			if (!firstp)
757 				firstp = elemp;
758 
759 			prevp = elemp;
760 			elemp = elemp->mnte_next;
761 		}
762 
763 		if (vfsp == lastvfsp)
764 			break;
765 	}
766 
767 	/*
768 	 * Any remaining visible database elements that are still alive must be
769 	 * killed now, because their corresponding vfs_ts must have been
770 	 * unmounted.
771 	 */
772 	for (; elemp; elemp = elemp->mnte_next) {
773 		if (MNTFS_ELEM_IS_ALIVE(elemp) &&
774 		    (!elemp->mnte_hidden || show_hidden))
775 			vfs_mono_time(&elemp->mnte_death);
776 	}
777 
778 	/* Initialise the snapshot. */
779 	vfs_mono_time(&snapp->mnts_time);
780 	snapp->mnts_last_mtime = last_mtime;
781 	snapp->mnts_first = snapp->mnts_next = firstp;
782 	snapp->mnts_flags = show_hidden ? MNTS_SHOWHIDDEN : 0;
783 	snapp->mnts_nmnts = nmnts;
784 	snapp->mnts_text_size = total_text_size;
785 	snapp->mnts_foffset = snapp->mnts_ieoffset = 0;
786 
787 	/*
788 	 * Record /etc/mnttab's current size and mtime for possible future use
789 	 * by mntgetattr().
790 	 */
791 	mnd->mnt_size = normal_text_size;
792 	mnd->mnt_mtime = last_mtime;
793 	if (show_hidden) {
794 		mnd->mnt_hidden_size = total_text_size;
795 		mnd->mnt_hidden_mtime = last_mtime;
796 	}
797 
798 	/* Clean up. */
799 	rw_exit(dblockp);
800 	vfs_list_unlock();
801 	if (dummyvfsp != NULL)
802 		refstr_rele(dummyvfsp->vfs_mntpt);
803 	kmem_free(tempelemp->mnte_text, entry_length);
804 	kmem_free(tempelemp, sizeof (mntelem_t));
805 }
806 
807 /*
808  * Public function to convert vfs_mntopts into a string.
809  * A buffer of sufficient size is allocated, which is returned via bufp,
810  * and whose length is returned via lenp.
811  */
812 void
813 mntfs_getmntopts(struct vfs *vfsp, char **bufp, size_t *lenp)
814 {
815 	size_t len;
816 	char *buf;
817 
818 	vfs_list_read_lock();
819 
820 	len = mntfs_optsize(vfsp) + 1;
821 	buf = kmem_alloc(len, KM_NOSLEEP);
822 	if (buf == NULL) {
823 		*bufp = NULL;
824 		vfs_list_unlock();
825 		return;
826 	}
827 	buf[len - 1] = '\0';
828 	(void) mntfs_optprint(vfsp, buf);
829 	ASSERT(buf[len - 1] == '\0');
830 
831 	vfs_list_unlock();
832 	*bufp = buf;
833 	*lenp = len;
834 }
835 
836 /* ARGSUSED */
837 static int
838 mntopen(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
839 {
840 	vnode_t *vp = *vpp;
841 	mntnode_t *nmnp;
842 
843 	/*
844 	 * Not allowed to open for writing, return error.
845 	 */
846 	if (flag & FWRITE)
847 		return (EPERM);
848 	/*
849 	 * Create a new mnt/vnode for each open, this will give us a handle to
850 	 * hang the snapshot on.
851 	 */
852 	nmnp = mntgetnode(vp);
853 
854 	*vpp = MTOV(nmnp);
855 	atomic_add_32(&MTOD(nmnp)->mnt_nopen, 1);
856 	VN_RELE(vp);
857 	return (0);
858 }
859 
860 /* ARGSUSED */
861 static int
862 mntclose(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
863 	caller_context_t *ct)
864 {
865 	mntnode_t *mnp = VTOM(vp);
866 
867 	/* Clean up any locks or shares held by the current process */
868 	cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
869 	cleanshares(vp, ttoproc(curthread)->p_pid);
870 
871 	if (count > 1)
872 		return (0);
873 	if (vp->v_count == 1) {
874 		rw_enter(&mnp->mnt_contents, RW_WRITER);
875 		mntfs_freesnap(mnp, &mnp->mnt_read);
876 		mntfs_freesnap(mnp, &mnp->mnt_ioctl);
877 		rw_exit(&mnp->mnt_contents);
878 		atomic_add_32(&MTOD(mnp)->mnt_nopen, -1);
879 	}
880 	return (0);
881 }
882 
883 /* ARGSUSED */
884 static int
885 mntread(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cred, caller_context_t *ct)
886 {
887 	mntnode_t *mnp = VTOM(vp);
888 	zone_t *zonep = MTOD(mnp)->mnt_zone_ref.zref_zone;
889 	mntsnap_t *snapp = &mnp->mnt_read;
890 	off_t off = uio->uio_offset;
891 	size_t len = uio->uio_resid;
892 	char *bufferp;
893 	size_t available, copylen;
894 	size_t written = 0;
895 	mntelem_t *elemp;
896 	krwlock_t *dblockp = &zonep->zone_mntfs_db_lock;
897 	int error = 0;
898 	off_t	ieoffset;
899 
900 	rw_enter(&mnp->mnt_contents, RW_WRITER);
901 	if (snapp->mnts_nmnts == 0 || (off == (off_t)0))
902 		mntfs_snapshot(mnp, snapp);
903 
904 	if ((size_t)(off + len) > snapp->mnts_text_size)
905 		len = snapp->mnts_text_size - off;
906 
907 	if (off < 0 || len > snapp->mnts_text_size) {
908 		rw_exit(&mnp->mnt_contents);
909 		return (EFAULT);
910 	}
911 
912 	if (len == 0) {
913 		rw_exit(&mnp->mnt_contents);
914 		return (0);
915 	}
916 
917 	/*
918 	 * For the file offset provided, locate the corresponding database
919 	 * element and calculate the corresponding offset within its text. If
920 	 * the file offset is the same as that reached during the last read(2)
921 	 * then use the saved element and intra-element offset.
922 	 */
923 	rw_enter(dblockp, RW_READER);
924 	if (off == 0 || (off == snapp->mnts_foffset)) {
925 		elemp = snapp->mnts_next;
926 		ieoffset = snapp->mnts_ieoffset;
927 	} else {
928 		off_t total_off;
929 		/*
930 		 * Find the element corresponding to the requested file offset
931 		 * by walking through the database and summing the text sizes
932 		 * of the individual elements. If the requested file offset is
933 		 * greater than that reached on the last visit then we can start
934 		 * at the last seen element; otherwise, we have to start at the
935 		 * beginning.
936 		 */
937 		if (off > snapp->mnts_foffset) {
938 			elemp = snapp->mnts_next;
939 			total_off = snapp->mnts_foffset - snapp->mnts_ieoffset;
940 		} else {
941 			elemp = snapp->mnts_first;
942 			total_off = 0;
943 		}
944 		while (off > total_off + elemp->mnte_text_size) {
945 			total_off += elemp->mnte_text_size;
946 			elemp = mntfs_get_next_elem(snapp, elemp);
947 			ASSERT(elemp != NULL);
948 		}
949 		/* Calculate the intra-element offset. */
950 		if (off > total_off)
951 			ieoffset = off - total_off;
952 		else
953 			ieoffset = 0;
954 	}
955 
956 	/*
957 	 * Create a buffer and populate it with the text from successive
958 	 * database elements until it is full.
959 	 */
960 	bufferp = kmem_alloc(len, KM_SLEEP);
961 	while (written < len) {
962 		available = elemp->mnte_text_size - ieoffset;
963 		copylen = MIN(len - written, available);
964 		bcopy(elemp->mnte_text + ieoffset, bufferp + written, copylen);
965 		written += copylen;
966 		if (copylen == available) {
967 			elemp = mntfs_get_next_elem(snapp, elemp);
968 			ASSERT(elemp != NULL || written == len);
969 			ieoffset = 0;
970 		} else {
971 			ieoffset += copylen;
972 		}
973 	}
974 	rw_exit(dblockp);
975 
976 	/*
977 	 * Write the populated buffer, update the snapshot's state if
978 	 * successful and then advertise our read.
979 	 */
980 	error = uiomove(bufferp, len, UIO_READ, uio);
981 	if (error == 0) {
982 		snapp->mnts_next = elemp;
983 		snapp->mnts_foffset = off + len;
984 		snapp->mnts_ieoffset = ieoffset;
985 	}
986 	vfs_mnttab_readop();
987 	rw_exit(&mnp->mnt_contents);
988 
989 	/* Clean up. */
990 	kmem_free(bufferp, len);
991 	return (error);
992 }
993 
994 static int
995 mntgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
996 	caller_context_t *ct)
997 {
998 	int mask = vap->va_mask;
999 	int error;
1000 	mntnode_t *mnp = VTOM(vp);
1001 	timespec_t mtime, old_mtime;
1002 	size_t size, old_size;
1003 	mntdata_t *mntdata = MTOD(VTOM(vp));
1004 	mntsnap_t *rsnapp, *isnapp;
1005 	extern timespec_t vfs_mnttab_ctime;
1006 
1007 
1008 	/* AT_MODE, AT_UID and AT_GID are derived from the underlying file. */
1009 	if (mask & AT_MODE|AT_UID|AT_GID) {
1010 		if (error = VOP_GETATTR(mnp->mnt_mountvp, vap, flags, cr, ct))
1011 			return (error);
1012 	}
1013 
1014 	/*
1015 	 * There are some minor subtleties in the determination of
1016 	 * /etc/mnttab's size and mtime. We wish to avoid any condition in
1017 	 * which, in the vicinity of a change to the in-kernel mnttab, we
1018 	 * return an old value for one but a new value for the other. We cannot
1019 	 * simply hold vfslist for the entire calculation because we might need
1020 	 * to call mntfs_snapshot(), which calls vfs_list_read_lock().
1021 	 */
1022 	if (mask & AT_SIZE|AT_NBLOCKS) {
1023 		rw_enter(&mnp->mnt_contents, RW_WRITER);
1024 
1025 		vfs_list_read_lock();
1026 		vfs_mnttab_modtime(&mtime);
1027 		if (mnp->mnt_flags & MNT_SHOWHIDDEN) {
1028 			old_mtime = mntdata->mnt_hidden_mtime;
1029 			old_size = mntdata->mnt_hidden_size;
1030 		} else {
1031 			old_mtime = mntdata->mnt_mtime;
1032 			old_size = mntdata->mnt_size;
1033 		}
1034 		vfs_list_unlock();
1035 
1036 		rsnapp = &mnp->mnt_read;
1037 		isnapp = &mnp->mnt_ioctl;
1038 		if (rsnapp->mnts_nmnts || isnapp->mnts_nmnts) {
1039 			/*
1040 			 * The mntnode already has at least one snapshot from
1041 			 * which to take the size; the user will understand from
1042 			 * mnttab(4) that the current size of the in-kernel
1043 			 * mnttab is irrelevant.
1044 			 */
1045 			size = rsnapp->mnts_nmnts ? rsnapp->mnts_text_size :
1046 			    isnapp->mnts_text_size;
1047 		} else if (mntfs_newest(&mtime, &old_mtime) == MNTFS_NEITHER) {
1048 			/*
1049 			 * There is no existing valid snapshot but the in-kernel
1050 			 * mnttab has not changed since the time that the last
1051 			 * one was generated. Use the old file size; note that
1052 			 * it is guaranteed to be consistent with mtime, which
1053 			 * may be returned to the user later.
1054 			 */
1055 			size = old_size;
1056 		} else {
1057 			/*
1058 			 * There is no snapshot and the in-kernel mnttab has
1059 			 * changed since the last one was created. We generate a
1060 			 * new snapshot which we use for not only the size but
1061 			 * also the mtime, thereby ensuring that the two are
1062 			 * consistent.
1063 			 */
1064 			mntfs_snapshot(mnp, rsnapp);
1065 			size = rsnapp->mnts_text_size;
1066 			mtime = rsnapp->mnts_last_mtime;
1067 			mntfs_freesnap(mnp, rsnapp);
1068 		}
1069 
1070 		rw_exit(&mnp->mnt_contents);
1071 	} else if (mask & AT_ATIME|AT_MTIME) {
1072 		vfs_list_read_lock();
1073 		vfs_mnttab_modtime(&mtime);
1074 		vfs_list_unlock();
1075 	}
1076 
1077 	/* Always look like a regular file. */
1078 	if (mask & AT_TYPE)
1079 		vap->va_type = VREG;
1080 	/* Mode should basically be read only. */
1081 	if (mask & AT_MODE)
1082 		vap->va_mode &= 07444;
1083 	if (mask & AT_FSID)
1084 		vap->va_fsid = vp->v_vfsp->vfs_dev;
1085 	/* Nodeid is always ROOTINO. */
1086 	if (mask & AT_NODEID)
1087 		vap->va_nodeid = (ino64_t)MNTROOTINO;
1088 	/*
1089 	 * Set nlink to the number of open vnodes for mnttab info
1090 	 * plus one for existing.
1091 	 */
1092 	if (mask & AT_NLINK)
1093 		vap->va_nlink = mntdata->mnt_nopen + 1;
1094 	if (mask & AT_SIZE)
1095 		vap->va_size = size;
1096 	if (mask & AT_ATIME)
1097 		vap->va_atime = mtime;
1098 	if (mask & AT_MTIME)
1099 		vap->va_mtime = mtime;
1100 	if (mask & AT_CTIME)
1101 		vap->va_ctime = vfs_mnttab_ctime;
1102 	if (mask & AT_RDEV)
1103 		vap->va_rdev = 0;
1104 	if (mask & AT_BLKSIZE)
1105 		vap->va_blksize = DEV_BSIZE;
1106 	if (mask & AT_NBLOCKS)
1107 		vap->va_nblocks = btod(size);
1108 	if (mask & AT_SEQ)
1109 		vap->va_seq = 0;
1110 
1111 	return (0);
1112 }
1113 
1114 static int
1115 mntaccess(vnode_t *vp, int mode, int flags, cred_t *cr,
1116 	caller_context_t *ct)
1117 {
1118 	mntnode_t *mnp = VTOM(vp);
1119 
1120 	if (mode & (VWRITE|VEXEC))
1121 		return (EROFS);
1122 
1123 	/*
1124 	 * Do access check on the underlying directory vnode.
1125 	 */
1126 	return (VOP_ACCESS(mnp->mnt_mountvp, mode, flags, cr, ct));
1127 }
1128 
1129 
1130 /*
1131  * New /mntfs vnode required; allocate it and fill in most of the fields.
1132  */
1133 static mntnode_t *
1134 mntgetnode(vnode_t *dp)
1135 {
1136 	mntnode_t *mnp;
1137 	vnode_t *vp;
1138 
1139 	mnp = kmem_zalloc(sizeof (mntnode_t), KM_SLEEP);
1140 	mnp->mnt_vnode = vn_alloc(KM_SLEEP);
1141 	mnp->mnt_mountvp = VTOM(dp)->mnt_mountvp;
1142 	rw_init(&mnp->mnt_contents, NULL, RW_DEFAULT, NULL);
1143 	vp = MTOV(mnp);
1144 	vp->v_flag = VNOCACHE|VNOMAP|VNOSWAP|VNOMOUNT;
1145 	vn_setops(vp, mntvnodeops);
1146 	vp->v_vfsp = dp->v_vfsp;
1147 	vp->v_type = VREG;
1148 	vp->v_data = (caddr_t)mnp;
1149 
1150 	return (mnp);
1151 }
1152 
1153 /*
1154  * Free the storage obtained from mntgetnode().
1155  */
1156 static void
1157 mntfreenode(mntnode_t *mnp)
1158 {
1159 	vnode_t *vp = MTOV(mnp);
1160 
1161 	rw_destroy(&mnp->mnt_contents);
1162 	vn_invalid(vp);
1163 	vn_free(vp);
1164 	kmem_free(mnp, sizeof (*mnp));
1165 }
1166 
1167 
1168 /* ARGSUSED */
1169 static int
1170 mntfsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct)
1171 {
1172 	return (0);
1173 }
1174 
1175 /* ARGSUSED */
1176 static void
1177 mntinactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
1178 {
1179 	mntnode_t *mnp = VTOM(vp);
1180 
1181 	mntfreenode(mnp);
1182 }
1183 
1184 /*
1185  * lseek(2) is supported only to rewind the file by resetmnttab(3C). Rewinding
1186  * has a special meaning for /etc/mnttab: it forces mntfs to refresh the
1187  * snapshot at the next ioctl().
1188  *
1189  * mnttab(4) explains that "the snapshot...is taken any time a read(2) is
1190  * performed at offset 0". We therefore ignore the read snapshot here.
1191  */
1192 /* ARGSUSED */
1193 static int
1194 mntseek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct)
1195 {
1196 	mntnode_t *mnp = VTOM(vp);
1197 
1198 	if (*noffp == 0) {
1199 		rw_enter(&mnp->mnt_contents, RW_WRITER);
1200 		mnp->mnt_ioctl.mnts_flags |= MNTS_REWIND;
1201 		rw_exit(&mnp->mnt_contents);
1202 	}
1203 
1204 	return (0);
1205 }
1206 
1207 /*
1208  * Return the answer requested to poll().
1209  * POLLRDBAND will return when the mtime of the mnttab
1210  * information is newer than the latest one read for this open.
1211  */
1212 /* ARGSUSED */
1213 static int
1214 mntpoll(vnode_t *vp, short ev, int any, short *revp, pollhead_t **phpp,
1215 	caller_context_t *ct)
1216 {
1217 	mntnode_t *mnp = VTOM(vp);
1218 	mntsnap_t *snapp;
1219 
1220 	rw_enter(&mnp->mnt_contents, RW_READER);
1221 	if (mntfs_newest(&mnp->mnt_ioctl.mnts_last_mtime,
1222 	    &mnp->mnt_read.mnts_last_mtime) == MNTFS_FIRST)
1223 		snapp = &mnp->mnt_ioctl;
1224 	else
1225 		snapp = &mnp->mnt_read;
1226 
1227 	*revp = 0;
1228 	*phpp = (pollhead_t *)NULL;
1229 	if (ev & POLLIN)
1230 		*revp |= POLLIN;
1231 
1232 	if (ev & POLLRDNORM)
1233 		*revp |= POLLRDNORM;
1234 
1235 	if (ev & POLLRDBAND) {
1236 		vfs_mnttab_poll(&snapp->mnts_last_mtime, phpp);
1237 		if (*phpp == (pollhead_t *)NULL)
1238 			*revp |= POLLRDBAND;
1239 	}
1240 	rw_exit(&mnp->mnt_contents);
1241 
1242 	if (*revp || *phpp != NULL || any) {
1243 		return (0);
1244 	}
1245 	/*
1246 	 * If someone is polling an unsupported poll events (e.g.
1247 	 * POLLOUT, POLLPRI, etc.), just return POLLERR revents.
1248 	 * That way we will ensure that we don't return a 0
1249 	 * revents with a NULL pollhead pointer.
1250 	 */
1251 	*revp = POLLERR;
1252 	return (0);
1253 }
1254 
1255 /*
1256  * mntfs_same_word() returns 1 if two words are the same in the context of
1257  * MNTIOC_GETMNTANY and 0 otherwise.
1258  *
1259  * worda is a memory address that lies somewhere in the buffer bufa; it cannot
1260  * be NULL since this is used to indicate to getmntany(3C) that the user does
1261  * not wish to match a particular field. The text to which worda points is
1262  * supplied by the user; if it is not null-terminated then it cannot match.
1263  *
1264  * Buffer bufb contains a line from /etc/mnttab, in which the fields are
1265  * delimited by tab or new-line characters. offb is the offset of the second
1266  * word within this buffer.
1267  *
1268  * mntfs_same_word() returns 1 if the words are the same and 0 otherwise.
1269  */
1270 int
1271 mntfs_same_word(char *worda, char *bufa, size_t sizea, off_t offb, char *bufb,
1272     size_t sizeb)
1273 {
1274 	char *wordb = bufb + offb;
1275 	int bytes_remaining;
1276 
1277 	ASSERT(worda != NULL);
1278 
1279 	bytes_remaining = MIN(((bufa + sizea) - worda),
1280 	    ((bufb + sizeb) - wordb));
1281 	while (bytes_remaining && *worda == *wordb) {
1282 		worda++;
1283 		wordb++;
1284 		bytes_remaining--;
1285 	}
1286 	if (bytes_remaining &&
1287 	    *worda == '\0' && (*wordb == '\t' || *wordb == '\n'))
1288 		return (1);
1289 	else
1290 		return (0);
1291 }
1292 
1293 /*
1294  * mntfs_special_info_string() returns which, if either, of VBLK or VCHR
1295  * corresponds to a supplied path. If the path is a special device then the
1296  * function optionally sets the major and minor numbers.
1297  */
1298 vtype_t
1299 mntfs_special_info_string(char *path, uint_t *major, uint_t *minor, cred_t *cr)
1300 {
1301 	vattr_t vattr;
1302 	vnode_t *vp;
1303 	vtype_t type;
1304 	int error;
1305 
1306 	if (path == NULL || *path != '/' ||
1307 	    lookupnameat(path + 1, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp, rootdir))
1308 		return (0);
1309 
1310 	vattr.va_mask = AT_TYPE | AT_RDEV;
1311 	error = VOP_GETATTR(vp, &vattr, ATTR_REAL, cr, NULL);
1312 	VN_RELE(vp);
1313 
1314 	if (error == 0 && ((type = vattr.va_type) == VBLK || type == VCHR)) {
1315 		if (major && minor) {
1316 			*major = getmajor(vattr.va_rdev);
1317 			*minor = getminor(vattr.va_rdev);
1318 		}
1319 		return (type);
1320 	} else {
1321 		return (0);
1322 	}
1323 }
1324 
1325 /*
1326  * mntfs_special_info_element() extracts the name of the mounted resource
1327  * for a given element and copies it into a null-terminated string, which it
1328  * then passes to mntfs_special_info_string().
1329  */
1330 vtype_t
1331 mntfs_special_info_element(mntelem_t *elemp, cred_t *cr)
1332 {
1333 	char *newpath;
1334 	vtype_t type;
1335 
1336 	newpath = kmem_alloc(elemp->mnte_text_size, KM_SLEEP);
1337 	bcopy(elemp->mnte_text, newpath, (off_t)(elemp->mnte_tab.mnt_mountp));
1338 	*(newpath + (off_t)elemp->mnte_tab.mnt_mountp - 1) = '\0';
1339 	type = mntfs_special_info_string(newpath, NULL, NULL, cr);
1340 	kmem_free(newpath, elemp->mnte_text_size);
1341 
1342 	return (type);
1343 }
1344 
1345 /*
1346  * Convert an address that points to a byte within a user buffer into an
1347  * address that points to the corresponding offset within a kernel buffer. If
1348  * the user address is NULL then make no conversion. If the address does not
1349  * lie within the buffer then reset it to NULL.
1350  */
1351 char *
1352 mntfs_import_addr(char *uaddr, char *ubufp, char *kbufp, size_t bufsize)
1353 {
1354 	if (uaddr < ubufp || uaddr >= ubufp + bufsize)
1355 		return (NULL);
1356 	else
1357 		return (kbufp + (uaddr - ubufp));
1358 }
1359 
1360 /*
1361  * These 32-bit versions are to support STRUCT_DECL(9F) etc. in
1362  * mntfs_copyout_element() and mntioctl().
1363  */
1364 #ifdef _SYSCALL32_IMPL
1365 typedef struct extmnttab32 {
1366 	uint32_t	mnt_special;
1367 	uint32_t	mnt_mountp;
1368 	uint32_t	mnt_fstype;
1369 	uint32_t	mnt_mntopts;
1370 	uint32_t	mnt_time;
1371 	uint_t		mnt_major;
1372 	uint_t		mnt_minor;
1373 } extmnttab32_t;
1374 
1375 typedef struct mnttab32 {
1376 	uint32_t	mnt_special;
1377 	uint32_t	mnt_mountp;
1378 	uint32_t	mnt_fstype;
1379 	uint32_t	mnt_mntopts;
1380 	uint32_t	mnt_time;
1381 } mnttab32_t;
1382 
1383 struct mntentbuf32 {
1384 	uint32_t	mbuf_emp;
1385 	uint_t		mbuf_bufsize;
1386 	uint32_t	mbuf_buf;
1387 };
1388 #endif
1389 
1390 /*
1391  * mntfs_copyout_element() is common code for the MNTIOC_GETMNTENT,
1392  * MNTIOC_GETEXTMNTENT and MNTIOC_GETMNTANY ioctls. Having identifed the
1393  * database element desired by the user, this function copies out the text and
1394  * the pointers to the relevant userland addresses. It returns 0 on success
1395  * and non-zero otherwise.
1396  */
1397 int
1398 mntfs_copyout_elem(mntelem_t *elemp, struct extmnttab *uemp,
1399     char *ubufp, int cmd, int datamodel)
1400 {
1401 		STRUCT_DECL(extmnttab, ktab);
1402 		char *dbbufp = elemp->mnte_text;
1403 		size_t dbbufsize = elemp->mnte_text_size;
1404 		struct extmnttab *dbtabp = &elemp->mnte_tab;
1405 		size_t ssize;
1406 		char *kbufp;
1407 		int error = 0;
1408 
1409 
1410 		/*
1411 		 * We create a struct extmnttab within the kernel of the size
1412 		 * determined by the user's data model. We then populate its
1413 		 * fields by combining the start address of the text buffer
1414 		 * supplied by the user, ubufp, with the offsets stored for
1415 		 * this database element within dbtabp, a pointer to a struct
1416 		 * extmnttab.
1417 		 *
1418 		 * Note that if the corresponding field is "-" this signifies
1419 		 * no real content, and we set the address to NULL. This does
1420 		 * not apply to mnt_time.
1421 		 */
1422 		STRUCT_INIT(ktab, datamodel);
1423 		STRUCT_FSETP(ktab, mnt_special,
1424 		    MNTFS_REAL_FIELD(dbbufp) ? ubufp : NULL);
1425 		STRUCT_FSETP(ktab, mnt_mountp,
1426 		    MNTFS_REAL_FIELD(dbbufp + (off_t)dbtabp->mnt_mountp) ?
1427 		    ubufp + (off_t)dbtabp->mnt_mountp : NULL);
1428 		STRUCT_FSETP(ktab, mnt_fstype,
1429 		    MNTFS_REAL_FIELD(dbbufp + (off_t)dbtabp->mnt_fstype) ?
1430 		    ubufp + (off_t)dbtabp->mnt_fstype : NULL);
1431 		STRUCT_FSETP(ktab, mnt_mntopts,
1432 		    MNTFS_REAL_FIELD(dbbufp + (off_t)dbtabp->mnt_mntopts) ?
1433 		    ubufp + (off_t)dbtabp->mnt_mntopts : NULL);
1434 		STRUCT_FSETP(ktab, mnt_time,
1435 		    ubufp + (off_t)dbtabp->mnt_time);
1436 		if (cmd == MNTIOC_GETEXTMNTENT) {
1437 			STRUCT_FSETP(ktab, mnt_major, dbtabp->mnt_major);
1438 			STRUCT_FSETP(ktab, mnt_minor, dbtabp->mnt_minor);
1439 			ssize = SIZEOF_STRUCT(extmnttab, datamodel);
1440 		} else {
1441 			ssize = SIZEOF_STRUCT(mnttab, datamodel);
1442 		}
1443 		if (copyout(STRUCT_BUF(ktab), uemp, ssize))
1444 			return (EFAULT);
1445 
1446 		/*
1447 		 * We create a text buffer in the kernel into which we copy the
1448 		 * /etc/mnttab entry for this element. We change the tab and
1449 		 * new-line delimiters to null bytes before copying out the
1450 		 * buffer.
1451 		 */
1452 		kbufp = kmem_alloc(dbbufsize, KM_SLEEP);
1453 		bcopy(elemp->mnte_text, kbufp, dbbufsize);
1454 		*(kbufp + (off_t)dbtabp->mnt_mountp - 1) =
1455 		    *(kbufp + (off_t)dbtabp->mnt_fstype - 1) =
1456 		    *(kbufp + (off_t)dbtabp->mnt_mntopts - 1) =
1457 		    *(kbufp + (off_t)dbtabp->mnt_time - 1) =
1458 		    *(kbufp + dbbufsize - 1) = '\0';
1459 		if (copyout(kbufp, ubufp, dbbufsize))
1460 			error = EFAULT;
1461 
1462 		kmem_free(kbufp, dbbufsize);
1463 		return (error);
1464 }
1465 
1466 /* ARGSUSED */
1467 static int
1468 mntioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, cred_t *cr,
1469     int *rvalp, caller_context_t *ct)
1470 {
1471 	uint_t *up = (uint_t *)arg;
1472 	mntnode_t *mnp = VTOM(vp);
1473 	mntsnap_t *snapp = &mnp->mnt_ioctl;
1474 	int error = 0;
1475 	zone_t *zonep = MTOD(mnp)->mnt_zone_ref.zref_zone;
1476 	krwlock_t *dblockp = &zonep->zone_mntfs_db_lock;
1477 	model_t datamodel = flag & DATAMODEL_MASK;
1478 
1479 	switch (cmd) {
1480 
1481 	case MNTIOC_NMNTS:  		/* get no. of mounted resources */
1482 	{
1483 		rw_enter(&mnp->mnt_contents, RW_READER);
1484 		if (snapp->mnts_nmnts == 0 ||
1485 		    (snapp->mnts_flags & MNTS_REWIND)) {
1486 			if (!rw_tryupgrade(&mnp->mnt_contents)) {
1487 				rw_exit(&mnp->mnt_contents);
1488 				rw_enter(&mnp->mnt_contents, RW_WRITER);
1489 			}
1490 			if (snapp->mnts_nmnts == 0 ||
1491 			    (snapp->mnts_flags & MNTS_REWIND))
1492 				mntfs_snapshot(mnp, snapp);
1493 		}
1494 		rw_exit(&mnp->mnt_contents);
1495 
1496 		if (suword32(up, snapp->mnts_nmnts) != 0)
1497 			error = EFAULT;
1498 		break;
1499 	}
1500 
1501 	case MNTIOC_GETDEVLIST:  	/* get mounted device major/minor nos */
1502 	{
1503 		size_t len;
1504 		uint_t *devlist;
1505 		mntelem_t *elemp;
1506 		int i = 0;
1507 
1508 		rw_enter(&mnp->mnt_contents, RW_READER);
1509 		if (snapp->mnts_nmnts == 0 ||
1510 		    (snapp->mnts_flags & MNTS_REWIND)) {
1511 			if (!rw_tryupgrade(&mnp->mnt_contents)) {
1512 				rw_exit(&mnp->mnt_contents);
1513 				rw_enter(&mnp->mnt_contents, RW_WRITER);
1514 			}
1515 			if (snapp->mnts_nmnts == 0 ||
1516 			    (snapp->mnts_flags & MNTS_REWIND))
1517 				mntfs_snapshot(mnp, snapp);
1518 			rw_downgrade(&mnp->mnt_contents);
1519 		}
1520 
1521 		/* Create a local buffer to hold the device numbers. */
1522 		len = 2 * snapp->mnts_nmnts * sizeof (uint_t);
1523 		devlist = kmem_alloc(len, KM_SLEEP);
1524 
1525 		/*
1526 		 * Walk the database elements for this snapshot and add their
1527 		 * major and minor numbers.
1528 		 */
1529 		rw_enter(dblockp, RW_READER);
1530 		for (elemp = snapp->mnts_first; elemp;
1531 		    elemp = mntfs_get_next_elem(snapp, elemp)) {
1532 				devlist[2 * i] = elemp->mnte_tab.mnt_major;
1533 				devlist[2 * i + 1] = elemp->mnte_tab.mnt_minor;
1534 				i++;
1535 		}
1536 		rw_exit(dblockp);
1537 		ASSERT(i == snapp->mnts_nmnts);
1538 		rw_exit(&mnp->mnt_contents);
1539 
1540 		error = xcopyout(devlist, up, len);
1541 		kmem_free(devlist, len);
1542 		break;
1543 	}
1544 
1545 	case MNTIOC_SETTAG:		/* set tag on mounted file system */
1546 	case MNTIOC_CLRTAG:		/* clear tag on mounted file system */
1547 	{
1548 		struct mnttagdesc *dp = (struct mnttagdesc *)arg;
1549 		STRUCT_DECL(mnttagdesc, tagdesc);
1550 		char *cptr;
1551 		uint32_t major, minor;
1552 		char tagbuf[MAX_MNTOPT_TAG];
1553 		char *pbuf;
1554 		size_t len;
1555 		uint_t start = 0;
1556 		mntdata_t *mntdata = MTOD(mnp);
1557 		zone_t *zone = mntdata->mnt_zone_ref.zref_zone;
1558 
1559 		STRUCT_INIT(tagdesc, flag & DATAMODEL_MASK);
1560 		if (copyin(dp, STRUCT_BUF(tagdesc), STRUCT_SIZE(tagdesc))) {
1561 			error = EFAULT;
1562 			break;
1563 		}
1564 		pbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1565 		if (zone != global_zone) {
1566 			(void) strcpy(pbuf, zone->zone_rootpath);
1567 			/* truncate "/" and nul */
1568 			start = zone->zone_rootpathlen - 2;
1569 			ASSERT(pbuf[start] == '/');
1570 		}
1571 		cptr = STRUCT_FGETP(tagdesc, mtd_mntpt);
1572 		error = copyinstr(cptr, pbuf + start, MAXPATHLEN - start, &len);
1573 		if (error) {
1574 			kmem_free(pbuf, MAXPATHLEN);
1575 			break;
1576 		}
1577 		if (start != 0 && pbuf[start] != '/') {
1578 			kmem_free(pbuf, MAXPATHLEN);
1579 			error = EINVAL;
1580 			break;
1581 		}
1582 		cptr = STRUCT_FGETP(tagdesc, mtd_tag);
1583 		if ((error = copyinstr(cptr, tagbuf, MAX_MNTOPT_TAG, &len))) {
1584 			kmem_free(pbuf, MAXPATHLEN);
1585 			break;
1586 		}
1587 		major = STRUCT_FGET(tagdesc, mtd_major);
1588 		minor = STRUCT_FGET(tagdesc, mtd_minor);
1589 		if (cmd == MNTIOC_SETTAG)
1590 			error = vfs_settag(major, minor, pbuf, tagbuf, cr);
1591 		else
1592 			error = vfs_clrtag(major, minor, pbuf, tagbuf, cr);
1593 		kmem_free(pbuf, MAXPATHLEN);
1594 		break;
1595 	}
1596 
1597 	case MNTIOC_SHOWHIDDEN:
1598 	{
1599 		rw_enter(&mnp->mnt_contents, RW_WRITER);
1600 		mnp->mnt_flags |= MNT_SHOWHIDDEN;
1601 		rw_exit(&mnp->mnt_contents);
1602 		break;
1603 	}
1604 
1605 	case MNTIOC_GETMNTANY:
1606 	{
1607 		STRUCT_DECL(mntentbuf, embuf);	/* Our copy of user's embuf */
1608 		STRUCT_DECL(extmnttab, ktab);	/* Out copy of user's emp */
1609 		struct extmnttab *uemp;		/* uaddr of user's emp */
1610 		char *ubufp;			/* uaddr of user's text buf */
1611 		size_t ubufsize;		/* size of the above */
1612 		struct extmnttab preftab;	/* our version of user's emp */
1613 		char *prefbuf;			/* our copy of user's text */
1614 		mntelem_t *elemp;		/* a database element */
1615 		struct extmnttab *dbtabp;	/* element's extmnttab */
1616 		char *dbbufp;			/* element's text buf */
1617 		size_t dbbufsize;		/* size of the above */
1618 		vtype_t type;			/* type, if any, of special */
1619 
1620 
1621 		/*
1622 		 * embuf is a struct embuf within the kernel. We copy into it
1623 		 * the struct embuf supplied by the user.
1624 		 */
1625 		STRUCT_INIT(embuf, datamodel);
1626 		if (copyin((void *) arg, STRUCT_BUF(embuf),
1627 		    STRUCT_SIZE(embuf))) {
1628 			error = EFAULT;
1629 			break;
1630 		}
1631 		uemp = STRUCT_FGETP(embuf, mbuf_emp);
1632 		ubufp = STRUCT_FGETP(embuf, mbuf_buf);
1633 		ubufsize = STRUCT_FGET(embuf, mbuf_bufsize);
1634 
1635 		/*
1636 		 * Check that the text buffer offered by the user is the
1637 		 * agreed size.
1638 		 */
1639 		if (ubufsize != MNT_LINE_MAX) {
1640 			error = EINVAL;
1641 			break;
1642 		}
1643 
1644 		/* Copy the user-supplied entry into a local buffer. */
1645 		prefbuf = kmem_alloc(MNT_LINE_MAX, KM_SLEEP);
1646 		if (copyin(ubufp, prefbuf, MNT_LINE_MAX)) {
1647 			kmem_free(prefbuf, MNT_LINE_MAX);
1648 			error = EFAULT;
1649 			break;
1650 		}
1651 
1652 		/* Ensure that any string within it is null-terminated. */
1653 		*(prefbuf + MNT_LINE_MAX - 1) = 0;
1654 
1655 		/* Copy in the user-supplied mpref */
1656 		STRUCT_INIT(ktab, datamodel);
1657 		if (copyin(uemp, STRUCT_BUF(ktab),
1658 		    SIZEOF_STRUCT(mnttab, datamodel))) {
1659 			kmem_free(prefbuf, MNT_LINE_MAX);
1660 			error = EFAULT;
1661 			break;
1662 		}
1663 
1664 		/*
1665 		 * Copy the members of the user's pref struct into a local
1666 		 * struct. The pointers need to be offset and verified to
1667 		 * ensure that they lie within the bounds of the buffer.
1668 		 */
1669 		preftab.mnt_special = mntfs_import_addr(STRUCT_FGETP(ktab,
1670 		    mnt_special), ubufp, prefbuf, MNT_LINE_MAX);
1671 		preftab.mnt_mountp = mntfs_import_addr(STRUCT_FGETP(ktab,
1672 		    mnt_mountp), ubufp, prefbuf, MNT_LINE_MAX);
1673 		preftab.mnt_fstype = mntfs_import_addr(STRUCT_FGETP(ktab,
1674 		    mnt_fstype), ubufp, prefbuf, MNT_LINE_MAX);
1675 		preftab.mnt_mntopts = mntfs_import_addr(STRUCT_FGETP(ktab,
1676 		    mnt_mntopts), ubufp, prefbuf, MNT_LINE_MAX);
1677 		preftab.mnt_time = mntfs_import_addr(STRUCT_FGETP(ktab,
1678 		    mnt_time), ubufp, prefbuf, MNT_LINE_MAX);
1679 
1680 		/*
1681 		 * If the user specifies a mounted resource that is a special
1682 		 * device then we capture its mode and major and minor numbers;
1683 		 * cf. the block comment below.
1684 		 */
1685 		type = mntfs_special_info_string(preftab.mnt_special,
1686 		    &preftab.mnt_major, &preftab.mnt_minor, cr);
1687 
1688 		rw_enter(&mnp->mnt_contents, RW_WRITER);
1689 		if (snapp->mnts_nmnts == 0 ||
1690 		    (snapp->mnts_flags & MNTS_REWIND))
1691 			mntfs_snapshot(mnp, snapp);
1692 
1693 		/*
1694 		 * This is the core functionality that implements getmntany().
1695 		 * We walk through the mntfs database until we find an element
1696 		 * matching the user's preferences that are contained in
1697 		 * preftab. Typically, this means checking that the text
1698 		 * matches. However, the mounted resource is special: if the
1699 		 * user is looking for a special device then we must find a
1700 		 * database element with the same major and minor numbers and
1701 		 * the same type, i.e. VBLK or VCHR. The type is not recorded
1702 		 * in the element because it cannot be inferred from the vfs_t.
1703 		 * We therefore check the type of suitable candidates via
1704 		 * mntfs_special_info_element(); since this calls into the
1705 		 * underlying file system we make sure to drop the database lock
1706 		 * first.
1707 		 */
1708 		elemp = snapp->mnts_next;
1709 		rw_enter(dblockp, RW_READER);
1710 		for (;;) {
1711 			for (; elemp; elemp = mntfs_get_next_elem(snapp,
1712 			    elemp)) {
1713 				dbtabp = &elemp->mnte_tab;
1714 				dbbufp = elemp->mnte_text;
1715 				dbbufsize = elemp->mnte_text_size;
1716 
1717 				if (((type &&
1718 				    dbtabp->mnt_major == preftab.mnt_major &&
1719 				    dbtabp->mnt_minor == preftab.mnt_minor &&
1720 				    MNTFS_REAL_FIELD(dbbufp)) ||
1721 				    (!type && (!preftab.mnt_special ||
1722 				    mntfs_same_word(preftab.mnt_special,
1723 				    prefbuf, MNT_LINE_MAX, (off_t)0, dbbufp,
1724 				    dbbufsize)))) &&
1725 
1726 				    (!preftab.mnt_mountp || mntfs_same_word(
1727 				    preftab.mnt_mountp, prefbuf, MNT_LINE_MAX,
1728 				    (off_t)dbtabp->mnt_mountp, dbbufp,
1729 				    dbbufsize)) &&
1730 
1731 				    (!preftab.mnt_fstype || mntfs_same_word(
1732 				    preftab.mnt_fstype, prefbuf, MNT_LINE_MAX,
1733 				    (off_t)dbtabp->mnt_fstype, dbbufp,
1734 				    dbbufsize)) &&
1735 
1736 				    (!preftab.mnt_mntopts || mntfs_same_word(
1737 				    preftab.mnt_mntopts, prefbuf, MNT_LINE_MAX,
1738 				    (off_t)dbtabp->mnt_mntopts, dbbufp,
1739 				    dbbufsize)) &&
1740 
1741 				    (!preftab.mnt_time || mntfs_same_word(
1742 				    preftab.mnt_time, prefbuf, MNT_LINE_MAX,
1743 				    (off_t)dbtabp->mnt_time, dbbufp,
1744 				    dbbufsize)))
1745 					break;
1746 			}
1747 			rw_exit(dblockp);
1748 
1749 			if (elemp == NULL || type == 0 ||
1750 			    type == mntfs_special_info_element(elemp, cr))
1751 				break;
1752 
1753 			rw_enter(dblockp, RW_READER);
1754 			elemp = mntfs_get_next_elem(snapp, elemp);
1755 		}
1756 
1757 		kmem_free(prefbuf, MNT_LINE_MAX);
1758 
1759 		/* If we failed to find a match then return EOF. */
1760 		if (elemp == NULL) {
1761 			rw_exit(&mnp->mnt_contents);
1762 			*rvalp = MNTFS_EOF;
1763 			break;
1764 		}
1765 
1766 		/*
1767 		 * Check that the text buffer offered by the user will be large
1768 		 * enough to accommodate the text for this entry.
1769 		 */
1770 		if (elemp->mnte_text_size > MNT_LINE_MAX) {
1771 			rw_exit(&mnp->mnt_contents);
1772 			*rvalp = MNTFS_TOOLONG;
1773 			break;
1774 		}
1775 
1776 		/*
1777 		 * Populate the user's struct mnttab and text buffer using the
1778 		 * element's contents.
1779 		 */
1780 		if (mntfs_copyout_elem(elemp, uemp, ubufp, cmd, datamodel)) {
1781 			error = EFAULT;
1782 		} else {
1783 			rw_enter(dblockp, RW_READER);
1784 			elemp = mntfs_get_next_elem(snapp, elemp);
1785 			rw_exit(dblockp);
1786 			snapp->mnts_next = elemp;
1787 		}
1788 		rw_exit(&mnp->mnt_contents);
1789 		break;
1790 	}
1791 
1792 	case MNTIOC_GETMNTENT:
1793 	case MNTIOC_GETEXTMNTENT:
1794 	{
1795 		STRUCT_DECL(mntentbuf, embuf);	/* Our copy of user's embuf */
1796 		struct extmnttab *uemp;		/* uaddr of user's emp */
1797 		char *ubufp;			/* uaddr of user's text buf */
1798 		size_t ubufsize;		/* size of the above */
1799 		mntelem_t *elemp;		/* a database element */
1800 
1801 
1802 		rw_enter(&mnp->mnt_contents, RW_WRITER);
1803 		if (snapp->mnts_nmnts == 0 ||
1804 		    (snapp->mnts_flags & MNTS_REWIND))
1805 			mntfs_snapshot(mnp, snapp);
1806 		if ((elemp = snapp->mnts_next) == NULL) {
1807 			rw_exit(&mnp->mnt_contents);
1808 			*rvalp = MNTFS_EOF;
1809 			break;
1810 		}
1811 
1812 		/*
1813 		 * embuf is a struct embuf within the kernel. We copy into it
1814 		 * the struct embuf supplied by the user.
1815 		 */
1816 		STRUCT_INIT(embuf, datamodel);
1817 		if (copyin((void *) arg, STRUCT_BUF(embuf),
1818 		    STRUCT_SIZE(embuf))) {
1819 			rw_exit(&mnp->mnt_contents);
1820 			error = EFAULT;
1821 			break;
1822 		}
1823 		uemp = STRUCT_FGETP(embuf, mbuf_emp);
1824 		ubufp = STRUCT_FGETP(embuf, mbuf_buf);
1825 		ubufsize = STRUCT_FGET(embuf, mbuf_bufsize);
1826 
1827 		/*
1828 		 * Check that the text buffer offered by the user will be large
1829 		 * enough to accommodate the text for this entry.
1830 		 */
1831 		if (elemp->mnte_text_size > ubufsize) {
1832 			rw_exit(&mnp->mnt_contents);
1833 			*rvalp = MNTFS_TOOLONG;
1834 			break;
1835 		}
1836 
1837 		/*
1838 		 * Populate the user's struct mnttab and text buffer using the
1839 		 * element's contents.
1840 		 */
1841 		if (mntfs_copyout_elem(elemp, uemp, ubufp, cmd, datamodel)) {
1842 			error = EFAULT;
1843 		} else {
1844 			rw_enter(dblockp, RW_READER);
1845 			elemp = mntfs_get_next_elem(snapp, elemp);
1846 			rw_exit(dblockp);
1847 			snapp->mnts_next = elemp;
1848 		}
1849 		rw_exit(&mnp->mnt_contents);
1850 		break;
1851 	}
1852 
1853 	default:
1854 		error = EINVAL;
1855 		break;
1856 	}
1857 
1858 	return (error);
1859 }
1860 
1861 /*
1862  * mntfs provides a new vnode for each open(2). Two vnodes will represent the
1863  * same instance of /etc/mnttab if they share the same (zone-specific) vfs.
1864  */
1865 /* ARGSUSED */
1866 int
1867 mntcmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct)
1868 {
1869 	return (vp1 != NULL && vp2 != NULL && vp1->v_vfsp == vp2->v_vfsp);
1870 }
1871 
1872 /*
1873  * /mntfs vnode operations vector
1874  */
1875 const fs_operation_def_t mnt_vnodeops_template[] = {
1876 	VOPNAME_OPEN,		{ .vop_open = mntopen },
1877 	VOPNAME_CLOSE,		{ .vop_close = mntclose },
1878 	VOPNAME_READ,		{ .vop_read = mntread },
1879 	VOPNAME_IOCTL,		{ .vop_ioctl = mntioctl },
1880 	VOPNAME_GETATTR,	{ .vop_getattr = mntgetattr },
1881 	VOPNAME_ACCESS,		{ .vop_access = mntaccess },
1882 	VOPNAME_FSYNC,		{ .vop_fsync = mntfsync },
1883 	VOPNAME_INACTIVE,	{ .vop_inactive = mntinactive },
1884 	VOPNAME_SEEK,		{ .vop_seek = mntseek },
1885 	VOPNAME_POLL,		{ .vop_poll = mntpoll },
1886 	VOPNAME_CMP,		{ .vop_cmp = mntcmp },
1887 	VOPNAME_DISPOSE,	{ .error = fs_error },
1888 	VOPNAME_SHRLOCK,	{ .error = fs_error },
1889 	NULL,			NULL
1890 };
1891