Esempio n. 1
0
// 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})
}
Esempio n. 2
0
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)
}
Esempio n. 3
0
// 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)
}
Esempio n. 4
0
// 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)
}
Esempio n. 5
0
// 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)
}
Esempio n. 6
0
// 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
}
Esempio n. 7
0
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)
}
Esempio n. 8
0
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()
	}
}
Esempio n. 9
0
// 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)
}