mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-28 16:43:09 +00:00
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
This commit is contained in:
parent
01cd0a978f
commit
f8ecc40737
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=189539
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user