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*/