From 38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Thu, 12 May 2016 23:56:07 -0400 Subject: Moving to glide --- vendor/github.com/mattermost/rsc/fuse/serve.go | 1022 ++++++++++++++++++++++++ 1 file changed, 1022 insertions(+) create mode 100644 vendor/github.com/mattermost/rsc/fuse/serve.go (limited to 'vendor/github.com/mattermost/rsc/fuse/serve.go') diff --git a/vendor/github.com/mattermost/rsc/fuse/serve.go b/vendor/github.com/mattermost/rsc/fuse/serve.go new file mode 100644 index 000000000..fa4f27e3f --- /dev/null +++ b/vendor/github.com/mattermost/rsc/fuse/serve.go @@ -0,0 +1,1022 @@ +// 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 +} -- cgit v1.2.3-1-g7c22