summaryrefslogtreecommitdiffstats
path: root/sys/kern/vfs_default.c
diff options
context:
space:
mode:
authormarcus <marcus@FreeBSD.org>2009-03-08 19:05:53 +0000
committermarcus <marcus@FreeBSD.org>2009-03-08 19:05:53 +0000
commit399996bc30b5483814c38a0591e585f0cbe8d4cf (patch)
tree1ee7e0cf0c231c3fc97b2ac4472f681c6c17351e /sys/kern/vfs_default.c
parent5dd07919448d34998c1ec92a5ae00e72b94ae323 (diff)
downloadFreeBSD-src-399996bc30b5483814c38a0591e585f0cbe8d4cf.zip
FreeBSD-src-399996bc30b5483814c38a0591e585f0cbe8d4cf.tar.gz
Add a default implementation for VOP_VPTOCNP(9) which scans the parent
directory of a vnode to find a dirent with a matching file number. The name from that dirent is then used to provide the component name. Note: if the initial vnode argument is not a directory itself, then the default VOP_VPTOCNP(9) implementation still returns ENOENT. Reviewed by: kib Approved by: kib Tested by: pho
Diffstat (limited to 'sys/kern/vfs_default.c')
-rw-r--r--sys/kern/vfs_default.c236
1 files changed, 235 insertions, 1 deletions
diff --git a/sys/kern/vfs_default.c b/sys/kern/vfs_default.c
index e047a90..1001497 100644
--- a/sys/kern/vfs_default.c
+++ b/sys/kern/vfs_default.c
@@ -48,8 +48,11 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/fcntl.h>
#include <sys/unistd.h>
#include <sys/vnode.h>
+#include <sys/dirent.h>
#include <sys/poll.h>
#include <vm/vm.h>
@@ -63,6 +66,14 @@ __FBSDID("$FreeBSD$");
static int vop_nolookup(struct vop_lookup_args *);
static int vop_nostrategy(struct vop_strategy_args *);
+static int get_next_dirent(struct vnode *vp, struct dirent **dpp,
+ char *dirbuf, int dirbuflen, off_t *off,
+ char **cpos, int *len, int *eofflag,
+ struct thread *td);
+static int dirent_exists(struct vnode *vp, const char *dirname,
+ struct thread *td);
+
+#define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4)
/*
* This vnode table stores what we want to do if the filesystem doesn't
@@ -98,7 +109,7 @@ struct vop_vector default_vnodeops = {
.vop_revoke = VOP_PANIC,
.vop_strategy = vop_nostrategy,
.vop_unlock = vop_stdunlock,
- .vop_vptocnp = VOP_ENOENT,
+ .vop_vptocnp = vop_stdvptocnp,
.vop_vptofh = vop_stdvptofh,
};
@@ -210,6 +221,108 @@ vop_nostrategy (struct vop_strategy_args *ap)
return (EOPNOTSUPP);
}
+static int
+get_next_dirent(struct vnode *vp, struct dirent **dpp, char *dirbuf,
+ int dirbuflen, off_t *off, char **cpos, int *len,
+ int *eofflag, struct thread *td)
+{
+ int error, reclen;
+ struct uio uio;
+ struct iovec iov;
+ struct dirent *dp;
+
+ KASSERT(VOP_ISLOCKED(vp), ("vp %p is not locked", vp));
+ KASSERT(vp->v_type == VDIR, ("vp %p is not a directory", vp));
+
+ if (*len == 0) {
+ iov.iov_base = dirbuf;
+ iov.iov_len = dirbuflen;
+
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_offset = *off;
+ uio.uio_resid = dirbuflen;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_rw = UIO_READ;
+ uio.uio_td = td;
+
+ *eofflag = 0;
+
+#ifdef MAC
+ error = mac_vnode_check_readdir(td->td_ucred, vp);
+ if (error == 0)
+#endif
+ error = VOP_READDIR(vp, &uio, td->td_ucred, eofflag,
+ NULL, NULL);
+ if (error)
+ return (error);
+
+ *off = uio.uio_offset;
+
+ *cpos = dirbuf;
+ *len = (dirbuflen - uio.uio_resid);
+ }
+
+ dp = (struct dirent *)(*cpos);
+ reclen = dp->d_reclen;
+ *dpp = dp;
+
+ /* check for malformed directory.. */
+ if (reclen < DIRENT_MINSIZE)
+ return (EINVAL);
+
+ *cpos += reclen;
+ *len -= reclen;
+
+ return (0);
+}
+
+/*
+ * Check if a named file exists in a given directory vnode.
+ */
+static int
+dirent_exists(struct vnode *vp, const char *dirname, struct thread *td)
+{
+ char *dirbuf, *cpos;
+ int error, eofflag, dirbuflen, len, found;
+ off_t off;
+ struct dirent *dp;
+ struct vattr va;
+
+ KASSERT(VOP_ISLOCKED(vp), ("vp %p is not locked", vp));
+ KASSERT(vp->v_type == VDIR, ("vp %p is not a directory", vp));
+
+ found = 0;
+
+ error = VOP_GETATTR(vp, &va, td->td_ucred);
+ if (error)
+ return (found);
+
+ dirbuflen = DEV_BSIZE;
+ if (dirbuflen < va.va_blocksize)
+ dirbuflen = va.va_blocksize;
+ dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK);
+
+ off = 0;
+ len = 0;
+ do {
+ error = get_next_dirent(vp, &dp, dirbuf, dirbuflen, &off,
+ &cpos, &len, &eofflag, td);
+ if (error)
+ goto out;
+
+ if ((dp->d_type != DT_WHT) &&
+ !strcmp(dp->d_name, dirname)) {
+ found = 1;
+ goto out;
+ }
+ } while (len > 0 || !eofflag);
+
+out:
+ free(dirbuf, M_TEMP);
+ return (found);
+}
+
/*
* Advisory record locking support
*/
@@ -557,6 +670,127 @@ vop_stdvptofh(struct vop_vptofh_args *ap)
return (EOPNOTSUPP);
}
+int
+vop_stdvptocnp(struct vop_vptocnp_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct vnode **dvp = ap->a_vpp;
+ char *buf = ap->a_buf;
+ int *buflen = ap->a_buflen;
+ char *dirbuf, *cpos;
+ int i, error, eofflag, dirbuflen, flags, locked, len, covered;
+ off_t off;
+ ino_t fileno;
+ struct vattr va;
+ struct nameidata nd;
+ struct thread *td;
+ struct dirent *dp;
+ struct vnode *mvp;
+
+ i = *buflen;
+ error = 0;
+ covered = 0;
+ td = curthread;
+
+ if (vp->v_type != VDIR)
+ return (ENOENT);
+
+ error = VOP_GETATTR(vp, &va, td->td_ucred);
+ if (error)
+ return (error);
+
+ VREF(vp);
+ locked = VOP_ISLOCKED(vp);
+ VOP_UNLOCK(vp, 0);
+ NDINIT_ATVP(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE,
+ "..", vp, td);
+ flags = FREAD;
+ error = vn_open(&nd, &flags, 0, NULL);
+ if (error) {
+ vn_lock(vp, locked | LK_RETRY);
+ return (error);
+ }
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+
+ mvp = *dvp = nd.ni_vp;
+
+ if (vp->v_mount != (*dvp)->v_mount &&
+ ((*dvp)->v_vflag & VV_ROOT) &&
+ ((*dvp)->v_mount->mnt_flag & MNT_UNION)) {
+ *dvp = (*dvp)->v_mount->mnt_vnodecovered;
+ VREF(mvp);
+ VOP_UNLOCK(mvp, 0);
+ vn_close(mvp, FREAD, td->td_ucred, td);
+ VREF(*dvp);
+ vn_lock(*dvp, LK_EXCLUSIVE | LK_RETRY);
+ covered = 1;
+ }
+
+ fileno = va.va_fileid;
+
+ dirbuflen = DEV_BSIZE;
+ if (dirbuflen < va.va_blocksize)
+ dirbuflen = va.va_blocksize;
+ dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK);
+
+ if ((*dvp)->v_type != VDIR) {
+ error = ENOENT;
+ goto out;
+ }
+
+ off = 0;
+ len = 0;
+ do {
+ /* call VOP_READDIR of parent */
+ error = get_next_dirent(*dvp, &dp, dirbuf, dirbuflen, &off,
+ &cpos, &len, &eofflag, td);
+ if (error)
+ goto out;
+
+ if ((dp->d_type != DT_WHT) &&
+ (dp->d_fileno == fileno)) {
+ if (covered) {
+ VOP_UNLOCK(*dvp, 0);
+ vn_lock(mvp, LK_EXCLUSIVE | LK_RETRY);
+ if (dirent_exists(mvp, dp->d_name, td)) {
+ error = ENOENT;
+ VOP_UNLOCK(mvp, 0);
+ vn_lock(*dvp, LK_EXCLUSIVE | LK_RETRY);
+ goto out;
+ }
+ VOP_UNLOCK(mvp, 0);
+ vn_lock(*dvp, LK_EXCLUSIVE | LK_RETRY);
+ }
+ i -= dp->d_namlen;
+
+ if (i < 0) {
+ error = ENOMEM;
+ goto out;
+ }
+ bcopy(dp->d_name, buf + i, dp->d_namlen);
+ error = 0;
+ goto out;
+ }
+ } while (len > 0 || !eofflag);
+ error = ENOENT;
+
+out:
+ free(dirbuf, M_TEMP);
+ if (!error) {
+ *buflen = i;
+ vhold(*dvp);
+ }
+ if (covered) {
+ vput(*dvp);
+ vrele(mvp);
+ } else {
+ VOP_UNLOCK(mvp, 0);
+ vn_close(mvp, FREAD, td->td_ucred, td);
+ }
+ vn_lock(vp, locked | LK_RETRY);
+ return (error);
+}
+
/*
* vfs default ops
* used to fill the vfs function table to get reasonable default return values.
OpenPOWER on IntegriCloud