-
Peter J. Keleher authoredPeter J. Keleher authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
dfsSolution.go 11.59 KiB
// memfs implements a simple in-memory file system.
// Copyright Pete Keleher
package main
/*
Two main files are ../fuse.go and ../fs/serve.go
*/
import (
"fmt"
. "github.com/mattn/go-getopt"
"log"
"os"
"os/signal"
"time"
"bazil.org/fuse"
"bazil.org/fuse/fs"
"golang.org/x/net/context"
)
/*
Need to implement these types from bazil/fuse/fs!
type FS interface {
// Root is called to obtain the Node for the file system root.
Root() (Node, error)
}
type Node interface {
// Attr fills attr with the standard metadata for the node.
Attr(ctx context.Context, attr *fuse.Attr) error
}
*/
//=============================================================================
type Dfs struct{}
type DNode struct {
nid uint64
name string
attr fuse.Attr
dirty bool
kids map[string]*DNode
data []uint8
}
var root *DNode
var nextInd uint64
var nodeMap = make(map[uint64]*DNode) // not currently queried
var debug = false
var mountPoint = "dss"
var conn *fuse.Conn
var uid = os.Geteuid()
var gid = os.Getegid()
var _ fs.Node = (*DNode)(nil)
var _ fs.FS = (*Dfs)(nil)
//=============================================================================
func p_out(s string, args ...interface{}) {
if !debug {
return
}
fmt.Printf("\t"+s, args...)
}
func p_err(s string, args ...interface{}) {
fmt.Printf(s, args...)
}
func p_call(s string, args ...interface{}) {
if !debug {
return
}
fmt.Printf(s, args...)
}
func p_exit(s string, args ...interface{}) {
fmt.Printf(s, args...)
os.Exit(1)
}
//=============================================================================
func (Dfs) Root() (n fs.Node, err error) {
p_call("func (Dfs) Root() (n fs.Node, err error) \n")
p_out("root returns as %d\n", int(root.attr.Inode))
return root, nil
}
//=============================================================================
func (n *DNode) isDir() bool {
return (n.attr.Mode & os.ModeDir) != 0
}
func (n *DNode) fuseType() fuse.DirentType {
if n.isDir() {
return fuse.DT_Dir
} else {
return fuse.DT_File
}
}
func (n *DNode) init(name string, mode os.FileMode) {
nextInd++
n.attr.Inode = nextInd
n.attr.Nlink = 1
n.name = name
tm := time.Now()
n.attr.Atime = tm
n.attr.Mtime = tm
n.attr.Ctime = tm
n.attr.Crtime = tm
n.attr.Mode = mode
n.attr.Gid = uint32(gid)
n.attr.Uid = uint32(uid)
n.kids = make(map[string]*DNode)
p_out("inited node inode %d, %q\n", nextInd, name)
}
func (n *DNode) Attr(ctx context.Context, attr *fuse.Attr) error {
p_call("func (n *DNode) Attr(ctx context.Context, attr *fuse.Attr) error \n")
p_out("Attr() on %q (%d)\n", n.name, n.attr.Inode)
*attr = n.attr
return nil
}
func (n *DNode) Lookup(ctx context.Context, name string) (fs.Node, error) {
p_call("func (n *DNode) Lookup(ctx context.Context, name string) (fs.Node, error) \n")
p_out("Lookup on %q from %q\n", name, n.name)
if k, ok := n.kids[name]; ok {
return k, nil
}
return nil, fuse.ENOENT
}
func (n *DNode) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
p_call("func (n *DNode) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) \n")
p_out("readdirall %q\n", n.name)
dirs := make([]fuse.Dirent, 0, 10)
for k, v := range n.kids {
dirs = append(dirs, fuse.Dirent{Inode: v.attr.Inode, Name: k, Type: v.fuseType()})
}
return dirs, nil
}
func (n *DNode) Getattrff(ctx context.Context, req *fuse.GetattrRequest, resp *fuse.GetattrResponse) error {
p_call("func (n *DNode) Getattr(ctx context.Context, req *fuse.GetattrRequest, resp *fuse.GetattrResponse) error \n")
p_out("getattr '%s': %v\n", n.name, n.attr)
resp.Attr = n.attr
return nil
}
// must be defined or editing w/ vi or emacs fails. Doesn't have to do anything
func (n *DNode) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
p_call("func (n *DNode) Fsync(ctx context.Context, req *fuse.FsyncRequest) error \n")
p_out("FSYNC\n")
return nil
}
func (n *DNode) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {
p_call("func (n *DNode) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error \n")
p_out("SETATTR: %v\n", req)
if fuse.SetattrValid.Mode(req.Valid) {
p_out("SETTING Mode %v\n", req.Mode)
n.attr.Mode = req.Mode
}
if fuse.SetattrValid.Uid(req.Valid) {
p_out("SETTING Uid %v\n", req.Uid)
n.attr.Uid = req.Uid
}
if fuse.SetattrValid.Gid(req.Valid) {
p_out("SETTING Gid %v\n", req.Gid)
n.attr.Gid = req.Gid
}
if fuse.SetattrValid.Size(req.Valid) {
p_out("SETTING Size %v (TRUNCATE)\n", req.Size)
n.attr.Size = req.Size
n.data = n.data[:req.Size]
// n.attr.Mtime = time.Now()
// n.attr.Atime = n.attr.Mtime
}
if fuse.SetattrValid.Atime(req.Valid) {
p_out("SETTING Atime %v\n", req.Atime)
n.attr.Atime = req.Atime
}
if fuse.SetattrValid.Mtime(req.Valid) {
p_out("SETTING Mtime %v\n", req.Mtime)
n.attr.Mtime = req.Mtime
}
if fuse.SetattrValid.Handle(req.Valid) {
p_out("SETTING handle, but don't have one: %v\n", req.Handle)
}
// if fuse.SetattrValid.AtimeNow(req.Valid) {
// p_err("ERROR: n.attr.AtimeNow = req.AtimeNow\n")
// }
// if fuse.SetattrValid.MtimeNow(req.Valid) {
// p_err("ERROR: n.attr.MtimeNow = req.MtimeNow\n")
// }
// if fuse.SetattrValid.LockOwner(req.Valid) {
// p_err("ERROR: n.attr.LockOwner = req.LockOwner\n")
// }
if fuse.SetattrValid.Crtime(req.Valid) {
p_out("SETTING Crtime %v\n", req.Crtime)
n.attr.Crtime = req.Crtime
}
if fuse.SetattrValid.Chgtime(req.Valid) {
p_out("SETTING n.attr.Chgtime = req.Chgtime: %v\n", req.Chgtime)
n.attr.Ctime = req.Chgtime
}
if fuse.SetattrValid.Bkuptime(req.Valid) {
p_exit("ERROR: n.attr.Bkuptime = req.Bkuptime: %v\n", req.Bkuptime)
}
if fuse.SetattrValid.Flags(req.Valid) {
p_out("SETTING Flags %v\n", req.Flags)
n.attr.Flags = req.Flags
}
resp.Attr = n.attr
return nil
}
// func (n fs.Node) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error){}
// func (n fs.Node) Release(ctx context.Context, req *fuse.ReleaseRequest) error {}
// func (n fs.Node) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error {}
// func (n fs.Node) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error {}
// func (n fs.Node) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error {}
// func (n fs.Node) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error {}
func (p *DNode) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
p_call("func (p *DNode) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) \n")
p_out("mkdir %q in %q\n", req.Name, p.name)
d := new(DNode)
d.init(req.Name, os.ModeDir|0755)
p.kids[req.Name] = d
return d, nil
}
func (p *DNode) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
p_call("func (p *DNode) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) \n")
p_out("Create [%s]: (from %q) %q, mode %o\n", req, p.name, req.Name, req.Mode)
n := new(DNode)
n.init(req.Name, req.Mode)
p.kids[req.Name] = n
return n, n, nil
}
func (n *DNode) ReadAll(ctx context.Context) ([]byte, error) {
p_call("func (n *DNode) ReadAll(ctx context.Context) ([]byte, error) \n")
return n.data, nil
}
func (n *DNode) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error {
p_call("func (n *DNode) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error \n")
olen := uint64(len(n.data))
wlen := uint64(len(req.Data))
off := uint64(req.Offset)
limit := off + wlen
if olen != n.attr.Size {
p_out("BAD SIZE MATCH %d %d\n", olen, n.attr.Size)
}
p_out("WRITING [%v] to %q, %d bytes, offset %d, oldlen %d\n",
req, n.name, wlen, off, olen)
if limit > olen {
b := make([]byte, limit)
var tocopy uint64
if off < olen {
tocopy = off
} else {
tocopy = olen
}
copy(b[0:tocopy], n.data[0:tocopy])
n.data = b
n.attr.Size = limit
}
copy(n.data[off:limit], req.Data[:])
resp.Size = int(wlen)
n.dirty = true
return nil
}
func (n *DNode) Flush(ctx context.Context, req *fuse.FlushRequest) error {
p_call("func (n *DNode) Flush(ctx context.Context, req *fuse.FlushRequest) error \n")
p_out("flush [%v] %q (dirty: %t, now %d bytes)\n", req, n.name, n.dirty, len(n.data))
if !n.dirty {
return nil
}
n.attr.Atime = time.Now()
n.attr.Mtime = n.attr.Atime
n.dirty = false
return nil
}
// hard links. Note that 'name' is not modified, so potential debugging problem.
func (p *DNode) Link(ctx context.Context, req *fuse.LinkRequest, oldNode fs.Node) (fs.Node, error) {
p_call("func (p *DNode) Link(ctx context.Context, req *fuse.LinkRequest, oldNode fs.Node) (fs.Node, error) \n")
p_out("Link Call -- receiver id %d", p.nid)
targetNode := oldNode.(*DNode)
targetNode.attr.Nlink++
p.kids[req.NewName] = targetNode
return targetNode, nil
}
func (n *DNode) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
p_call("func (n *DNode) Remove(ctx context.Context, req *fuse.RemoveRequest) error \n")
p_out("remove %q [name: %s, isdir %t]\n", n.name, req.Name, req.Dir)
kid := n.kids[req.Name]
if (kid != nil) && (len(kid.kids) != 0) {
return fuse.EPERM
} else {
kid.attr.Nlink--
delete(n.kids, req.Name)
}
return nil
}
func (p *DNode) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (fs.Node, error) {
p_call("func (p *DNode) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (fs.Node, error) \n")
p_out("Sym Link Call -- receiver id %d, new name: %q, target name: %q\n", p.nid, req.NewName, req.Target)
d := new(DNode)
d.init(req.NewName, os.ModeSymlink|0777)
p.kids[req.NewName] = d
d.data = []byte(req.Target)
d.dirty = true
d.attr.Size = uint64(len(d.data))
return d, nil
}
func (n *DNode) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) {
p_call("func (n *DNode) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) \n")
p_out("Read Link Call -- receiver id %d\n", n.nid)
s := string(n.data)
p_out("Returned String %q\n", s)
return s, nil
}
func (n *DNode) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error {
p_call("func (n *DNode) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error \n")
p_out("RENAME (%s)%d, %q - %q, %d\n", req, int(n.attr.Inode), req.OldName, req.NewName, int(req.NewDir))
if outDir, ok := newDir.(*DNode); ok {
outDir.kids[req.NewName] = n.kids[req.OldName]
outDir.kids[req.NewName].name = req.NewName
delete(n.kids, req.OldName)
}
return nil
}
//=============================================================================
func main() {
var c int
for {
if c = Getopt("dm:"); c == EOF {
break
}
switch c {
case 'd':
debug = !debug
case 'm':
mountPoint = OptArg
default:
println("usage: main.go [-d | -m <mountpt>]", c)
os.Exit(1)
}
}
p_out("main\n")
root = new(DNode)
root.init("", os.ModeDir|0755)
nodeMap[uint64(root.attr.Inode)] = root
p_out("root inode %d\n", int(root.attr.Inode))
if _, err := os.Stat(mountPoint); err != nil {
os.Mkdir(mountPoint, 0755)
}
fuse.Unmount(mountPoint)
conn, err := fuse.Mount(mountPoint, fuse.FSName("dssFS"), fuse.Subtype("project P1"),
fuse.LocalVolume(), fuse.VolumeName("dssFS"))
if err != nil {
log.Fatal(err)
}
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt, os.Kill)
go func() {
<-ch
defer conn.Close()
fuse.Unmount(mountPoint)
os.Exit(1)
}()
err = fs.Serve(conn, Dfs{})
p_out("AFTER\n")
if err != nil {
log.Fatal(err)
}
// check if the mount process has an error to report
<-conn.Ready
if err := conn.MountError; err != nil {
log.Fatal(err)
}
}