func (c *serveConn) serve(r fuse.Request) { intr := make(Intr) req := &serveRequest{Request: r, Intr: intr} c.debug(request{ Op: opName(r), Request: r.Hdr(), In: r, }) var node Node var snode *serveNode c.meta.Lock() hdr := r.Hdr() if id := hdr.Node; id != 0 { if id < fuse.NodeID(len(c.node)) { snode = c.node[uint(id)] } if snode == nil { c.meta.Unlock() c.debug(response{ Op: opName(r), Request: logResponseHeader{ID: hdr.ID}, Error: fuse.ESTALE.ErrnoName(), // this is the only place that sets both Error and // Out; not sure if i want to do that; might get rid // of len(c.node) things altogether Out: logMissingNode{ MaxNode: fuse.NodeID(len(c.node)), }, }) r.RespondError(fuse.ESTALE) return } node = snode.node } 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? // // TODO this might have been because of missing done() calls 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{}) { msg := response{ Op: opName(r), Request: logResponseHeader{ID: hdr.ID}, } if err, ok := resp.(error); ok { msg.Error = err.Error() if ferr, ok := err.(fuse.ErrorNumber); ok { errno := ferr.Errno() msg.Errno = errno.ErrnoName() if errno == err { // it's just a fuse.Errno with no extra detail; // skip the textual message for log readability msg.Error = "" } } else { msg.Errno = fuse.DefaultErrno.ErrnoName() } } else { msg.Out = resp } c.debug(msg) c.meta.Lock() delete(c.req, hdr.ID) 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(fuse.ENOSYS) r.RespondError(fuse.ENOSYS) // FS operations. case *fuse.InitRequest: s := &fuse.InitResponse{ MaxWrite: 128 * 1024, Flags: fuse.InitBigWrites, } if fs, ok := c.fs.(FSIniter); ok { if err := fs.Init(r, s, intr); err != nil { done(err) r.RespondError(err) break } } done(s) r.Respond(s) case *fuse.StatfsRequest: s := &fuse.StatfsResponse{} if fs, ok := c.fs.(FSStatfser); ok { if err := fs.Statfs(r, s, intr); err != nil { done(err) r.RespondError(err) break } } done(s) r.Respond(s) // Node operations. case *fuse.GetattrRequest: s := &fuse.GetattrResponse{} if n, ok := node.(NodeGetattrer); ok { if err := n.Getattr(r, s, intr); err != nil { done(err) r.RespondError(err) break } } else { s.AttrValid = attrValidTime s.Attr = snode.attr() } done(s) r.Respond(s) case *fuse.SetattrRequest: s := &fuse.SetattrResponse{} if n, ok := node.(NodeSetattrer); 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 = attrValidTime } s.Attr = snode.attr() done(s) r.Respond(s) case *fuse.SymlinkRequest: s := &fuse.SymlinkResponse{} n, ok := node.(NodeSymlinker) if !ok { done(fuse.EIO) // XXX or EPERM like Mkdir? r.RespondError(fuse.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 *fuse.ReadlinkRequest: n, ok := node.(NodeReadlinker) if !ok { done(fuse.EIO) /// XXX or EPERM? r.RespondError(fuse.EIO) break } target, err := n.Readlink(r, intr) if err != nil { done(err) r.RespondError(err) break } done(target) r.Respond(target) case *fuse.LinkRequest: n, ok := node.(NodeLinker) if !ok { done(fuse.EIO) /// XXX or EPERM? r.RespondError(fuse.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 { c.debug(logLinkRequestOldNodeNotFound{ Request: r.Hdr(), In: r, }) done(fuse.EIO) r.RespondError(fuse.EIO) break } n2, err := n.Link(r, oldNode.node, intr) if err != nil { done(err) r.RespondError(err) break } s := &fuse.LookupResponse{} c.saveLookup(s, snode, r.NewName, n2) done(s) r.Respond(s) case *fuse.RemoveRequest: n, ok := node.(NodeRemover) if !ok { done(fuse.EIO) /// XXX or EPERM? r.RespondError(fuse.EIO) break } err := n.Remove(r, intr) if err != nil { done(err) r.RespondError(err) break } done(nil) r.Respond() case *fuse.AccessRequest: if n, ok := node.(NodeAccesser); ok { if err := n.Access(r, intr); err != nil { done(err) r.RespondError(err) break } } done(nil) r.Respond() case *fuse.LookupRequest: var n2 Node var err fuse.Error s := &fuse.LookupResponse{} if n, ok := node.(NodeStringLookuper); ok { n2, err = n.Lookup(r.Name, intr) } else if n, ok := node.(NodeRequestLookuper); ok { n2, err = n.Lookup(r, s, intr) } else { done(fuse.ENOENT) r.RespondError(fuse.ENOENT) break } if err != nil { done(err) r.RespondError(err) break } c.saveLookup(s, snode, r.Name, n2) done(s) r.Respond(s) case *fuse.MkdirRequest: s := &fuse.MkdirResponse{} n, ok := node.(NodeMkdirer) if !ok { done(fuse.EPERM) r.RespondError(fuse.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 *fuse.OpenRequest: s := &fuse.OpenResponse{} var h2 Handle if n, ok := node.(NodeOpener); 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 *fuse.CreateRequest: n, ok := node.(NodeCreater) if !ok { // If we send back ENOSYS, FUSE will try mknod+open. done(fuse.EPERM) r.RespondError(fuse.EPERM) break } s := &fuse.CreateResponse{OpenResponse: fuse.OpenResponse{}} 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) s.Handle = c.saveHandle(h2, hdr.Node) done(s) r.Respond(s) case *fuse.GetxattrRequest: n, ok := node.(NodeGetxattrer) if !ok { done(fuse.ENOTSUP) r.RespondError(fuse.ENOTSUP) break } s := &fuse.GetxattrResponse{} err := n.Getxattr(r, s, intr) if err != nil { done(err) r.RespondError(err) break } if r.Size != 0 && uint64(len(s.Xattr)) > uint64(r.Size) { done(fuse.ERANGE) r.RespondError(fuse.ERANGE) break } done(s) r.Respond(s) case *fuse.ListxattrRequest: n, ok := node.(NodeListxattrer) if !ok { done(fuse.ENOTSUP) r.RespondError(fuse.ENOTSUP) break } s := &fuse.ListxattrResponse{} err := n.Listxattr(r, s, intr) if err != nil { done(err) r.RespondError(err) break } if r.Size != 0 && uint64(len(s.Xattr)) > uint64(r.Size) { done(fuse.ERANGE) r.RespondError(fuse.ERANGE) break } done(s) r.Respond(s) case *fuse.SetxattrRequest: n, ok := node.(NodeSetxattrer) if !ok { done(fuse.ENOTSUP) r.RespondError(fuse.ENOTSUP) break } err := n.Setxattr(r, intr) if err != nil { done(err) r.RespondError(err) break } done(nil) r.Respond() case *fuse.RemovexattrRequest: n, ok := node.(NodeRemovexattrer) if !ok { done(fuse.ENOTSUP) r.RespondError(fuse.ENOTSUP) break } err := n.Removexattr(r, intr) if err != nil { done(err) r.RespondError(err) break } done(nil) r.Respond() case *fuse.ForgetRequest: forget := c.dropNode(hdr.Node, r.N) if forget { n, ok := node.(NodeForgetter) if ok { n.Forget() } } done(nil) r.Respond() // Handle operations. case *fuse.ReadRequest: shandle := c.getHandle(r.Handle) if shandle == nil { done(fuse.ESTALE) r.RespondError(fuse.ESTALE) return } handle := shandle.handle s := &fuse.ReadResponse{Data: make([]byte, 0, r.Size)} if r.Dir { if h, ok := handle.(HandleReadDirer); ok { if shandle.readData == nil { dirs, err := h.ReadDir(intr) if err != nil { done(err) r.RespondError(err) break } var data []byte for _, dir := range dirs { if dir.Inode == 0 { dir.Inode = c.dynamicInode(snode.inode, dir.Name) } data = fuse.AppendDirent(data, dir) } shandle.readData = data } fuseutil.HandleRead(r, s, shandle.readData) done(s) r.Respond(s) break } } else { if h, ok := handle.(HandleReadAller); 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 } fuseutil.HandleRead(r, s, shandle.readData) done(s) r.Respond(s) break } h, ok := handle.(HandleReader) if !ok { fmt.Printf("NO READ FOR %T\n", handle) done(fuse.EIO) r.RespondError(fuse.EIO) break } if err := h.Read(r, s, intr); err != nil { done(err) r.RespondError(err) break } } done(s) r.Respond(s) case *fuse.WriteRequest: shandle := c.getHandle(r.Handle) if shandle == nil { done(fuse.ESTALE) r.RespondError(fuse.ESTALE) return } s := &fuse.WriteResponse{} if h, ok := shandle.handle.(HandleWriter); ok { if err := h.Write(r, s, intr); err != nil { done(err) r.RespondError(err) break } done(s) r.Respond(s) break } done(fuse.EIO) r.RespondError(fuse.EIO) case *fuse.FlushRequest: shandle := c.getHandle(r.Handle) if shandle == nil { done(fuse.ESTALE) r.RespondError(fuse.ESTALE) return } handle := shandle.handle if h, ok := handle.(HandleFlusher); ok { if err := h.Flush(r, intr); err != nil { done(err) r.RespondError(err) break } } done(nil) r.Respond() case *fuse.ReleaseRequest: shandle := c.getHandle(r.Handle) if shandle == nil { done(fuse.ESTALE) r.RespondError(fuse.ESTALE) return } handle := shandle.handle // No matter what, release the handle. c.dropHandle(r.Handle) if h, ok := handle.(HandleReleaser); ok { if err := h.Release(r, intr); err != nil { done(err) r.RespondError(err) break } } done(nil) r.Respond() case *fuse.DestroyRequest: if fs, ok := c.fs.(FSDestroyer); ok { fs.Destroy() } done(nil) r.Respond() case *fuse.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 { c.debug(renameNewDirNodeNotFound{ Request: r.Hdr(), In: r, }) done(fuse.EIO) r.RespondError(fuse.EIO) break } n, ok := node.(NodeRenamer) if !ok { done(fuse.EIO) // XXX or EPERM like Mkdir? r.RespondError(fuse.EIO) break } err := n.Rename(r, newDirNode.node, intr) if err != nil { done(err) r.RespondError(err) break } done(nil) r.Respond() case *fuse.MknodRequest: n, ok := node.(NodeMknoder) if !ok { done(fuse.EIO) r.RespondError(fuse.EIO) break } n2, err := n.Mknod(r, intr) if err != nil { done(err) r.RespondError(err) break } s := &fuse.LookupResponse{} c.saveLookup(s, snode, r.Name, n2) done(s) r.Respond(s) case *fuse.FsyncRequest: n, ok := node.(NodeFsyncer) if !ok { done(fuse.EIO) r.RespondError(fuse.EIO) break } err := n.Fsync(r, intr) if err != nil { done(err) r.RespondError(err) break } done(nil) r.Respond() case *fuse.InterruptRequest: c.meta.Lock() ireq := c.req[r.IntrID] if ireq != nil && ireq.Intr != nil { close(ireq.Intr) ireq.Intr = nil } c.meta.Unlock() done(nil) r.Respond() /* case *FsyncdirRequest: done(ENOSYS) r.RespondError(ENOSYS) case *GetlkRequest, *SetlkRequest, *SetlkwRequest: done(ENOSYS) r.RespondError(ENOSYS) case *BmapRequest: done(ENOSYS) r.RespondError(ENOSYS) case *SetvolnameRequest, *GetxtimesRequest, *ExchangeRequest: done(ENOSYS) r.RespondError(ENOSYS) */ } }
// Record a fuse.Request, after zeroing header fields that are hard to // reproduce. // // Make sure to record a copy, not the original request. func (r *RequestRecorder) RecordRequest(req fuse.Request) { hdr := req.Hdr() *hdr = fuse.Header{} r.rec.Record(req) }