mirror of https://github.com/perkeep/perkeep.git
1023 lines
22 KiB
Go
1023 lines
22 KiB
Go
// Copyright 2012 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// FUSE service loop, for servers that wish to use it.
|
|
|
|
package fuse
|
|
|
|
import (
|
|
"fmt"
|
|
"hash/fnv"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
// TODO: FINISH DOCS
|
|
|
|
// An Intr is a channel that signals that a request has been interrupted.
|
|
// Being able to receive from the channel means the request has been
|
|
// interrupted.
|
|
type Intr chan struct{}
|
|
|
|
func (Intr) String() string { return "fuse.Intr" }
|
|
|
|
// An FS is the interface required of a file system.
|
|
//
|
|
// Root() (Node, Error)
|
|
//
|
|
// Root is called to obtain the Node for the file system root.
|
|
//
|
|
// Optional Methods
|
|
//
|
|
// An FS implementation may implement
|
|
// additional methods to handle the corresponding FUSE requests:
|
|
//
|
|
// Init(req *InitRequest, resp *InitResponse) Error
|
|
//
|
|
// Init is called to initialize the FUSE connection.
|
|
// It can inspect the request and adjust the response as desired.
|
|
// The default response sets MaxReadahead to 0 and MaxWrite to 4096.
|
|
// Init must return promptly.
|
|
//
|
|
// Statfs(resp *StatfsResponse, intr Intr) Error
|
|
//
|
|
// Statfs is called to obtain file system metadata. It should write that data to resp.
|
|
//
|
|
// Rename(req *RenameRequest, intr Intr) Error
|
|
//
|
|
// XXXX this is not implemented like this. Instead, Rename is a method
|
|
// on the source dierctory node, and takes a newDir Node parameter. Fix it like this?
|
|
// Rename is called to rename the file req.OldName in the directory req.OldDir to
|
|
// become the file req.NewName in the directory req.NewDir.
|
|
//
|
|
type FS interface {
|
|
Root() (Node, Error)
|
|
}
|
|
|
|
// A Node is the interface required of a file or directory.
|
|
// See the documentation for type FS for general information
|
|
// pertaining to all methods.
|
|
//
|
|
// Getattr(resp *GetattrResponse, intr Intr) fuse.Error
|
|
//
|
|
// Getattr obtains the standard metadata for the receiver.
|
|
// It should store that metadata in resp.
|
|
//
|
|
// Open(xxx, intr Intr) (Handle, fuse.Error)
|
|
//
|
|
// Open opens the receiver.
|
|
// XXX note about access. XXX OpenFlags.
|
|
// XXX note that the Node may be a file or directory.
|
|
//
|
|
// Optional Methods
|
|
//
|
|
// An Node implementation may implement additional methods
|
|
// to handle the corresponding FUSE requests.
|
|
//
|
|
// These optional requests can be called for both file and directory nodes:
|
|
//
|
|
// Access
|
|
//
|
|
// Access checks whether the calling context has permission for
|
|
// the given operations on the receiver. If so, Access should return nil. If not, Access should
|
|
// return EPERM. Note that this call affects the result of the access(2) system 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.
|
|
//
|
|
// Getxattr
|
|
//
|
|
// Getxattr obtains an extended attribute for the receiver.
|
|
// XXX
|
|
//
|
|
// Listxattr
|
|
//
|
|
// Listxattr lists the extended attributes recorded for the receiver.
|
|
//
|
|
// Removexattr
|
|
//
|
|
// Removexattr removes an extended attribute from the receiver.
|
|
//
|
|
// Setattr
|
|
//
|
|
// Setattr sets the standard metadata for the receiver.
|
|
//
|
|
// Setxattr
|
|
//
|
|
// Setxattr sets an extended attribute for the receiver.
|
|
//
|
|
// Optional Directory Methods
|
|
//
|
|
// These optional requests will be called only for directory nodes:
|
|
//
|
|
// Create(xxx)
|
|
//
|
|
// Create creates
|
|
//
|
|
// Link(xxx)
|
|
//
|
|
// Link XXX
|
|
//
|
|
// Lookup(name string, intr Intr) (Node, Error)
|
|
//
|
|
// Lookup looks up a specific entry in the receiver,
|
|
// which must be a directory. Lookup should return a Node
|
|
// corresponding to the entry. If the name does not exist in
|
|
// the directory, Lookup should return nil, err.
|
|
//
|
|
// Lookup need not to handle the names "." and "..".
|
|
//
|
|
// Mkdir
|
|
//
|
|
// Mkdir creates XXX
|
|
//
|
|
// Mknod XXX
|
|
//
|
|
// XXX
|
|
//
|
|
// Remove
|
|
//
|
|
// 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).
|
|
//
|
|
// Symlink
|
|
//
|
|
// Symlink creates a new symbolic link in the receiver, which must be a directory.
|
|
// The entry
|
|
//
|
|
// Optional Symlink Methods
|
|
//
|
|
// This optional request will be called only for symbolic link nodes:
|
|
//
|
|
// Readlink
|
|
//
|
|
// Readlink reads a symbolic link.
|
|
type Node interface {
|
|
Attr() Attr
|
|
}
|
|
|
|
var startTime = time.Now()
|
|
|
|
func nodeAttr(inode uint64, n Node) (attr Attr) {
|
|
attr = n.Attr()
|
|
if attr.Nlink == 0 {
|
|
attr.Nlink = 1
|
|
}
|
|
if attr.Atime.IsZero() {
|
|
attr.Atime = startTime
|
|
}
|
|
if attr.Mtime.IsZero() {
|
|
attr.Mtime = startTime
|
|
}
|
|
if attr.Ctime.IsZero() {
|
|
attr.Ctime = startTime
|
|
}
|
|
if attr.Crtime.IsZero() {
|
|
attr.Crtime = startTime
|
|
}
|
|
if attr.Inode == 0 {
|
|
attr.Inode = inode
|
|
}
|
|
return
|
|
}
|
|
|
|
// A Handle is the interface required of an opened file or directory.
|
|
// See the documentation for type FS for general information
|
|
// pertaining to all methods.
|
|
//
|
|
// Flush
|
|
//
|
|
// 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.
|
|
//
|
|
// Optional Methods
|
|
//
|
|
// A Handle implementation may implement additional methods to handle
|
|
// the corresponding FUSE requests. The most common to implement are
|
|
// Read, ReadDir, and Write.
|
|
//
|
|
// Fsync
|
|
//
|
|
// Getlk
|
|
//
|
|
// Read
|
|
//
|
|
// Readdir
|
|
//
|
|
// Release
|
|
//
|
|
// Setlk
|
|
//
|
|
// Setlkw
|
|
//
|
|
// Write
|
|
//
|
|
type Handle interface {
|
|
}
|
|
|
|
// Serve serves the FUSE connection by making calls to the methods
|
|
// of fs and the Nodes and Handles it makes available. It returns only
|
|
// when the connection has been closed or an unexpected error occurs.
|
|
func (c *Conn) Serve(fs FS) error {
|
|
if c.req != nil {
|
|
panic("fuse: Serve called twice")
|
|
}
|
|
c.req = map[RequestID]*serveRequest{}
|
|
|
|
root, err := fs.Root()
|
|
if err != nil {
|
|
return fmt.Errorf("cannot obtain root node: %v", syscall.Errno(err.(Errno)).Error())
|
|
}
|
|
c.node = append(c.node, nil, &serveNode{name: "/", node: root})
|
|
c.handle = append(c.handle, nil)
|
|
|
|
for {
|
|
req, err := c.ReadRequest()
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
return err
|
|
}
|
|
|
|
go c.serve(fs, req)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type serveConn struct {
|
|
meta sync.Mutex
|
|
req map[RequestID]*serveRequest
|
|
node []*serveNode
|
|
handle []*serveHandle
|
|
freeNode []NodeID
|
|
freeHandle []HandleID
|
|
nodeGen uint64
|
|
nodeHandles []map[HandleID]bool // open handles for a node; slice index is NodeID
|
|
}
|
|
|
|
type serveRequest struct {
|
|
Request Request
|
|
Intr Intr
|
|
}
|
|
|
|
type serveNode struct {
|
|
name string
|
|
node Node
|
|
inode uint64
|
|
isDir bool
|
|
}
|
|
|
|
func (sn *serveNode) attr() (attr Attr) {
|
|
attr = nodeAttr(sn.inode, sn.node)
|
|
if attr.Inode == 0 {
|
|
sn.inode = hash(sn.name)
|
|
attr.Inode = sn.inode
|
|
}
|
|
sn.isDir = attr.Mode&os.ModeDir != 0
|
|
return
|
|
}
|
|
|
|
func hash(s string) uint64 {
|
|
f := fnv.New64()
|
|
f.Write([]byte(s))
|
|
return f.Sum64()
|
|
}
|
|
|
|
type serveHandle struct {
|
|
handle Handle
|
|
readData []byte
|
|
trunc bool
|
|
writeData []byte
|
|
nodeID NodeID
|
|
}
|
|
|
|
func (c *Conn) saveNode(name string, node Node) (id NodeID, gen uint64, sn *serveNode) {
|
|
sn = &serveNode{name: name, node: node}
|
|
c.meta.Lock()
|
|
if n := len(c.freeNode); n > 0 {
|
|
id = c.freeNode[n-1]
|
|
c.freeNode = c.freeNode[:n-1]
|
|
c.node[id] = sn
|
|
c.nodeGen++
|
|
} else {
|
|
id = NodeID(len(c.node))
|
|
c.node = append(c.node, sn)
|
|
}
|
|
gen = c.nodeGen
|
|
c.meta.Unlock()
|
|
return
|
|
}
|
|
|
|
func (c *Conn) saveHandle(handle Handle, nodeID NodeID) (id HandleID, shandle *serveHandle) {
|
|
c.meta.Lock()
|
|
shandle = &serveHandle{handle: handle, nodeID: nodeID}
|
|
if n := len(c.freeHandle); n > 0 {
|
|
id = c.freeHandle[n-1]
|
|
c.freeHandle = c.freeHandle[:n-1]
|
|
c.handle[id] = shandle
|
|
} else {
|
|
id = HandleID(len(c.handle))
|
|
c.handle = append(c.handle, shandle)
|
|
}
|
|
|
|
// Update mapping from node ID -> set of open Handle IDs.
|
|
for len(c.nodeHandles) <= int(nodeID) {
|
|
c.nodeHandles = append(c.nodeHandles, nil)
|
|
}
|
|
if c.nodeHandles[nodeID] == nil {
|
|
c.nodeHandles[nodeID] = make(map[HandleID]bool)
|
|
}
|
|
c.nodeHandles[nodeID][id] = true
|
|
|
|
c.meta.Unlock()
|
|
return
|
|
}
|
|
|
|
func (c *Conn) dropNode(id NodeID) {
|
|
c.meta.Lock()
|
|
c.node[id] = nil
|
|
if len(c.nodeHandles) > int(id) {
|
|
c.nodeHandles[id] = nil
|
|
}
|
|
c.freeNode = append(c.freeNode, id)
|
|
c.meta.Unlock()
|
|
}
|
|
|
|
func (c *Conn) dropHandle(id HandleID) {
|
|
c.meta.Lock()
|
|
h := c.handle[id]
|
|
delete(c.nodeHandles[h.nodeID], id)
|
|
c.handle[id] = nil
|
|
c.freeHandle = append(c.freeHandle, id)
|
|
c.meta.Unlock()
|
|
}
|
|
|
|
func (c *Conn) serve(fs FS, r Request) {
|
|
intr := make(Intr)
|
|
req := &serveRequest{Request: r, Intr: intr}
|
|
|
|
Debugf("<- %s", req)
|
|
var node Node
|
|
var handle Handle
|
|
var snode *serveNode
|
|
var shandle *serveHandle
|
|
c.meta.Lock()
|
|
hdr := r.Hdr()
|
|
if id := hdr.Node; id != 0 {
|
|
if id < NodeID(len(c.node)) {
|
|
snode = c.node[uint(id)]
|
|
}
|
|
if snode == nil {
|
|
c.meta.Unlock()
|
|
println("missing node", id, len(c.node), snode)
|
|
Debugf("-> %#x %v", hdr.ID, ESTALE)
|
|
r.RespondError(ESTALE)
|
|
return
|
|
}
|
|
node = snode.node
|
|
}
|
|
if id := r.handle(); id != 0 {
|
|
if id < HandleID(len(c.handle)) {
|
|
shandle = c.handle[uint(id)]
|
|
}
|
|
if shandle == nil {
|
|
println("missing handle", id, len(c.handle), shandle)
|
|
c.meta.Unlock()
|
|
Debugf("-> %#x %v", hdr.ID, ESTALE)
|
|
r.RespondError(ESTALE)
|
|
return
|
|
}
|
|
handle = shandle.handle
|
|
}
|
|
intr = make(chan struct{})
|
|
if c.req[hdr.ID] != nil {
|
|
// This happens with OSXFUSE. Assume it's okay and
|
|
// that we'll never see an interrupt for this one.
|
|
// Otherwise everything wedges. TODO: Report to OSXFUSE?
|
|
intr = nil
|
|
} else {
|
|
c.req[hdr.ID] = req
|
|
}
|
|
c.meta.Unlock()
|
|
|
|
// Call this before responding.
|
|
// After responding is too late: we might get another request
|
|
// with the same ID and be very confused.
|
|
done := func(resp interface{}) {
|
|
Debugf("-> %#x %v", hdr.ID, resp)
|
|
c.meta.Lock()
|
|
c.req[hdr.ID] = nil
|
|
c.meta.Unlock()
|
|
}
|
|
|
|
switch r := r.(type) {
|
|
default:
|
|
// Note: To FUSE, ENOSYS means "this server never implements this request."
|
|
// It would be inappropriate to return ENOSYS for other operations in this
|
|
// switch that might only be unavailable in some contexts, not all.
|
|
done(ENOSYS)
|
|
r.RespondError(ENOSYS)
|
|
|
|
// FS operations.
|
|
case *InitRequest:
|
|
s := &InitResponse{
|
|
MaxWrite: 4096,
|
|
}
|
|
if fs, ok := fs.(interface {
|
|
Init(*InitRequest, *InitResponse, Intr) Error
|
|
}); ok {
|
|
if err := fs.Init(r, s, intr); err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
}
|
|
done(s)
|
|
r.Respond(s)
|
|
|
|
case *StatfsRequest:
|
|
s := &StatfsResponse{}
|
|
if fs, ok := fs.(interface {
|
|
Statfs(*StatfsRequest, *StatfsResponse, Intr) Error
|
|
}); ok {
|
|
if err := fs.Statfs(r, s, intr); err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
}
|
|
done(s)
|
|
r.Respond(s)
|
|
|
|
// Node operations.
|
|
case *GetattrRequest:
|
|
s := &GetattrResponse{}
|
|
if n, ok := node.(interface {
|
|
Getattr(*GetattrRequest, *GetattrResponse, Intr) Error
|
|
}); ok {
|
|
if err := n.Getattr(r, s, intr); err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
} else {
|
|
s.AttrValid = 1 * time.Minute
|
|
s.Attr = snode.attr()
|
|
}
|
|
done(s)
|
|
r.Respond(s)
|
|
|
|
case *SetattrRequest:
|
|
s := &SetattrResponse{}
|
|
|
|
// Special-case truncation, if no other bits are set
|
|
// and the open Handles all have a WriteAll method.
|
|
if r.Valid&SetattrSize != 0 && r.Size == 0 {
|
|
type writeAll interface {
|
|
WriteAll([]byte, Intr) Error
|
|
}
|
|
switch r.Valid {
|
|
case SetattrLockOwner | SetattrSize, SetattrSize:
|
|
// Seen on Linux. Handle isn't set.
|
|
c.meta.Lock()
|
|
for hid := range c.nodeHandles[hdr.Node] {
|
|
shandle := c.handle[hid]
|
|
if _, ok := shandle.handle.(writeAll); ok {
|
|
shandle.trunc = true
|
|
}
|
|
}
|
|
c.meta.Unlock()
|
|
case SetattrHandle | SetattrSize:
|
|
// Seen on OS X; the Handle is provided.
|
|
if _, ok := handle.(writeAll); ok {
|
|
shandle.trunc = true
|
|
}
|
|
}
|
|
}
|
|
|
|
log.Printf("setattr %v", r)
|
|
if n, ok := node.(interface {
|
|
Setattr(*SetattrRequest, *SetattrResponse, Intr) Error
|
|
}); ok {
|
|
if err := n.Setattr(r, s, intr); err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
done(s)
|
|
r.Respond(s)
|
|
break
|
|
}
|
|
|
|
if s.AttrValid == 0 {
|
|
s.AttrValid = 1 * time.Minute
|
|
}
|
|
s.Attr = snode.attr()
|
|
done(s)
|
|
r.Respond(s)
|
|
|
|
case *SymlinkRequest:
|
|
s := &SymlinkResponse{}
|
|
n, ok := node.(interface {
|
|
Symlink(*SymlinkRequest, Intr) (Node, Error)
|
|
})
|
|
if !ok {
|
|
done(EIO) // XXX or EPERM like Mkdir?
|
|
r.RespondError(EIO)
|
|
break
|
|
}
|
|
n2, err := n.Symlink(r, intr)
|
|
if err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
c.saveLookup(&s.LookupResponse, snode, r.NewName, n2)
|
|
done(s)
|
|
r.Respond(s)
|
|
|
|
case *ReadlinkRequest:
|
|
n, ok := node.(interface {
|
|
Readlink(*ReadlinkRequest, Intr) (string, Error)
|
|
})
|
|
if !ok {
|
|
done(EIO) /// XXX or EPERM?
|
|
r.RespondError(EIO)
|
|
break
|
|
}
|
|
target, err := n.Readlink(r, intr)
|
|
if err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
done(target)
|
|
r.Respond(target)
|
|
|
|
case *LinkRequest:
|
|
n, ok := node.(interface {
|
|
Link(r *LinkRequest, old Node, intr Intr) (Node, Error)
|
|
})
|
|
if !ok {
|
|
log.Printf("Node %T doesn't implement fuse Link", node)
|
|
done(EIO) /// XXX or EPERM?
|
|
r.RespondError(EIO)
|
|
break
|
|
}
|
|
c.meta.Lock()
|
|
var oldNode *serveNode
|
|
if int(r.OldNode) < len(c.node) {
|
|
oldNode = c.node[r.OldNode]
|
|
}
|
|
c.meta.Unlock()
|
|
if oldNode == nil {
|
|
log.Printf("In LinkRequest, node %d not found", r.OldNode)
|
|
done(EIO)
|
|
r.RespondError(EIO)
|
|
break
|
|
}
|
|
n2, err := n.Link(r, oldNode.node, intr)
|
|
if err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
s := &LookupResponse{}
|
|
c.saveLookup(s, snode, r.NewName, n2)
|
|
done(s)
|
|
r.Respond(s)
|
|
|
|
case *RemoveRequest:
|
|
n, ok := node.(interface {
|
|
Remove(*RemoveRequest, Intr) Error
|
|
})
|
|
if !ok {
|
|
done(EIO) /// XXX or EPERM?
|
|
r.RespondError(EIO)
|
|
break
|
|
}
|
|
err := n.Remove(r, intr)
|
|
if err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
done(nil)
|
|
r.Respond()
|
|
|
|
case *AccessRequest:
|
|
if n, ok := node.(interface {
|
|
Access(*AccessRequest, Intr) Error
|
|
}); ok {
|
|
if err := n.Access(r, intr); err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
}
|
|
done(r)
|
|
r.Respond()
|
|
|
|
case *LookupRequest:
|
|
var n2 Node
|
|
var err Error
|
|
s := &LookupResponse{}
|
|
if n, ok := node.(interface {
|
|
Lookup(string, Intr) (Node, Error)
|
|
}); ok {
|
|
n2, err = n.Lookup(r.Name, intr)
|
|
} else if n, ok := node.(interface {
|
|
Lookup(*LookupRequest, *LookupResponse, Intr) (Node, Error)
|
|
}); ok {
|
|
n2, err = n.Lookup(r, s, intr)
|
|
} else {
|
|
done(ENOENT)
|
|
r.RespondError(ENOENT)
|
|
break
|
|
}
|
|
if err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
c.saveLookup(s, snode, r.Name, n2)
|
|
done(s)
|
|
r.Respond(s)
|
|
|
|
case *MkdirRequest:
|
|
s := &MkdirResponse{}
|
|
n, ok := node.(interface {
|
|
Mkdir(*MkdirRequest, Intr) (Node, Error)
|
|
})
|
|
if !ok {
|
|
done(EPERM)
|
|
r.RespondError(EPERM)
|
|
break
|
|
}
|
|
n2, err := n.Mkdir(r, intr)
|
|
if err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
c.saveLookup(&s.LookupResponse, snode, r.Name, n2)
|
|
done(s)
|
|
r.Respond(s)
|
|
|
|
case *OpenRequest:
|
|
s := &OpenResponse{Flags: OpenDirectIO}
|
|
var h2 Handle
|
|
if n, ok := node.(interface {
|
|
Open(*OpenRequest, *OpenResponse, Intr) (Handle, Error)
|
|
}); ok {
|
|
hh, err := n.Open(r, s, intr)
|
|
if err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
h2 = hh
|
|
} else {
|
|
h2 = node
|
|
}
|
|
s.Handle, _ = c.saveHandle(h2, hdr.Node)
|
|
done(s)
|
|
r.Respond(s)
|
|
|
|
case *CreateRequest:
|
|
n, ok := node.(interface {
|
|
Create(*CreateRequest, *CreateResponse, Intr) (Node, Handle, Error)
|
|
})
|
|
if !ok {
|
|
// If we send back ENOSYS, FUSE will try mknod+open.
|
|
done(EPERM)
|
|
r.RespondError(EPERM)
|
|
break
|
|
}
|
|
s := &CreateResponse{OpenResponse: OpenResponse{Flags: OpenDirectIO}}
|
|
n2, h2, err := n.Create(r, s, intr)
|
|
if err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
c.saveLookup(&s.LookupResponse, snode, r.Name, n2)
|
|
h, shandle := c.saveHandle(h2, hdr.Node)
|
|
s.Handle = h
|
|
shandle.trunc = true
|
|
done(s)
|
|
r.Respond(s)
|
|
|
|
case *GetxattrRequest, *SetxattrRequest, *ListxattrRequest, *RemovexattrRequest:
|
|
// TODO: Use n.
|
|
done(ENOSYS)
|
|
r.RespondError(ENOSYS)
|
|
|
|
case *ForgetRequest:
|
|
n, ok := node.(interface {
|
|
Forget()
|
|
})
|
|
if ok {
|
|
n.Forget()
|
|
}
|
|
c.dropNode(hdr.Node)
|
|
done(r)
|
|
r.Respond()
|
|
|
|
// Handle operations.
|
|
case *ReadRequest:
|
|
s := &ReadResponse{Data: make([]byte, 0, r.Size)}
|
|
if snode.isDir {
|
|
if h, ok := handle.(interface {
|
|
ReadDir(Intr) ([]Dirent, Error)
|
|
}); ok {
|
|
if shandle.readData == nil {
|
|
attr := snode.attr()
|
|
dirs, err := h.ReadDir(intr)
|
|
if err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
var data []byte
|
|
data = AppendDirent(data, Dirent{Inode: attr.Inode, Name: "."})
|
|
data = AppendDirent(data, Dirent{Inode: attr.Inode, Name: ".."})
|
|
for _, dir := range dirs {
|
|
if dir.Inode == 0 {
|
|
dir.Inode = hash(path.Join(snode.name, dir.Name))
|
|
}
|
|
data = AppendDirent(data, dir)
|
|
}
|
|
shandle.readData = data
|
|
}
|
|
HandleRead(r, s, shandle.readData)
|
|
done(s)
|
|
r.Respond(s)
|
|
break
|
|
}
|
|
} else {
|
|
if h, ok := handle.(interface {
|
|
ReadAll(Intr) ([]byte, Error)
|
|
}); ok {
|
|
if shandle.readData == nil {
|
|
data, err := h.ReadAll(intr)
|
|
if err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
if data == nil {
|
|
data = []byte{}
|
|
}
|
|
shandle.readData = data
|
|
}
|
|
HandleRead(r, s, shandle.readData)
|
|
done(s)
|
|
r.Respond(s)
|
|
break
|
|
}
|
|
}
|
|
h, ok := handle.(interface {
|
|
Read(*ReadRequest, *ReadResponse, Intr) Error
|
|
})
|
|
if !ok {
|
|
fmt.Printf("NO READ FOR %T\n", handle)
|
|
done(EIO)
|
|
r.RespondError(EIO)
|
|
break
|
|
}
|
|
if err := h.Read(r, s, intr); err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
done(s)
|
|
r.Respond(s)
|
|
|
|
case *WriteRequest:
|
|
s := &WriteResponse{}
|
|
if shandle.trunc && r.Offset == int64(len(shandle.writeData)) {
|
|
shandle.writeData = append(shandle.writeData, r.Data...)
|
|
s.Size = len(r.Data)
|
|
done(s)
|
|
r.Respond(s)
|
|
break
|
|
}
|
|
if h, ok := handle.(interface {
|
|
Write(*WriteRequest, *WriteResponse, Intr) Error
|
|
}); ok {
|
|
if err := h.Write(r, s, intr); err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
done(s)
|
|
r.Respond(s)
|
|
break
|
|
}
|
|
println("NO WRITE")
|
|
done(EIO)
|
|
r.RespondError(EIO)
|
|
|
|
case *FlushRequest:
|
|
if shandle.trunc {
|
|
h := handle.(interface {
|
|
WriteAll([]byte, Intr) Error
|
|
})
|
|
if err := h.WriteAll(shandle.writeData, intr); err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
shandle.writeData = nil
|
|
shandle.trunc = false
|
|
}
|
|
if h, ok := handle.(interface {
|
|
Flush(*FlushRequest, Intr) Error
|
|
}); ok {
|
|
if err := h.Flush(r, intr); err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
}
|
|
done(nil)
|
|
r.Respond()
|
|
|
|
case *ReleaseRequest:
|
|
// No matter what, release the handle.
|
|
c.dropHandle(r.handle())
|
|
if h, ok := handle.(interface {
|
|
Release(*ReleaseRequest, Intr) Error
|
|
}); ok {
|
|
if err := h.Release(r, intr); err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
}
|
|
done(nil)
|
|
r.Respond()
|
|
|
|
case *DestroyRequest:
|
|
fs, ok := fs.(interface {
|
|
Destroy()
|
|
})
|
|
if ok {
|
|
fs.Destroy()
|
|
}
|
|
done(nil)
|
|
r.Respond()
|
|
|
|
case *RenameRequest:
|
|
c.meta.Lock()
|
|
var newDirNode *serveNode
|
|
if int(r.NewDir) < len(c.node) {
|
|
newDirNode = c.node[r.NewDir]
|
|
}
|
|
c.meta.Unlock()
|
|
if newDirNode == nil {
|
|
println("RENAME NEW DIR NODE NOT FOUND")
|
|
done(EIO)
|
|
r.RespondError(EIO)
|
|
break
|
|
}
|
|
n, ok := node.(interface {
|
|
Rename(r *RenameRequest, newDir Node, intr Intr) Error
|
|
})
|
|
if !ok {
|
|
log.Printf("Node %T missing Rename method", node)
|
|
done(EIO) // XXX or EPERM like Mkdir?
|
|
r.RespondError(EIO)
|
|
break
|
|
}
|
|
err := n.Rename(r, newDirNode.node, intr)
|
|
if err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
done(nil)
|
|
r.Respond()
|
|
|
|
case *MknodRequest:
|
|
n, ok := node.(interface {
|
|
Mknod(r *MknodRequest, intr Intr) (Node, Error)
|
|
})
|
|
if !ok {
|
|
log.Printf("Node %T missing Mknod method", node)
|
|
done(EIO)
|
|
r.RespondError(EIO)
|
|
break
|
|
}
|
|
n2, err := n.Mknod(r, intr)
|
|
if err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
s := &LookupResponse{}
|
|
c.saveLookup(s, snode, r.Name, n2)
|
|
done(s)
|
|
r.Respond(s)
|
|
|
|
case *FsyncRequest:
|
|
n, ok := node.(interface {
|
|
Fsync(r *FsyncRequest, intr Intr) Error
|
|
})
|
|
if !ok {
|
|
log.Printf("Node %T missing Fsync method", node)
|
|
done(EIO)
|
|
r.RespondError(EIO)
|
|
break
|
|
}
|
|
err := n.Fsync(r, intr)
|
|
if err != nil {
|
|
done(err)
|
|
r.RespondError(err)
|
|
break
|
|
}
|
|
done(nil)
|
|
r.Respond()
|
|
|
|
/* case *FsyncdirRequest:
|
|
done(ENOSYS)
|
|
r.RespondError(ENOSYS)
|
|
|
|
case *GetlkRequest, *SetlkRequest, *SetlkwRequest:
|
|
done(ENOSYS)
|
|
r.RespondError(ENOSYS)
|
|
|
|
// One of a kind.
|
|
case *InterruptRequest:
|
|
c.meta.Lock()
|
|
ireq := c.req[r.OldID]
|
|
if ireq != nil && ireq.Intr != nil {
|
|
close(ireq.Intr)
|
|
ireq.Intr = nil
|
|
}
|
|
c.meta.Unlock()
|
|
done(nil)
|
|
r.Respond()
|
|
|
|
case *BmapRequest:
|
|
done(ENOSYS)
|
|
r.RespondError(ENOSYS)
|
|
|
|
case *SetvolnameRequest, *GetxtimesRequest, *ExchangeRequest:
|
|
done(ENOSYS)
|
|
r.RespondError(ENOSYS)
|
|
*/
|
|
}
|
|
}
|
|
|
|
func (c *Conn) saveLookup(s *LookupResponse, snode *serveNode, elem string, n2 Node) {
|
|
name := path.Join(snode.name, elem)
|
|
var sn *serveNode
|
|
s.Node, s.Generation, sn = c.saveNode(name, n2)
|
|
if s.EntryValid == 0 {
|
|
s.EntryValid = 1 * time.Minute
|
|
}
|
|
if s.AttrValid == 0 {
|
|
s.AttrValid = 1 * time.Minute
|
|
}
|
|
s.Attr = sn.attr()
|
|
}
|
|
|
|
// HandleRead handles a read request assuming that data is the entire file content.
|
|
// It adjusts the amount returned in resp according to req.Offset and req.Size.
|
|
func HandleRead(req *ReadRequest, resp *ReadResponse, data []byte) {
|
|
if req.Offset >= int64(len(data)) {
|
|
data = nil
|
|
} else {
|
|
data = data[req.Offset:]
|
|
}
|
|
if len(data) > req.Size {
|
|
data = data[:req.Size]
|
|
}
|
|
n := copy(resp.Data[:req.Size], data)
|
|
resp.Data = resp.Data[:n]
|
|
}
|
|
|
|
// DataHandle returns a read-only Handle that satisfies reads
|
|
// using the given data.
|
|
func DataHandle(data []byte) Handle {
|
|
return &dataHandle{data}
|
|
}
|
|
|
|
type dataHandle struct {
|
|
data []byte
|
|
}
|
|
|
|
func (d *dataHandle) Read(intr Intr) ([]byte, Error) {
|
|
return d.data, nil
|
|
}
|