server: dispatch handleWrite on empty writes from writable fids

Fids opened OWRITE or ORDWR with zero bytes written were silently
dropped on clunk — the old writeBuf-length guard prevented handleWrite
from being called, leaving the previous file content intact.

Track writable by open mode instead, and remove the blanket empty-input
early return in handleWrite. /s/new retains its own empty guard since
a zero-byte write there is meaningless.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Levi Neely 2026-04-26 12:49:58 +02:00
parent fb4d76a3aa
commit b44ce18d54
1 changed files with 7 additions and 5 deletions

View File

@ -1032,16 +1032,18 @@ func (s *Server) clunk(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
f, ok := cs.fids[fc.Fid]
var path string
var data []byte
var writable bool
if ok {
if len(f.writeBuf) > 0 {
if m := f.mode & 3; m == plan9.OWRITE || m == plan9.ORDWR {
path = f.path
writable = true
data = make([]byte, len(f.writeBuf))
copy(data, f.writeBuf)
}
delete(cs.fids, fc.Fid)
}
cs.mu.Unlock()
if len(data) > 0 {
if writable {
s.log.Debug("Tclunk flush path=%q writeBuf=%d", path, len(data))
input := strings.TrimSpace(string(data))
if s.isAsyncWrite(path) {
@ -1120,11 +1122,11 @@ func (s *Server) remove(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
// as a goroutine because they block for the entire agent turn).
func (s *Server) handleWrite(path, input string) error {
s.log.Debug("handleWrite path=%q input_len=%d", path, len(input))
if input == "" {
return nil
}
if path == "/s/new" {
if input == "" {
return nil
}
return storeWrite(s.sessionStore, "new", []byte(input))
}