// Returns nil for invalid handles. func (c *serveConn) getHandle(id fuse.HandleID) (shandle *serveHandle) { c.meta.Lock() defer c.meta.Unlock() if id < fuse.HandleID(len(c.handle)) { shandle = c.handle[uint(id)] } if shandle == nil { c.debug(missingHandle{ Handle: id, MaxHandle: fuse.HandleID(len(c.handle)), }) } return }
// Allocate a file handle, held by the kernel until Release func (sc *serveConn) open(req *fuse.OpenRequest) { // This will be cheap, Lookup always preceeds Open, so the cache is warm f, err := sc.db.FileByInode(uint64(req.Header.Node)) if err != nil { req.RespondError(fuse.ENOENT) return } var hId uint64 if !req.Flags.IsReadOnly() { // write access requested if *readOnly { // TODO: if allow_other, require uid == invoking uid to allow writes req.RespondError(fuse.EPERM) return } r, w := io.Pipe() // plumbing between WriteRequest and Drive go sc.updateInDrive(f.File, r) hId = sc.allocHandle(req.Header.Node, w) } else { hId = sc.allocHandle(req.Header.Node, nil) } resp := fuse.OpenResponse{Handle: fuse.HandleID(hId)} fuse.Debug(fmt.Sprintf("Open Response: %+v", resp)) req.Respond(&resp) }
func handleIDGenerator() <-chan fuse.HandleID { outChan := make(chan fuse.HandleID) go func() { for nextId := fuse.HandleID(1); ; nextId++ { outChan <- nextId } }() return outChan }
// Returns nil for invalid handles. func (c *serveConn) getHandle(id fuse.HandleID) (shandle *serveHandle) { c.meta.Lock() defer c.meta.Unlock() if id < fuse.HandleID(len(c.handle)) { shandle = c.handle[uint(id)] } if shandle == nil { println("missing handle", id, len(c.handle), shandle) } return }
func (c *serveConn) saveHandle(handle Handle, nodeID fuse.NodeID) (id fuse.HandleID) { 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 = fuse.HandleID(len(c.handle)) c.handle = append(c.handle, shandle) } c.meta.Unlock() return }
func (c *serveConn) saveHandle(handle Handle, nodeID fuse.NodeID) (id fuse.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 = fuse.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[fuse.HandleID]bool) } c.nodeHandles[nodeID][id] = true c.meta.Unlock() return }
// Create file in drive, allocate kernel filehandle for writes func (sc *serveConn) create(req *fuse.CreateRequest) { if *readOnly && !req.Flags.IsReadOnly() { req.RespondError(fuse.EPERM) return } pInode := uint64(req.Header.Node) parent, err := sc.db.FileByInode(pInode) if err != nil { debug.Printf("failed to get parent file: %v", err) req.RespondError(fuse.EIO) return } p := &drive.ParentReference{Id: parent.Id} f := &drive.File{Title: req.Name} f.Parents = []*drive.ParentReference{p} f, err = sc.service.Files.Insert(f).Do() if err != nil { debug.Printf("Files.Insert(f).Do(): %v", err) req.RespondError(fuse.EIO) return } inode, err := sc.db.InodeForFileId(f.Id) if err != nil { debug.Printf("failed creating inode for %v: %v", req.Name, err) req.RespondError(fuse.EIO) return } r, w := io.Pipe() // plumbing between WriteRequest and Drive h := sc.allocHandle(fuse.NodeID(inode), w) go sc.updateInDrive(f, r) // Tell fuse and the OS about the file df, err := sc.db.UpdateFile(nil, f) if err != nil { debug.Printf("failed to update levelDB for %v: %v", f.Id, err) // The write has happened to drive, but we failed to update the kernel. // The Changes API will update Fuse, and when the kernel metadata for // the parent directory expires, the new file will become visible. req.RespondError(fuse.EIO) return } resp := fuse.CreateResponse{ // describes the opened handle OpenResponse: fuse.OpenResponse{ Handle: fuse.HandleID(h), Flags: fuse.OpenNonSeekable, }, // describes the created file LookupResponse: fuse.LookupResponse{ Node: fuse.NodeID(inode), EntryValid: *driveMetadataLatency, Attr: sc.attrFromFile(*df), }, } fuse.Debug(fmt.Sprintf("Create(%v in %v): %+v", req.Name, parent.Title, resp)) req.Respond(&resp) }