Beispiel #1
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);
}
Beispiel #2
0
STATIC int linvfs_readdir(
	struct file *filp,
	void *dirent,
	filldir_t filldir)
{
	int			error = 0;
	vnode_t			*vp;
	uio_t			uio;
	iovec_t			iov;
	int			eof = 0;
	cred_t			cred;		/* Temporary cred workaround */
	caddr_t			read_buf;
	int			namelen, size = 0;
	size_t			rlen = PAGE_CACHE_SIZE << 2;
	off_t			start_offset;
	dirent_t		*dbp = NULL;

        vp = LINVFS_GET_VP(filp->f_dentry->d_inode);

	ASSERT(vp);
	/* Try fairly hard to get memory */
	do {
		if ((read_buf = (caddr_t)kmalloc(rlen, GFP_KERNEL)))
			break;
		rlen >>= 1;
	} while (rlen >= 1024);

	if (read_buf == NULL)
		return -ENOMEM;

	uio.uio_iov = &iov;
	uio.uio_fmode = filp->f_mode;
	uio.uio_segflg = UIO_SYSSPACE;
	uio.uio_offset = filp->f_pos;

	while (!eof) {
		uio.uio_resid = iov.iov_len = rlen;
		iov.iov_base = read_buf;
		uio.uio_iovcnt = 1;

		start_offset = uio.uio_offset;
		
		VOP_READDIR(vp, &uio, &cred, &eof, error);
		if ((uio.uio_offset == start_offset) || error) {
			size = 0;
			break;
		}

		size = rlen - uio.uio_resid;
		dbp = (dirent_t *)read_buf;
		while (size > 0) {
			namelen = strlen(dbp->d_name);

			if (filldir(dirent, dbp->d_name, namelen,
					(off_t) dbp->d_off,
					(linux_ino_t) dbp->d_ino,
					DT_UNKNOWN)) {
				goto done;
			}
			size -= dbp->d_reclen;
			dbp = nextdp(dbp);
		}
	}
done:
	if (!error) {
		if (size == 0)
			filp->f_pos = uio.uio_offset;
		else if (dbp)
			filp->f_pos = dbp->d_off;
	}

	kfree(read_buf);
	return -error;
}