// 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) } }