Home | History | Annotate | Download | only in fs
      1     0    stevel /*
      2     0    stevel  * CDDL HEADER START
      3     0    stevel  *
      4     0    stevel  * The contents of this file are subject to the terms of the
      5  3921  jk115741  * Common Development and Distribution License (the "License").
      6  3921  jk115741  * You may not use this file except in compliance with the License.
      7     0    stevel  *
      8     0    stevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9     0    stevel  * or http://www.opensolaris.org/os/licensing.
     10     0    stevel  * See the License for the specific language governing permissions
     11     0    stevel  * and limitations under the License.
     12     0    stevel  *
     13     0    stevel  * When distributing Covered Code, include this CDDL HEADER in each
     14     0    stevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15     0    stevel  * If applicable, add the following below this CDDL HEADER, with the
     16     0    stevel  * fields enclosed by brackets "[]" replaced with your own identifying
     17     0    stevel  * information: Portions Copyright [yyyy] [name of copyright owner]
     18     0    stevel  *
     19     0    stevel  * CDDL HEADER END
     20     0    stevel  */
     21  3921  jk115741 /* Portions Copyright 2007 Shivakumar GN */
     22     0    stevel /*
     23  6036       mec  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24     0    stevel  * Use is subject to license terms.
     25     0    stevel  */
     26     0    stevel 
     27     0    stevel #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28     0    stevel 
     29     0    stevel #include <sys/types.h>
     30     0    stevel #include <sys/cmn_err.h>
     31     0    stevel #include <sys/debug.h>
     32     0    stevel #include <sys/dirent.h>
     33     0    stevel #include <sys/kmem.h>
     34     0    stevel #include <sys/mman.h>
     35     0    stevel #include <sys/mutex.h>
     36     0    stevel #include <sys/sysmacros.h>
     37     0    stevel #include <sys/systm.h>
     38  6492      timh #include <sys/sunddi.h>
     39     0    stevel #include <sys/uio.h>
     40     0    stevel #include <sys/vmsystm.h>
     41     0    stevel #include <sys/vfs.h>
     42     0    stevel #include <sys/vnode.h>
     43     0    stevel 
     44     0    stevel #include <vm/as.h>
     45     0    stevel #include <vm/seg_vn.h>
     46     0    stevel 
     47     0    stevel #include <sys/gfs.h>
     48     0    stevel 
     49     0    stevel /*
     50     0    stevel  * Generic pseudo-filesystem routines.
     51     0    stevel  *
     52     0    stevel  * There are significant similarities between the implementation of certain file
     53     0    stevel  * system entry points across different filesystems.  While one could attempt to
     54     0    stevel  * "choke up on the bat" and incorporate common functionality into a VOP
     55  3921  jk115741  * preamble or postamble, such an approach is limited in the benefit it can
     56     0    stevel  * provide.  In this file we instead define a toolkit of routines which can be
     57     0    stevel  * called from a filesystem (with in-kernel pseudo-filesystems being the focus
     58     0    stevel  * of the exercise) in a more component-like fashion.
     59     0    stevel  *
     60     0    stevel  * There are three basic classes of routines:
     61     0    stevel  *
     62     0    stevel  * 1) Lowlevel support routines
     63     0    stevel  *
     64     0    stevel  *    These routines are designed to play a support role for existing
     65  3921  jk115741  *    pseudo-filesystems (such as procfs).  They simplify common tasks,
     66  5331       amw  *    without forcing the filesystem to hand over management to GFS.  The
     67     0    stevel  *    routines covered are:
     68     0    stevel  *
     69     0    stevel  *	gfs_readdir_init()
     70     0    stevel  *	gfs_readdir_emit()
     71     0    stevel  *	gfs_readdir_emitn()
     72     0    stevel  *	gfs_readdir_pred()
     73     0    stevel  *	gfs_readdir_fini()
     74     0    stevel  *	gfs_lookup_dot()
     75     0    stevel  *
     76     0    stevel  * 2) Complete GFS management
     77     0    stevel  *
     78     0    stevel  *    These routines take a more active role in management of the
     79     0    stevel  *    pseudo-filesystem.  They handle the relationship between vnode private
     80     0    stevel  *    data and VFS data, as well as the relationship between vnodes in the
     81  3921  jk115741  *    directory hierarchy.
     82     0    stevel  *
     83     0    stevel  *    In order to use these interfaces, the first member of every private
     84     0    stevel  *    v_data must be a gfs_file_t or a gfs_dir_t.  This hands over all control
     85     0    stevel  *    to GFS.
     86     0    stevel  *
     87     0    stevel  * 	gfs_file_create()
     88     0    stevel  * 	gfs_dir_create()
     89     0    stevel  * 	gfs_root_create()
     90     0    stevel  *
     91     0    stevel  *	gfs_file_inactive()
     92     0    stevel  *	gfs_dir_inactive()
     93     0    stevel  *	gfs_dir_lookup()
     94     0    stevel  *	gfs_dir_readdir()
     95     0    stevel  *
     96     0    stevel  * 	gfs_vop_inactive()
     97     0    stevel  * 	gfs_vop_lookup()
     98     0    stevel  * 	gfs_vop_readdir()
     99     0    stevel  * 	gfs_vop_map()
    100  3957  th199096  *
    101  3957  th199096  * 3) Single File pseudo-filesystems
    102  3957  th199096  *
    103  3957  th199096  *    This routine creates a rooted file to be overlayed ontop of another
    104  3957  th199096  *    file in the physical filespace.
    105  3957  th199096  *
    106  3957  th199096  *    Note that the parent is NULL (actually the vfs), but there is nothing
    107  3957  th199096  *    technically keeping such a file from utilizing the "Complete GFS
    108  3957  th199096  *    management" set of routines.
    109  3957  th199096  *
    110  3957  th199096  * 	gfs_root_create_file()
    111     0    stevel  */
    112     0    stevel 
    113     0    stevel /*
    114     0    stevel  * gfs_make_opsvec: take an array of vnode type definitions and create
    115     0    stevel  * their vnodeops_t structures
    116     0    stevel  *
    117     0    stevel  * This routine takes an array of gfs_opsvec_t's.  It could
    118     0    stevel  * alternatively take an array of gfs_opsvec_t*'s, which would allow
    119     0    stevel  * vnode types to be completely defined in files external to the caller
    120     0    stevel  * of gfs_make_opsvec().  As it stands, much more sharing takes place --
    121     0    stevel  * both the caller and the vnode type provider need to access gfsv_ops
    122     0    stevel  * and gfsv_template, and the caller also needs to know gfsv_name.
    123     0    stevel  */
    124     0    stevel int
    125     0    stevel gfs_make_opsvec(gfs_opsvec_t *vec)
    126     0    stevel {
    127     0    stevel 	int error, i;
    128     0    stevel 
    129     0    stevel 	for (i = 0; ; i++) {
    130     0    stevel 		if (vec[i].gfsv_name == NULL)
    131     0    stevel 			return (0);
    132     0    stevel 		error = vn_make_ops(vec[i].gfsv_name, vec[i].gfsv_template,
    133     0    stevel 		    vec[i].gfsv_ops);
    134     0    stevel 		if (error)
    135     0    stevel 			break;
    136     0    stevel 	}
    137     0    stevel 
    138     0    stevel 	cmn_err(CE_WARN, "gfs_make_opsvec: bad vnode ops template for '%s'",
    139     0    stevel 	    vec[i].gfsv_name);
    140     0    stevel 	for (i--; i >= 0; i--) {
    141     0    stevel 		vn_freevnodeops(*vec[i].gfsv_ops);
    142     0    stevel 		*vec[i].gfsv_ops = NULL;
    143     0    stevel 	}
    144     0    stevel 	return (error);
    145     0    stevel }
    146     0    stevel 
    147     0    stevel /*
    148     0    stevel  * Low level directory routines
    149     0    stevel  *
    150     0    stevel  * These routines provide some simple abstractions for reading directories.
    151     0    stevel  * They are designed to be used by existing pseudo filesystems (namely procfs)
    152     0    stevel  * that already have a complicated management infrastructure.
    153     0    stevel  */
    154     0    stevel 
    155     0    stevel /*
    156  5663  ck153898  * gfs_get_parent_ino: used to obtain a parent inode number and the
    157  5663  ck153898  * inode number of the given vnode in preparation for calling gfs_readdir_init.
    158  5663  ck153898  */
    159  5663  ck153898 int
    160  5663  ck153898 gfs_get_parent_ino(vnode_t *dvp, cred_t *cr, caller_context_t *ct,
    161  5663  ck153898     ino64_t *pino, ino64_t *ino)
    162  5663  ck153898 {
    163  5663  ck153898 	vnode_t *parent;
    164  5663  ck153898 	gfs_dir_t *dp = dvp->v_data;
    165  5663  ck153898 	int error;
    166  5663  ck153898 
    167  5663  ck153898 	*ino = dp->gfsd_file.gfs_ino;
    168  5663  ck153898 	parent = dp->gfsd_file.gfs_parent;
    169  5663  ck153898 
    170  5663  ck153898 	if (parent == NULL) {
    171  5663  ck153898 		*pino = *ino;		/* root of filesystem */
    172  5663  ck153898 	} else if (dvp->v_flag & V_XATTRDIR) {
    173  5663  ck153898 		vattr_t va;
    174  5663  ck153898 
    175  5663  ck153898 		va.va_mask = AT_NODEID;
    176  5663  ck153898 		error = VOP_GETATTR(parent, &va, 0, cr, ct);
    177  5663  ck153898 		if (error)
    178  5663  ck153898 			return (error);
    179  5663  ck153898 		*pino = va.va_nodeid;
    180  5663  ck153898 	} else {
    181  5663  ck153898 		*pino = ((gfs_file_t *)(parent->v_data))->gfs_ino;
    182  5663  ck153898 	}
    183  5663  ck153898 
    184  5663  ck153898 	return (0);
    185  5663  ck153898 }
    186  5663  ck153898 
    187  5663  ck153898 /*
    188     0    stevel  * gfs_readdir_init: initiate a generic readdir
    189     0    stevel  *   st		- a pointer to an uninitialized gfs_readdir_state_t structure
    190     0    stevel  *   name_max	- the directory's maximum file name length
    191     0    stevel  *   ureclen	- the exported file-space record length (1 for non-legacy FSs)
    192     0    stevel  *   uiop	- the uiop passed to readdir
    193     0    stevel  *   parent	- the parent directory's inode
    194     0    stevel  *   self	- this directory's inode
    195  5663  ck153898  *   flags	- flags from VOP_READDIR
    196     0    stevel  *
    197     0    stevel  * Returns 0 or a non-zero errno.
    198     0    stevel  *
    199     0    stevel  * Typical VOP_READDIR usage of gfs_readdir_*:
    200     0    stevel  *
    201     0    stevel  *	if ((error = gfs_readdir_init(...)) != 0)
    202     0    stevel  *		return (error);
    203     0    stevel  *	eof = 0;
    204     0    stevel  *	while ((error = gfs_readdir_pred(..., &voffset)) != 0) {
    205     0    stevel  *		if (!consumer_entry_at(voffset))
    206     0    stevel  *			voffset = consumer_next_entry(voffset);
    207     0    stevel  *		if (consumer_eof(voffset)) {
    208     0    stevel  *			eof = 1
    209     0    stevel  *			break;
    210     0    stevel  *		}
    211     0    stevel  *		if ((error = gfs_readdir_emit(..., voffset,
    212     0    stevel  *		    consumer_ino(voffset), consumer_name(voffset))) != 0)
    213     0    stevel  *			break;
    214     0    stevel  *	}
    215     0    stevel  *	return (gfs_readdir_fini(..., error, eofp, eof));
    216     0    stevel  *
    217     0    stevel  * As you can see, a zero result from gfs_readdir_pred() or
    218     0    stevel  * gfs_readdir_emit() indicates that processing should continue,
    219     0    stevel  * whereas a non-zero result indicates that the loop should terminate.
    220     0    stevel  * Most consumers need do nothing more than let gfs_readdir_fini()
    221     0    stevel  * determine what the cause of failure was and return the appropriate
    222     0    stevel  * value.
    223     0    stevel  */
    224     0    stevel int
    225     0    stevel gfs_readdir_init(gfs_readdir_state_t *st, int name_max, int ureclen,
    226  5663  ck153898     uio_t *uiop, ino64_t parent, ino64_t self, int flags)
    227     0    stevel {
    228  5663  ck153898 	size_t dirent_size;
    229  5663  ck153898 
    230     0    stevel 	if (uiop->uio_loffset < 0 || uiop->uio_resid <= 0 ||
    231     0    stevel 	    (uiop->uio_loffset % ureclen) != 0)
    232     0    stevel 		return (EINVAL);
    233     0    stevel 
    234     0    stevel 	st->grd_ureclen = ureclen;
    235     0    stevel 	st->grd_oresid = uiop->uio_resid;
    236     0    stevel 	st->grd_namlen = name_max;
    237  5663  ck153898 	if (flags & V_RDDIR_ENTFLAGS)
    238  5663  ck153898 		dirent_size = EDIRENT_RECLEN(st->grd_namlen);
    239  5663  ck153898 	else
    240  5663  ck153898 		dirent_size = DIRENT64_RECLEN(st->grd_namlen);
    241  5663  ck153898 	st->grd_dirent = kmem_zalloc(dirent_size, KM_SLEEP);
    242     0    stevel 	st->grd_parent = parent;
    243     0    stevel 	st->grd_self = self;
    244  5663  ck153898 	st->grd_flags = flags;
    245     0    stevel 
    246     0    stevel 	return (0);
    247     0    stevel }
    248     0    stevel 
    249     0    stevel /*
    250     0    stevel  * gfs_readdir_emit_int: internal routine to emit directory entry
    251     0    stevel  *
    252  5663  ck153898  *   st		- the current readdir state, which must have d_ino/ed_ino
    253  5663  ck153898  *		  and d_name/ed_name set
    254     0    stevel  *   uiop	- caller-supplied uio pointer
    255     0    stevel  *   next	- the offset of the next entry
    256     0    stevel  */
    257     0    stevel static int
    258   847    maybee gfs_readdir_emit_int(gfs_readdir_state_t *st, uio_t *uiop, offset_t next)
    259     0    stevel {
    260     0    stevel 	int reclen;
    261  5663  ck153898 	dirent64_t *dp;
    262  5663  ck153898 	edirent_t *edp;
    263     0    stevel 
    264  5663  ck153898 	if (st->grd_flags & V_RDDIR_ENTFLAGS) {
    265  5663  ck153898 		edp = st->grd_dirent;
    266  5663  ck153898 		reclen = EDIRENT_RECLEN(strlen(edp->ed_name));
    267  5663  ck153898 	} else {
    268  5663  ck153898 		dp = st->grd_dirent;
    269  5663  ck153898 		reclen = DIRENT64_RECLEN(strlen(dp->d_name));
    270  5663  ck153898 	}
    271     0    stevel 
    272     0    stevel 	if (reclen > uiop->uio_resid) {
    273     0    stevel 		/*
    274     0    stevel 		 * Error if no entries were returned yet
    275     0    stevel 		 */
    276     0    stevel 		if (uiop->uio_resid == st->grd_oresid)
    277     0    stevel 			return (EINVAL);
    278     0    stevel 		return (-1);
    279     0    stevel 	}
    280     0    stevel 
    281  5663  ck153898 	if (st->grd_flags & V_RDDIR_ENTFLAGS) {
    282  5663  ck153898 		edp->ed_off = next;
    283  5663  ck153898 		edp->ed_reclen = (ushort_t)reclen;
    284  5663  ck153898 	} else {
    285  5663  ck153898 		dp->d_off = next;
    286  5663  ck153898 		dp->d_reclen = (ushort_t)reclen;
    287  5663  ck153898 	}
    288     0    stevel 
    289     0    stevel 	if (uiomove((caddr_t)st->grd_dirent, reclen, UIO_READ, uiop))
    290     0    stevel 		return (EFAULT);
    291     0    stevel 
    292     0    stevel 	uiop->uio_loffset = next;
    293     0    stevel 
    294     0    stevel 	return (0);
    295     0    stevel }
    296     0    stevel 
    297     0    stevel /*
    298     0    stevel  * gfs_readdir_emit: emit a directory entry
    299     0    stevel  *   voff       - the virtual offset (obtained from gfs_readdir_pred)
    300     0    stevel  *   ino        - the entry's inode
    301     0    stevel  *   name       - the entry's name
    302  5663  ck153898  *   eflags	- value for ed_eflags (if processing edirent_t)
    303     0    stevel  *
    304     0    stevel  * Returns a 0 on success, a non-zero errno on failure, or -1 if the
    305     0    stevel  * readdir loop should terminate.  A non-zero result (either errno or
    306     0    stevel  * -1) from this function is typically passed directly to
    307     0    stevel  * gfs_readdir_fini().
    308     0    stevel  */
    309     0    stevel int
    310     0    stevel gfs_readdir_emit(gfs_readdir_state_t *st, uio_t *uiop, offset_t voff,
    311  5663  ck153898     ino64_t ino, const char *name, int eflags)
    312     0    stevel {
    313     0    stevel 	offset_t off = (voff + 2) * st->grd_ureclen;
    314     0    stevel 
    315  5663  ck153898 	if (st->grd_flags & V_RDDIR_ENTFLAGS) {
    316  5663  ck153898 		edirent_t *edp = st->grd_dirent;
    317  5663  ck153898 
    318  5663  ck153898 		edp->ed_ino = ino;
    319  5663  ck153898 		(void) strncpy(edp->ed_name, name, st->grd_namlen);
    320  5663  ck153898 		edp->ed_eflags = eflags;
    321  5663  ck153898 	} else {
    322  5663  ck153898 		dirent64_t *dp = st->grd_dirent;
    323  5663  ck153898 
    324  5663  ck153898 		dp->d_ino = ino;
    325  5663  ck153898 		(void) strncpy(dp->d_name, name, st->grd_namlen);
    326  5663  ck153898 	}
    327     0    stevel 
    328     0    stevel 	/*
    329     0    stevel 	 * Inter-entry offsets are invalid, so we assume a record size of
    330     0    stevel 	 * grd_ureclen and explicitly set the offset appropriately.
    331     0    stevel 	 */
    332   847    maybee 	return (gfs_readdir_emit_int(st, uiop, off + st->grd_ureclen));
    333     0    stevel }
    334     0    stevel 
    335     0    stevel /*
    336     0    stevel  * gfs_readdir_emitn: like gfs_readdir_emit(), but takes an integer
    337     0    stevel  * instead of a string for the entry's name.
    338     0    stevel  */
    339     0    stevel int
    340     0    stevel gfs_readdir_emitn(gfs_readdir_state_t *st, uio_t *uiop, offset_t voff,
    341     0    stevel     ino64_t ino, unsigned long num)
    342     0    stevel {
    343     0    stevel 	char buf[40];
    344     0    stevel 
    345     0    stevel 	numtos(num, buf);
    346  5663  ck153898 	return (gfs_readdir_emit(st, uiop, voff, ino, buf, 0));
    347     0    stevel }
    348     0    stevel 
    349     0    stevel /*
    350     0    stevel  * gfs_readdir_pred: readdir loop predicate
    351     0    stevel  *   voffp - a pointer in which the next virtual offset should be stored
    352     0    stevel  *
    353     0    stevel  * Returns a 0 on success, a non-zero errno on failure, or -1 if the
    354     0    stevel  * readdir loop should terminate.  A non-zero result (either errno or
    355     0    stevel  * -1) from this function is typically passed directly to
    356     0    stevel  * gfs_readdir_fini().
    357     0    stevel  */
    358     0    stevel int
    359     0    stevel gfs_readdir_pred(gfs_readdir_state_t *st, uio_t *uiop, offset_t *voffp)
    360     0    stevel {
    361     0    stevel 	offset_t off, voff;
    362     0    stevel 	int error;
    363     0    stevel 
    364     0    stevel top:
    365     0    stevel 	if (uiop->uio_resid <= 0)
    366     0    stevel 		return (-1);
    367     0    stevel 
    368     0    stevel 	off = uiop->uio_loffset / st->grd_ureclen;
    369     0    stevel 	voff = off - 2;
    370     0    stevel 	if (off == 0) {
    371     0    stevel 		if ((error = gfs_readdir_emit(st, uiop, voff, st->grd_self,
    372  5663  ck153898 		    ".", 0)) == 0)
    373     0    stevel 			goto top;
    374     0    stevel 	} else if (off == 1) {
    375     0    stevel 		if ((error = gfs_readdir_emit(st, uiop, voff, st->grd_parent,
    376  5663  ck153898 		    "..", 0)) == 0)
    377     0    stevel 			goto top;
    378     0    stevel 	} else {
    379     0    stevel 		*voffp = voff;
    380     0    stevel 		return (0);
    381     0    stevel 	}
    382     0    stevel 
    383     0    stevel 	return (error);
    384     0    stevel }
    385     0    stevel 
    386     0    stevel /*
    387     0    stevel  * gfs_readdir_fini: generic readdir cleanup
    388     0    stevel  *   error	- if positive, an error to return
    389     0    stevel  *   eofp	- the eofp passed to readdir
    390     0    stevel  *   eof	- the eof value
    391     0    stevel  *
    392     0    stevel  * Returns a 0 on success, a non-zero errno on failure.  This result
    393     0    stevel  * should be returned from readdir.
    394     0    stevel  */
    395     0    stevel int
    396     0    stevel gfs_readdir_fini(gfs_readdir_state_t *st, int error, int *eofp, int eof)
    397     0    stevel {
    398  5663  ck153898 	size_t dirent_size;
    399  5663  ck153898 
    400  5663  ck153898 	if (st->grd_flags & V_RDDIR_ENTFLAGS)
    401  5663  ck153898 		dirent_size = EDIRENT_RECLEN(st->grd_namlen);
    402  5663  ck153898 	else
    403  5663  ck153898 		dirent_size = DIRENT64_RECLEN(st->grd_namlen);
    404  5663  ck153898 	kmem_free(st->grd_dirent, dirent_size);
    405     0    stevel 	if (error > 0)
    406     0    stevel 		return (error);
    407     0    stevel 	if (eofp)
    408     0    stevel 		*eofp = eof;
    409     0    stevel 	return (0);
    410     0    stevel }
    411     0    stevel 
    412     0    stevel /*
    413     0    stevel  * gfs_lookup_dot
    414     0    stevel  *
    415     0    stevel  * Performs a basic check for "." and ".." directory entries.
    416     0    stevel  */
    417     0    stevel int
    418     0    stevel gfs_lookup_dot(vnode_t **vpp, vnode_t *dvp, vnode_t *pvp, const char *nm)
    419     0    stevel {
    420     0    stevel 	if (*nm == '\0' || strcmp(nm, ".") == 0) {
    421     0    stevel 		VN_HOLD(dvp);
    422     0    stevel 		*vpp = dvp;
    423     0    stevel 		return (0);
    424     0    stevel 	} else if (strcmp(nm, "..") == 0) {
    425     0    stevel 		if (pvp == NULL) {
    426     0    stevel 			ASSERT(dvp->v_flag & VROOT);
    427     0    stevel 			VN_HOLD(dvp);
    428     0    stevel 			*vpp = dvp;
    429     0    stevel 		} else {
    430     0    stevel 			VN_HOLD(pvp);
    431     0    stevel 			*vpp = pvp;
    432     0    stevel 		}
    433     0    stevel 		return (0);
    434     0    stevel 	}
    435     0    stevel 
    436     0    stevel 	return (-1);
    437     0    stevel }
    438     0    stevel 
    439     0    stevel /*
    440     0    stevel  * gfs_file_create(): create a new GFS file
    441     0    stevel  *
    442     0    stevel  *   size	- size of private data structure (v_data)
    443     0    stevel  *   pvp	- parent vnode (GFS directory)
    444     0    stevel  *   ops	- vnode operations vector
    445     0    stevel  *
    446     0    stevel  * In order to use this interface, the parent vnode must have been created by
    447     0    stevel  * gfs_dir_create(), and the private data stored in v_data must have a
    448     0    stevel  * 'gfs_file_t' as its first field.
    449     0    stevel  *
    450     0    stevel  * Given these constraints, this routine will automatically:
    451     0    stevel  *
    452     0    stevel  * 	- Allocate v_data for the vnode
    453     0    stevel  * 	- Initialize necessary fields in the vnode
    454     0    stevel  * 	- Hold the parent
    455     0    stevel  */
    456     0    stevel vnode_t *
    457     0    stevel gfs_file_create(size_t size, vnode_t *pvp, vnodeops_t *ops)
    458     0    stevel {
    459     0    stevel 	gfs_file_t *fp;
    460     0    stevel 	vnode_t *vp;
    461     0    stevel 
    462     0    stevel 	/*
    463     0    stevel 	 * Allocate vnode and internal data structure
    464     0    stevel 	 */
    465     0    stevel 	fp = kmem_zalloc(size, KM_SLEEP);
    466     0    stevel 	vp = vn_alloc(KM_SLEEP);
    467     0    stevel 
    468     0    stevel 	/*
    469     0    stevel 	 * Set up various pointers
    470     0    stevel 	 */
    471     0    stevel 	fp->gfs_vnode = vp;
    472     0    stevel 	fp->gfs_parent = pvp;
    473     0    stevel 	vp->v_data = fp;
    474     0    stevel 	fp->gfs_size = size;
    475     0    stevel 	fp->gfs_type = GFS_FILE;
    476     0    stevel 
    477     0    stevel 	/*
    478     0    stevel 	 * Initialize vnode and hold parent.
    479     0    stevel 	 */
    480     0    stevel 	vn_setops(vp, ops);
    481     0    stevel 	if (pvp) {
    482     0    stevel 		VN_SET_VFS_TYPE_DEV(vp, pvp->v_vfsp, VREG, 0);
    483     0    stevel 		VN_HOLD(pvp);
    484     0    stevel 	}
    485     0    stevel 
    486     0    stevel 	return (vp);
    487     0    stevel }
    488     0    stevel 
    489     0    stevel /*
    490     0    stevel  * gfs_dir_create: creates a new directory in the parent
    491     0    stevel  *
    492     0    stevel  *   size	- size of private data structure (v_data)
    493     0    stevel  *   pvp	- parent vnode (GFS directory)
    494     0    stevel  *   ops	- vnode operations vector
    495     0    stevel  *   entries	- NULL-terminated list of static entries (if any)
    496     0    stevel  *   maxlen	- maximum length of a directory entry
    497     0    stevel  *   readdir_cb	- readdir callback (see gfs_dir_readdir)
    498     0    stevel  *   inode_cb	- inode callback (see gfs_dir_readdir)
    499     0    stevel  *   lookup_cb	- lookup callback (see gfs_dir_lookup)
    500     0    stevel  *
    501     0    stevel  * In order to use this function, the first member of the private vnode
    502     0    stevel  * structure (v_data) must be a gfs_dir_t.  For each directory, there are
    503     0    stevel  * static entries, defined when the structure is initialized, and dynamic
    504     0    stevel  * entries, retrieved through callbacks.
    505     0    stevel  *
    506     0    stevel  * If a directory has static entries, then it must supply a inode callback,
    507     0    stevel  * which will compute the inode number based on the parent and the index.
    508     0    stevel  * For a directory with dynamic entries, the caller must supply a readdir
    509     0    stevel  * callback and a lookup callback.  If a static lookup fails, we fall back to
    510     0    stevel  * the supplied lookup callback, if any.
    511     0    stevel  *
    512     0    stevel  * This function also performs the same initialization as gfs_file_create().
    513     0    stevel  */
    514     0    stevel vnode_t *
    515     0    stevel gfs_dir_create(size_t struct_size, vnode_t *pvp, vnodeops_t *ops,
    516     0    stevel     gfs_dirent_t *entries, gfs_inode_cb inode_cb, int maxlen,
    517     0    stevel     gfs_readdir_cb readdir_cb, gfs_lookup_cb lookup_cb)
    518     0    stevel {
    519     0    stevel 	vnode_t *vp;
    520     0    stevel 	gfs_dir_t *dp;
    521     0    stevel 	gfs_dirent_t *de;
    522     0    stevel 
    523     0    stevel 	vp = gfs_file_create(struct_size, pvp, ops);
    524     0    stevel 	vp->v_type = VDIR;
    525     0    stevel 
    526     0    stevel 	dp = vp->v_data;
    527     0    stevel 	dp->gfsd_file.gfs_type = GFS_DIR;
    528     0    stevel 	dp->gfsd_maxlen = maxlen;
    529     0    stevel 
    530     0    stevel 	if (entries != NULL) {
    531     0    stevel 		for (de = entries; de->gfse_name != NULL; de++)
    532     0    stevel 			dp->gfsd_nstatic++;
    533     0    stevel 
    534     0    stevel 		dp->gfsd_static = kmem_alloc(
    535     0    stevel 		    dp->gfsd_nstatic * sizeof (gfs_dirent_t), KM_SLEEP);
    536     0    stevel 		bcopy(entries, dp->gfsd_static,
    537     0    stevel 		    dp->gfsd_nstatic * sizeof (gfs_dirent_t));
    538     0    stevel 	}
    539     0    stevel 
    540     0    stevel 	dp->gfsd_readdir = readdir_cb;
    541     0    stevel 	dp->gfsd_lookup = lookup_cb;
    542     0    stevel 	dp->gfsd_inode = inode_cb;
    543     0    stevel 
    544     0    stevel 	mutex_init(&dp->gfsd_lock, NULL, MUTEX_DEFAULT, NULL);
    545     0    stevel 
    546     0    stevel 	return (vp);
    547     0    stevel }
    548     0    stevel 
    549     0    stevel /*
    550     0    stevel  * gfs_root_create(): create a root vnode for a GFS filesystem
    551     0    stevel  *
    552     0    stevel  * Similar to gfs_dir_create(), this creates a root vnode for a filesystem.  The
    553     0    stevel  * only difference is that it takes a vfs_t instead of a vnode_t as its parent.
    554     0    stevel  */
    555     0    stevel vnode_t *
    556     0    stevel gfs_root_create(size_t size, vfs_t *vfsp, vnodeops_t *ops, ino64_t ino,
    557     0    stevel     gfs_dirent_t *entries, gfs_inode_cb inode_cb, int maxlen,
    558     0    stevel     gfs_readdir_cb readdir_cb, gfs_lookup_cb lookup_cb)
    559     0    stevel {
    560     0    stevel 	vnode_t *vp = gfs_dir_create(size, NULL, ops, entries, inode_cb,
    561     0    stevel 	    maxlen, readdir_cb, lookup_cb);
    562     0    stevel 
    563     0    stevel 	/* Manually set the inode */
    564     0    stevel 	((gfs_file_t *)vp->v_data)->gfs_ino = ino;
    565     0    stevel 
    566     0    stevel 	VFS_HOLD(vfsp);
    567     0    stevel 	VN_SET_VFS_TYPE_DEV(vp, vfsp, VDIR, 0);
    568     0    stevel 	vp->v_flag |= VROOT | VNOCACHE | VNOMAP | VNOSWAP | VNOMOUNT;
    569     0    stevel 
    570     0    stevel 	return (vp);
    571     0    stevel }
    572     0    stevel 
    573     0    stevel /*
    574  3957  th199096  * gfs_root_create_file(): create a root vnode for a GFS file as a filesystem
    575  3957  th199096  *
    576  3957  th199096  * Similar to gfs_root_create(), this creates a root vnode for a file to
    577  3957  th199096  * be the pseudo-filesystem.
    578  3957  th199096  */
    579  3957  th199096 vnode_t *
    580  3957  th199096 gfs_root_create_file(size_t size, vfs_t *vfsp, vnodeops_t *ops, ino64_t ino)
    581  3957  th199096 {
    582  3957  th199096 	vnode_t	*vp = gfs_file_create(size, NULL, ops);
    583  3957  th199096 
    584  3957  th199096 	((gfs_file_t *)vp->v_data)->gfs_ino = ino;
    585  3957  th199096 
    586  3957  th199096 	VFS_HOLD(vfsp);
    587  3957  th199096 	VN_SET_VFS_TYPE_DEV(vp, vfsp, VREG, 0);
    588  3957  th199096 	vp->v_flag |= VROOT | VNOCACHE | VNOMAP | VNOSWAP | VNOMOUNT;
    589  3957  th199096 
    590  3957  th199096 	return (vp);
    591  3957  th199096 }
    592  3957  th199096 
    593  3957  th199096 /*
    594     0    stevel  * gfs_file_inactive()
    595     0    stevel  *
    596     0    stevel  * Called from the VOP_INACTIVE() routine.  If necessary, this routine will
    597     0    stevel  * remove the given vnode from the parent directory and clean up any references
    598     0    stevel  * in the VFS layer.
    599     0    stevel  *
    600     0    stevel  * If the vnode was not removed (due to a race with vget), then NULL is
    601     0    stevel  * returned.  Otherwise, a pointer to the private data is returned.
    602     0    stevel  */
    603     0    stevel void *
    604     0    stevel gfs_file_inactive(vnode_t *vp)
    605     0    stevel {
    606     0    stevel 	int i;
    607     0    stevel 	gfs_dirent_t *ge = NULL;
    608     0    stevel 	gfs_file_t *fp = vp->v_data;
    609     0    stevel 	gfs_dir_t *dp = NULL;
    610     0    stevel 	void *data;
    611     0    stevel 
    612  5331       amw 	if (fp->gfs_parent == NULL || (vp->v_flag & V_XATTRDIR))
    613     0    stevel 		goto found;
    614     0    stevel 
    615     0    stevel 	dp = fp->gfs_parent->v_data;
    616     0    stevel 
    617     0    stevel 	/*
    618     0    stevel 	 * First, see if this vnode is cached in the parent.
    619     0    stevel 	 */
    620     0    stevel 	gfs_dir_lock(dp);
    621     0    stevel 
    622     0    stevel 	/*
    623     0    stevel 	 * Find it in the set of static entries.
    624     0    stevel 	 */
    625     0    stevel 	for (i = 0; i < dp->gfsd_nstatic; i++)  {
    626     0    stevel 		ge = &dp->gfsd_static[i];
    627     0    stevel 
    628     0    stevel 		if (ge->gfse_vnode == vp)
    629     0    stevel 			goto found;
    630     0    stevel 	}
    631     0    stevel 
    632     0    stevel 	/*
    633     0    stevel 	 * If 'ge' is NULL, then it is a dynamic entry.
    634     0    stevel 	 */
    635     0    stevel 	ge = NULL;
    636     0    stevel 
    637     0    stevel found:
    638  5331       amw 	if (vp->v_flag & V_XATTRDIR) {
    639  5331       amw 		mutex_enter(&fp->gfs_parent->v_lock);
    640  5331       amw 	}
    641     0    stevel 	mutex_enter(&vp->v_lock);
    642     0    stevel 	if (vp->v_count == 1) {
    643     0    stevel 		/*
    644     0    stevel 		 * Really remove this vnode
    645     0    stevel 		 */
    646     0    stevel 		data = vp->v_data;
    647     0    stevel 		if (ge != NULL) {
    648     0    stevel 			/*
    649     0    stevel 			 * If this was a statically cached entry, simply set the
    650     0    stevel 			 * cached vnode to NULL.
    651     0    stevel 			 */
    652     0    stevel 			ge->gfse_vnode = NULL;
    653     0    stevel 		}
    654  5331       amw 		if (vp->v_flag & V_XATTRDIR) {
    655  5331       amw 			fp->gfs_parent->v_xattrdir = NULL;
    656  5331       amw 			mutex_exit(&fp->gfs_parent->v_lock);
    657  5331       amw 		}
    658     0    stevel 		mutex_exit(&vp->v_lock);
    659     0    stevel 
    660     0    stevel 		/*
    661     0    stevel 		 * Free vnode and release parent
    662     0    stevel 		 */
    663     0    stevel 		if (fp->gfs_parent) {
    664  5331       amw 			if (dp) {
    665  5331       amw 				gfs_dir_unlock(dp);
    666  5331       amw 			}
    667     0    stevel 			VN_RELE(fp->gfs_parent);
    668     0    stevel 		} else {
    669     0    stevel 			ASSERT(vp->v_vfsp != NULL);
    670     0    stevel 			VFS_RELE(vp->v_vfsp);
    671     0    stevel 		}
    672     0    stevel 		vn_free(vp);
    673     0    stevel 	} else {
    674     0    stevel 		vp->v_count--;
    675     0    stevel 		data = NULL;
    676     0    stevel 		mutex_exit(&vp->v_lock);
    677  5331       amw 		if (vp->v_flag & V_XATTRDIR) {
    678  5331       amw 			mutex_exit(&fp->gfs_parent->v_lock);
    679  5331       amw 		}
    680     0    stevel 		if (dp)
    681     0    stevel 			gfs_dir_unlock(dp);
    682     0    stevel 	}
    683     0    stevel 
    684     0    stevel 	return (data);
    685     0    stevel }
    686     0    stevel 
    687     0    stevel /*
    688     0    stevel  * gfs_dir_inactive()
    689     0    stevel  *
    690     0    stevel  * Same as above, but for directories.
    691     0    stevel  */
    692     0    stevel void *
    693     0    stevel gfs_dir_inactive(vnode_t *vp)
    694     0    stevel {
    695     0    stevel 	gfs_dir_t *dp;
    696     0    stevel 
    697     0    stevel 	ASSERT(vp->v_type == VDIR);
    698     0    stevel 
    699     0    stevel 	if ((dp = gfs_file_inactive(vp)) != NULL) {
    700     0    stevel 		mutex_destroy(&dp->gfsd_lock);
    701     0    stevel 		if (dp->gfsd_nstatic)
    702     0    stevel 			kmem_free(dp->gfsd_static,
    703     0    stevel 			    dp->gfsd_nstatic * sizeof (gfs_dirent_t));
    704     0    stevel 	}
    705     0    stevel 
    706     0    stevel 	return (dp);
    707     0    stevel }
    708     0    stevel 
    709     0    stevel /*
    710  6492      timh  * gfs_dir_lookup_dynamic()
    711     0    stevel  *
    712  6492      timh  * This routine looks up the provided name amongst the dynamic entries
    713  6492      timh  * in the gfs directory and returns the corresponding vnode, if found.
    714     0    stevel  *
    715  6492      timh  * The gfs directory is expected to be locked by the caller prior to
    716  6492      timh  * calling this function.  The directory will be unlocked during the
    717  6492      timh  * execution of this function, but will be locked upon return from the
    718  6492      timh  * function.  This function returns 0 on success, non-zero on error.
    719     0    stevel  *
    720  6492      timh  * The dynamic lookups are performed by invoking the lookup
    721  6492      timh  * callback, which is passed to this function as the first argument.
    722  6492      timh  * The arguments to the callback are:
    723     0    stevel  *
    724  6492      timh  * int gfs_lookup_cb(vnode_t *pvp, const char *nm, vnode_t **vpp, cred_t *cr,
    725  6492      timh  *     int flags, int *deflgs, pathname_t *rpnp);
    726     0    stevel  *
    727     0    stevel  *	pvp	- parent vnode
    728     0    stevel  *	nm	- name of entry
    729     0    stevel  *	vpp	- pointer to resulting vnode
    730  5331       amw  *	cr	- pointer to cred
    731  6492      timh  *	flags	- flags value from lookup request
    732  6492      timh  *		ignored here; currently only used to request
    733  6492      timh  *		insensitive lookups
    734  6492      timh  *	direntflgs - output parameter, directory entry flags
    735  6492      timh  *		ignored here; currently only used to indicate a lookup
    736  6492      timh  *		has more than one possible match when case is not considered
    737  6492      timh  *	realpnp	- output parameter, real pathname
    738  6492      timh  *		ignored here; when lookup was performed case-insensitively,
    739  6492      timh  *		this field contains the "real" name of the file.
    740     0    stevel  *
    741     0    stevel  * 	Returns 0 on success, non-zero on error.
    742     0    stevel  */
    743  6492      timh static int
    744  6492      timh gfs_dir_lookup_dynamic(gfs_lookup_cb callback, gfs_dir_t *dp,
    745  6492      timh     const char *nm, vnode_t *dvp, vnode_t **vpp, cred_t *cr, int flags,
    746  6492      timh     int *direntflags, pathname_t *realpnp)
    747     0    stevel {
    748  6492      timh 	gfs_file_t *fp;
    749  6492      timh 	ino64_t ino;
    750  6492      timh 	int ret;
    751  6492      timh 
    752  6492      timh 	ASSERT(GFS_DIR_LOCKED(dp));
    753  6492      timh 
    754  6492      timh 	/*
    755  6492      timh 	 * Drop the directory lock, as the lookup routine
    756  6492      timh 	 * will need to allocate memory, or otherwise deadlock on this
    757  6492      timh 	 * directory.
    758  6492      timh 	 */
    759  6492      timh 	gfs_dir_unlock(dp);
    760  6492      timh 	ret = callback(dvp, nm, vpp, &ino, cr, flags, direntflags, realpnp);
    761  6492      timh 	gfs_dir_lock(dp);
    762  6492      timh 
    763  6492      timh 	/*
    764  6492      timh 	 * The callback for extended attributes returns a vnode
    765  6492      timh 	 * with v_data from an underlying fs.
    766  6492      timh 	 */
    767  6492      timh 	if (ret == 0 && !IS_XATTRDIR(dvp)) {
    768  6492      timh 		fp = (gfs_file_t *)((*vpp)->v_data);
    769  6492      timh 		fp->gfs_index = -1;
    770  6492      timh 		fp->gfs_ino = ino;
    771  6492      timh 	}
    772  6492      timh 
    773  6492      timh 	return (ret);
    774  6492      timh }
    775  6492      timh 
    776  6492      timh /*
    777  6492      timh  * gfs_dir_lookup_static()
    778  6492      timh  *
    779  6492      timh  * This routine looks up the provided name amongst the static entries
    780  6492      timh  * in the gfs directory and returns the corresponding vnode, if found.
    781  6492      timh  * The first argument to the function is a pointer to the comparison
    782  6492      timh  * function this function should use to decide if names are a match.
    783  6492      timh  *
    784  6492      timh  * If a match is found, and GFS_CACHE_VNODE is set and the vnode
    785  6492      timh  * exists, we simply return the existing vnode.  Otherwise, we call
    786  6492      timh  * the static entry's callback routine, caching the result if
    787  6492      timh  * necessary.  If the idx pointer argument is non-NULL, we use it to
    788  6492      timh  * return the index of the matching static entry.
    789  6492      timh  *
    790  6492      timh  * The gfs directory is expected to be locked by the caller prior to calling
    791  6492      timh  * this function.  The directory may be unlocked during the execution of
    792  6492      timh  * this function, but will be locked upon return from the function.
    793  6492      timh  *
    794  6492      timh  * This function returns 0 if a match is found, ENOENT if not.
    795  6492      timh  */
    796  6492      timh static int
    797  6492      timh gfs_dir_lookup_static(int (*compare)(const char *, const char *),
    798  6492      timh     gfs_dir_t *dp, const char *nm, vnode_t *dvp, int *idx,
    799  6492      timh     vnode_t **vpp, pathname_t *rpnp)
    800  6492      timh {
    801  6492      timh 	gfs_dirent_t *ge;
    802  6492      timh 	vnode_t *vp = NULL;
    803     0    stevel 	int i;
    804     0    stevel 
    805  6492      timh 	ASSERT(GFS_DIR_LOCKED(dp));
    806     0    stevel 
    807     0    stevel 	/*
    808     0    stevel 	 * Search static entries.
    809     0    stevel 	 */
    810     0    stevel 	for (i = 0; i < dp->gfsd_nstatic; i++) {
    811     0    stevel 		ge = &dp->gfsd_static[i];
    812     0    stevel 
    813  6492      timh 		if (compare(ge->gfse_name, nm) == 0) {
    814  6492      timh 			if (rpnp)
    815  6492      timh 				(void) strlcpy(rpnp->pn_buf, ge->gfse_name,
    816  6492      timh 				    rpnp->pn_bufsize);
    817  6492      timh 
    818     0    stevel 			if (ge->gfse_vnode) {
    819     0    stevel 				ASSERT(ge->gfse_flags & GFS_CACHE_VNODE);
    820     0    stevel 				vp = ge->gfse_vnode;
    821     0    stevel 				VN_HOLD(vp);
    822  6492      timh 				break;
    823     0    stevel 			}
    824     0    stevel 
    825     0    stevel 			/*
    826  3921  jk115741 			 * We drop the directory lock, as the constructor will
    827     0    stevel 			 * need to do KM_SLEEP allocations.  If we return from
    828     0    stevel 			 * the constructor only to find that a parallel
    829     0    stevel 			 * operation has completed, and GFS_CACHE_VNODE is set
    830  6492      timh 			 * for this entry, we discard the result in favor of
    831  6492      timh 			 * the cached vnode.
    832     0    stevel 			 */
    833     0    stevel 			gfs_dir_unlock(dp);
    834     0    stevel 			vp = ge->gfse_ctor(dvp);
    835     0    stevel 			gfs_dir_lock(dp);
    836     0    stevel 
    837     0    stevel 			((gfs_file_t *)vp->v_data)->gfs_index = i;
    838     0    stevel 
    839     0    stevel 			/* Set the inode according to the callback. */
    840     0    stevel 			((gfs_file_t *)vp->v_data)->gfs_ino =
    841     0    stevel 			    dp->gfsd_inode(dvp, i);
    842     0    stevel 
    843     0    stevel 			if (ge->gfse_flags & GFS_CACHE_VNODE) {
    844     0    stevel 				if (ge->gfse_vnode == NULL) {
    845     0    stevel 					ge->gfse_vnode = vp;
    846     0    stevel 				} else {
    847     0    stevel 					/*
    848     0    stevel 					 * A parallel constructor beat us to it;
    849     0    stevel 					 * return existing vnode.  We have to be
    850     0    stevel 					 * careful because we can't release the
    851     0    stevel 					 * current vnode while holding the
    852     0    stevel 					 * directory lock; its inactive routine
    853     0    stevel 					 * will try to lock this directory.
    854     0    stevel 					 */
    855     0    stevel 					vnode_t *oldvp = vp;
    856     0    stevel 					vp = ge->gfse_vnode;
    857     0    stevel 					VN_HOLD(vp);
    858     0    stevel 
    859     0    stevel 					gfs_dir_unlock(dp);
    860     0    stevel 					VN_RELE(oldvp);
    861     0    stevel 					gfs_dir_lock(dp);
    862     0    stevel 				}
    863     0    stevel 			}
    864  6492      timh 			break;
    865     0    stevel 		}
    866     0    stevel 	}
    867     0    stevel 
    868  6492      timh 	if (vp == NULL)
    869  6492      timh 		return (ENOENT);
    870  6492      timh 	else if (idx)
    871  6492      timh 		*idx = i;
    872  6492      timh 	*vpp = vp;
    873  6492      timh 	return (0);
    874  6492      timh }
    875     0    stevel 
    876  6492      timh /*
    877  6492      timh  * gfs_dir_lookup()
    878  6492      timh  *
    879  6492      timh  * Looks up the given name in the directory and returns the corresponding
    880  6492      timh  * vnode, if found.
    881  6492      timh  *
    882  6492      timh  * First, we search statically defined entries, if any, with a call to
    883  6492      timh  * gfs_dir_lookup_static().  If no static entry is found, and we have
    884  6492      timh  * a callback function we try a dynamic lookup via gfs_dir_lookup_dynamic().
    885  6492      timh  *
    886  6492      timh  * This function returns 0 on success, non-zero on error.
    887  6492      timh  */
    888  6492      timh int
    889  6492      timh gfs_dir_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp, cred_t *cr,
    890  6492      timh     int flags, int *direntflags, pathname_t *realpnp)
    891  6492      timh {
    892  6492      timh 	gfs_dir_t *dp = dvp->v_data;
    893  6492      timh 	boolean_t casecheck;
    894  6492      timh 	vnode_t *dynvp = NULL;
    895  6492      timh 	vnode_t *vp = NULL;
    896  6492      timh 	int (*compare)(const char *, const char *);
    897  6492      timh 	int error, idx;
    898     0    stevel 
    899  6492      timh 	ASSERT(dvp->v_type == VDIR);
    900  6492      timh 
    901  6492      timh 	if (gfs_lookup_dot(vpp, dvp, dp->gfsd_file.gfs_parent, nm) == 0)
    902  6492      timh 		return (0);
    903  6492      timh 
    904  6492      timh 	casecheck = (flags & FIGNORECASE) != 0 && direntflags != NULL;
    905  6492      timh 	if (vfs_has_feature(dvp->v_vfsp, VFSFT_NOCASESENSITIVE) ||
    906  6492      timh 	    (flags & FIGNORECASE))
    907  6492      timh 		compare = strcasecmp;
    908  6492      timh 	else
    909  6492      timh 		compare = strcmp;
    910  6492      timh 
    911  6492      timh 	gfs_dir_lock(dp);
    912  6492      timh 
    913  6492      timh 	error = gfs_dir_lookup_static(compare, dp, nm, dvp, &idx, &vp, realpnp);
    914  6492      timh 
    915  6492      timh 	if (vp && casecheck) {
    916  6492      timh 		gfs_dirent_t *ge;
    917  6492      timh 		int i;
    918  6492      timh 
    919  6492      timh 		for (i = idx + 1; i < dp->gfsd_nstatic; i++) {
    920  6492      timh 			ge = &dp->gfsd_static[i];
    921  6492      timh 
    922  6492      timh 			if (strcasecmp(ge->gfse_name, nm) == 0) {
    923  6492      timh 				*direntflags |= ED_CASE_CONFLICT;
    924  6492      timh 				goto out;
    925  6492      timh 			}
    926  5331       amw 		}
    927  6492      timh 	}
    928  6492      timh 
    929  6492      timh 	if ((error || casecheck) && dp->gfsd_lookup)
    930  6492      timh 		error = gfs_dir_lookup_dynamic(dp->gfsd_lookup, dp, nm, dvp,
    931  6492      timh 		    &dynvp, cr, flags, direntflags, vp ? NULL : realpnp);
    932  6492      timh 
    933  6492      timh 	if (vp && dynvp) {
    934  6492      timh 		/* static and dynamic entries are case-insensitive conflict */
    935  6492      timh 		ASSERT(casecheck);
    936  6492      timh 		*direntflags |= ED_CASE_CONFLICT;
    937  6492      timh 		VN_RELE(dynvp);
    938  6492      timh 	} else if (vp == NULL) {
    939  6492      timh 		vp = dynvp;
    940  6492      timh 	} else if (error == ENOENT) {
    941  6492      timh 		error = 0;
    942  6492      timh 	} else if (error) {
    943  6492      timh 		VN_RELE(vp);
    944  6492      timh 		vp = NULL;
    945     0    stevel 	}
    946     0    stevel 
    947     0    stevel out:
    948     0    stevel 	gfs_dir_unlock(dp);
    949     0    stevel 
    950  6492      timh 	*vpp = vp;
    951  6492      timh 	return (error);
    952     0    stevel }
    953     0    stevel 
    954     0    stevel /*
    955     0    stevel  * gfs_dir_readdir: does a readdir() on the given directory
    956     0    stevel  *
    957     0    stevel  *    dvp	- directory vnode
    958     0    stevel  *    uiop	- uio structure
    959     0    stevel  *    eofp	- eof pointer
    960     0    stevel  *    data	- arbitrary data passed to readdir callback
    961     0    stevel  *
    962     0    stevel  * This routine does all the readdir() dirty work.  Even so, the caller must
    963     0    stevel  * supply two callbacks in order to get full compatibility.
    964     0    stevel  *
    965     0    stevel  * If the directory contains static entries, an inode callback must be
    966     0    stevel  * specified.  This avoids having to create every vnode and call VOP_GETATTR()
    967     0    stevel  * when reading the directory.  This function has the following arguments:
    968     0    stevel  *
    969     0    stevel  *	ino_t gfs_inode_cb(vnode_t *vp, int index);
    970     0    stevel  *
    971     0    stevel  * 	vp	- vnode for the directory
    972     0    stevel  * 	index	- index in original gfs_dirent_t array
    973     0    stevel  *
    974     0    stevel  * 	Returns the inode number for the given entry.
    975     0    stevel  *
    976     0    stevel  * For directories with dynamic entries, a readdir callback must be provided.
    977     0    stevel  * This is significantly more complex, thanks to the particulars of
    978     0    stevel  * VOP_READDIR().
    979     0    stevel  *
    980  5663  ck153898  *	int gfs_readdir_cb(vnode_t *vp, void *dp, int *eofp,
    981  5663  ck153898  *	    offset_t *off, offset_t *nextoff, void *data, int flags)
    982     0    stevel  *
    983     0    stevel  *	vp	- directory vnode
    984     0    stevel  *	dp	- directory entry, sized according to maxlen given to
    985     0    stevel  *		  gfs_dir_create().  callback must fill in d_name and
    986  5663  ck153898  *		  d_ino (if a dirent64_t), or ed_name, ed_ino, and ed_eflags
    987  5663  ck153898  *		  (if an edirent_t). edirent_t is used if V_RDDIR_ENTFLAGS
    988  5663  ck153898  *		  is set in 'flags'.
    989     0    stevel  *	eofp	- callback must set to 1 when EOF has been reached
    990     0    stevel  *	off	- on entry, the last offset read from the directory.  Callback
    991     0    stevel  *		  must set to the offset of the current entry, typically left
    992     0    stevel  *		  untouched.
    993     0    stevel  *	nextoff	- callback must set to offset of next entry.  Typically
    994     0    stevel  *		  (off + 1)
    995     0    stevel  *	data	- caller-supplied data
    996  5663  ck153898  *	flags	- VOP_READDIR flags
    997     0    stevel  *
    998     0    stevel  *	Return 0 on success, or error on failure.
    999     0    stevel  */
   1000     0    stevel int
   1001  5331       amw gfs_dir_readdir(vnode_t *dvp, uio_t *uiop, int *eofp, void *data, cred_t *cr,
   1002  5663  ck153898     caller_context_t *ct, int flags)
   1003     0    stevel {
   1004     0    stevel 	gfs_readdir_state_t gstate;
   1005     0    stevel 	int error, eof = 0;
   1006     0    stevel 	ino64_t ino, pino;
   1007     0    stevel 	offset_t off, next;
   1008     0    stevel 	gfs_dir_t *dp = dvp->v_data;
   1009     0    stevel 
   1010  5663  ck153898 	error = gfs_get_parent_ino(dvp, cr, ct, &pino, &ino);
   1011  5663  ck153898 	if (error)
   1012  5663  ck153898 		return (error);
   1013     0    stevel 
   1014     0    stevel 	if ((error = gfs_readdir_init(&gstate, dp->gfsd_maxlen, 1, uiop,
   1015  5663  ck153898 	    pino, ino, flags)) != 0)
   1016     0    stevel 		return (error);
   1017     0    stevel 
   1018     0    stevel 	while ((error = gfs_readdir_pred(&gstate, uiop, &off)) == 0 &&
   1019     0    stevel 	    !eof) {
   1020     0    stevel 
   1021     0    stevel 		if (off >= 0 && off < dp->gfsd_nstatic) {
   1022     0    stevel 			ino = dp->gfsd_inode(dvp, off);
   1023     0    stevel 
   1024     0    stevel 			if ((error = gfs_readdir_emit(&gstate, uiop,
   1025  5663  ck153898 			    off, ino, dp->gfsd_static[off].gfse_name, 0))
   1026     0    stevel 			    != 0)
   1027     0    stevel 				break;
   1028     0    stevel 
   1029     0    stevel 		} else if (dp->gfsd_readdir) {
   1030     0    stevel 			off -= dp->gfsd_nstatic;
   1031     0    stevel 
   1032     0    stevel 			if ((error = dp->gfsd_readdir(dvp,
   1033     0    stevel 			    gstate.grd_dirent, &eof, &off, &next,
   1034  5663  ck153898 			    data, flags)) != 0 || eof)
   1035     0    stevel 				break;
   1036     0    stevel 
   1037     0    stevel 			off += dp->gfsd_nstatic + 2;
   1038     0    stevel 			next += dp->gfsd_nstatic + 2;
   1039     0    stevel 
   1040     0    stevel 			if ((error = gfs_readdir_emit_int(&gstate, uiop,
   1041   847    maybee 			    next)) != 0)
   1042     0    stevel 				break;
   1043     0    stevel 		} else {
   1044     0    stevel 			/*
   1045     0    stevel 			 * Offset is beyond the end of the static entries, and
   1046     0    stevel 			 * we have no dynamic entries.  Set EOF.
   1047     0    stevel 			 */
   1048     0    stevel 			eof = 1;
   1049     0    stevel 		}
   1050     0    stevel 	}
   1051     0    stevel 
   1052     0    stevel 	return (gfs_readdir_fini(&gstate, error, eofp, eof));
   1053     0    stevel }
   1054     0    stevel 
   1055     0    stevel 
   1056     0    stevel /*
   1057     0    stevel  * gfs_vop_lookup: VOP_LOOKUP() entry point
   1058     0    stevel  *
   1059     0    stevel  * For use directly in vnode ops table.  Given a GFS directory, calls
   1060     0    stevel  * gfs_dir_lookup() as necessary.
   1061     0    stevel  */
   1062     0    stevel /* ARGSUSED */
   1063     0    stevel int
   1064     0    stevel gfs_vop_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
   1065  5331       amw     int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
   1066  5331       amw     int *direntflags, pathname_t *realpnp)
   1067     0    stevel {
   1068  6492      timh 	return (gfs_dir_lookup(dvp, nm, vpp, cr, flags, direntflags, realpnp));
   1069     0    stevel }
   1070     0    stevel 
   1071     0    stevel /*
   1072     0    stevel  * gfs_vop_readdir: VOP_READDIR() entry point
   1073     0    stevel  *
   1074     0    stevel  * For use directly in vnode ops table.  Given a GFS directory, calls
   1075     0    stevel  * gfs_dir_readdir() as necessary.
   1076     0    stevel  */
   1077     0    stevel /* ARGSUSED */
   1078     0    stevel int
   1079  5331       amw gfs_vop_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp,
   1080  5331       amw     caller_context_t *ct, int flags)
   1081     0    stevel {
   1082  5663  ck153898 	return (gfs_dir_readdir(vp, uiop, eofp, NULL, cr, ct, flags));
   1083     0    stevel }
   1084     0    stevel 
   1085     0    stevel 
   1086     0    stevel /*
   1087     0    stevel  * gfs_vop_map: VOP_MAP() entry point
   1088     0    stevel  *
   1089     0    stevel  * Convenient routine for handling pseudo-files that wish to allow mmap() calls.
   1090     0    stevel  * This function only works for readonly files, and uses the read function for
   1091     0    stevel  * the vnode to fill in the data.  The mapped data is immediately faulted in and
   1092     0    stevel  * filled with the necessary data during this call; there are no getpage() or
   1093     0    stevel  * putpage() routines.
   1094     0    stevel  */
   1095     0    stevel /* ARGSUSED */
   1096     0    stevel int
   1097     0    stevel gfs_vop_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp,
   1098  5331       amw     size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cred,
   1099  5331       amw     caller_context_t *ct)
   1100     0    stevel {
   1101     0    stevel 	int rv;
   1102     0    stevel 	ssize_t resid = len;
   1103     0    stevel 
   1104     0    stevel 	/*
   1105     0    stevel 	 * Check for bad parameters
   1106     0    stevel 	 */
   1107     0    stevel #ifdef _ILP32
   1108     0    stevel 	if (len > MAXOFF_T)
   1109     0    stevel 		return (ENOMEM);
   1110     0    stevel #endif
   1111     0    stevel 	if (vp->v_flag & VNOMAP)
   1112     0    stevel 		return (ENOTSUP);
   1113     0    stevel 	if (off > MAXOFF_T)
   1114     0    stevel 		return (EFBIG);
   1115     0    stevel 	if ((long)off < 0 || (long)(off + len) < 0)
   1116     0    stevel 		return (EINVAL);
   1117     0    stevel 	if (vp->v_type != VREG)
   1118     0    stevel 		return (ENODEV);
   1119     0    stevel 	if ((prot & (PROT_EXEC | PROT_WRITE)) != 0)
   1120     0    stevel 		return (EACCES);
   1121     0    stevel 
   1122     0    stevel 	/*
   1123     0    stevel 	 * Find appropriate address if needed, otherwise clear address range.
   1124     0    stevel 	 */
   1125     0    stevel 	as_rangelock(as);
   1126  6036       mec 	rv = choose_addr(as, addrp, len, off, ADDR_VACALIGN, flags);
   1127  6036       mec 	if (rv != 0) {
   1128  6036       mec 		as_rangeunlock(as);
   1129  6036       mec 		return (rv);
   1130     0    stevel 	}
   1131     0    stevel 
   1132     0    stevel 	/*
   1133     0    stevel 	 * Create mapping
   1134     0    stevel 	 */
   1135     0    stevel 	rv = as_map(as, *addrp, len, segvn_create, zfod_argsp);
   1136     0    stevel 	as_rangeunlock(as);
   1137     0    stevel 	if (rv != 0)
   1138     0    stevel 		return (rv);
   1139     0    stevel 
   1140     0    stevel 	/*
   1141     0    stevel 	 * Fill with data from read()
   1142     0    stevel 	 */
   1143     0    stevel 	rv = vn_rdwr(UIO_READ, vp, *addrp, len, off, UIO_USERSPACE,
   1144     0    stevel 	    0, (rlim64_t)0, cred, &resid);
   1145     0    stevel 
   1146     0    stevel 	if (rv == 0 && resid != 0)
   1147     0    stevel 		rv = ENXIO;
   1148     0    stevel 
   1149     0    stevel 	if (rv != 0) {
   1150     0    stevel 		as_rangelock(as);
   1151     0    stevel 		(void) as_unmap(as, *addrp, len);
   1152     0    stevel 		as_rangeunlock(as);
   1153     0    stevel 	}
   1154     0    stevel 
   1155     0    stevel 	return (rv);
   1156     0    stevel }
   1157     0    stevel 
   1158     0    stevel /*
   1159     0    stevel  * gfs_vop_inactive: VOP_INACTIVE() entry point
   1160     0    stevel  *
   1161     0    stevel  * Given a vnode that is a GFS file or directory, call gfs_file_inactive() or
   1162     0    stevel  * gfs_dir_inactive() as necessary, and kmem_free()s associated private data.
   1163     0    stevel  */
   1164     0    stevel /* ARGSUSED */
   1165     0    stevel void
   1166  5331       amw gfs_vop_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
   1167     0    stevel {
   1168     0    stevel 	gfs_file_t *fp = vp->v_data;
   1169     0    stevel 	void *data;
   1170     0    stevel 
   1171     0    stevel 	if (fp->gfs_type == GFS_DIR)
   1172     0    stevel 		data = gfs_dir_inactive(vp);
   1173     0    stevel 	else
   1174     0    stevel 		data = gfs_file_inactive(vp);
   1175     0    stevel 
   1176     0    stevel 	if (data != NULL)
   1177     0    stevel 		kmem_free(data, fp->gfs_size);
   1178     0    stevel }
   1179