// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: MIT package nfsfs import ( "errors" ) // ErrReadOnly marks an operation a read only export refuses, NFS4ERR_ROFS // on the wire. var ErrReadOnly = errors.New("nfsfs: the export is read only") // A roFS wraps an export and hides its mutating halves. The Writer half // is gone, so the dispatcher answers NFS4ERR_ROFS for every operation // that would change anything through it. The optional halves keep their // reads and refuse their writes with ErrReadOnly, so an export served // read only still reports its extended attributes, its holes and its // named attributes, and refuses to touch them. type roFS struct{ FS } // ReadOnly serves fs read only: the reads of every half pass through, the // writes of the mutating halves answer ErrReadOnly, and the mutating // Writer half disappears from the type. func ReadOnly(fs FS) FS { return roFS{fs} } // GetXattr reads one named attribute of the object. func (ro roFS) GetXattr(h Handle, name string, max int) ([]byte, error) { x, ok := ro.FS.(XattrFS) if !ok { return nil, ErrXattrNotSupp } return x.GetXattr(h, name, max) } // ListXattr names the attributes of the object. func (ro roFS) ListXattr(h Handle, max int) ([]string, error) { x, ok := ro.FS.(XattrFS) if !ok { return nil, ErrXattrNotSupp } return x.ListXattr(h, max) } // SetXattr refuses a write on a read only export. func (ro roFS) SetXattr(h Handle, name string, value []byte, mode uint32) error { return ErrReadOnly } // RemoveXattr refuses a write on a read only export. func (ro roFS) RemoveXattr(h Handle, name string) error { return ErrReadOnly } // SeekHole reports the first hole at or after the offset. func (ro roFS) SeekHole(h Handle, offset int64) (int64, bool, error) { s, ok := ro.FS.(interface { SeekHole(Handle, int64) (int64, bool, error) SeekData(Handle, int64) (int64, bool, error) }) if !ok { return 0, false, ErrNoSparse } return s.SeekHole(h, offset) } // SeekData reports the first data byte at or after the offset. func (ro roFS) SeekData(h Handle, offset int64) (int64, bool, error) { s, ok := ro.FS.(interface { SeekHole(Handle, int64) (int64, bool, error) SeekData(Handle, int64) (int64, bool, error) }) if !ok { return 0, false, ErrNoSparse } return s.SeekData(h, offset) } // Allocate refuses a space reservation on a read only export. func (ro roFS) Allocate(h Handle, offset, length int64) error { return ErrReadOnly } // Deallocate refuses a hole punch on a read only export. func (ro roFS) Deallocate(h Handle, offset, length int64) error { return ErrReadOnly }