// Pass sequential writes on to the correct handle for uploading func (sc *serveConn) write(req *fuse.WriteRequest) { if *readOnly { req.RespondError(fuse.EPERM) return } // TODO: if allow_other, require uid == invoking uid to allow writes h, err := sc.handleById(req.Handle) if err != nil { fuse.Debug(fmt.Sprintf("inodeByNodeID(%v): %v", req.Handle, err)) req.RespondError(fuse.ESTALE) return } if h.lastByte != req.Offset { fuse.Debug(fmt.Sprintf("non-sequential write: got %v, expected %v", req.Offset, h.lastByte)) req.RespondError(fuse.EIO) return } n, err := h.writer.Write(req.Data) if err != nil { req.RespondError(fuse.EIO) return } sc.Lock() h.lastByte += int64(n) sc.handles[req.Handle] = h sc.Unlock() req.Respond(&fuse.WriteResponse{n}) }
func (sc *serveConn) readDir(req *fuse.ReadRequest) { inode := uint64(req.Header.Node) resp := &fuse.ReadResponse{make([]byte, 0, req.Size)} var dirs []fuse.Dirent file, err := sc.db.FileByInode(inode) if err != nil { fuse.Debug(fmt.Sprintf("FileByInode(%d): %v", inode, err)) req.RespondError(fuse.EIO) return } for _, inode := range file.Children { f, err := sc.db.FileByInode(inode) if err != nil { fuse.Debug(fmt.Sprintf("child: FileByInode(%d): %v", inode, err)) req.RespondError(fuse.EIO) return } childType := fuse.DT_File if f.MimeType == driveFolderMimeType { childType = fuse.DT_Dir } dirs = append(dirs, fuse.Dirent{Inode: f.Inode, Name: f.Title, Type: childType}) } fuse.Debug(fmt.Sprintf("%+v", dirs)) var data []byte for _, dir := range dirs { data = fuse.AppendDirent(data, dir) } fuseutil.HandleRead(req, resp, data) req.Respond(resp) }
// Return a Dirent for all children of an inode, or ENOENT func (sc *serveConn) lookup(req *fuse.LookupRequest) { inode := uint64(req.Header.Node) resp := &fuse.LookupResponse{} var err error file, err := sc.db.FileByInode(inode) if err != nil { fuse.Debug(fmt.Sprintf("FileByInode lookup failure for %d: %v", inode, err)) req.RespondError(fuse.ENOENT) return } for _, cInode := range file.Children { cf, err := sc.db.FileByInode(cInode) if err != nil { fuse.Debug(fmt.Sprintf("FileByInode(%v): %v", cInode, err)) req.RespondError(fuse.EIO) return } if cf.Title == req.Name { resp.Node = fuse.NodeID(cInode) resp.EntryValid = *driveMetadataLatency resp.Attr = sc.attrFromFile(*cf) fuse.Debug(fmt.Sprintf("Lookup(%v in %v): %v", req.Name, inode, cInode)) req.Respond(resp) return } } fuse.Debug(fmt.Sprintf("Lookup(%v in %v): ENOENT", req.Name, inode)) req.RespondError(fuse.ENOENT) }
// 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) }
// gettattr returns fuse.Attr for the inode described by req.Header.Node func (sc *serveConn) getattr(req *fuse.GetattrRequest) { inode := uint64(req.Header.Node) f, err := sc.db.FileByInode(inode) if err != nil { fuse.Debug(fmt.Sprintf("FileByInode(%v): %v", inode, err)) req.RespondError(fuse.EIO) return } /* TODO: getattr during upload must return current file size sc.Lock() sc.Unlock() */ resp := &fuse.GetattrResponse{} resp.Attr = sc.attrFromFile(*f) fuse.Debug(resp) req.Respond(resp) }
// FuseServe receives and dispatches Requests from the kernel func (sc *serveConn) Serve() error { for { req, err := sc.conn.ReadRequest() if err != nil { if err == io.EOF { break } return err } fuse.Debug(fmt.Sprintf("%+v", req)) go sc.serve(req) } return nil }
func (sc *serveConn) mkdir(req *fuse.MkdirRequest) { if *readOnly { req.RespondError(fuse.EPERM) return } // TODO: if allow_other, require uid == invoking uid to allow writes pInode := uint64(req.Header.Node) pId, err := sc.db.FileIdForInode(pInode) if err != nil { debug.Printf("failed to get parent fileid: %v", err) req.RespondError(fuse.EIO) return } p := []*drive.ParentReference{&drive.ParentReference{Id: pId}} file := &drive.File{Title: req.Name, MimeType: driveFolderMimeType, Parents: p} file, err = sc.service.Files.Insert(file).Do() if err != nil { debug.Printf("Insert failed: %v", err) req.RespondError(fuse.EIO) return } debug.Printf("Child of %v created in drive: %+v", file.Parents[0].Id, file) f, err := sc.db.UpdateFile(nil, file) 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 dir will become visible. req.RespondError(fuse.EIO) return } sc.db.FlushCachedInode(pInode) resp := &fuse.MkdirResponse{} resp.Node = fuse.NodeID(f.Inode) resp.EntryValid = *driveMetadataLatency resp.Attr.Valid = *driveMetadataLatency resp.Attr = sc.attrFromFile(*f) fuse.Debug(fmt.Sprintf("Mkdir(%v): %+v", req.Name, f)) req.Respond(resp) }
func (sc *serveConn) serve(req fuse.Request) { switch req := req.(type) { default: // ENOSYS means "this server never implements this request." //done(fuse.ENOSYS) fuse.Debug(fmt.Sprintf("ENOSYS: %+v", req)) req.RespondError(fuse.ENOSYS) case *fuse.InitRequest: resp := fuse.InitResponse{MaxWrite: 128 * 1024, Flags: fuse.InitBigWrites & fuse.InitAsyncRead, } req.Respond(&resp) case *fuse.StatfsRequest: var numfiles uint64 if f, err := sc.db.AllFileIds(); err != nil { numfiles = uint64(len(f)) } req.Respond( &fuse.StatfsResponse{ Files: numfiles, Bsize: blockSize, }, ) case *fuse.GetattrRequest: sc.getattr(req) case *fuse.LookupRequest: sc.lookup(req) // Ack that the kernel has forgotten the metadata about an inode case *fuse.ForgetRequest: req.Respond() // Hand back the inode as the HandleID case *fuse.OpenRequest: sc.open(req) // Silently ignore attempts to change permissions case *fuse.SetattrRequest: inode := uint64(req.Header.Node) f, err := sc.db.FileByInode(inode) if err != nil { fuse.Debug(fmt.Sprintf("FileByInode(%v): %v", inode, err)) req.RespondError(fuse.EIO) return } req.Respond(&fuse.SetattrResponse{Attr: sc.attrFromFile(*f)}) case *fuse.CreateRequest: // TODO: if allow_other, require uid == invoking uid to allow writes sc.create(req) // Return Dirents for directories, or requested portion of file case *fuse.ReadRequest: if req.Dir { sc.readDir(req) } else { sc.read(req) } // Return MkdirResponse (it's LookupResponse, essentially) of new dir case *fuse.MkdirRequest: sc.mkdir(req) // Removes the inode described by req.Header.Node // Respond() for success, RespondError otherwise case *fuse.RemoveRequest: sc.remove(req) // req.Header.Node describes the current parent directory // req.NewDir describes the target directory (may be the same) // req.OldName and req.NewName describe any (or no) change in name case *fuse.RenameRequest: sc.rename(req) // Responds with the number of bytes written on success, RespondError otherwise case *fuse.WriteRequest: sc.write(req) // Ack that the kernel has forgotten the metadata about an inode case *fuse.FlushRequest: req.Respond() // Ack release of the kernel's mapping an inode->fileId case *fuse.ReleaseRequest: sc.release(req) case *fuse.DestroyRequest: req.Respond() } }
// 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) }