⬢ DragonFlyBSD Kernel Audit
DF-3133 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/dirfs/dirfs_subr.c b/sys/vfs/dirfs/dirfs_subr.c
index 0fda4856..da88b7b2 100644
--- a/sys/vfs/dirfs/dirfs_subr.c
+++ b/sys/vfs/dirfs/dirfs_subr.c
@@ -776,6 +776,25 @@ dirfs_node_chsize(dirfs_node_t dnp, off_t nsize)
 	}
 
 	tmp = dirfs_node_absolute_path(dmp, dnp, &pathfree);
+	/*
+	 * The absolute path is NULL for unlinked-but-open nodes (dn_parent
+	 * cleared by dirfs_nremove/dirfs_nrmdir/dirfs_nrename) and when the
+	 * accumulated absolute path exceeds MAXPATHLEN.
+	 *
+	 * NEVER touch the buffer cache before the host truncate() is known
+	 * to succeed: a failed truncate (EFAULT/EPERM/EACCES/ENOSPC) must
+	 * leave the vnode's buffers and pager exactly as they were, else
+	 * acknowledged dirty writes are discarded and the VM object desyncs
+	 * from the host file.
+	 */
+	if (tmp == NULL) {
+		error = ENAMETOOLONG;
+		goto done;
+	}
+	if (truncate(tmp, nsize) < 0) {
+		error = errno;
+		goto done;
+	}
 	if (nsize < osize) {
 		error = nvtruncbuf(vp, nsize, biosize, -1, 0);
 	} else {
@@ -783,10 +802,9 @@ dirfs_node_chsize(dirfs_node_t dnp, off_t nsize)
 				    biosize, biosize,
 				    -1, -1, 0);
 	}
-	if (error == 0 && truncate(tmp, nsize) < 0)
-		error = errno;
 	if (error == 0)
 		dnp->dn_size = nsize;
+done:
 	dbg(9, "TRUNCATE %016jx %016jx\n", (intmax_t)nsize, dnp->dn_size);
 	/*dirfs_node_stat(DIRFS_NOFD, tmp, dnp); don't need to do this*/