func (c *serveConn) saveNode(inode uint64, node Node) (id fuse.NodeID, gen uint64) { c.meta.Lock() defer c.meta.Unlock() var ref *NodeRef if nodeRef, ok := node.(nodeRef); ok { ref = nodeRef.nodeRef() if ref.id != 0 { // dropNode guarantees that NodeRef is zeroed at the same // time as the NodeID is removed from serveConn.node, as // guarded by c.meta; this means sn cannot be nil here sn := c.node[ref.id] sn.refs++ return ref.id, ref.generation } } sn := &serveNode{inode: inode, node: node, refs: 1} 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 = fuse.NodeID(len(c.node)) c.node = append(c.node, sn) } gen = c.nodeGen if ref != nil { ref.id = id ref.generation = gen } return }
func (c *serveConn) serve(r fuse.Request) { intr := make(Intr) req := &serveRequest{Request: r, Intr: intr} dprintf("%v\n", 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() dprintf("%v\n", 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 } dprintf("%v\n", msg) c.meta.Lock() delete(c.req, hdr.ID) c.meta.Unlock() } Req: 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: uid = r.Header.Uid gid = r.Header.Gid s := &fuse.InitResponse{ MaxWrite: 16*1024, } done(s) r.Respond(s) case *fuse.StatfsRequest: // fake it so the other end always thinks we have resources s := &fuse.StatfsResponse{} done(s) r.Respond(s) // Node operations. case *fuse.GetattrRequest: s := &fuse.GetattrResponse{} s.AttrValid = attrValidTime a, err := snode.attr() if err != nil { done(err) r.RespondError(err) break } s.Attr = a done(s) r.Respond(s) case *fuse.SetattrRequest: s := &fuse.SetattrResponse{} n, ok := node.(NodeSetAttrer) if !ok { done(fuse.EPERM) r.RespondError(fuse.EPERM) break } if err := n.SetAttr(r, intr); err != nil { done(err) r.RespondError(err) break } if s.AttrValid == 0 { s.AttrValid = attrValidTime } a, err := snode.attr() if err != nil { done(err) r.RespondError(err) break } s.Attr = a done(s) r.Respond(s) case *fuse.GetxattrRequest: s := &fuse.GetxattrResponse{} n, ok := node.(NodeXAttrer) if !ok || r.Position>0 { done(fuse.ENODATA) r.RespondError(fuse.ENODATA) break } v, err := n.Xattr(r.Name) if err != nil { done(fuse.ENODATA) r.RespondError(fuse.ENODATA) break } s.Xattr = v done(s) r.Respond(s) case *fuse.ListxattrRequest: s := &fuse.ListxattrResponse{} if n, ok := node.(NodeXAttrer); ok && r.Position==0 { v := n.Xattrs() if len(v) > 0 { s.Append(v...) } } done(s) r.Respond(s) case *fuse.SetxattrRequest: n, ok := node.(NodeXAttrer) if !ok || r.Position>0 { done(fuse.EPERM) r.RespondError(fuse.EPERM) break } err := n.Wxattr(r.Name, r.Xattr) if err != nil { done(fuse.EPERM) r.RespondError(fuse.EPERM) break } done(nil) r.Respond() case *fuse.RemovexattrRequest: n, ok := node.(NodeXAttrer) if !ok { done(fuse.ENOTSUP) r.RespondError(fuse.ENOTSUP) break } err := n.Wxattr(r.Name, nil) if err != nil { done(fuse.EPERM) r.RespondError(fuse.EPERM) break } done(nil) r.Respond() case *fuse.SymlinkRequest: done(fuse.EPERM) r.RespondError(fuse.EPERM) case *fuse.ReadlinkRequest: done(fuse.EPERM) r.RespondError(fuse.EPERM) case *fuse.LinkRequest: done(fuse.EPERM) r.RespondError(fuse.EPERM) case *fuse.RemoveRequest: n, ok := node.(NodeRemover) if !ok { done(fuse.EIO) /// XXX or EPERM? r.RespondError(fuse.EIO) break } err := n.Remove(r.Name, intr) if err != nil { done(err) r.RespondError(err) break } done(nil) r.Respond() case *fuse.AccessRequest: done(nil) r.Respond() case *fuse.LookupRequest: var n2 Node var err fuse.Error s := &fuse.LookupResponse{} if n, ok := node.(NodeLookuper); ok { n2, err = n.Lookup(r.Name, 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.Name, r.Mode, 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: var h2 Handle n := node hh, err := n.Open(r.Flags, intr) if err != nil { done(err) r.RespondError(err) break } h2 = hh // Using DirectIO requires buffers to be page aligned for exec // to work on served files. // But not using DirectIO means that UNIX stats the file and // then reads the data, which means we can't use it for Ctl files. flags := fuse.OpenPurgeAttr | fuse.OpenPurgeUBC if xh, ok := hh.(HandleIsCtler); ok && xh.IsCtl() { flags = fuse.OpenDirectIO } if r.Flags&3==fuse.OpenFlags(os.O_WRONLY) && runtime.GOOS=="darwin" { /* This is required for append on osx */ flags = fuse.OpenPurgeAttr | fuse.OpenPurgeUBC } s := &fuse.OpenResponse{Flags: flags} s.Handle = c.saveHandle(h2, hdr.Node) done(s) r.Respond(s) case *fuse.CreateRequest: n, ok := node.(NodeCreater) if !ok { done(fuse.EPERM) r.RespondError(fuse.EPERM) break } flags := fuse.OpenPurgeAttr | fuse.OpenPurgeUBC // flags := fuse.OpenDirectIO s := &fuse.CreateResponse{OpenResponse: fuse.OpenResponse{Flags: flags}} n2, h2, err := n.Create(r.Name, r.Flags, r.Mode, 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.ForgetRequest: forget := c.dropNode(hdr.Node, r.N) if forget { n, ok := node.(NodePuter) if ok { n.PutNode() } } 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 { h := handle 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 { err := errors.New("bad inode") done(err) r.RespondError(err) break Req } data = fuse.AppendDirent(data, dir) } shandle.readData = data } fuseutil.HandleRead(r, s, shandle.readData) done(s) r.Respond(s) break } h := handle rdata, err := h.Read(r.Offset, r.Size, intr) if err != nil { done(err) r.RespondError(err) break } s.Data = rdata 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 { n, err := h.Write(r.Data, r.Offset, intr) if err != nil { done(err) r.RespondError(err) break } s.Size = n done(s) r.Respond(s) break } done(fuse.EPERM) r.RespondError(fuse.EPERM) case *fuse.FlushRequest: shandle := c.getHandle(r.Handle) if shandle == nil { done(fuse.ESTALE) r.RespondError(fuse.ESTALE) return } handle := shandle.handle h := handle if err := h.Close(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.(HandlePuter); ok { h.PutHandle() } done(nil) r.Respond() case *fuse.DestroyRequest: 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 { dprintf("%v\n", renameNewDirNodeNotFound{ Request: r.Hdr(), In: r, }) done(fuse.EIO) r.RespondError(fuse.EIO) break } n, ok := node.(NodeRenamer) if !ok { done(fuse.EPERM) r.RespondError(fuse.EPERM) break } err := n.Rename(r.OldName, r.NewName, newDirNode.node, intr) if err != nil { done(err) r.RespondError(err) break } done(nil) r.Respond() case *fuse.MknodRequest: done(fuse.EIO) r.RespondError(fuse.EIO) case *fuse.FsyncRequest: n, ok := node.(NodeFsyncer) if !ok { // if there's no implementation, we pretend it's done. done(nil) r.Respond() break } err := n.Fsync(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) */ } }