DF-5817 / fix.diff
--- a/sys/vfs/hammer2/hammer2_vnops.c 2026-06-29 12:51:19.000000000 +0000 +++ b/sys/vfs/hammer2/hammer2_vnops.c 2026-10-13 05:02:22.968295490 +0000 @@ -828,7 +828,6 @@ hammer2_vop_write(struct vop_write_args *ap) { hammer2_inode_t *ip; - thread_t td; struct vnode *vp; struct uio *uio; int error; @@ -864,16 +863,6 @@ seqcount = ioflag >> IO_SEQSHIFT; /* - * Check resource limit - */ - if (uio->uio_resid > 0 && (td = uio->uio_td) != NULL && td->td_proc && - uio->uio_offset + uio->uio_resid > - td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) { - lwpsignal(td->td_proc, td->td_lwp, SIGXFSZ); - return (EFBIG); - } - - /* * The transaction interlocks against flush initiations * (note: but will run concurrently with the actual flush). * @@ -1019,6 +1008,38 @@ old_eof = ip->meta.size; /* + * If the file is flagged append-only (chflags uappnd/sappnd), + * writes must occur at EOF. The descriptor-level O_APPEND + * enforcement in nlookup() only applies to opens that happen + * while the flag is set; it cannot retroactively fix already- + * open descriptors. (Matches ffs_write: ufs_readwrite.c) + */ + if ((ip->meta.uflags & APPEND) && uio->uio_offset != ip->meta.size) { + hammer2_mtx_unlock(&ip->lock); + hammer2_mtx_unlock(&ip->truncate_lock); + return (EPERM); + } + + /* + * Check resource limit. This must be done AFTER the IO_APPEND + * offset override above, otherwise append writes bypass the + * file-size rlimit entirely (no EFBIG, no SIGXFSZ). + * (Matches ffs_write ordering: ufs_readwrite.c) + */ + if (uio->uio_resid > 0) { + thread_t td = uio->uio_td; + + if (td != NULL && td->td_proc && + uio->uio_offset + uio->uio_resid > + td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) { + hammer2_mtx_unlock(&ip->lock); + hammer2_mtx_unlock(&ip->truncate_lock); + lwpsignal(td->td_proc, td->td_lwp, SIGXFSZ); + return (EFBIG); + } + } + + /* * Extend the file if necessary. If the write fails at some point * we will truncate it back down to cover as much as we were able * to write. |