Finish initial Node implementation.

This commit is contained in:
Ben Johnson
2020-10-14 13:01:49 -06:00
parent 410d56f334
commit 4160638a92
2 changed files with 95 additions and 25 deletions

View File

@@ -2,6 +2,7 @@ package main
import ( import (
"context" "context"
"os"
"bazil.org/fuse" "bazil.org/fuse"
"bazil.org/fuse/fs" "bazil.org/fuse/fs"
@@ -18,7 +19,9 @@ var _ fs.HandleReader = (*Handle)(nil)
var _ fs.HandleReleaser = (*Handle)(nil) var _ fs.HandleReleaser = (*Handle)(nil)
var _ fs.HandleWriter = (*Handle)(nil) var _ fs.HandleWriter = (*Handle)(nil)
type Handle struct{} type Handle struct {
f *os.File
}
// Flush is called each time the file or directory is closed. // Flush is called each time the file or directory is closed.
// Because there can be multiple file descriptors referring to a // Because there can be multiple file descriptors referring to a

115
node.go
View File

@@ -3,6 +3,7 @@ package main
import ( import (
"context" "context"
"io/ioutil" "io/ioutil"
"log"
"os" "os"
"path/filepath" "path/filepath"
"syscall" "syscall"
@@ -15,7 +16,6 @@ import (
var _ fs.Node = (*Node)(nil) var _ fs.Node = (*Node)(nil)
var _ fs.NodeAccesser = (*Node)(nil) var _ fs.NodeAccesser = (*Node)(nil)
var _ fs.NodeCreater = (*Node)(nil) var _ fs.NodeCreater = (*Node)(nil)
var _ fs.NodeForgetter = (*Node)(nil)
var _ fs.NodeFsyncer = (*Node)(nil) var _ fs.NodeFsyncer = (*Node)(nil)
var _ fs.NodeGetxattrer = (*Node)(nil) var _ fs.NodeGetxattrer = (*Node)(nil)
var _ fs.NodeLinker = (*Node)(nil) var _ fs.NodeLinker = (*Node)(nil)
@@ -23,7 +23,6 @@ var _ fs.NodeListxattrer = (*Node)(nil)
var _ fs.NodeMkdirer = (*Node)(nil) var _ fs.NodeMkdirer = (*Node)(nil)
var _ fs.NodeMknoder = (*Node)(nil) var _ fs.NodeMknoder = (*Node)(nil)
var _ fs.NodeOpener = (*Node)(nil) var _ fs.NodeOpener = (*Node)(nil)
var _ fs.NodePoller = (*Node)(nil)
var _ fs.NodeReadlinker = (*Node)(nil) var _ fs.NodeReadlinker = (*Node)(nil)
var _ fs.NodeRemover = (*Node)(nil) var _ fs.NodeRemover = (*Node)(nil)
var _ fs.NodeRemovexattrer = (*Node)(nil) var _ fs.NodeRemovexattrer = (*Node)(nil)
@@ -34,6 +33,8 @@ var _ fs.NodeStringLookuper = (*Node)(nil)
var _ fs.NodeSymlinker = (*Node)(nil) var _ fs.NodeSymlinker = (*Node)(nil)
// var _ fs.NodeRequestLookuper = (*Node)(nil) // var _ fs.NodeRequestLookuper = (*Node)(nil)
// var _ fs.NodeForgetter = (*Node)(nil)
// var _ fs.NodePoller = (*Node)(nil)
type Node struct { type Node struct {
fs *FS // base filesystem fs *FS // base filesystem
@@ -49,6 +50,8 @@ func (n *Node) srcpath() string {
} }
func (n *Node) Attr(ctx context.Context, a *fuse.Attr) error { func (n *Node) Attr(ctx context.Context, a *fuse.Attr) error {
log.Printf("[attr] node=%q", n.path)
fi, err := os.Stat(n.srcpath()) fi, err := os.Stat(n.srcpath())
if err != nil { if err != nil {
return err return err
@@ -84,6 +87,8 @@ func (n *Node) Attr(ctx context.Context, a *fuse.Attr) error {
// //
// Lookup need not to handle the names "." and "..". // Lookup need not to handle the names "." and "..".
func (n *Node) Lookup(ctx context.Context, name string) (fs.Node, error) { func (n *Node) Lookup(ctx context.Context, name string) (fs.Node, error) {
log.Printf("[lookup] node=%q name=%q", n.path, name)
path := filepath.Join(n.path, name) path := filepath.Join(n.path, name)
srcpath := filepath.Join(n.fs.SourcePath, path) srcpath := filepath.Join(n.fs.SourcePath, path)
if _, err := os.Stat(srcpath); os.IsNotExist(err) { if _, err := os.Stat(srcpath); os.IsNotExist(err) {
@@ -93,6 +98,8 @@ func (n *Node) Lookup(ctx context.Context, name string) (fs.Node, error) {
} }
func (n *Node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { func (n *Node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
log.Printf("[readdirall] node=%q", n.path)
fis, err := ioutil.ReadDir(n.srcpath()) fis, err := ioutil.ReadDir(n.srcpath())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -124,6 +131,8 @@ func (n *Node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
// For example, the method should not change the mode of the file // For example, the method should not change the mode of the file
// unless req.Valid.Mode() is true. // unless req.Valid.Mode() is true.
func (n *Node) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error { func (n *Node) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {
log.Printf("[setattr] node=%q req=%s", n.path, req.String())
// Obtain current file stat. // Obtain current file stat.
srcpath := n.srcpath() srcpath := n.srcpath()
fi, err := os.Stat(srcpath) fi, err := os.Stat(srcpath)
@@ -203,6 +212,7 @@ func (n *Node) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse
// Symlink creates a new symbolic link in the receiver, which must be a directory. // Symlink creates a new symbolic link in the receiver, which must be a directory.
func (n *Node) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (fs.Node, error) { func (n *Node) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (fs.Node, error) {
log.Printf("[symlink] node=%q newname=%q target=%q", n.path, req.NewName, req.Target)
if err := os.Symlink(req.Target, req.NewName); err != nil { if err := os.Symlink(req.Target, req.NewName); err != nil {
return nil, err return nil, err
} }
@@ -211,20 +221,34 @@ func (n *Node) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (fs.Node,
// Readlink reads a symbolic link. // Readlink reads a symbolic link.
func (n *Node) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) { func (n *Node) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) {
panic("TODO") log.Printf("[readlink] node=%q", n.path)
return os.Readlink(n.srcpath())
} }
// Link creates a new directory entry in the receiver based on an // Link creates a new directory entry in the receiver based on an
// existing Node. Receiver must be a directory. // existing Node. Receiver must be a directory.
func (n *Node) Link(ctx context.Context, req *fuse.LinkRequest, old fs.Node) (fs.Node, error) { func (n *Node) Link(ctx context.Context, req *fuse.LinkRequest, _old fs.Node) (fs.Node, error) {
panic("TODO") old := _old.(*Node)
log.Printf("[link] node=%q oldnode=%q name=%q", n.path, old.srcpath(), req.NewName)
// assert(n.IsDir())
if err := os.Link(old.srcpath(), req.NewName); err != nil {
return nil, err
}
return NewNode(n.fs, req.NewName), nil
} }
// Remove removes the entry with the given name from // Remove removes the entry with the given name from
// the receiver, which must be a directory. The entry to be removed // the receiver, which must be a directory. The entry to be removed
// may correspond to a file (unlink) or to a directory (rmdir). // may correspond to a file (unlink) or to a directory (rmdir).
func (n *Node) Remove(ctx context.Context, req *fuse.RemoveRequest) error { func (n *Node) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
panic("TODO") log.Printf("[remove] node=%q name=%q dir=%v", n.path, req.Name, req.Dir)
if req.Dir {
return syscall.Rmdir(filepath.Join(n.srcpath(), req.Name))
}
return syscall.Unlink(filepath.Join(n.srcpath(), req.Name))
} }
// Access checks whether the calling context has permission for // Access checks whether the calling context has permission for
@@ -236,7 +260,9 @@ func (n *Node) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
// implemented, the Node behaves as if it always returns nil // implemented, the Node behaves as if it always returns nil
// (permission granted), relying on checks in Open instead. // (permission granted), relying on checks in Open instead.
func (n *Node) Access(ctx context.Context, req *fuse.AccessRequest) error { func (n *Node) Access(ctx context.Context, req *fuse.AccessRequest) error {
panic("TODO") log.Printf("[access] node=%q mask=%#o", n.path, req.Mask)
return syscall.Access(n.srcpath(), req.Mask)
} }
//type NodeRequestLookuper interface { //type NodeRequestLookuper interface {
@@ -246,7 +272,11 @@ func (n *Node) Access(ctx context.Context, req *fuse.AccessRequest) error {
//} //}
func (n *Node) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) { func (n *Node) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
panic("TODO") log.Printf("[mkdir] node=%q mode=%#o umask=%#o", n.path, req.Mode, req.Umask)
if err := syscall.Mkdir(filepath.Join(n.srcpath(), req.Name), uint32(req.Mode&req.Umask)); err != nil {
return nil, err
}
return NewNode(n.fs, filepath.Join(n.path, req.Name)), nil
} }
// Open opens the receiver. After a successful open, a client // Open opens the receiver. After a successful open, a client
@@ -260,13 +290,25 @@ func (n *Node) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, erro
// //
// XXX note about access. XXX OpenFlags. // XXX note about access. XXX OpenFlags.
func (n *Node) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) { func (n *Node) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
panic("TODO") log.Printf("[open] node=%q dir=%v flags=%#o", n.path, req.Dir, req.Flags)
// TODO(bbj): Where does mode come from?
f, err := os.OpenFile(n.srcpath(), int(req.Flags), 0777)
if err != nil {
return nil, err
}
return &Handle{f: f}, nil
} }
// Create creates a new directory entry in the receiver, which // Create creates a new directory entry in the receiver, which must be a directory.
// must be a directory.
func (n *Node) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) { func (n *Node) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
panic("TODO") log.Printf("[create] node=%q name=%q flags=%#o mode=%#o umask=%#o", n.path, req.Name, req.Flags, req.Mode, req.Umask)
f, err := os.OpenFile(filepath.Join(n.srcpath(), req.Name), int(req.Flags), req.Mode&req.Umask)
if err != nil {
return nil, nil, err
}
return NewNode(n.fs, filepath.Join(n.path, req.Name)), Handle{f: f}, nil
} }
// Forget about this node. This node will not receive further // Forget about this node. This node will not receive further
@@ -274,18 +316,33 @@ func (n *Node) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.C
// //
// Forget is not necessarily seen on unmount, as all nodes are // Forget is not necessarily seen on unmount, as all nodes are
// implicitly forgotten as part of the unmount. // implicitly forgotten as part of the unmount.
func (n *Node) Forget() { panic("TODO") } // func (n *Node) Forget() { panic("TODO") }
func (n *Node) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error { func (n *Node) Rename(ctx context.Context, req *fuse.RenameRequest, _newDir fs.Node) error {
panic("TODO") newDir := _newDir.(*Node)
log.Printf("[rename] node=%q oldname=%q newnode=%q newname=%q", n.path, req.NewName, newDir.path, req.OldName)
return os.Rename(filepath.Join(n.srcpath(), req.OldName), filepath.Join(newDir.srcpath(), req.NewName))
} }
func (n *Node) Mknod(ctx context.Context, req *fuse.MknodRequest) (fs.Node, error) { func (n *Node) Mknod(ctx context.Context, req *fuse.MknodRequest) (fs.Node, error) {
panic("TODO") log.Printf("[mknod] node=%q name=%q mode=%#o rdev=%q newname=%q", n.path, req.Name, req.Mode, req.Rdev, req.Umask)
if err := syscall.Mknod(filepath.Join(n.srcpath(), req.Name), uint32(req.Mode&req.Umask), int(req.Rdev)); err != nil {
return nil, err
}
return NewNode(n.fs, filepath.Join(n.path, req.Name)), nil
} }
func (n *Node) Fsync(ctx context.Context, req *fuse.FsyncRequest) error { func (n *Node) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
panic("TODO") log.Printf("[fsync] node=%q flags=%#o dir=%v", n.path, req.Flags, req.Dir)
f, err := os.Open(n.srcpath())
if err != nil {
return err
}
defer f.Close()
// TODO(bbj): Handle fdatasync()
return f.Sync()
} }
// Getxattr gets an extended attribute by the given name from the // Getxattr gets an extended attribute by the given name from the
@@ -293,29 +350,39 @@ func (n *Node) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
// //
// If there is no xattr by that name, returns fuse.ErrNoXattr. // If there is no xattr by that name, returns fuse.ErrNoXattr.
func (n *Node) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error { func (n *Node) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error {
panic("TODO") log.Printf("[getxattr] node=%q name=%q", n.path, req.Name)
// TODO(bbj): Handle req.Size & returned syscall.Getxattr() size.
_, err := syscall.Getxattr(n.srcpath(), req.Name, resp.Xattr)
return err
} }
// Listxattr lists the extended attributes recorded for the node. // Listxattr lists the extended attributes recorded for the node.
func (n *Node) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error { func (n *Node) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error {
panic("TODO") log.Printf("[listxattr] node=%q", n.path)
// TODO(bbj): Handle req.Size & returned syscall.Getxattr() size.
_, err := syscall.Listxattr(n.srcpath(), resp.Xattr)
return err
} }
// Setxattr sets an extended attribute with the given name and // Setxattr sets an extended attribute with the given name and
// value for the node. // value for the node.
func (n *Node) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error { func (n *Node) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error {
panic("TODO") log.Printf("[setxattr] node=%q name=%q data=%q flags=%#o", n.path, req.Name, string(req.Xattr), req.Flags)
return syscall.Setxattr(n.srcpath(), req.Name, req.Xattr, int(req.Flags))
} }
// Removexattr removes an extended attribute for the name. // Removexattr removes an extended attribute for the name.
// //
// If there is no xattr by that name, returns fuse.ErrNoXattr. // If there is no xattr by that name, returns fuse.ErrNoXattr.
func (n *Node) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error { func (n *Node) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error {
panic("TODO") log.Printf("[removexattr] node=%q name=%q", n.path, req.Name)
return syscall.Removexattr(n.srcpath(), req.Name)
} }
// Poll checks whether the node is currently ready for I/O, and // Poll checks whether the node is currently ready for I/O, and
// may request a wakeup when it is. See HandlePoller. // may request a wakeup when it is. See HandlePoller.
func (n *Node) Poll(ctx context.Context, req *fuse.PollRequest, resp *fuse.PollResponse) error { // func (n *Node) Poll(ctx context.Context, req *fuse.PollRequest, resp *fuse.PollResponse) error {
panic("TODO") // panic("TODO")
} // }