ollie/virtfs/file.go

108 lines
3.5 KiB
Go

package virtfs
import (
"context"
"os"
"time"
)
// Standard permission constants for EDSL-built trees.
const (
PermChildDir os.FileMode = 0555
PermIdx os.FileMode = 0444
PermMkdir os.FileMode = 0755
PermMkdirPrivate os.FileMode = 0700
)
// File is an open handle to a single named blob.
type File interface {
Stat() (os.FileInfo, error)
Read() ([]byte, error)
Write(data []byte) error
Close() error // called when fid is clunked/closed
BlockingRead(ctx context.Context, base string) (content []byte, nextBase string, err error)
// Read-side contracts (mutually exclusive — at most one true):
// BlockingReadMode — blocks up to 500ms for next value, returns it or empty, EOF on 2nd read
// StreamMode — blocks indefinitely, streams data incrementally, FD lives until EOF
// RdwrMode — write blocks until result produced, read returns result once
BlockingReadMode() bool
StreamMode() bool
RdwrMode() bool
}
// fileConfig implements File via function pointers.
type fileConfig struct {
StatFn func() (os.FileInfo, error)
ReadFn func() ([]byte, error)
WriteFn func([]byte) error
CloseFn func() error
BlockingReadFn func(context.Context, string) ([]byte, string, error)
BlockingRead_ bool // one-shot waiter: block 500ms, return once, EOF on 2nd read
StreamMode_ bool // indefinite stream: block forever, keep FD open
Rdwr_ bool // rdwr: write blocks on write, read returns result once
}
func (e *fileConfig) Stat() (os.FileInfo, error) { return e.StatFn() }
func (e *fileConfig) Read() ([]byte, error) { return e.ReadFn() }
func (e *fileConfig) Write(data []byte) error { return e.WriteFn(data) }
func (e *fileConfig) Close() error {
if e.CloseFn != nil {
return e.CloseFn()
}
return nil
}
func (e *fileConfig) BlockingRead(ctx context.Context, base string) ([]byte, string, error) {
return e.BlockingReadFn(ctx, base)
}
func (e *fileConfig) BlockingReadMode() bool { return e.BlockingRead_ }
func (e *fileConfig) StreamMode() bool { return e.StreamMode_ }
func (e *fileConfig) RdwrMode() bool { return e.Rdwr_ }
// SyntheticFileInfo implements os.FileInfo for entries with no backing file.
type SyntheticFileInfo struct {
Name_ string
Mode_ os.FileMode
Size_ int64
IsDir_ bool
UID_ string
GID_ string
Vers_ uint32
}
func (f *SyntheticFileInfo) Name() string { return f.Name_ }
func (f *SyntheticFileInfo) Size() int64 { return f.Size_ }
func (f *SyntheticFileInfo) Mode() os.FileMode { return f.Mode_ }
func (f *SyntheticFileInfo) ModTime() time.Time { return time.Time{} }
func (f *SyntheticFileInfo) IsDir() bool { return f.IsDir_ }
func (f *SyntheticFileInfo) Sys() any { return nil }
// syntheticEntry implements os.DirEntry for entries with no backing file.
type syntheticEntry struct {
name string
mode os.FileMode
isDir bool
}
func (e *syntheticEntry) Name() string { return e.name }
func (e *syntheticEntry) IsDir() bool { return e.isDir }
func (e *syntheticEntry) Type() os.FileMode {
if e.isDir {
return os.ModeDir
}
return 0
}
func (e *syntheticEntry) Info() (os.FileInfo, error) {
return &SyntheticFileInfo{Name_: e.name, Mode_: e.mode, IsDir_: e.isDir}, nil
}
// fileEntry returns a synthetic file DirEntry.
func fileEntry(name string, mode os.FileMode) os.DirEntry {
return &syntheticEntry{name: name, mode: mode}
}
// dirEntry returns a synthetic directory DirEntry.
func dirEntry(name string, mode os.FileMode) os.DirEntry {
return &syntheticEntry{name: name, mode: mode, isDir: true}
}