diff options
Diffstat (limited to 'vendor/github.com/mattermost/rsc/fuse/serve.go')
-rw-r--r-- | vendor/github.com/mattermost/rsc/fuse/serve.go | 1022 |
1 files changed, 0 insertions, 1022 deletions
diff --git a/vendor/github.com/mattermost/rsc/fuse/serve.go b/vendor/github.com/mattermost/rsc/fuse/serve.go deleted file mode 100644 index fa4f27e3f..000000000 --- a/vendor/github.com/mattermost/rsc/fuse/serve.go +++ /dev/null @@ -1,1022 +0,0 @@ -// 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 -} |