Rewrite coda_readdir() to directly process the container file.

Passing this operation down to the file system holding the container
cannot work for anything but UFS and UFS doesn't allow reading
directory from a plain file since ~2015.

Fixes PR kern/55775 Coda client, its in-kernel part, opens wrong files ...
This commit is contained in:
hannken 2020-11-14 11:42:56 +00:00
parent 9b9c303ee5
commit b05a64bbf4
2 changed files with 126 additions and 48 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: coda.h,v 1.20 2018/04/19 21:50:07 christos Exp $ */
/* $NetBSD: coda.h,v 1.21 2020/11/14 11:42:56 hannken Exp $ */
/*
@ -61,6 +61,10 @@ Mellon the rights to redistribute these changes without encumbrance.
#define CODA_MAXSYMLINKS 10
#endif
#ifndef CODA_DIRBLKSIZ
#define CODA_DIRBLKSIZ 0x1000
#endif
#if defined(DJGPP) || defined(__CYGWIN32__)
#ifdef KERNEL
typedef unsigned long u_long;
@ -169,7 +173,7 @@ struct venus_dirent {
#ifndef _VENUS_DIRENT_T_
#define _VENUS_DIRENT_T_ 1
struct venus_dirent {
unsigned long d_fileno; /* file number of entry */
unsigned int d_fileno; /* file number of entry */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* file type, see below */
unsigned char d_namlen; /* length of string in d_name */

View File

@ -1,4 +1,4 @@
/* $NetBSD: coda_vnops.c,v 1.113 2020/11/14 11:42:05 hannken Exp $ */
/* $NetBSD: coda_vnops.c,v 1.114 2020/11/14 11:42:56 hannken Exp $ */
/*
*
@ -46,7 +46,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: coda_vnops.c,v 1.113 2020/11/14 11:42:05 hannken Exp $");
__KERNEL_RCSID(0, "$NetBSD: coda_vnops.c,v 1.114 2020/11/14 11:42:56 hannken Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -62,6 +62,7 @@ __KERNEL_RCSID(0, "$NetBSD: coda_vnops.c,v 1.113 2020/11/14 11:42:05 hannken Exp
#include <sys/select.h>
#include <sys/vnode.h>
#include <sys/kauth.h>
#include <sys/dirent.h>
#include <miscfs/genfs/genfs.h>
#include <miscfs/specfs/specdev.h>
@ -1528,70 +1529,143 @@ int
coda_readdir(void *v)
{
/* true args */
struct vop_readdir_args *ap = v;
vnode_t *vp = ap->a_vp;
struct cnode *cp = VTOC(vp);
struct uio *uiop = ap->a_uio;
kauth_cred_t cred = ap->a_cred;
int *eofflag = ap->a_eofflag;
off_t **cookies = ap->a_cookies;
int *ncookies = ap->a_ncookies;
struct vop_readdir_args *ap = v;
vnode_t *vp = ap->a_vp;
struct cnode *cp = VTOC(vp);
struct uio *uiop = ap->a_uio;
kauth_cred_t cred = ap->a_cred;
int *eofflag = ap->a_eofflag;
/* upcall decl */
/* locals */
int error = 0;
enum vtype saved_type;
size_t initial_resid = uiop->uio_resid;
int error = 0;
int opened_internally = 0;
int ncookies;
char *buf;
struct vnode *cvp;
struct dirent *dirp;
MARK_ENTRY(CODA_READDIR_STATS);
MARK_ENTRY(CODA_READDIR_STATS);
CODADEBUG(CODA_READDIR, myprintf(("%s: (%p, %lu, %lld)\n", __func__,
uiop->uio_iov->iov_base, (unsigned long) uiop->uio_resid,
(long long) uiop->uio_offset)); )
CODADEBUG(CODA_READDIR, myprintf(("%s: (%p, %lu, %lld)\n", __func__,
uiop->uio_iov->iov_base, (unsigned long) uiop->uio_resid,
(long long) uiop->uio_offset)); )
/* Check for readdir of control object. */
if (IS_CTL_VP(vp)) {
MARK_INT_FAIL(CODA_READDIR_STATS);
return(ENOENT);
}
{
/* Redirect the request to UFS. */
/* Check for readdir of control object. */
if (IS_CTL_VP(vp)) {
MARK_INT_FAIL(CODA_READDIR_STATS);
return ENOENT;
}
/* If directory is not already open do an "internal open" on it. */
int opened_internally = 0;
if (cp->c_ovp == NULL) {
opened_internally = 1;
MARK_INT_GEN(CODA_OPEN_STATS);
error = VOP_OPEN(vp, FREAD, cred);
opened_internally = 1;
MARK_INT_GEN(CODA_OPEN_STATS);
error = VOP_OPEN(vp, FREAD, cred);
#ifdef CODA_VERBOSE
printf("%s: Internally Opening %p\n", __func__, vp);
printf("%s: Internally Opening %p\n", __func__, vp);
#endif
if (error) return(error);
} else
vp = cp->c_ovp;
if (error)
return error;
KASSERT(cp->c_ovp != NULL);
}
cvp = cp->c_ovp;
/* Have UFS handle the call. */
CODADEBUG(CODA_READDIR, myprintf(("%s: fid = %s, refcnt = %d\n",
__func__, coda_f2s(&cp->c_fid), vrefcnt(vp))); )
saved_type = vp->v_type;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
vp->v_type = VDIR; /* pretend the container file is a dir */
error = VOP_READDIR(vp, uiop, cred, eofflag, cookies, ncookies);
vp->v_type = saved_type;
VOP_UNLOCK(vp);
__func__, coda_f2s(&cp->c_fid), vrefcnt(cvp))); )
if (ap->a_ncookies) {
ncookies = ap->a_uio->uio_resid / _DIRENT_RECLEN(dirp, 1);
*ap->a_ncookies = 0;
*ap->a_cookies = malloc(ncookies * sizeof (off_t),
M_TEMP, M_WAITOK);
}
buf = kmem_alloc(CODA_DIRBLKSIZ, KM_SLEEP);
dirp = kmem_alloc(sizeof(*dirp), KM_SLEEP);
vn_lock(cvp, LK_EXCLUSIVE | LK_RETRY);
while (error == 0) {
size_t resid = 0;
char *dp, *ep;
if (!ALIGNED_POINTER(uiop->uio_offset, uint32_t)) {
error = EINVAL;
break;
}
error = vn_rdwr(UIO_READ, cvp, buf,
CODA_DIRBLKSIZ, uiop->uio_offset,
UIO_SYSSPACE, IO_NODELOCKED, cred, &resid, curlwp);
if (error || resid == CODA_DIRBLKSIZ)
break;
for (dp = buf, ep = dp + CODA_DIRBLKSIZ - resid; dp < ep; ) {
off_t off;
struct venus_dirent *vd = (struct venus_dirent *)dp;
if (!ALIGNED_POINTER(vd, uint32_t) ||
!ALIGNED_POINTER(vd->d_reclen, uint32_t) ||
vd->d_reclen == 0) {
error = EINVAL;
break;
}
if (dp + vd->d_reclen > ep) {
error = ENAMETOOLONG;
break;
}
if (vd->d_namlen == 0) {
uiop->uio_offset += vd->d_reclen;
dp += vd->d_reclen;
continue;
}
dirp->d_fileno = vd->d_fileno;
dirp->d_type = vd->d_type;
dirp->d_namlen = vd->d_namlen;
dirp->d_reclen = _DIRENT_SIZE(dirp);
strlcpy(dirp->d_name, vd->d_name, dirp->d_namlen + 1);
if (uiop->uio_resid < dirp->d_reclen) {
error = ENAMETOOLONG;
break;
}
off = uiop->uio_offset;
error = uiomove(dirp, dirp->d_reclen, uiop);
uiop->uio_offset = off;
if (error)
break;
uiop->uio_offset += vd->d_reclen;
dp += vd->d_reclen;
if (ap->a_ncookies)
(*ap->a_cookies)[(*ap->a_ncookies)++] =
uiop->uio_offset;
}
}
VOP_UNLOCK(cvp);
kmem_free(dirp, sizeof(*dirp));
kmem_free(buf, CODA_DIRBLKSIZ);
if (eofflag && error == 0)
*eofflag = 1;
if (uiop->uio_resid < initial_resid && error == ENAMETOOLONG)
error = 0;
if (ap->a_ncookies && error) {
free(*ap->a_cookies, M_TEMP);
*ap->a_ncookies = 0;
*ap->a_cookies = NULL;
}
if (error)
MARK_INT_FAIL(CODA_READDIR_STATS);
MARK_INT_FAIL(CODA_READDIR_STATS);
else
MARK_INT_SAT(CODA_READDIR_STATS);
MARK_INT_SAT(CODA_READDIR_STATS);
/* Do an "internal close" if necessary. */
if (opened_internally) {
MARK_INT_GEN(CODA_CLOSE_STATS);
(void)VOP_CLOSE(vp, FREAD, cred);
MARK_INT_GEN(CODA_CLOSE_STATS);
(void)VOP_CLOSE(vp, FREAD, cred);
}
}
return(error);
return error;
}
/*