Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F175848772
D60603.id189506.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
D60603.id189506.diff
View Options
diff --git a/sys/fs/nfsclient/nfs_clbio.c b/sys/fs/nfsclient/nfs_clbio.c
--- a/sys/fs/nfsclient/nfs_clbio.c
+++ b/sys/fs/nfsclient/nfs_clbio.c
@@ -71,6 +71,8 @@
struct thread *td);
static int nfs_directio_write(struct vnode *vp, struct uio *uiop,
struct ucred *cred, int ioflag);
+static int ncl_check_record(struct buf *bp, int pos);
+static int ncl_record_start(struct buf *bp, int on);
/*
* Vnode op for VM getpages.
@@ -437,8 +439,9 @@
struct thread *td;
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
daddr_t lbn, rabn;
- int biosize, bcount, error, i, n, nra, on, save2, seqcount;
+ int biosize, bcount, error, i, n, newon, nra, on, reanchor, save2, seqcount;
off_t tmp_off;
+ bool copied;
KASSERT(uio->uio_rw == UIO_READ, ("ncl_read mode"));
if (uio->uio_resid == 0)
@@ -479,6 +482,7 @@
return error;
save2 = curthread_pflags2_set(TDP2_SBPAGES);
+ copied = false;
do {
u_quad_t nsize;
@@ -679,6 +683,30 @@
goto out;
}
+ /*
+ * A saved per-entry position can point inside a record when
+ * the block is rebuilt from a modified directory. Move
+ * back to the start of the record which contains it so
+ * that only whole records are returned. This does not
+ * preserve the identity of the saved position across
+ * changes to the synthesized layout; entries can be
+ * repeated or skipped. The position is committed only
+ * once a complete record can be copied.
+ */
+ reanchor = 0;
+ if (on > 0 && (bp->b_flags & B_INVAL) == 0) {
+ newon = ncl_record_start(bp, on);
+ if (newon < 0) {
+ brelse(bp);
+ error = EIO;
+ goto out;
+ }
+ if (newon != on) {
+ reanchor = on - newon;
+ on = newon;
+ }
+ }
+
/*
* If not eof and read aheads are enabled, start one.
* (You need the current block first, so that you have the
@@ -722,9 +750,45 @@
* right here.
*/
n = lmin(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid - on);
- if (np->n_direofoffset && n > np->n_direofoffset - uio->uio_offset)
- n = np->n_direofoffset - uio->uio_offset;
+ if (np->n_direofoffset &&
+ n > np->n_direofoffset - (uio->uio_offset - reanchor))
+ n = np->n_direofoffset - (uio->uio_offset - reanchor);
NFSUNLOCKNODE(np);
+ /*
+ * Return only whole records. When the position above was
+ * re-anchored, the caller's read may end in the middle of a
+ * record.
+ */
+ if (n > 0) {
+ int pos2, reclen2;
+
+ for (pos2 = 0; pos2 < n; pos2 += reclen2) {
+ reclen2 = ncl_check_record(bp, on + pos2);
+ if (reclen2 < 0) {
+ brelse(bp);
+ error = EIO;
+ goto out;
+ }
+ if (pos2 + reclen2 > n)
+ break;
+ }
+ if (pos2 == 0) {
+ if (!copied) {
+ brelse(bp);
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * The remaining space is smaller than the
+ * next record; stop here.
+ */
+ n = 0;
+ } else {
+ if (reanchor != 0)
+ uio->uio_offset -= reanchor;
+ n = pos2;
+ }
+ }
break;
default:
printf(" ncl_bioread: type %x unexpected\n", vp->v_type);
@@ -734,6 +798,8 @@
if (n > 0) {
error = vn_io_fault_uiomove(bp->b_data + on, (int)n, uio);
+ if (error == 0)
+ copied = true;
}
if (vp->v_type == VLNK)
n = 0;
@@ -1542,6 +1608,75 @@
return (EIO);
}
+/*
+ * Validate the directory record at byte offset 'pos' of a directory
+ * block and return its length, or -1 when the block is malformed.
+ */
+static int
+ncl_check_record(struct buf *bp, int pos)
+{
+ struct dirent *dp;
+ off_t off;
+
+ if (pos < 0 || bp->b_bcount - pos < (int)offsetof(struct dirent, d_name))
+ return (-1);
+ dp = (struct dirent *)(bp->b_data + pos);
+ off = (off_t)bp->b_lblkno * NFS_DIRBLKSIZ + pos;
+ if (dp->d_reclen < _GENERIC_DIRLEN(dp->d_namlen) ||
+ (dp->d_reclen & (sizeof(uint64_t) - 1)) != 0 ||
+ dp->d_reclen > DIRBLKSIZ - (off & (DIRBLKSIZ - 1)) ||
+ dp->d_reclen > bp->b_bcount - pos)
+ return (-1);
+ return (dp->d_reclen);
+}
+
+/*
+ * Set next-entry offsets in the client's synthesized directory stream.
+ * Successful nonempty readdir RPCs pad the entire buffer; require records
+ * to cover it exactly without crossing DIRBLKSIZ boundaries.
+ */
+static int
+ncl_fix_d_off(struct buf *bp)
+{
+ struct dirent *dp;
+ off_t off;
+ int pos, reclen;
+
+ off = (off_t)bp->b_lblkno * NFS_DIRBLKSIZ;
+ for (pos = 0; pos < bp->b_bcount; pos += reclen) {
+ reclen = ncl_check_record(bp, pos);
+ if (reclen < 0) {
+ printf("ncl_doio: malformed directory block %jd\n",
+ (intmax_t)bp->b_lblkno);
+ return (EIO);
+ }
+ off += reclen;
+ dp = (struct dirent *)(bp->b_data + pos);
+ dp->d_off = off;
+ }
+ return (0);
+}
+
+/*
+ * Return the start of the record which contains the logical offset 'on'
+ * inside a directory block, or -1 when the block is malformed. When
+ * 'on' is already a record start, it is returned unchanged.
+ */
+static int
+ncl_record_start(struct buf *bp, int on)
+{
+ int pos, reclen;
+
+ for (pos = 0; pos < on; pos += reclen) {
+ reclen = ncl_check_record(bp, pos);
+ if (reclen < 0)
+ return (-1);
+ if (pos + reclen > on)
+ break;
+ }
+ return (pos);
+}
+
/*
* Do an I/O operation to/from a cache block. This may be called
* synchronously or from an nfsiod.
@@ -1639,6 +1774,8 @@
*/
if (error == 0 && uiop->uio_resid == bp->b_bcount)
bp->b_flags |= B_INVAL;
+ else if (error == 0)
+ error = ncl_fix_d_off(bp);
break;
default:
printf("ncl_doio: type %x unexpected\n", vp->v_type);
diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c
--- a/sys/fs/nfsclient/nfs_clvnops.c
+++ b/sys/fs/nfsclient/nfs_clvnops.c
@@ -2606,7 +2606,7 @@
struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct uio *uio = ap->a_uio;
- ssize_t tresid, left;
+ ssize_t tresid;
int error = 0;
struct vattr vattr;
@@ -2638,15 +2638,13 @@
NFSUNLOCKNODE(np);
/*
- * NFS always guarantees that directory entries don't straddle
- * DIRBLKSIZ boundaries. As such, we need to limit the size
- * to an exact multiple of DIRBLKSIZ, to avoid copying a partial
- * directory entry.
+ * A read may resume at any entry boundary inside a block. The
+ * client returns only whole records, so the requested size does
+ * not need to be rounded to a DIRBLKSIZ boundary.
*/
- left = uio->uio_resid % DIRBLKSIZ;
- if (left == uio->uio_resid)
+ if (uio->uio_offset < 0 ||
+ uio->uio_resid > OFF_MAX - uio->uio_offset)
return (EINVAL);
- uio->uio_resid -= left;
/*
* For readdirplus, if starting to read the directory,
@@ -2670,8 +2668,6 @@
*ap->a_eofflag = 1;
}
- /* Add the partial DIRBLKSIZ (left) back in. */
- uio->uio_resid += left;
return (error);
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Oct 14, 9:02 PM (4 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40699068
Default Alt Text
D60603.id189506.diff (6 KB)
Attached To
Mode
D60603: nfsclient: Make per-entry directory positions usable
Attached
Detach File
Event Timeline
Log In to Comment