Example #1
0
/*
 * gfs_readdir_fini: generic readdir cleanup
 *   error	- if positive, an error to return
 *   eofp	- the eofp passed to readdir
 *   eof	- the eof value
 *
 * Returns a 0 on success, a non-zero errno on failure.  This result
 * should be returned from readdir.
 */
int
gfs_readdir_fini(gfs_readdir_state_t *st, int error, int *eofp, int eof)
{
	size_t dirent_size;

	if (st->grd_flags & V_RDDIR_ENTFLAGS)
		dirent_size = EDIRENT_RECLEN(st->grd_namlen);
	else
		dirent_size = DIRENT64_RECLEN(st->grd_namlen);
	kmem_free(st->grd_dirent, dirent_size);
	if (error > 0)
		return (error);
	if (eofp)
		*eofp = eof;
	return (0);
}
Example #2
0
/*
 * gfs_readdir_emit_int: internal routine to emit directory entry
 *
 *   st		- the current readdir state, which must have d_ino/ed_ino
 *		  and d_name/ed_name set
 *   uiop	- caller-supplied uio pointer
 *   next	- the offset of the next entry
 */
static int
gfs_readdir_emit_int(gfs_readdir_state_t *st, uio_t *uiop, offset_t next)
{
	int reclen;
	dirent64_t *dp;
	edirent_t *edp;

	if (st->grd_flags & V_RDDIR_ENTFLAGS) {
		edp = st->grd_dirent;
		reclen = EDIRENT_RECLEN(strlen(edp->ed_name));
	} else {
		dp = st->grd_dirent;
		reclen = DIRENT64_RECLEN(strlen(dp->d_name));
	}

	if (reclen > uiop->uio_resid) {
		/*
		 * Error if no entries were returned yet
		 */
		if (uiop->uio_resid == st->grd_oresid)
			return (EINVAL);
		return (-1);
	}

	if (st->grd_flags & V_RDDIR_ENTFLAGS) {
		edp->ed_off = next;
		edp->ed_reclen = (ushort_t)reclen;
	} else {
		dp->d_off = next;
		dp->d_reclen = (ushort_t)reclen;
	}

	if (uiomove((caddr_t)st->grd_dirent, reclen, UIO_READ, uiop))
		return (EFAULT);

	uiop->uio_loffset = next;

	return (0);
}
Example #3
0
/*
 * gfs_readdir_init: initiate a generic readdir
 *   st		- a pointer to an uninitialized gfs_readdir_state_t structure
 *   name_max	- the directory's maximum file name length
 *   ureclen	- the exported file-space record length (1 for non-legacy FSs)
 *   uiop	- the uiop passed to readdir
 *   parent	- the parent directory's inode
 *   self	- this directory's inode
 *   flags	- flags from VOP_READDIR
 *
 * Returns 0 or a non-zero errno.
 *
 * Typical VOP_READDIR usage of gfs_readdir_*:
 *
 *	if ((error = gfs_readdir_init(...)) != 0)
 *		return (error);
 *	eof = 0;
 *	while ((error = gfs_readdir_pred(..., &voffset)) != 0) {
 *		if (!consumer_entry_at(voffset))
 *			voffset = consumer_next_entry(voffset);
 *		if (consumer_eof(voffset)) {
 *			eof = 1
 *			break;
 *		}
 *		if ((error = gfs_readdir_emit(..., voffset,
 *		    consumer_ino(voffset), consumer_name(voffset))) != 0)
 *			break;
 *	}
 *	return (gfs_readdir_fini(..., error, eofp, eof));
 *
 * As you can see, a zero result from gfs_readdir_pred() or
 * gfs_readdir_emit() indicates that processing should continue,
 * whereas a non-zero result indicates that the loop should terminate.
 * Most consumers need do nothing more than let gfs_readdir_fini()
 * determine what the cause of failure was and return the appropriate
 * value.
 */
int
gfs_readdir_init(gfs_readdir_state_t *st, int name_max, int ureclen,
    uio_t *uiop, ino64_t parent, ino64_t self, int flags)
{
	size_t dirent_size;

	if (uiop->uio_loffset < 0 || uiop->uio_resid <= 0 ||
	    (uiop->uio_loffset % ureclen) != 0)
		return (EINVAL);

	st->grd_ureclen = ureclen;
	st->grd_oresid = uiop->uio_resid;
	st->grd_namlen = name_max;
	if (flags & V_RDDIR_ENTFLAGS)
		dirent_size = EDIRENT_RECLEN(st->grd_namlen);
	else
		dirent_size = DIRENT64_RECLEN(st->grd_namlen);
	st->grd_dirent = kmem_zalloc(dirent_size, KM_SLEEP);
	st->grd_parent = parent;
	st->grd_self = self;
	st->grd_flags = flags;

	return (0);
}
Example #4
0
static int
xmem_readdir(struct vnode *vp, struct uio *uiop, struct cred *cred, int *eofp)
{
	struct xmemnode *xp = (struct xmemnode *)VTOXN(vp);
	struct xdirent *xdp;
	int error;
	register struct dirent64 *dp;
	register ulong_t offset;
	register ulong_t total_bytes_wanted;
	register long outcount = 0;
	register long bufsize;
	int reclen;
	caddr_t outbuf;

	if (uiop->uio_loffset >= MAXOFF_T) {
		if (eofp)
			*eofp = 1;
		return (0);
	}
	/*
	 * assuming system call has already called xmem_rwlock
	 */
	ASSERT(RW_READ_HELD(&xp->xn_rwlock));

	if (uiop->uio_iovcnt != 1)
		return (EINVAL);

	if (vp->v_type != VDIR)
		return (ENOTDIR);

	/*
	 * There's a window here where someone could have removed
	 * all the entries in the directory after we put a hold on the
	 * vnode but before we grabbed the rwlock.  Just return unless
	 * there are still references to the current file in which case panic.
	 */
	if (xp->xn_dir == NULL) {
		if (xp->xn_nlink)
			cmn_err(CE_PANIC, "empty directory 0x%p", (void *)xp);
		return (0);
	}

	/*
	 * Get space for multiple directory entries
	 */
	total_bytes_wanted = uiop->uio_iov->iov_len;
	bufsize = total_bytes_wanted + sizeof (struct dirent64);
	outbuf = kmem_alloc(bufsize, KM_SLEEP);

	dp = (struct dirent64 *)outbuf;


	offset = 0;
	xdp = xp->xn_dir;
	while (xdp) {
		offset = xdp->xd_offset;
		if (offset >= uiop->uio_offset) {
			reclen = (int)DIRENT64_RECLEN(strlen(xdp->xd_name));
			if (outcount + reclen > total_bytes_wanted)
				break;
			ASSERT(xdp->xd_xmemnode != NULL);

			/* use strncpy(9f) to zero out uninitialized bytes */

			ASSERT(strlen(xdp->xd_name) + 1 <=
			    DIRENT64_NAMELEN(reclen));
			(void) strncpy(dp->d_name, xdp->xd_name,
			    DIRENT64_NAMELEN(reclen));
			dp->d_reclen = (ushort_t)reclen;
			dp->d_ino = (ino64_t)xdp->xd_xmemnode->xn_nodeid;
			dp->d_off = (offset_t)xdp->xd_offset + 1;
			dp = (struct dirent64 *)
			    ((uintptr_t)dp + dp->d_reclen);
			outcount += reclen;
			ASSERT(outcount <= bufsize);
		}
		xdp = xdp->xd_next;
	}
	error = uiomove(outbuf, outcount, UIO_READ, uiop);
	if (!error) {
		/* If we reached the end of the list our offset */
		/* should now be just past the end. */
		if (!xdp) {
			offset += 1;
			if (eofp)
				*eofp = 1;
		} else if (eofp)
			*eofp = 0;
		uiop->uio_offset = offset;
	}
	gethrestime(&xp->xn_atime);
	kmem_free(outbuf, bufsize);
	return (error);
}
Example #5
0
/* ARGSUSED */
static int
fdreaddir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp, caller_context_t *ct,
	int flags)
{
	/* bp holds one dirent structure */
	u_offset_t bp[DIRENT64_RECLEN(FDNSIZE) / sizeof (u_offset_t)];
	struct dirent64 *dirent = (struct dirent64 *)bp;
	int reclen, nentries;
	rctl_qty_t fdno_ctl;
	int  n;
	int oresid;
	off_t off;

	if (uiop->uio_offset < 0 || uiop->uio_resid <= 0 ||
	    (uiop->uio_offset % FDSDSIZE) != 0)
		return (ENOENT);

	ASSERT(uiop->uio_loffset <= MAXOFF_T);
	oresid = uiop->uio_resid;
	bzero(bp, sizeof (bp));

	mutex_enter(&curproc->p_lock);
	fdno_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_NOFILE],
	    curproc->p_rctls, curproc);
	nentries = MIN(P_FINFO(curproc)->fi_nfiles, (int)fdno_ctl);
	mutex_exit(&curproc->p_lock);

	while (uiop->uio_resid > 0) {
		if ((off = uiop->uio_offset) == 0) {	/* "." */
			dirent->d_ino = (ino64_t)FDROOTINO;
			dirent->d_name[0] = '.';
			dirent->d_name[1] = '\0';
			reclen = DIRENT64_RECLEN(1);
		} else if (off == FDSDSIZE) {		/* ".." */
			dirent->d_ino = (ino64_t)FDROOTINO;
			dirent->d_name[0] = '.';
			dirent->d_name[1] = '.';
			dirent->d_name[2] = '\0';
			reclen = DIRENT64_RECLEN(2);
		} else {
			/*
			 * Return entries corresponding to the allowable
			 * number of file descriptors for this process.
			 */
			if ((n = (off-2*FDSDSIZE)/FDSDSIZE) >= nentries)
				break;
			dirent->d_ino = (ino64_t)fdtoi(n);
			numtos((ulong_t)n, dirent->d_name);
			reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
		}
		dirent->d_off = (offset_t)(uiop->uio_offset + FDSDSIZE);
		dirent->d_reclen = (ushort_t)reclen;

		if (reclen > uiop->uio_resid) {
			/*
			 * Error if no entries have been returned yet.
			 */
			if (uiop->uio_resid == oresid)
				return (EINVAL);
			break;
		}
		/*
		 * uiomove() updates both resid and offset by the same
		 * amount.  But we want offset to change in increments
		 * of FDSDSIZE, which is different from the number of bytes
		 * being returned to the user.  So we set uio_offset
		 * separately, ignoring what uiomove() does.
		 */
		if (uiomove((caddr_t)dirent, reclen, UIO_READ, uiop))
			return (EFAULT);
		uiop->uio_offset = off + FDSDSIZE;
	}
	if (eofp)
		*eofp = ((uiop->uio_offset-2*FDSDSIZE)/FDSDSIZE >= nentries);
	return (0);
}
Example #6
0
/* ARGSUSED */
static int
auto_readdir(
	vnode_t *vp,
	uio_t *uiop,
	cred_t *cred,
	int *eofp,
	caller_context_t *ct,
	int flags)
{
	struct autofs_rddirargs	rda;
	autofs_rddirres rd;
	fnnode_t *fnp = vntofn(vp);
	fnnode_t *cfnp, *nfnp;
	dirent64_t *dp;
	ulong_t offset;
	ulong_t outcount = 0, count = 0;
	size_t namelen;
	ulong_t alloc_count;
	void *outbuf = NULL;
	fninfo_t *fnip = vfstofni(vp->v_vfsp);
	struct iovec *iovp;
	int error = 0;
	int reached_max = 0;
	int myeof = 0;
	int this_reclen;
	struct autofs_globals *fngp = vntofn(fnip->fi_rootvp)->fn_globals;

	AUTOFS_DPRINT((4, "auto_readdir vp=%p offset=%lld\n",
	    (void *)vp, uiop->uio_loffset));

	if (eofp != NULL)
		*eofp = 0;

	if (uiop->uio_iovcnt != 1)
		return (EINVAL);

	iovp = uiop->uio_iov;
	alloc_count = iovp->iov_len;

	gethrestime(&fnp->fn_atime);
	fnp->fn_ref_time = fnp->fn_atime.tv_sec;

	dp = outbuf = kmem_zalloc(alloc_count, KM_SLEEP);

	/*
	 * Held when getdents calls VOP_RWLOCK....
	 */
	ASSERT(RW_READ_HELD(&fnp->fn_rwlock));
	if (uiop->uio_offset >= AUTOFS_DAEMONCOOKIE) {
again:
		/*
		 * Do readdir of daemon contents only
		 * Drop readers lock and reacquire after reply.
		 */
		rw_exit(&fnp->fn_rwlock);
		bzero(&rd, sizeof (struct autofs_rddirres));
		count = 0;
		rda.rda_map = fnip->fi_map;
		rda.rda_offset = (uint_t)uiop->uio_offset;
		rd.rd_rddir.rddir_entries = dp;
		rda.rda_count = rd.rd_rddir.rddir_size = (uint_t)alloc_count;
		rda.uid = crgetuid(cred);

		error = auto_calldaemon(fngp->fng_zoneid,
		    AUTOFS_READDIR,
		    xdr_autofs_rddirargs,
		    &rda,
		    xdr_autofs_rddirres,
		    (void *)&rd,
		    sizeof (autofs_rddirres),
		    TRUE);

		/*
		 * reacquire previously dropped lock
		 */
		rw_enter(&fnp->fn_rwlock, RW_READER);

		if (!error) {
			error = rd.rd_status;
			dp = rd.rd_rddir.rddir_entries;
		}

		if (error) {
			if (error == AUTOFS_SHUTDOWN) {
				/*
				 * treat as empty directory
				 */
				error = 0;
				myeof = 1;
				if (eofp)
					*eofp = 1;
			}
			goto done;
		}
		if (rd.rd_rddir.rddir_size) {
			dirent64_t *odp = dp;   /* next in output buffer */
			dirent64_t *cdp = dp;   /* current examined entry */

			/*
			 * Check for duplicates here
			 */
			do {
				this_reclen = cdp->d_reclen;
				if (auto_search(fnp, cdp->d_name,
				    NULL, cred)) {
					/*
					 * entry not found in kernel list,
					 * include it in readdir output.
					 *
					 * If we are skipping entries. then
					 * we need to copy this entry to the
					 * correct position in the buffer
					 * to be copied out.
					 */
					if (cdp != odp)
						bcopy(cdp, odp,
						    (size_t)this_reclen);
					odp = nextdp(odp);
					outcount += this_reclen;
				} else {
					/*
					 * Entry was found in the kernel
					 * list. If it is the first entry
					 * in this buffer, then just skip it
					 */
					if (odp == dp) {
						dp = nextdp(dp);
						odp = dp;
					}
				}
				count += this_reclen;
				cdp = (struct dirent64 *)
				    ((char *)cdp + this_reclen);
			} while (count < rd.rd_rddir.rddir_size);

			if (outcount)
				error = uiomove(dp, outcount, UIO_READ, uiop);
			uiop->uio_offset = rd.rd_rddir.rddir_offset;
		} else {
			if (rd.rd_rddir.rddir_eof == 0) {
				/*
				 * alloc_count not large enough for one
				 * directory entry
				 */
				error = EINVAL;
			}
		}
		if (rd.rd_rddir.rddir_eof && !error) {
			myeof = 1;
			if (eofp)
				*eofp = 1;
		}
		if (!error && !myeof && outcount == 0) {
			/*
			 * call daemon with new cookie, all previous
			 * elements happened to be duplicates
			 */
			dp = outbuf;
			goto again;
		}
		goto done;
	}

	if (uiop->uio_offset == 0) {
		/*
		 * first time: so fudge the . and ..
		 */
		this_reclen = DIRENT64_RECLEN(1);
		if (alloc_count < this_reclen) {
			error = EINVAL;
			goto done;
		}
		dp->d_ino = (ino64_t)fnp->fn_nodeid;
		dp->d_off = (off64_t)1;
		dp->d_reclen = (ushort_t)this_reclen;

		/* use strncpy(9f) to zero out uninitialized bytes */

		(void) strncpy(dp->d_name, ".",
		    DIRENT64_NAMELEN(this_reclen));
		outcount += dp->d_reclen;
		dp = nextdp(dp);

		this_reclen = DIRENT64_RECLEN(2);
		if (alloc_count < outcount + this_reclen) {
			error = EINVAL;
			goto done;
		}
		dp->d_reclen = (ushort_t)this_reclen;
		dp->d_ino = (ino64_t)fnp->fn_parent->fn_nodeid;
		dp->d_off = (off64_t)2;

		/* use strncpy(9f) to zero out uninitialized bytes */

		(void) strncpy(dp->d_name, "..",
		    DIRENT64_NAMELEN(this_reclen));
		outcount += dp->d_reclen;
		dp = nextdp(dp);
	}

	offset = 2;
	cfnp = fnp->fn_dirents;
	while (cfnp != NULL) {
		nfnp = cfnp->fn_next;
		offset = cfnp->fn_offset;
		if ((offset >= uiop->uio_offset) &&
		    (!(cfnp->fn_flags & MF_LOOKUP))) {
			int reclen;

			/*
			 * include node only if its offset is greater or
			 * equal to the one required and it is not in
			 * transient state (not being looked-up)
			 */
			namelen = strlen(cfnp->fn_name);
			reclen = (int)DIRENT64_RECLEN(namelen);
			if (outcount + reclen > alloc_count) {
				reached_max = 1;
				break;
			}
			dp->d_reclen = (ushort_t)reclen;
			dp->d_ino = (ino64_t)cfnp->fn_nodeid;
			if (nfnp != NULL) {
				/*
				 * get the offset of the next element
				 */
				dp->d_off = (off64_t)nfnp->fn_offset;
			} else {
				/*
				 * This is the last element, make
				 * offset one plus the current
				 */
				dp->d_off = (off64_t)cfnp->fn_offset + 1;
			}

			/* use strncpy(9f) to zero out uninitialized bytes */

			(void) strncpy(dp->d_name, cfnp->fn_name,
			    DIRENT64_NAMELEN(reclen));
			outcount += dp->d_reclen;
			dp = nextdp(dp);
		}
		cfnp = nfnp;
	}

	if (outcount)
		error = uiomove(outbuf, outcount, UIO_READ, uiop);

	if (!error) {
		if (reached_max) {
			/*
			 * This entry did not get added to the buffer on this,
			 * call. We need to add it on the next call therefore
			 * set uio_offset to this entry's offset.  If there
			 * wasn't enough space for one dirent, return EINVAL.
			 */
			uiop->uio_offset = offset;
			if (outcount == 0)
				error = EINVAL;
		} else if (autofs_nobrowse ||
		    auto_nobrowse_option(fnip->fi_opts) ||
		    (fnip->fi_flags & MF_DIRECT) ||
		    (fnp->fn_trigger != NULL) ||
		    (((vp->v_flag & VROOT) == 0) &&
		    ((fntovn(fnp->fn_parent))->v_flag & VROOT) &&
		    (fnp->fn_dirents == NULL))) {
			/*
			 * done reading directory entries
			 */
			uiop->uio_offset = offset + 1;
			if (eofp)
				*eofp = 1;
		} else {
			/*
			 * Need to get the rest of the entries from the daemon.
			 */
			uiop->uio_offset = AUTOFS_DAEMONCOOKIE;
		}
	}

done:
	kmem_free(outbuf, alloc_count);
	AUTOFS_DPRINT((5, "auto_readdir vp=%p offset=%lld eof=%d\n",
	    (void *)vp, uiop->uio_loffset, myeof));
	return (error);
}
Example #7
0
/*ARGSUSED*/
static int
bootfs_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp,
    caller_context_t *ct, int flags)
{
	bootfs_node_t *bnp = (bootfs_node_t *)vp->v_data;
	dirent64_t *dp;
	void *buf;
	ulong_t bsize, brem;
	offset_t coff, roff;
	int dlen, ret;
	bootfs_node_t *dnp;
	boolean_t first = B_TRUE;

	if (uiop->uio_loffset >= MAXOFF_T) {
		if (eofp != NULL)
			*eofp = 1;
		return (0);
	}

	if (uiop->uio_iovcnt != 1)
		return (EINVAL);

	if (!(uiop->uio_iov->iov_len > 0))
		return (EINVAL);

	if (vp->v_type != VDIR)
		return (ENOTDIR);

	roff = uiop->uio_loffset;
	coff = 0;
	brem = bsize = uiop->uio_iov->iov_len;
	buf = kmem_alloc(bsize, KM_SLEEP);
	dp = buf;

	/*
	 * Recall that offsets here are done based on the name of the dirent
	 * excluding the null terminator. Therefore `.` is always at 0, `..` is
	 * always at 1, and then the first real dirent is at 3. This offset is
	 * what's actually stored when we update the offset in the structure.
	 */
	if (roff == 0) {
		dlen = DIRENT64_RECLEN(1);
		if (first == B_TRUE) {
			if (dlen > brem) {
				kmem_free(buf, bsize);
				return (EINVAL);
			}
			first = B_FALSE;
		}
		dp->d_ino = (ino64_t)bnp->bvn_attr.va_nodeid;
		dp->d_off = 0;
		dp->d_reclen = (ushort_t)dlen;
		(void) strncpy(dp->d_name, ".", DIRENT64_NAMELEN(dlen));
		dp = (struct dirent64 *)((uintptr_t)dp + dp->d_reclen);
		brem -= dlen;
	}

	if (roff <= 1) {
		dlen = DIRENT64_RECLEN(2);
		if (first == B_TRUE) {
			if (dlen > brem) {
				kmem_free(buf, bsize);
				return (EINVAL);
			}
			first = B_FALSE;
		}
		dp->d_ino = (ino64_t)bnp->bvn_parent->bvn_attr.va_nodeid;
		dp->d_off = 1;
		dp->d_reclen = (ushort_t)dlen;
		(void) strncpy(dp->d_name, "..", DIRENT64_NAMELEN(dlen));
		dp = (struct dirent64 *)((uintptr_t)dp + dp->d_reclen);
		brem -= dlen;
	}

	coff = 3;
	for (dnp = avl_first(&bnp->bvn_dir); dnp != NULL;
	    dnp = AVL_NEXT(&bnp->bvn_dir, dnp)) {
		size_t nlen = strlen(dnp->bvn_name);

		if (roff > coff) {
			coff += nlen;
			continue;
		}

		dlen = DIRENT64_RECLEN(nlen);
		if (dlen > brem) {
			if (first == B_TRUE) {
				kmem_free(buf, bsize);
				return (EINVAL);
			}
			break;
		}
		first = B_FALSE;

		dp->d_ino = (ino64_t)dnp->bvn_attr.va_nodeid;
		dp->d_off = coff;
		dp->d_reclen = (ushort_t)dlen;
		(void) strncpy(dp->d_name, dnp->bvn_name,
		    DIRENT64_NAMELEN(dlen));
		dp = (struct dirent64 *)((uintptr_t)dp + dp->d_reclen);
		brem -= dlen;
		coff += nlen;
	}

	ret = uiomove(buf, (bsize - brem), UIO_READ, uiop);

	if (ret == 0) {
		if (dnp == NULL) {
			coff++;
			if (eofp != NULL)
				*eofp = 1;
		} else if (eofp != NULL) {
			*eofp = 0;
		}
		uiop->uio_loffset = coff;
	}
	gethrestime(&bnp->bvn_attr.va_atime);
	kmem_free(buf, bsize);
	return (ret);
}
Example #8
0
/*
 * Given a directory, return the full, resolved path.  This looks up "..",
 * searches for the given vnode in the parent, appends the component, etc.  It
 * is used to implement vnodetopath() and getcwd() when the cached path fails.
 */
static int
dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, int flags,
    cred_t *cr)
{
	pathname_t pn, rpn, emptypn;
	vnode_t *cmpvp, *pvp = NULL;
	vnode_t *startvp = vp;
	int err = 0, vprivs;
	size_t complen;
	char *dbuf;
	dirent64_t *dp;
	char		*bufloc;
	size_t		dlen = DIRENT64_RECLEN(MAXPATHLEN);
	refstr_t	*mntpt;

	/* Operation only allowed on directories */
	ASSERT(vp->v_type == VDIR);

	/* We must have at least enough space for "/" */
	if (buflen < 2)
		return (ENAMETOOLONG);

	/* Start at end of string with terminating null */
	bufloc = &buf[buflen - 1];
	*bufloc = '\0';

	pn_alloc(&pn);
	pn_alloc(&rpn);
	dbuf = kmem_alloc(dlen, KM_SLEEP);
	bzero(&emptypn, sizeof (emptypn));

	/*
	 * Begin with an additional reference on vp.  This will be decremented
	 * during the loop.
	 */
	VN_HOLD(vp);

	for (;;) {
		/*
		 * Return if we've reached the root.  If the buffer is empty,
		 * return '/'.  We explicitly don't use vn_compare(), since it
		 * compares the real vnodes.  A lofs mount of '/' would produce
		 * incorrect results otherwise.
		 */
		if (VN_CMP(vrootp, vp)) {
			if (*bufloc == '\0')
				*--bufloc = '/';
			break;
		}

		/*
		 * If we've reached the VFS root, something has gone wrong.  We
		 * should have reached the root in the above check.  The only
		 * explantation is that 'vp' is not contained withing the given
		 * root, in which case we return EPERM.
		 */
		if (VN_CMP(rootdir, vp)) {
			err = EPERM;
			goto out;
		}

		/*
		 * Shortcut: see if this vnode is a mountpoint.  If so,
		 * grab the path information from the vfs_t.
		 */
		if (vp->v_flag & VROOT) {

			mntpt = vfs_getmntpoint(vp->v_vfsp);
			if ((err = pn_set(&pn, (char *)refstr_value(mntpt)))
			    == 0) {
				refstr_rele(mntpt);
				rpn.pn_path = rpn.pn_buf;

				/*
				 * Ensure the mountpoint still exists.
				 */
				VN_HOLD(vrootp);
				if (vrootp != rootdir)
					VN_HOLD(vrootp);
				if (lookuppnvp(&pn, &rpn, flags, NULL,
				    &cmpvp, vrootp, vrootp, cr) == 0) {

					if (VN_CMP(vp, cmpvp)) {
						VN_RELE(cmpvp);

						complen = strlen(rpn.pn_path);
						bufloc -= complen;
						if (bufloc < buf) {
							err = ERANGE;
							goto out;
						}
						bcopy(rpn.pn_path, bufloc,
						    complen);
						break;
					} else {
						VN_RELE(cmpvp);
					}
				}
			} else {
				refstr_rele(mntpt);
			}
		}

		/*
		 * Shortcut: see if this vnode has correct v_path. If so,
		 * we have the work done.
		 */
		mutex_enter(&vp->v_lock);
		if (vp->v_path != NULL) {

			if ((err = pn_set(&pn, vp->v_path)) == 0) {
				mutex_exit(&vp->v_lock);
				rpn.pn_path = rpn.pn_buf;

				/*
				 * Ensure the v_path pointing to correct vnode
				 */
				VN_HOLD(vrootp);
				if (vrootp != rootdir)
					VN_HOLD(vrootp);
				if (lookuppnvp(&pn, &rpn, flags, NULL,
				    &cmpvp, vrootp, vrootp, cr) == 0) {

					if (VN_CMP(vp, cmpvp)) {
						VN_RELE(cmpvp);

						complen = strlen(rpn.pn_path);
						bufloc -= complen;
						if (bufloc < buf) {
							err = ERANGE;
							goto out;
						}
						bcopy(rpn.pn_path, bufloc,
						    complen);
						break;
					} else {
						VN_RELE(cmpvp);
					}
				}
			} else {
				mutex_exit(&vp->v_lock);
			}
		} else {
			mutex_exit(&vp->v_lock);
		}

		/*
		 * Shortcuts failed, search for this vnode in its parent.  If
		 * this is a mountpoint, then get the vnode underneath.
		 */
		if (vp->v_flag & VROOT)
			vp = vn_under(vp);
		if ((err = VOP_LOOKUP(vp, "..", &pvp, &emptypn, 0, vrootp, cr,
		    NULL, NULL, NULL)) != 0)
			goto out;

		/*
		 * With extended attributes, it's possible for a directory to
		 * have a parent that is a regular file.  Check for that here.
		 */
		if (pvp->v_type != VDIR) {
			err = ENOTDIR;
			goto out;
		}

		/*
		 * If this is true, something strange has happened.  This is
		 * only true if we are the root of a filesystem, which should
		 * have been caught by the check above.
		 */
		if (VN_CMP(pvp, vp)) {
			err = ENOENT;
			goto out;
		}

		/*
		 * Check if we have read and search privilege so, that
		 * we can lookup the path in the directory
		 */
		vprivs = (flags & LOOKUP_CHECKREAD) ? VREAD | VEXEC : VEXEC;
		if ((err = VOP_ACCESS(pvp, vprivs, 0, cr, NULL)) != 0) {
			goto out;
		}

		/*
		 * Try to obtain the path component from dnlc cache
		 * before searching through the directory.
		 */
		if ((cmpvp = dnlc_reverse_lookup(vp, dbuf, dlen)) != NULL) {
			/*
			 * If we got parent vnode as a result,
			 * then the answered path is correct.
			 */
			if (VN_CMP(cmpvp, pvp)) {
				VN_RELE(cmpvp);
				complen = strlen(dbuf);
				bufloc -= complen;
				if (bufloc <= buf) {
					err = ENAMETOOLONG;
					goto out;
				}
				bcopy(dbuf, bufloc, complen);

				/* Prepend a slash to the current path */
				*--bufloc = '/';

				/* And continue with the next component */
				VN_RELE(vp);
				vp = pvp;
				pvp = NULL;
				continue;
			} else {
				VN_RELE(cmpvp);
			}
		}

		/*
		 * Search the parent directory for the entry corresponding to
		 * this vnode.
		 */
		if ((err = dirfindvp(vrootp, pvp, vp, cr, dbuf, dlen, &dp))
		    != 0)
			goto out;
		complen = strlen(dp->d_name);
		bufloc -= complen;
		if (bufloc <= buf) {
			err = ENAMETOOLONG;
			goto out;
		}
		bcopy(dp->d_name, bufloc, complen);

		/* Prepend a slash to the current path.  */
		*--bufloc = '/';

		/* And continue with the next component */
		VN_RELE(vp);
		vp = pvp;
		pvp = NULL;
	}

	/*
	 * Place the path at the beginning of the buffer.
	 */
	if (bufloc != buf)
		ovbcopy(bufloc, buf, buflen - (bufloc - buf));

out:
	/*
	 * If the error was ESTALE and the current directory to look in
	 * was the root for this lookup, the root for a mounted file
	 * system, or the starting directory for lookups, then
	 * return ENOENT instead of ESTALE.  In this case, no recovery
	 * is possible by the higher level.  If ESTALE was returned for
	 * some intermediate directory along the path, then recovery
	 * is potentially possible and retrying from the higher level
	 * will either correct the situation by purging stale cache
	 * entries or eventually get back to the point where no recovery
	 * is possible.
	 */
	if (err == ESTALE &&
	    (VN_CMP(vp, vrootp) || (vp->v_flag & VROOT) || vp == startvp))
		err = ENOENT;

	kmem_free(dbuf, dlen);
	VN_RELE(vp);
	if (pvp)
		VN_RELE(pvp);
	pn_free(&pn);
	pn_free(&rpn);

	return (err);
}
Example #9
0
/*
 * If DV_BUILD is set, we call into nexus driver to do a BUS_CONFIG_ALL.
 * Otherwise, simply return cached dv_node's. Hotplug code always call
 * devfs_clean() to invalid the dv_node cache.
 */
static int
devfs_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, int *eofp)
{
	struct dv_node *ddv, *dv;
	struct dirent64 *de, *bufp;
	offset_t diroff;
	offset_t	soff;
	size_t reclen, movesz;
	int error;
	struct vattr va;
	size_t bufsz;

	ddv = VTODV(dvp);
	dcmn_err2(("devfs_readdir %s: offset %lld len %ld\n",
	    ddv->dv_name, uiop->uio_loffset, uiop->uio_iov->iov_len));
	ASSERT(ddv->dv_attr || ddv->dv_attrvp);
	ASSERT(RW_READ_HELD(&ddv->dv_contents));

	if (uiop->uio_loffset >= MAXOFF_T) {
		if (eofp)
			*eofp = 1;
		return (0);
	}

	if (uiop->uio_iovcnt != 1)
		return (EINVAL);

	if (dvp->v_type != VDIR)
		return (ENOTDIR);

	/* Load the initial contents */
	if (ddv->dv_flags & DV_BUILD) {
		if (!rw_tryupgrade(&ddv->dv_contents)) {
			rw_exit(&ddv->dv_contents);
			rw_enter(&ddv->dv_contents, RW_WRITER);
		}

		/* recheck and fill */
		if (ddv->dv_flags & DV_BUILD)
			dv_filldir(ddv);

		rw_downgrade(&ddv->dv_contents);
	}

	soff = uiop->uio_offset;
	bufsz = uiop->uio_iov->iov_len;
	de = bufp = kmem_alloc(bufsz, KM_SLEEP);
	movesz = 0;
	dv = (struct dv_node *)-1;

	/*
	 * Move as many entries into the uio structure as it will take.
	 * Special case "." and "..".
	 */
	diroff = 0;
	if (soff == 0) {				/* . */
		reclen = DIRENT64_RECLEN(strlen("."));
		if ((movesz + reclen) > bufsz)
			goto full;
		de->d_ino = (ino64_t)ddv->dv_ino;
		de->d_off = (off64_t)diroff + 1;
		de->d_reclen = (ushort_t)reclen;

		/* use strncpy(9f) to zero out uninitialized bytes */

		(void) strncpy(de->d_name, ".", DIRENT64_NAMELEN(reclen));
		movesz += reclen;
		de = (dirent64_t *)((char *)de + reclen);
		dcmn_err3(("devfs_readdir: A: diroff %lld, soff %lld: '%s' "
		    "reclen %lu\n", diroff, soff, ".", reclen));
	}

	diroff++;
	if (soff <= 1) {				/* .. */
		reclen = DIRENT64_RECLEN(strlen(".."));
		if ((movesz + reclen) > bufsz)
			goto full;
		de->d_ino = (ino64_t)ddv->dv_dotdot->dv_ino;
		de->d_off = (off64_t)diroff + 1;
		de->d_reclen = (ushort_t)reclen;

		/* use strncpy(9f) to zero out uninitialized bytes */

		(void) strncpy(de->d_name, "..", DIRENT64_NAMELEN(reclen));
		movesz += reclen;
		de = (dirent64_t *)((char *)de + reclen);
		dcmn_err3(("devfs_readdir: B: diroff %lld, soff %lld: '%s' "
		    "reclen %lu\n", diroff, soff, "..", reclen));
	}

	diroff++;
	for (dv = ddv->dv_dot; dv; dv = dv->dv_next, diroff++) {
		/*
		 * although DDM_INTERNAL_PATH minor nodes are skipped for
		 * readdirs outside the kernel, they still occupy directory
		 * offsets
		 */
		if (diroff < soff ||
		    ((dv->dv_flags & DV_INTERNAL) && (cred != kcred)))
			continue;

		reclen = DIRENT64_RECLEN(strlen(dv->dv_name));
		if ((movesz + reclen) > bufsz) {
			dcmn_err3(("devfs_readdir: C: diroff "
			    "%lld, soff %lld: '%s' reclen %lu\n",
			    diroff, soff, dv->dv_name, reclen));
			goto full;
		}
		de->d_ino = (ino64_t)dv->dv_ino;
		de->d_off = (off64_t)diroff + 1;
		de->d_reclen = (ushort_t)reclen;

		/* use strncpy(9f) to zero out uninitialized bytes */

		ASSERT(strlen(dv->dv_name) + 1 <=
		    DIRENT64_NAMELEN(reclen));
		(void) strncpy(de->d_name, dv->dv_name,
		    DIRENT64_NAMELEN(reclen));

		movesz += reclen;
		de = (dirent64_t *)((char *)de + reclen);
		dcmn_err4(("devfs_readdir: D: diroff "
		    "%lld, soff %lld: '%s' reclen %lu\n", diroff, soff,
		    dv->dv_name, reclen));
	}

	/* the buffer is full, or we exhausted everything */
full:	dcmn_err3(("devfs_readdir: moving %lu bytes: "
	    "diroff %lld, soff %lld, dv %p\n",
	    movesz, diroff, soff, (void *)dv));

	if ((movesz == 0) && dv)
		error = EINVAL;		/* cannot be represented */
	else {
		error = uiomove(bufp, movesz, UIO_READ, uiop);
		if (error == 0) {
			if (eofp)
				*eofp = dv ? 0 : 1;
			uiop->uio_offset = diroff;
		}

		va.va_mask = AT_ATIME;
		gethrestime(&va.va_atime);
		rw_exit(&ddv->dv_contents);
		(void) devfs_setattr(dvp, &va, 0, cred, NULL);
		rw_enter(&ddv->dv_contents, RW_READER);
	}

	kmem_free(bufp, bufsz);
	return (error);
}
Example #10
0
/***********************************************************************
 * iumfs_add_entry_to_dir_nolock()
 *
 *  ディレクトリに、引数で渡された名前の新しいエントリを追加する。
 *  implementation of iumfs_add_entry_to_dir().
 *  This function must be called afer getting lock of dirinp->i_dlock.
 *
 *  引数:
 *
 *     dirvp     : ディレクトリの vnode 構造体
 *     name      : 追加するディレクトリエントリ(ファイル)の名前
 *     name_size : ディレクトリエントリの名前の長さ。
 *     nodeid    : 追加するディレクトリエントリのノード番号(inode番号)
 *
 *  戻り値
 *
 *    正常時   : 確保したディレクトリエントリ用のメモリのサイズ
 *                すでにエントリが存在しているときは 0
 *      エラー時 : -1
 *
 ***********************************************************************/
int
iumfs_add_entry_to_dir_nolock(vnode_t *dirvp, char *name, int name_size, ino_t nodeid)
{
    iumnode_t *dirinp;
    dirent64_t *new_dentp; // 新しいディレクトリエントリのポインタ
    uchar_t *newp; // kmem_zalloc() で確保したメモリへのポインタ
    offset_t dent_total; // 全てのディレクトリエントリの合計サイズ
    int err;
    offset_t offset;
    dirent64_t *dentp;    

    DEBUG_PRINT((CE_CONT, "iumfs_add_entry_to_dir_nolock is called\n"));
    DEBUG_PRINT((CE_CONT, "iumfs_add_entry_to_dir_nolock: name=\"%s\", name_size=%d, nodeid=%d\n", name, name_size, nodeid));

    dirinp = VNODE2IUMNODE(dirvp);
    ASSERT(mutex_owned(&(dirinp->i_dlock))); // must hold the mutext before called.

    /*
     * ディレクトリの中にすでにエントリがないかどうかをチェックする。
     */
    dentp = (dirent64_t *) dirinp->data;    
    for (offset = 0; offset < dirinp->dlen; offset += dentp->d_reclen) {
        dentp = (dirent64_t *) ((char *) dirinp->data + offset);
        if ((strcmp(dentp->d_name, name) == 0)) {
            // すでにエントリが存在する。
            return(0);
        }
    }    

    /*
     * 確保するディレクトリエントリ分のサイズを求める
     */
    if (dirinp->data != NULL && dirinp->dlen > 0)
        dent_total = dirinp->dlen + DIRENT64_RECLEN(name_size);
    else
        dent_total = DIRENT64_RECLEN(name_size);

    DEBUG_PRINT((CE_CONT, "iumfs_add_entry_to_dir_nolock: dent_total=%d\n", dent_total));

    /*
     * ディレクトリエントリ用の領域を新たに確保
     */
    newp = (uchar_t *) kmem_zalloc(dent_total, KM_NOSLEEP);
    if (newp == NULL) {
        cmn_err(CE_CONT, "iumfs_add_entry_to_dir_nolock: kmem_zalloc failed");
        err = ENOMEM;
        goto error;
    }

    /*
     * 既存のディレクトリエントリがあれば、それを新しく確保した
     * メモリにコピーし、その後開放する。
     */
    if (dirinp->data != NULL && dirinp->dlen > 0) {
        bcopy(dirinp->data, newp, dirinp->dlen);
        /*
         * 危険な感じだが、ロックを取らずにこのデータを読む関数は
         * いないので、ここでフリーしても Panic は起きない・・はず。
         */
        kmem_free(dirinp->data, dirinp->dlen);

        new_dentp = (dirent64_t *) (newp + dirinp->dlen);
    } else {
        new_dentp = (dirent64_t *) newp;
    }

    /*
     * dirent 構造体に新しいディレクトリエントリの情報をセット
     */
    new_dentp->d_ino = nodeid;
    new_dentp->d_off = dirinp->dlen; /* 新しい dirent のオフセット。つまり現在の総 diernt の長さ */
    new_dentp->d_reclen = DIRENT64_RECLEN(name_size);
    bcopy(name, new_dentp->d_name, name_size);

    /*
     *  ディレクトリの iumnode の "data" が新しく確保したアドレスを
     *  さすように変更。
     */
    dirinp->data = (void *) newp;
    dirinp->dlen = dent_total;
    DEBUG_PRINT((CE_CONT, "iumfs_add_entry_to_dir: new directory size=%d\n", dirinp->dlen));

    DIRENT_SANITY_CHECK("iumfs_add_entry_to_dir", dirinp);    

    /*
     * ディレクトリのサイズも新しく確保したメモリのサイズに変更
     * 同時にアクセス時間、変更時間も変更
     */
    dirinp->vattr.va_size = dent_total;
    dirinp->vattr.va_atime = iumfs_get_current_time();
    dirinp->vattr.va_mtime = iumfs_get_current_time();

    /*
     * 正常終了。最終的に確保したディレクトリエントリのサイズを返す。
     */
    DEBUG_PRINT((CE_CONT, "iumfs_add_entry_to_dir_nolock: return(%d)", dent_total));
    return (dent_total);

error:
    if (newp)
        kmem_free(newp, dent_total);
    DEBUG_PRINT((CE_CONT, "iumfs_add_entry_to_dir_nolock: return(-1)"));
    return (-1);
}