This repository has been archived on 2026-08-16. You can view files and clone it, but cannot push or open issues or pull requests.
ollie-9p/cmd/ollie-9p-mount/node.go

177 lines
4.2 KiB
Go

package main
import (
"context"
"sync"
"syscall"
"9fans.net/go/plan9"
"9fans.net/go/plan9/client"
"github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/fuse"
)
// p9Dir is a FUSE directory node backed by a 9P path.
type p9Dir struct {
fs.Inode
fsys *client.Fsys
path string // 9P path (empty string = root)
}
// p9File is a FUSE file node backed by a 9P path.
type p9File struct {
fs.Inode
fsys *client.Fsys
path string
mu sync.Mutex
}
var _ = (fs.NodeGetattrer)((*p9Dir)(nil))
var _ = (fs.NodeReaddirer)((*p9Dir)(nil))
var _ = (fs.NodeLookuper)((*p9Dir)(nil))
var _ = (fs.NodeGetattrer)((*p9File)(nil))
var _ = (fs.NodeOpener)((*p9File)(nil))
var _ = (fs.NodeReader)((*p9File)(nil))
var _ = (fs.NodeWriter)((*p9File)(nil))
var _ = (fs.NodeSetattrer)((*p9File)(nil))
func p9path(base, name string) string {
if base == "" {
return name
}
return base + "/" + name
}
// --- p9Dir ---
func (d *p9Dir) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
out.Mode = 0555 | syscall.S_IFDIR
return 0
}
func (d *p9Dir) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
path := d.path
if path == "" {
path = "/"
}
fid, err := d.fsys.Open(path, plan9.OREAD)
if err != nil {
return nil, syscall.EIO
}
defer fid.Close()
dirs, err := fid.Dirreadall()
if err != nil {
return nil, syscall.EIO
}
entries := make([]fuse.DirEntry, len(dirs))
for i, dir := range dirs {
m := uint32(dir.Mode) & 0777
if dir.Mode&plan9.DMDIR != 0 {
m |= syscall.S_IFDIR
}
entries[i] = fuse.DirEntry{Name: dir.Name, Mode: m}
}
return fs.NewListDirStream(entries), 0
}
func (d *p9Dir) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) {
child := p9path(d.path, name)
dir, err := d.fsys.Stat(child)
if err != nil {
return nil, syscall.ENOENT
}
if dir.Mode&plan9.DMDIR != 0 {
out.Mode = uint32(dir.Mode)&0777 | syscall.S_IFDIR
node := &p9Dir{fsys: d.fsys, path: child}
return d.NewInode(ctx, node, fs.StableAttr{Mode: syscall.S_IFDIR}), 0
}
out.Mode = uint32(dir.Mode) & 0777
out.Size = dir.Length
node := &p9File{fsys: d.fsys, path: child}
return d.NewInode(ctx, node, fs.StableAttr{}), 0
}
// --- p9File ---
func (f *p9File) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
dir, err := f.fsys.Stat(f.path)
if err != nil {
return syscall.ENOENT
}
out.Mode = uint32(dir.Mode) & 0777
out.Size = dir.Length
return 0
}
func (f *p9File) Setattr(ctx context.Context, fh fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
// Accept truncate silently — 9P files are append/overwrite on write.
dir, err := f.fsys.Stat(f.path)
if err != nil {
return syscall.ENOENT
}
out.Mode = uint32(dir.Mode) & 0777
return 0
}
func (f *p9File) Open(ctx context.Context, flags uint32) (fs.FileHandle, uint32, syscall.Errno) {
return nil, fuse.FOPEN_DIRECT_IO, 0
}
func (f *p9File) Read(ctx context.Context, fh fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
fid, err := f.fsys.Open(f.path, plan9.OREAD)
if err != nil {
return nil, syscall.EIO
}
defer fid.Close()
// The 9fans client doesn't support Seek; read from offset by reading and discarding.
// For small offsets this is fine; large seeks on big files would need a smarter approach.
if off > 0 {
buf := make([]byte, min(off, 8192))
remaining := off
for remaining > 0 {
n := int64(len(buf))
if n > remaining {
n = remaining
}
nr, err := fid.Read(buf[:n])
if err != nil || nr == 0 {
break
}
remaining -= int64(nr)
}
}
n, err := fid.Read(dest)
if n == 0 {
return fuse.ReadResultData(nil), 0
}
return fuse.ReadResultData(dest[:n]), 0
}
func (f *p9File) Write(ctx context.Context, fh fs.FileHandle, data []byte, off int64) (uint32, syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
fid, err := f.fsys.Open(f.path, plan9.OWRITE)
if err != nil {
return 0, syscall.EACCES
}
n, err := fid.Write(data)
if err != nil {
fid.Close()
return 0, syscall.EIO
}
if err := fid.Close(); err != nil {
return 0, syscall.EACCES
}
return uint32(n), 0
}
func min(a, b int64) int64 {
if a < b {
return a
}
return b
}