2015-02-27 00:54:16 +03:00
|
|
|
// Copyright 2015 Google Inc. All Rights Reserved.
|
|
|
|
// Author: jacobsa@google.com (Aaron Jacobs)
|
|
|
|
|
2015-02-27 00:57:18 +03:00
|
|
|
package fuse
|
2015-02-27 00:54:16 +03:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
|
|
|
|
"golang.org/x/net/context"
|
|
|
|
|
2015-02-27 01:32:18 +03:00
|
|
|
bazilfuse "bazil.org/fuse"
|
2015-02-27 00:54:16 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// An object that terminates one end of the userspace <-> FUSE VFS connection.
|
|
|
|
type server struct {
|
|
|
|
logger *log.Logger
|
|
|
|
fs FileSystem
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a server that relays requests to the supplied file system.
|
|
|
|
func newServer(fs FileSystem) (s *server, err error) {
|
|
|
|
s = &server{
|
|
|
|
logger: getLogger(),
|
|
|
|
fs: fs,
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Serve the fuse connection by repeatedly reading requests from the supplied
|
|
|
|
// FUSE connection, responding as dictated by the file system. Return when the
|
|
|
|
// connection is closed or an unexpected error occurs.
|
2015-02-27 01:32:18 +03:00
|
|
|
func (s *server) Serve(c *bazilfuse.Conn) (err error) {
|
2015-02-27 00:54:16 +03:00
|
|
|
// Read a message at a time, dispatching to goroutines doing the actual
|
|
|
|
// processing.
|
|
|
|
for {
|
2015-02-27 01:32:18 +03:00
|
|
|
var fuseReq bazilfuse.Request
|
2015-02-27 00:54:16 +03:00
|
|
|
fuseReq, err = c.ReadRequest()
|
|
|
|
|
|
|
|
// ReadRequest returns EOF when the connection has been closed.
|
|
|
|
//
|
|
|
|
// TODO(jacobsa): Remove this and verify it's actually needed.
|
|
|
|
if err == io.EOF {
|
|
|
|
err = nil
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, forward on errors.
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("Conn.ReadRequest: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
go s.handleFuseRequest(fuseReq)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-27 01:32:18 +03:00
|
|
|
func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
|
2015-02-27 00:54:16 +03:00
|
|
|
// Log the request.
|
|
|
|
s.logger.Println("Received:", fuseReq)
|
|
|
|
|
|
|
|
// TODO(jacobsa): Support cancellation when interrupted, if we can coax the
|
|
|
|
// system into reproducing such requests.
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
// Attempt to handle it.
|
|
|
|
switch typed := fuseReq.(type) {
|
2015-02-27 01:32:18 +03:00
|
|
|
case *bazilfuse.InitRequest:
|
2015-02-27 00:54:16 +03:00
|
|
|
// Responding to this is required to make mounting work, at least on OS X.
|
|
|
|
// We don't currently expose the capability for the file system to
|
|
|
|
// intercept this.
|
2015-02-27 01:32:18 +03:00
|
|
|
fuseResp := &bazilfuse.InitResponse{}
|
2015-02-27 00:54:16 +03:00
|
|
|
s.logger.Println("Responding:", fuseResp)
|
|
|
|
typed.Respond(fuseResp)
|
|
|
|
|
2015-02-27 01:32:18 +03:00
|
|
|
case *bazilfuse.StatfsRequest:
|
2015-02-27 00:54:16 +03:00
|
|
|
// Responding to this is required to make mounting work, at least on OS X.
|
|
|
|
// We don't currently expose the capability for the file system to
|
|
|
|
// intercept this.
|
2015-02-27 01:32:18 +03:00
|
|
|
fuseResp := &bazilfuse.StatfsResponse{}
|
2015-02-27 00:54:16 +03:00
|
|
|
s.logger.Println("Responding:", fuseResp)
|
|
|
|
typed.Respond(fuseResp)
|
|
|
|
|
2015-02-27 01:32:18 +03:00
|
|
|
case *bazilfuse.OpenRequest:
|
2015-02-27 01:55:50 +03:00
|
|
|
// We support only directories at this point.
|
|
|
|
if !typed.Dir {
|
|
|
|
s.logger.Println("We don't yet support files. Returning ENOSYS.")
|
|
|
|
typed.RespondError(ENOSYS)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-02-27 00:54:16 +03:00
|
|
|
// Convert the request.
|
2015-02-27 01:55:50 +03:00
|
|
|
req := &OpenDirRequest{
|
2015-02-27 00:54:16 +03:00
|
|
|
Inode: InodeID(typed.Header.Node),
|
|
|
|
Flags: typed.Flags,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call the file system.
|
2015-02-27 01:55:50 +03:00
|
|
|
resp, err := s.fs.OpenDir(ctx, req)
|
|
|
|
if err != nil {
|
2015-02-27 00:54:16 +03:00
|
|
|
s.logger.Print("Responding:", err)
|
|
|
|
typed.RespondError(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-02-27 01:55:50 +03:00
|
|
|
// Convert the response.
|
|
|
|
fuseResp := &bazilfuse.OpenResponse{
|
|
|
|
Handle: bazilfuse.HandleID(resp.Handle),
|
|
|
|
}
|
|
|
|
|
2015-02-27 00:54:16 +03:00
|
|
|
s.logger.Print("Responding:", fuseResp)
|
|
|
|
typed.Respond(fuseResp)
|
|
|
|
|
2015-02-27 04:23:05 +03:00
|
|
|
case *bazilfuse.ReadRequest:
|
|
|
|
// We support only directories at this point.
|
|
|
|
if !typed.Dir {
|
|
|
|
s.logger.Println("We don't yet support files. Returning ENOSYS.")
|
|
|
|
typed.RespondError(ENOSYS)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert the request.
|
|
|
|
req := &ReadDirRequest{
|
|
|
|
Inode: InodeID(typed.Header.Node),
|
|
|
|
Handle: HandleID(typed.Handle),
|
|
|
|
Offset: DirOffset(typed.Offset),
|
|
|
|
Size: typed.Size,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call the file system.
|
|
|
|
resp, err := s.fs.ReadDir(ctx, req)
|
|
|
|
if err != nil {
|
|
|
|
s.logger.Print("Responding:", err)
|
|
|
|
typed.RespondError(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert the response.
|
|
|
|
fuseResp := &bazilfuse.ReadResponse{
|
|
|
|
Data: resp.Data,
|
|
|
|
}
|
|
|
|
|
|
|
|
s.logger.Print("Responding:", fuseResp)
|
|
|
|
typed.Respond(fuseResp)
|
|
|
|
|
2015-02-27 00:54:16 +03:00
|
|
|
default:
|
|
|
|
s.logger.Println("Unhandled type. Returning ENOSYS.")
|
|
|
|
typed.RespondError(ENOSYS)
|
|
|
|
}
|
|
|
|
}
|