From 40d368dec5fdcc7bfb9260e40217c21cce6743d5 Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Thu, 15 Oct 2020 15:29:27 -0600 Subject: [PATCH] Implement Handle --- handle.go | 138 ++++++++++++++++++++++++++++++++++++++++++++++++------ node.go | 106 +++++++++++++++++++++++------------------ 2 files changed, 184 insertions(+), 60 deletions(-) diff --git a/handle.go b/handle.go index 4cc5e24..d35d889 100644 --- a/handle.go +++ b/handle.go @@ -2,7 +2,11 @@ package main import ( "context" + "io" + "log" "os" + "sort" + "syscall" "bazil.org/fuse" "bazil.org/fuse/fs" @@ -13,12 +17,13 @@ var _ fs.HandleFlusher = (*Handle)(nil) var _ fs.HandleLocker = (*Handle)(nil) var _ fs.HandlePOSIXLocker = (*Handle)(nil) var _ fs.HandlePoller = (*Handle)(nil) -var _ fs.HandleReadAller = (*Handle)(nil) var _ fs.HandleReadDirAller = (*Handle)(nil) var _ fs.HandleReader = (*Handle)(nil) var _ fs.HandleReleaser = (*Handle)(nil) var _ fs.HandleWriter = (*Handle)(nil) +// var _ fs.HandleReadAller = (*Handle)(nil) + type Handle struct { f *os.File } @@ -26,16 +31,28 @@ type Handle struct { // Flush is called each time the file or directory is closed. // Because there can be multiple file descriptors referring to a // single opened file, Flush can be called multiple times. -func (h *Handle) Flush(ctx context.Context, req *fuse.FlushRequest) error { - panic("TODO") +func (h *Handle) Flush(ctx context.Context, req *fuse.FlushRequest) (err error) { + defer func() { log.Printf("[flush] handle=%q -- err=%v", h.f.Name(), err) }() + return h.f.Sync() } -func (h *Handle) ReadAll(ctx context.Context) ([]byte, error) { - panic("TODO") -} +func (h *Handle) ReadDirAll(ctx context.Context) (ents []fuse.Dirent, err error) { + defer func() { log.Printf("[readdirall] handle=%q -- ents=%d err=%v", h.f.Name(), len(ents), err) }() -func (h *Handle) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { - panic("TODO") + fis, err := h.f.Readdir(-1) + if err != nil { + return nil, err + } + + // Convert FileInfo objects to FUSE directory entries. + ents = make([]fuse.Dirent, len(fis)) + for i, fi := range fis { + statt := fi.Sys().(*syscall.Stat_t) + ents[i] = fuse.Dirent{Inode: statt.Ino, Name: fi.Name()} + } + + sort.Slice(ents, func(i, j int) bool { return ents[i].Name < ents[j].Name }) + return ents, nil } // Read requests to read data from the handle. @@ -47,8 +64,32 @@ func (h *Handle) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { // // Note that reads beyond the size of the file as reported by Attr // are not even attempted (except in OpenDirectIO mode). -func (h *Handle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { - panic("TODO") +func (h *Handle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) (err error) { + defer func() { + log.Printf("[read] handle=%q offset=%d size=%d flags=%#o lockowner=%d fileflags=%#o -- data=%d err=%v", + h.f.Name(), + req.Offset, + req.Size, + req.Flags, + req.LockOwner, + req.FileFlags, + len(resp.Data), + err, + ) + }() + + // TODO: Flags ReadFlags + // TODO: LockOwner + // TODO: FileFlags OpenFlags + + buf := make([]byte, req.Size) + n, err := h.f.ReadAt(buf, req.Offset) + if err != nil && err != io.EOF { + return err + } + resp.Data = buf[:n] + + return nil } // Write requests to write data into the handle at the given offset. @@ -62,12 +103,34 @@ func (h *Handle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.Rea // Writes that grow the file are expected to update the file size // (as seen through Attr). Note that file size changes are // communicated also through Setattr. -func (h *Handle) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error { - panic("TODO") +func (h *Handle) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) (err error) { + defer func() { + log.Printf( + "[write] handle=%q offset=%d size=%d flags=%#o lockowner=%d fileflags=%#o -- data=%d err=%v", + h.f.Name(), + req.Offset, + len(req.Data), + req.Flags, + req.LockOwner, + req.FileFlags, + resp.Size, + err, + ) + }() + + // Offset int64 + // Data []byte + // Flags WriteFlags + // LockOwner LockOwner + // FileFlags OpenFlags + + resp.Size, err = h.f.WriteAt(req.Data, req.Offset) + return err } -func (h *Handle) Release(ctx context.Context, req *fuse.ReleaseRequest) error { - panic("TODO") +func (h *Handle) Release(ctx context.Context, req *fuse.ReleaseRequest) (err error) { + defer func() { log.Printf("[release] handle=%q -- err=%v", h.f.Name(), err) }() + return h.f.Close() } // Poll checks whether the handle is currently ready for I/O, and @@ -87,6 +150,20 @@ func (h *Handle) Release(ctx context.Context, req *fuse.ReleaseRequest) error { // The Go runtime uses epoll-based I/O whenever possible, even for // regular files. func (h *Handle) Poll(ctx context.Context, req *fuse.PollRequest, resp *fuse.PollResponse) error { + log.Printf("[poll] handle=%q flags=%#o events=%#o", h.f.Name(), req.Flags, req.Events) + + //type PollRequest struct { + // Header `json:"-"` + // Handle HandleID + // + // Flags PollFlags + // Events PollEvents + //} + + //type PollResponse struct { + // REvents PollEvents + //} + panic("TODO") } @@ -98,12 +175,27 @@ func (h *Handle) Poll(ctx context.Context, req *fuse.PollRequest, resp *fuse.Pol // (UnlockRequest, ReleaseRequest, FlushRequest) and also // in e.g. ReadRequest, WriteRequest. func (h *Handle) Lock(ctx context.Context, req *fuse.LockRequest) error { + log.Printf("[lock] handle=%q lockowner=%d lock=%#o lockflags=%#o", h.f.Name(), req.LockOwner, req.Lock, req.LockFlags) + + // type LockRequest struct { + // Header + // Handle HandleID + // // LockOwner is a unique identifier for the originating client, to identify locks. + // LockOwner LockOwner + // Lock FileLock + // LockFlags LockFlags + // } + panic("TODO") } // LockWait acquires a lock on a byte range of the node, waiting // until the lock can be obtained (or context is canceled). func (h *Handle) LockWait(ctx context.Context, req *fuse.LockWaitRequest) error { + log.Printf("[lockwait] handle=%q lockowner=%d lock=%#o lockflags=%#o", h.f.Name(), req.LockOwner, req.Lock, req.LockFlags) + + // type LockWaitRequest LockRequest + panic("TODO") } @@ -111,6 +203,10 @@ func (h *Handle) LockWait(ctx context.Context, req *fuse.LockWaitRequest) error // be released also implicitly, see HandleFlockLocker and // HandlePOSIXLocker. func (h *Handle) Unlock(ctx context.Context, req *fuse.UnlockRequest) error { + log.Printf("[unlock] handle=%q lockowner=%d lock=%#o lockflags=%#o", h.f.Name(), req.LockOwner, req.Lock, req.LockFlags) + + // type UnlockRequest LockRequest + panic("TODO") } @@ -123,5 +219,19 @@ func (h *Handle) Unlock(ctx context.Context, req *fuse.UnlockRequest) error { // have Lock.Type F_UNLCK, and the whole struct should be // overwritten for in case of conflicting locks. func (h *Handle) QueryLock(ctx context.Context, req *fuse.QueryLockRequest, resp *fuse.QueryLockResponse) error { + log.Printf("[querylock] handle=%q lockowner=%d lock=%#o lockflags=%#o", h.f.Name(), req.LockOwner, req.Lock, req.LockFlags) + + // type QueryLockRequest struct { + // Header + // Handle HandleID + // LockOwner LockOwner + // Lock FileLock + // LockFlags LockFlags + // } + + // type QueryLockResponse struct { + // Lock FileLock + // } + panic("TODO") } diff --git a/node.go b/node.go index c407dfe..ff2163a 100644 --- a/node.go +++ b/node.go @@ -49,8 +49,8 @@ func (n *Node) srcpath() string { return filepath.Join(n.fs.SourcePath, n.path) } -func (n *Node) Attr(ctx context.Context, a *fuse.Attr) error { - log.Printf("[attr] node=%q", n.path) +func (n *Node) Attr(ctx context.Context, a *fuse.Attr) (err error) { + defer func() { log.Printf("[attr] node=%q -- attr=%s err=%v", n.path, a.String(), err) }() fi, err := os.Stat(n.srcpath()) if err != nil { @@ -86,8 +86,8 @@ func (n *Node) Attr(ctx context.Context, a *fuse.Attr) error { // the directory, Lookup should return ENOENT. // // Lookup need not to handle the names "." and "..". -func (n *Node) Lookup(ctx context.Context, name string) (fs.Node, error) { - log.Printf("[lookup] node=%q name=%q", n.path, name) +func (n *Node) Lookup(ctx context.Context, name string) (_ fs.Node, err error) { + defer func() { log.Printf("[lookup] node=%q name=%q -- err=%v", n.path, name, err) }() path := filepath.Join(n.path, name) srcpath := filepath.Join(n.fs.SourcePath, path) @@ -97,15 +97,15 @@ func (n *Node) Lookup(ctx context.Context, name string) (fs.Node, error) { return NewNode(n.fs, path), nil } -func (n *Node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { - log.Printf("[readdirall] node=%q", n.path) +func (n *Node) ReadDirAll(ctx context.Context) (ents []fuse.Dirent, err error) { + defer func() { log.Printf("[readdirall] node=%q -- ents=%d err=%v", n.path, len(ents), err) }() fis, err := ioutil.ReadDir(n.srcpath()) if err != nil { return nil, err } - ents := make([]fuse.Dirent, len(fis)) + ents = make([]fuse.Dirent, len(fis)) for i, fi := range fis { statt := fi.Sys().(*syscall.Stat_t) ents[i] = fuse.Dirent{Inode: statt.Ino, Name: fi.Name()} @@ -130,8 +130,8 @@ func (n *Node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { // req.Valid is a bitmask of what fields are actually being set. // For example, the method should not change the mode of the file // unless req.Valid.Mode() is true. -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()) +func (n *Node) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) (err error) { + defer func() { log.Printf("[setattr] node=%q req=%s -- err=%v", n.path, req.String(), err) }() // Obtain current file stat. srcpath := n.srcpath() @@ -200,10 +200,10 @@ func (n *Node) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse // TODO: Not sure what these are for. if req.Valid.Handle() { - panic("TODO?") + println("TODO: setattr.handle") } if req.Valid.LockOwner() { - panic("TODO?") + println("TODO: setattr.lockowner") } // Update response attributes. @@ -211,8 +211,10 @@ 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. -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) +func (n *Node) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (_ fs.Node, err error) { + defer func() { + log.Printf("[symlink] node=%q newname=%q target=%q -- err=%v", n.path, req.NewName, req.Target, err) + }() if err := os.Symlink(req.Target, req.NewName); err != nil { return nil, err } @@ -220,17 +222,19 @@ func (n *Node) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (fs.Node, } // Readlink reads a symbolic link. -func (n *Node) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) { - log.Printf("[readlink] node=%q", n.path) +func (n *Node) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (_ string, err error) { + defer func() { log.Printf("[readlink] node=%q -- err=%v", n.path, err) }() return os.Readlink(n.srcpath()) } // Link creates a new directory entry in the receiver based on an // 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, err error) { old := _old.(*Node) - log.Printf("[link] node=%q oldnode=%q name=%q", n.path, old.srcpath(), req.NewName) + defer func() { + log.Printf("[link] node=%q oldnode=%q name=%q -- err=%v", n.path, old.srcpath(), req.NewName, err) + }() // assert(n.IsDir()) if err := os.Link(old.srcpath(), req.NewName); err != nil { @@ -242,8 +246,8 @@ func (n *Node) Link(ctx context.Context, req *fuse.LinkRequest, _old fs.Node) (f // Remove removes the entry with the given name from // the receiver, which must be a directory. The entry to be removed // may correspond to a file (unlink) or to a directory (rmdir). -func (n *Node) Remove(ctx context.Context, req *fuse.RemoveRequest) error { - log.Printf("[remove] node=%q name=%q dir=%v", n.path, req.Name, req.Dir) +func (n *Node) Remove(ctx context.Context, req *fuse.RemoveRequest) (err error) { + defer func() { log.Printf("[remove] node=%q name=%q dir=%v -- err=%v", n.path, req.Name, req.Dir, err) }() if req.Dir { return syscall.Rmdir(filepath.Join(n.srcpath(), req.Name)) @@ -259,8 +263,8 @@ func (n *Node) Remove(ctx context.Context, req *fuse.RemoveRequest) error { // call but not the open(2) system call. If Access is not // implemented, the Node behaves as if it always returns nil // (permission granted), relying on checks in Open instead. -func (n *Node) Access(ctx context.Context, req *fuse.AccessRequest) error { - log.Printf("[access] node=%q mask=%#o", n.path, req.Mask) +func (n *Node) Access(ctx context.Context, req *fuse.AccessRequest) (err error) { + defer func() { log.Printf("[access] node=%q mask=%#o -- err=%v", n.path, req.Mask, err) }() return syscall.Access(n.srcpath(), req.Mask) } @@ -271,9 +275,9 @@ func (n *Node) Access(ctx context.Context, req *fuse.AccessRequest) error { // Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (Node, error) //} -func (n *Node) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) { - 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 { +func (n *Node) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (_ fs.Node, err error) { + defer func() { log.Printf("[mkdir] node=%q mode=%#o umask=%#o -- err=%v", n.path, req.Mode, req.Umask, err) }() + 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 @@ -289,8 +293,8 @@ func (n *Node) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, erro // succeed, and the Node itself will be used as the Handle. // // XXX note about access. XXX OpenFlags. -func (n *Node) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) { - log.Printf("[open] node=%q dir=%v flags=%#o", n.path, req.Dir, req.Flags) +func (n *Node) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (_ fs.Handle, err error) { + defer func() { log.Printf("[open] node=%q dir=%v flags=%#o -- err=%v", n.path, req.Dir, req.Flags, err) }() // TODO(bbj): Where does mode come from? f, err := os.OpenFile(n.srcpath(), int(req.Flags), 0777) @@ -301,10 +305,12 @@ func (n *Node) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenR } // Create creates a new directory entry in the receiver, which must be a directory. -func (n *Node) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) { - log.Printf("[create] node=%q name=%q flags=%#o mode=%#o umask=%#o", n.path, req.Name, req.Flags, req.Mode, req.Umask) +func (n *Node) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (_ fs.Node, _ fs.Handle, err error) { + defer func() { + log.Printf("[create] node=%q name=%q flags=%#o mode=%#o umask=%#o -- err=%v", n.path, req.Name, req.Flags, req.Mode, req.Umask, err) + }() - f, err := os.OpenFile(filepath.Join(n.srcpath(), req.Name), int(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 } @@ -318,22 +324,26 @@ func (n *Node) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.C // implicitly forgotten as part of the unmount. // 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) (err error) { newDir := _newDir.(*Node) - log.Printf("[rename] node=%q oldname=%q newnode=%q newname=%q", n.path, req.NewName, newDir.path, req.OldName) + func() { + log.Printf("[rename] node=%q oldname=%q newnode=%q newname=%q -- err=%v", n.path, req.NewName, newDir.path, req.OldName, err) + }() 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) { - 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 { +func (n *Node) Mknod(ctx context.Context, req *fuse.MknodRequest) (_ fs.Node, err error) { + defer func() { + log.Printf("[mknod] node=%q name=%q mode=%#o rdev=%q newname=%q -- err=%v", n.path, req.Name, req.Mode, req.Rdev, req.Umask, err) + }() + 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 { - log.Printf("[fsync] node=%q flags=%#o dir=%v", n.path, req.Flags, req.Dir) +func (n *Node) Fsync(ctx context.Context, req *fuse.FsyncRequest) (err error) { + defer func() { log.Printf("[fsync] node=%q flags=%#o dir=%v -- err=%v", n.path, req.Flags, req.Dir, err) }() f, err := os.Open(n.srcpath()) if err != nil { @@ -349,35 +359,39 @@ func (n *Node) Fsync(ctx context.Context, req *fuse.FsyncRequest) error { // node. // // 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 { - log.Printf("[getxattr] node=%q name=%q", n.path, req.Name) +func (n *Node) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) (err error) { + defer func() { log.Printf("[getxattr] node=%q name=%q -- err=%T", n.path, req.Name, err) }() // TODO(bbj): Handle req.Size & returned syscall.Getxattr() size. - _, err := syscall.Getxattr(n.srcpath(), req.Name, resp.Xattr) + if _, err = syscall.Getxattr(n.srcpath(), req.Name, resp.Xattr); err == syscall.ENODATA { + return fuse.ErrNoXattr + } return err } // Listxattr lists the extended attributes recorded for the node. -func (n *Node) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error { - log.Printf("[listxattr] node=%q", n.path) +func (n *Node) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) (err error) { + defer func() { log.Printf("[listxattr] node=%q -- err=%v", n.path, err) }() // TODO(bbj): Handle req.Size & returned syscall.Getxattr() size. - _, err := syscall.Listxattr(n.srcpath(), resp.Xattr) + _, err = syscall.Listxattr(n.srcpath(), resp.Xattr) return err } // Setxattr sets an extended attribute with the given name and // value for the node. -func (n *Node) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error { - log.Printf("[setxattr] node=%q name=%q data=%q flags=%#o", n.path, req.Name, string(req.Xattr), req.Flags) +func (n *Node) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) (err error) { + defer func() { + log.Printf("[setxattr] node=%q name=%q data=%q flags=%#o", n.path, req.Name, string(req.Xattr), req.Flags, err) + }() return syscall.Setxattr(n.srcpath(), req.Name, req.Xattr, int(req.Flags)) } // Removexattr removes an extended attribute for the name. // // If there is no xattr by that name, returns fuse.ErrNoXattr. -func (n *Node) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error { - log.Printf("[removexattr] node=%q name=%q", n.path, req.Name) +func (n *Node) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) (err error) { + defer func() { log.Printf("[removexattr] node=%q name=%q -- err=%v", n.path, req.Name, err) }() return syscall.Removexattr(n.srcpath(), req.Name) }