Module Name:    src
Committed By:   riastradh
Date:           Mon Jun  4 19:46:00 UTC 2012

Modified Files:
        src/sys/ufs/ext2fs: ext2fs_rename.c

Log Message:
Fix ext2fs's scary cross-block directory message too.

(See rev. 1.3 of sys/ufs/ufs/ufs_rename.c for the analysis.)


To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.3 src/sys/ufs/ext2fs/ext2fs_rename.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/ufs/ext2fs/ext2fs_rename.c
diff -u src/sys/ufs/ext2fs/ext2fs_rename.c:1.2 src/sys/ufs/ext2fs/ext2fs_rename.c:1.3
--- src/sys/ufs/ext2fs/ext2fs_rename.c:1.2	Thu May 10 19:08:34 2012
+++ src/sys/ufs/ext2fs/ext2fs_rename.c	Mon Jun  4 19:45:59 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: ext2fs_rename.c,v 1.2 2012/05/10 19:08:34 riastradh Exp $	*/
+/*	$NetBSD: ext2fs_rename.c,v 1.3 2012/06/04 19:45:59 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ext2fs_rename.c,v 1.2 2012/05/10 19:08:34 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ext2fs_rename.c,v 1.3 2012/06/04 19:45:59 riastradh Exp $");
 
 #include <sys/param.h>
 #include <sys/buf.h>
@@ -487,10 +487,9 @@ ext2fs_gro_rename(struct mount *mp, kaut
 	 * inserting a new entry.  That may invalidate fulr, which we
 	 * need in order to remove the old entry.  In that case, we
 	 * need to recalculate what fulr should be.
-	 *
-	 * XXX I believe this is necessary only if tvp == NULL as well.
 	 */
-	if (!reparent_p && ext2fs_rename_ulr_overlap_p(fulr, tulr)) {
+	if (!reparent_p && (tvp == NULL) &&
+	    ext2fs_rename_ulr_overlap_p(fulr, tulr)) {
 		error = ext2fs_rename_recalculate_fulr(fdvp, fulr, tulr, fcnp);
 #if 0				/* XXX */
 		if (error)	/* XXX Try to back out changes?  */
@@ -571,13 +570,11 @@ ext2fs_rename_recalculate_fulr(struct vn
 	struct mount *mp;
 	struct ufsmount *ump;
 	/* XXX int is a silly type for this; blame ufsmount::um_dirblksiz.  */
-	int directory_block_mask;
-	unsigned long io_block_mask;
+	int dirblksiz;
+	doff_t search_start, search_end;
 	doff_t offset;		/* Offset of entry we're examining.  */
-	doff_t search_end;	/* Limit to our search.  */
 	struct buf *bp;		/* I/O block we're examining.  */
-	char *dirbuf;		/* Pointer into bp's data.  */
-	doff_t dirbuf_offset;	/* Offset of dirbuf from directory start.  */
+	char *dirbuf;		/* Pointer into directory at search_start.  */
 	struct ext2fs_direct *ep; /* Pointer to the entry we're examining.  */
 	/* XXX direct::d_reclen is 16-bit;
 	 * ufs_lookup_results::ulr_reclen is 32-bit.  Blah.  */
@@ -598,66 +595,55 @@ ext2fs_rename_recalculate_fulr(struct vn
 	KASSERT(ump != NULL);
 	KASSERT(ump == VTOI(dvp)->i_ump);
 
-	KASSERT(0 < ump->um_dirblksiz);
-	KASSERT((ump->um_dirblksiz & (ump->um_dirblksiz - 1)) == 0);
-	directory_block_mask = (ump->um_dirblksiz - 1);
-
-	KASSERT(0 < mp->mnt_stat.f_iosize);
-	KASSERT((mp->mnt_stat.f_iosize & (mp->mnt_stat.f_iosize - 1)) == 0);
-	io_block_mask = (mp->mnt_stat.f_iosize - 1);
+	dirblksiz = ump->um_dirblksiz;
+	KASSERT(0 < dirblksiz);
+	KASSERT((dirblksiz & (dirblksiz - 1)) == 0);
 
-	offset = tulr->ulr_offset;
+	/* A directory block may not span across multiple I/O blocks.  */
+	KASSERT(dirblksiz <= mp->mnt_stat.f_iosize);
+
+	/* Find the bounds of the search.  */
+	search_start = tulr->ulr_offset;
 	KASSERT(fulr->ulr_reclen < (EXT2FS_MAXDIRSIZE - fulr->ulr_offset));
 	search_end = (fulr->ulr_offset + fulr->ulr_reclen);
 
+	/* Compaction must happen only within a directory block. (*)  */
+	KASSERT(search_start <= search_end);
+	KASSERT((search_end - (search_start &~ (dirblksiz - 1))) <= dirblksiz);
+
 	dirbuf = NULL;
 	bp = NULL;
-	dirbuf_offset = offset;
-	error = ext2fs_blkatoff(dvp, (off_t)dirbuf_offset, &dirbuf, &bp);
+	error = ext2fs_blkatoff(dvp, (off_t)search_start, &dirbuf, &bp);
 	if (error)
 		return error;
 	KASSERT(dirbuf != NULL);
 	KASSERT(bp != NULL);
 
+	/*
+	 * Guarantee we sha'n't go past the end of the buffer we got.
+	 * dirbuf is bp->b_data + (search_start & (iosize - 1)), and
+	 * the valid range is [bp->b_data, bp->b_data + bp->b_bcount).
+	 */
+	KASSERT((search_end - search_start) <=
+	    (bp->b_bcount - (search_start & (mp->mnt_stat.f_iosize - 1))));
+
 	prev_reclen = fulr->ulr_count;
+	offset = search_start;
 
 	/*
-	 * Search from offset to search_end for the entry matching
+	 * Search from search_start to search_end for the entry matching
 	 * fcnp, which must be there because we found it before and it
 	 * should only at most have moved earlier.
 	 */
 	for (;;) {
+		KASSERT(search_start <= offset);
 		KASSERT(offset < search_end);
 
 		/*
-		 * If we are at an I/O block boundary, fetch the next block.
-		 */
-		if ((offset & io_block_mask) == 0) {
-#ifdef DIAGNOSTIC		/* XXX */
-			printf("%s: directory block of inode 0x%llx"
-			    " extends across I/O block boundary,"
-			    " which shouldn't happen!\n",
-			    mp->mnt_stat.f_mntonname,
-			    (unsigned long long)VTOI(dvp)->i_number);
-#endif
-			brelse(bp, 0);
-			dirbuf = NULL;
-			bp = NULL;
-			dirbuf_offset = offset;
-			error = ext2fs_blkatoff(dvp, (off_t)dirbuf_offset,
-			    &dirbuf, &bp);
-			if (error)
-				return error;
-			KASSERT(dirbuf != NULL);
-			KASSERT(bp != NULL);
-		}
-
-		/*
 		 * Examine the directory entry at offset.
 		 */
-		KASSERT(dirbuf_offset <= offset);
 		ep = (struct ext2fs_direct *)
-		    (dirbuf + (offset - dirbuf_offset));
+		    (dirbuf + (offset - search_start));
 		reclen = fs2h16(ep->e2d_reclen);
 
 		if (ep->e2d_ino == 0)
@@ -682,8 +668,17 @@ next:
 			return EIO;	/* XXX Panic?  What?  */
 		}
 
+		/* We may not move past the search end.  */
 		KASSERT(reclen < search_end);
 		KASSERT(offset < (search_end - reclen));
+
+		/*
+		 * We may not move across a directory block boundary;
+		 * see (*) above.
+		 */
+		KASSERT((offset &~ (dirblksiz - 1)) ==
+		    ((offset + reclen) &~ (dirblksiz - 1)));
+
 		prev_reclen = reclen;
 		offset += reclen;
 	}
@@ -698,7 +693,7 @@ next:
 	 * Record the preceding record length, but not if we're at the
 	 * start of a directory block.
 	 */
-	fulr->ulr_count = ((offset & directory_block_mask)? prev_reclen : 0);
+	fulr->ulr_count = ((offset & (dirblksiz - 1))? prev_reclen : 0);
 
 	brelse(bp, 0);
 	return 0;

Reply via email to