Commit f4231ede authored by Aaron Jacobs's avatar Aaron Jacobs

Copied over the juicy bits of bazilfuse, and made them work.

parents df9f2b75 aa32dd92
......@@ -23,15 +23,15 @@ import (
"golang.org/x/net/context"
"github.com/jacobsa/bazilfuse"
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/internal/fuseshim"
)
// A connection to the fuse kernel process.
type Connection struct {
debugLogger *log.Logger
errorLogger *log.Logger
wrapped *bazilfuse.Conn
wrapped *fuseshim.Conn
// The context from which all op contexts inherit.
parentCtx context.Context
......@@ -41,11 +41,11 @@ type Connection struct {
mu sync.Mutex
// A map from bazilfuse request ID (*not* the op ID for logging used above)
// to a function that cancel's its associated context.
// A map from fuseshim request ID (*not* the op ID for logging used above) to
// a function that cancel's its associated context.
//
// GUARDED_BY(mu)
cancelFuncs map[bazilfuse.RequestID]func()
cancelFuncs map[fuseshim.RequestID]func()
}
// Responsibility for closing the wrapped connection is transferred to the
......@@ -56,13 +56,13 @@ func newConnection(
parentCtx context.Context,
debugLogger *log.Logger,
errorLogger *log.Logger,
wrapped *bazilfuse.Conn) (c *Connection, err error) {
wrapped *fuseshim.Conn) (c *Connection, err error) {
c = &Connection{
debugLogger: debugLogger,
errorLogger: errorLogger,
wrapped: wrapped,
parentCtx: parentCtx,
cancelFuncs: make(map[bazilfuse.RequestID]func()),
cancelFuncs: make(map[fuseshim.RequestID]func()),
}
return
......@@ -104,7 +104,7 @@ func (c *Connection) debugLog(
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) recordCancelFunc(
reqID bazilfuse.RequestID,
reqID fuseshim.RequestID,
f func()) {
c.mu.Lock()
defer c.mu.Unlock()
......@@ -117,13 +117,13 @@ func (c *Connection) recordCancelFunc(
}
// Set up state for an op that is about to be returned to the user, given its
// underlying bazilfuse request.
// underlying fuseshim request.
//
// Return a context that should be used for the op.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) beginOp(
bfReq bazilfuse.Request) (ctx context.Context) {
bfReq fuseshim.Request) (ctx context.Context) {
reqID := bfReq.Hdr().ID
// Start with the parent context.
......@@ -137,7 +137,7 @@ func (c *Connection) beginOp(
// should not record any state keyed on their ID.
//
// Cf. https://github.com/osxfuse/osxfuse/issues/208
if _, ok := bfReq.(*bazilfuse.ForgetRequest); !ok {
if _, ok := bfReq.(*fuseshim.ForgetRequest); !ok {
var cancel func()
ctx, cancel = context.WithCancel(ctx)
c.recordCancelFunc(reqID, cancel)
......@@ -147,12 +147,12 @@ func (c *Connection) beginOp(
}
// Clean up all state associated with an op to which the user has responded,
// given its underlying bazilfuse request. This must be called before a
// response is sent to the kernel, to avoid a race where the request's ID might
// be reused by osxfuse.
// given its underlying fuseshim request. This must be called before a response
// is sent to the kernel, to avoid a race where the request's ID might be
// reused by osxfuse.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) finishOp(bfReq bazilfuse.Request) {
func (c *Connection) finishOp(bfReq fuseshim.Request) {
c.mu.Lock()
defer c.mu.Unlock()
......@@ -164,7 +164,7 @@ func (c *Connection) finishOp(bfReq bazilfuse.Request) {
//
// Special case: we don't do this for Forget requests. See the note in
// beginOp above.
if _, ok := bfReq.(*bazilfuse.ForgetRequest); !ok {
if _, ok := bfReq.(*fuseshim.ForgetRequest); !ok {
cancel, ok := c.cancelFuncs[reqID]
if !ok {
panic(fmt.Sprintf("Unknown request ID in finishOp: %v", reqID))
......@@ -176,7 +176,7 @@ func (c *Connection) finishOp(bfReq bazilfuse.Request) {
}
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) handleInterrupt(req *bazilfuse.InterruptRequest) {
func (c *Connection) handleInterrupt(req *fuseshim.InterruptRequest) {
c.mu.Lock()
defer c.mu.Unlock()
......@@ -212,8 +212,8 @@ func (c *Connection) handleInterrupt(req *bazilfuse.InterruptRequest) {
func (c *Connection) ReadOp() (op fuseops.Op, err error) {
// Keep going until we find a request we know how to convert.
for {
// Read a bazilfuse request.
var bfReq bazilfuse.Request
// Read a fuseshim request.
var bfReq fuseshim.Request
bfReq, err = c.wrapped.ReadRequest()
if err != nil {
......@@ -230,14 +230,14 @@ func (c *Connection) ReadOp() (op fuseops.Op, err error) {
// Special case: responding to statfs is required to make mounting work on
// OS X. We don't currently expose the capability for the file system to
// intercept this.
if statfsReq, ok := bfReq.(*bazilfuse.StatfsRequest); ok {
if statfsReq, ok := bfReq.(*fuseshim.StatfsRequest); ok {
c.debugLog(opID, 1, "-> (Statfs) OK")
statfsReq.Respond(&bazilfuse.StatfsResponse{})
statfsReq.Respond(&fuseshim.StatfsResponse{})
continue
}
// Special case: handle interrupt requests.
if interruptReq, ok := bfReq.(*bazilfuse.InterruptRequest); ok {
if interruptReq, ok := bfReq.(*fuseshim.InterruptRequest); ok {
c.handleInterrupt(interruptReq)
continue
}
......
......@@ -17,17 +17,17 @@ package fuse
import (
"syscall"
"github.com/jacobsa/bazilfuse"
"github.com/jacobsa/fuse/internal/fuseshim"
)
const (
// Errors corresponding to kernel error numbers. These may be treated
// specially by fuseops.Op.Respond methods.
EEXIST = bazilfuse.EEXIST
EINVAL = bazilfuse.Errno(syscall.EINVAL)
EIO = bazilfuse.EIO
ENOENT = bazilfuse.ENOENT
ENOSYS = bazilfuse.ENOSYS
ENOTDIR = bazilfuse.Errno(syscall.ENOTDIR)
ENOTEMPTY = bazilfuse.Errno(syscall.ENOTEMPTY)
EEXIST = fuseshim.EEXIST
EINVAL = fuseshim.Errno(syscall.EINVAL)
EIO = fuseshim.EIO
ENOENT = fuseshim.ENOENT
ENOSYS = fuseshim.ENOSYS
ENOTDIR = fuseshim.Errno(syscall.ENOTDIR)
ENOTEMPTY = fuseshim.Errno(syscall.ENOTEMPTY)
)
......@@ -20,7 +20,7 @@ import (
"reflect"
"strings"
"github.com/jacobsa/bazilfuse"
"github.com/jacobsa/fuse/internal/fuseshim"
"github.com/jacobsa/reqtrace"
"golang.org/x/net/context"
)
......@@ -30,7 +30,7 @@ import (
type internalOp interface {
Op
// Respond to the underlying bazilfuse request, successfully.
// Respond to the underlying fuseshim request, successfully.
respond()
}
......@@ -42,8 +42,8 @@ type commonOp struct {
// The op in which this struct is embedded.
op internalOp
// The underlying bazilfuse request for this op.
bazilReq bazilfuse.Request
// The underlying fuseshim request for this op.
bazilReq fuseshim.Request
// A function that can be used to log debug information about the op. The
// first argument is a call depth.
......@@ -81,7 +81,7 @@ func (o *commonOp) ShortDesc() (desc string) {
func (o *commonOp) init(
ctx context.Context,
op internalOp,
bazilReq bazilfuse.Request,
bazilReq fuseshim.Request,
debugLog func(int, string, ...interface{}),
errorLogger *log.Logger,
finished func(error)) {
......@@ -122,7 +122,7 @@ func (o *commonOp) Respond(err error) {
// Report that the user is responding.
o.finished(err)
// If successful, we should respond to bazilfuse with the appropriate struct.
// If successful, we should respond to fuseshim with the appropriate struct.
if err == nil {
o.op.respond()
return
......
......@@ -18,15 +18,15 @@ import (
"log"
"time"
"github.com/jacobsa/fuse/internal/fusekernel"
"github.com/jacobsa/fuse/internal/fuseshim"
"golang.org/x/net/context"
"github.com/jacobsa/bazilfuse"
)
// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
//
// Convert the supplied bazilfuse request struct to an Op. finished will be
// Convert the supplied fuseshim request struct to an Op. finished will be
// called with the error supplied to o.Respond when the user invokes that
// method, before a response is sent to the kernel.
//
......@@ -36,7 +36,7 @@ import (
// The debug logging function and error logger may be nil.
func Convert(
opCtx context.Context,
r bazilfuse.Request,
r fuseshim.Request,
debugLogForOp func(int, string, ...interface{}),
errorLogger *log.Logger,
finished func(error)) (o Op) {
......@@ -44,7 +44,7 @@ func Convert(
var io internalOp
switch typed := r.(type) {
case *bazilfuse.LookupRequest:
case *fuseshim.LookupRequest:
to := &LookUpInodeOp{
bfReq: typed,
Parent: InodeID(typed.Header.Node),
......@@ -53,7 +53,7 @@ func Convert(
io = to
co = &to.commonOp
case *bazilfuse.GetattrRequest:
case *fuseshim.GetattrRequest:
to := &GetInodeAttributesOp{
bfReq: typed,
Inode: InodeID(typed.Header.Node),
......@@ -61,32 +61,32 @@ func Convert(
io = to
co = &to.commonOp
case *bazilfuse.SetattrRequest:
case *fuseshim.SetattrRequest:
to := &SetInodeAttributesOp{
bfReq: typed,
Inode: InodeID(typed.Header.Node),
}
if typed.Valid&bazilfuse.SetattrSize != 0 {
if typed.Valid&fusekernel.SetattrSize != 0 {
to.Size = &typed.Size
}
if typed.Valid&bazilfuse.SetattrMode != 0 {
if typed.Valid&fusekernel.SetattrMode != 0 {
to.Mode = &typed.Mode
}
if typed.Valid&bazilfuse.SetattrAtime != 0 {
if typed.Valid&fusekernel.SetattrAtime != 0 {
to.Atime = &typed.Atime
}
if typed.Valid&bazilfuse.SetattrMtime != 0 {
if typed.Valid&fusekernel.SetattrMtime != 0 {
to.Mtime = &typed.Mtime
}
io = to
co = &to.commonOp
case *bazilfuse.ForgetRequest:
case *fuseshim.ForgetRequest:
to := &ForgetInodeOp{
bfReq: typed,
Inode: InodeID(typed.Header.Node),
......@@ -95,7 +95,7 @@ func Convert(
io = to
co = &to.commonOp
case *bazilfuse.MkdirRequest:
case *fuseshim.MkdirRequest:
to := &MkDirOp{
bfReq: typed,
Parent: InodeID(typed.Header.Node),
......@@ -105,18 +105,17 @@ func Convert(
io = to
co = &to.commonOp
case *bazilfuse.CreateRequest:
case *fuseshim.CreateRequest:
to := &CreateFileOp{
bfReq: typed,
Parent: InodeID(typed.Header.Node),
Name: typed.Name,
Mode: typed.Mode,
Flags: typed.Flags,
}
io = to
co = &to.commonOp
case *bazilfuse.SymlinkRequest:
case *fuseshim.SymlinkRequest:
to := &CreateSymlinkOp{
bfReq: typed,
Parent: InodeID(typed.Header.Node),
......@@ -126,7 +125,7 @@ func Convert(
io = to
co = &to.commonOp
case *bazilfuse.RenameRequest:
case *fuseshim.RenameRequest:
to := &RenameOp{
bfReq: typed,
OldParent: InodeID(typed.Header.Node),
......@@ -137,7 +136,7 @@ func Convert(
io = to
co = &to.commonOp
case *bazilfuse.RemoveRequest:
case *fuseshim.RemoveRequest:
if typed.Dir {
to := &RmDirOp{
bfReq: typed,
......@@ -156,12 +155,11 @@ func Convert(
co = &to.commonOp
}
case *bazilfuse.OpenRequest:
case *fuseshim.OpenRequest:
if typed.Dir {
to := &OpenDirOp{
bfReq: typed,
Inode: InodeID(typed.Header.Node),
Flags: typed.Flags,
}
io = to
co = &to.commonOp
......@@ -169,13 +167,12 @@ func Convert(
to := &OpenFileOp{
bfReq: typed,
Inode: InodeID(typed.Header.Node),
Flags: typed.Flags,
}
io = to
co = &to.commonOp
}
case *bazilfuse.ReadRequest:
case *fuseshim.ReadRequest:
if typed.Dir {
to := &ReadDirOp{
bfReq: typed,
......@@ -198,7 +195,7 @@ func Convert(
co = &to.commonOp
}
case *bazilfuse.ReleaseRequest:
case *fuseshim.ReleaseRequest:
if typed.Dir {
to := &ReleaseDirHandleOp{
bfReq: typed,
......@@ -215,7 +212,7 @@ func Convert(
co = &to.commonOp
}
case *bazilfuse.WriteRequest:
case *fuseshim.WriteRequest:
to := &WriteFileOp{
bfReq: typed,
Inode: InodeID(typed.Header.Node),
......@@ -226,7 +223,7 @@ func Convert(
io = to
co = &to.commonOp
case *bazilfuse.FsyncRequest:
case *fuseshim.FsyncRequest:
// We don't currently support this for directories.
if typed.Dir {
to := &unknownOp{}
......@@ -242,7 +239,7 @@ func Convert(
co = &to.commonOp
}
case *bazilfuse.FlushRequest:
case *fuseshim.FlushRequest:
to := &FlushFileOp{
bfReq: typed,
Inode: InodeID(typed.Header.Node),
......@@ -251,7 +248,7 @@ func Convert(
io = to
co = &to.commonOp
case *bazilfuse.ReadlinkRequest:
case *fuseshim.ReadlinkRequest:
to := &ReadSymlinkOp{
bfReq: typed,
Inode: InodeID(typed.Header.Node),
......@@ -280,8 +277,8 @@ func Convert(
func convertAttributes(
inode InodeID,
attr InodeAttributes,
expiration time.Time) bazilfuse.Attr {
return bazilfuse.Attr{
expiration time.Time) fuseshim.Attr {
return fuseshim.Attr{
Inode: uint64(inode),
Size: attr.Size,
Mode: attr.Mode,
......@@ -317,8 +314,8 @@ func convertExpirationTime(t time.Time) (d time.Duration) {
func convertChildInodeEntry(
in *ChildInodeEntry,
out *bazilfuse.LookupResponse) {
out.Node = bazilfuse.NodeID(in.Child)
out *fuseshim.LookupResponse) {
out.Node = fuseshim.NodeID(in.Child)
out.Generation = uint64(in.Generation)
out.Attr = convertAttributes(in.Child, in.Attributes, in.AttributesExpiration)
out.EntryValid = convertExpirationTime(in.EntryExpiration)
......
......@@ -19,7 +19,7 @@ import (
"os"
"time"
"github.com/jacobsa/bazilfuse"
"github.com/jacobsa/fuse/internal/fuseshim"
"golang.org/x/net/context"
)
......@@ -55,7 +55,7 @@ type Op interface {
// when resolving user paths to dentry structs, which are then cached.
type LookUpInodeOp struct {
commonOp
bfReq *bazilfuse.LookupRequest
bfReq *fuseshim.LookupRequest
// The ID of the directory inode to which the child belongs.
Parent InodeID
......@@ -84,7 +84,7 @@ func (o *LookUpInodeOp) ShortDesc() (desc string) {
}
func (o *LookUpInodeOp) respond() {
resp := bazilfuse.LookupResponse{}
resp := fuseshim.LookupResponse{}
convertChildInodeEntry(&o.Entry, &resp)
o.bfReq.Respond(&resp)
......@@ -97,7 +97,7 @@ func (o *LookUpInodeOp) respond() {
// field of ChildInodeEntry, etc.
type GetInodeAttributesOp struct {
commonOp
bfReq *bazilfuse.GetattrRequest
bfReq *fuseshim.GetattrRequest
// The inode of interest.
Inode InodeID
......@@ -110,7 +110,7 @@ type GetInodeAttributesOp struct {
}
func (o *GetInodeAttributesOp) respond() {
resp := bazilfuse.GetattrResponse{
resp := fuseshim.GetattrResponse{
Attr: convertAttributes(o.Inode, o.Attributes, o.AttributesExpiration),
}
......@@ -124,7 +124,7 @@ func (o *GetInodeAttributesOp) respond() {
// cases like ftrunctate(2).
type SetInodeAttributesOp struct {
commonOp
bfReq *bazilfuse.SetattrRequest
bfReq *fuseshim.SetattrRequest
// The inode of interest.
Inode InodeID
......@@ -143,7 +143,7 @@ type SetInodeAttributesOp struct {
}
func (o *SetInodeAttributesOp) respond() {
resp := bazilfuse.SetattrResponse{
resp := fuseshim.SetattrResponse{
Attr: convertAttributes(o.Inode, o.Attributes, o.AttributesExpiration),
}
......@@ -192,7 +192,7 @@ func (o *SetInodeAttributesOp) respond() {
// implicitly decrementing all lookup counts to zero.
type ForgetInodeOp struct {
commonOp
bfReq *bazilfuse.ForgetRequest
bfReq *fuseshim.ForgetRequest
// The inode whose reference count should be decremented.
Inode InodeID
......@@ -223,7 +223,7 @@ func (o *ForgetInodeOp) respond() {
// Therefore the file system should return EEXIST if the name already exists.
type MkDirOp struct {
commonOp
bfReq *bazilfuse.MkdirRequest
bfReq *fuseshim.MkdirRequest
// The ID of parent directory inode within which to create the child.
Parent InodeID
......@@ -245,7 +245,7 @@ func (o *MkDirOp) ShortDesc() (desc string) {
}
func (o *MkDirOp) respond() {
resp := bazilfuse.MkdirResponse{}
resp := fuseshim.MkdirResponse{}
convertChildInodeEntry(&o.Entry, &resp.LookupResponse)
o.bfReq.Respond(&resp)
......@@ -264,7 +264,7 @@ func (o *MkDirOp) respond() {
// Therefore the file system should return EEXIST if the name already exists.
type CreateFileOp struct {
commonOp
bfReq *bazilfuse.CreateRequest
bfReq *fuseshim.CreateRequest
// The ID of parent directory inode within which to create the child file.
Parent InodeID
......@@ -273,9 +273,6 @@ type CreateFileOp struct {
Name string
Mode os.FileMode
// Flags for the open operation.
Flags bazilfuse.OpenFlags
// Set by the file system: information about the inode that was created.
//
// The lookup count for the inode is implicitly incremented. See notes on
......@@ -299,9 +296,9 @@ func (o *CreateFileOp) ShortDesc() (desc string) {
}
func (o *CreateFileOp) respond() {
resp := bazilfuse.CreateResponse{
OpenResponse: bazilfuse.OpenResponse{
Handle: bazilfuse.HandleID(o.Handle),
resp := fuseshim.CreateResponse{
OpenResponse: fuseshim.OpenResponse{
Handle: fuseshim.HandleID(o.Handle),
},
}
......@@ -315,7 +312,7 @@ func (o *CreateFileOp) respond() {
// return EEXIST (cf. the notes on CreateFileOp and MkDirOp).
type CreateSymlinkOp struct {
commonOp
bfReq *bazilfuse.SymlinkRequest
bfReq *fuseshim.SymlinkRequest
// The ID of parent directory inode within which to create the child symlink.
Parent InodeID
......@@ -345,7 +342,7 @@ func (o *CreateSymlinkOp) ShortDesc() (desc string) {
}
func (o *CreateSymlinkOp) respond() {
resp := bazilfuse.SymlinkResponse{}
resp := fuseshim.SymlinkResponse{}
convertChildInodeEntry(&o.Entry, &resp.LookupResponse)
o.bfReq.Respond(&resp)
......@@ -392,7 +389,7 @@ func (o *CreateSymlinkOp) respond() {
//
type RenameOp struct {
commonOp
bfReq *bazilfuse.RenameRequest
bfReq *fuseshim.RenameRequest
// The old parent directory, and the name of the entry within it to be
// relocated.
......@@ -419,7 +416,7 @@ func (o *RenameOp) respond() {
// Sample implementation in ext2: ext2_rmdir (http://goo.gl/B9QmFf)
type RmDirOp struct {
commonOp
bfReq *bazilfuse.RemoveRequest
bfReq *fuseshim.RemoveRequest
// The ID of parent directory inode, and the name of the directory being
// removed within it.
......@@ -440,7 +437,7 @@ func (o *RmDirOp) respond() {
// Sample implementation in ext2: ext2_unlink (http://goo.gl/hY6r6C)
type UnlinkOp struct {
commonOp
bfReq *bazilfuse.RemoveRequest
bfReq *fuseshim.RemoveRequest
// The ID of parent directory inode, and the name of the entry being removed
// within it.
......@@ -465,14 +462,11 @@ func (o *UnlinkOp) respond() {
// https://github.com/osxfuse/osxfuse/issues/199).
type OpenDirOp struct {
commonOp
bfReq *bazilfuse.OpenRequest
bfReq *fuseshim.OpenRequest
// The ID of the inode to be opened.
Inode InodeID
// Mode and options flags.
Flags bazilfuse.OpenFlags
// Set by the file system: an opaque ID that will be echoed in follow-up
// calls for this directory using the same struct file in the kernel. In
// practice this usually means follow-up calls using the file descriptor
......@@ -485,8 +479,8 @@ type OpenDirOp struct {
}
func (o *OpenDirOp) respond() {
resp := bazilfuse.OpenResponse{
Handle: bazilfuse.HandleID(o.Handle),
resp := fuseshim.OpenResponse{
Handle: fuseshim.HandleID(o.Handle),
}
o.bfReq.Respond(&resp)
......@@ -496,7 +490,7 @@ func (o *OpenDirOp) respond() {
// Read entries from a directory previously opened with OpenDir.
type ReadDirOp struct {
commonOp
bfReq *bazilfuse.ReadRequest
bfReq *fuseshim.ReadRequest
// The directory inode that we are reading, and the handle previously
// returned by OpenDir when opening that inode.
......@@ -585,7 +579,7 @@ type ReadDirOp struct {
}
func (o *ReadDirOp) respond() {
resp := bazilfuse.ReadResponse{
resp := fuseshim.ReadResponse{
Data: o.Data,
}
......@@ -603,7 +597,7 @@ func (o *ReadDirOp) respond() {
// Errors from this op are ignored by the kernel (cf. http://goo.gl/RL38Do).
type ReleaseDirHandleOp struct {
commonOp
bfReq *bazilfuse.ReleaseRequest
bfReq *fuseshim.ReleaseRequest
// The handle ID to be released. The kernel guarantees that this ID will not
// be used in further calls to the file system (unless it is reissued by the
......@@ -628,14 +622,11 @@ func (o *ReleaseDirHandleOp) respond() {
// (cf.https://github.com/osxfuse/osxfuse/issues/199).
type OpenFileOp struct {
commonOp
bfReq *bazilfuse.OpenRequest
bfReq *fuseshim.OpenRequest
// The ID of the inode to be opened.
Inode InodeID
// Mode and options flags.
Flags bazilfuse.OpenFlags
// An opaque ID that will be echoed in follow-up calls for this file using
// the same struct file in the kernel. In practice this usually means
// follow-up calls using the file descriptor returned by open(2).
......@@ -647,8 +638,8 @@ type OpenFileOp struct {
}
func (o *OpenFileOp) respond() {
resp := bazilfuse.OpenResponse{
Handle: bazilfuse.HandleID(o.Handle),
resp := fuseshim.OpenResponse{
Handle: fuseshim.HandleID(o.Handle),
}
o.bfReq.Respond(&resp)
......@@ -662,7 +653,7 @@ func (o *OpenFileOp) respond() {
// more.
type ReadFileOp struct {
commonOp
bfReq *bazilfuse.ReadRequest
bfReq *fuseshim.ReadRequest
// The file inode that we are reading, and the handle previously returned by
// CreateFile or OpenFile when opening that inode.
......@@ -686,7 +677,7 @@ type ReadFileOp struct {
}
func (o *ReadFileOp) respond() {
resp := bazilfuse.ReadResponse{
resp := fuseshim.ReadResponse{
Data: o.Data,
}
......@@ -727,7 +718,7 @@ func (o *ReadFileOp) respond() {
// concurrent requests".)
type WriteFileOp struct {
commonOp
bfReq *bazilfuse.WriteRequest
bfReq *fuseshim.WriteRequest
// The file inode that we are modifying, and the handle previously returned
// by CreateFile or OpenFile when opening that inode.
......@@ -766,7 +757,7 @@ type WriteFileOp struct {
}
func (o *WriteFileOp) respond() {
resp := bazilfuse.WriteResponse{
resp := fuseshim.WriteResponse{
Size: len(o.Data),
}
......@@ -792,7 +783,7 @@ func (o *WriteFileOp) respond() {
// file (but which is not used in "real" file systems).
type SyncFileOp struct {
commonOp
bfReq *bazilfuse.FsyncRequest
bfReq *fuseshim.FsyncRequest
// The file and handle being sync'd.
Inode InodeID
......@@ -853,7 +844,7 @@ func (o *SyncFileOp) respond() {
// return any errors that occur.
type FlushFileOp struct {
commonOp
bfReq *bazilfuse.FlushRequest
bfReq *fuseshim.FlushRequest
// The file and handle being flushed.
Inode InodeID
......@@ -875,7 +866,7 @@ func (o *FlushFileOp) respond() {
// Errors from this op are ignored by the kernel (cf. http://goo.gl/RL38Do).
type ReleaseFileHandleOp struct {
commonOp
bfReq *bazilfuse.ReleaseRequest
bfReq *fuseshim.ReleaseRequest
// The handle ID to be released. The kernel guarantees that this ID will not
// be used in further calls to the file system (unless it is reissued by the
......@@ -910,7 +901,7 @@ func (o *unknownOp) respond() {
// Read the target of a symlink inode.
type ReadSymlinkOp struct {
commonOp
bfReq *bazilfuse.ReadlinkRequest
bfReq *fuseshim.ReadlinkRequest
// The symlink inode that we are reading.
Inode InodeID
......
......@@ -19,7 +19,7 @@ import (
"os"
"time"
"github.com/jacobsa/bazilfuse"
"github.com/jacobsa/fuse/internal/fuseshim"
)
// A 64-bit number used to uniquely identify a file or directory in the file
......@@ -38,7 +38,7 @@ const RootInodeID = 1
func init() {
// Make sure the constant above is correct. We do this at runtime rather than
// defining the constant in terms of bazilfuse.RootID for two reasons:
// defining the constant in terms of fuseshim.RootID for two reasons:
//
// 1. Users can more clearly see that the root ID is low and can therefore
// be used as e.g. an array index, with space reserved up to the root.
......@@ -46,12 +46,12 @@ func init() {
// 2. The constant can be untyped and can therefore more easily be used as
// an array index.
//
if RootInodeID != bazilfuse.RootID {
if RootInodeID != fuseshim.RootID {
panic(
fmt.Sprintf(
"Oops, RootInodeID is wrong: %v vs. %v",
RootInodeID,
bazilfuse.RootID))
fuseshim.RootID))
}
}
......
This diff is collapsed.
package fusekernel
import (
"time"
)
type Attr struct {
Ino uint64
Size uint64
Blocks uint64
Atime uint64
Mtime uint64
Ctime uint64
Crtime_ uint64 // OS X only
AtimeNsec uint32
MtimeNsec uint32
CtimeNsec uint32
CrtimeNsec uint32 // OS X only
Mode uint32
Nlink uint32
Uid uint32
Gid uint32
Rdev uint32
Flags_ uint32 // OS X only; see chflags(2)
Blksize uint32
padding uint32
}
func (a *Attr) SetCrtime(s uint64, ns uint32) {
a.Crtime_, a.CrtimeNsec = s, ns
}
func (a *Attr) SetFlags(f uint32) {
a.Flags_ = f
}
type SetattrIn struct {
setattrInCommon
// OS X only
Bkuptime_ uint64
Chgtime_ uint64
Crtime uint64
BkuptimeNsec uint32
ChgtimeNsec uint32
CrtimeNsec uint32
Flags_ uint32 // see chflags(2)
}
func (in *SetattrIn) BkupTime() time.Time {
return time.Unix(int64(in.Bkuptime_), int64(in.BkuptimeNsec))
}
func (in *SetattrIn) Chgtime() time.Time {
return time.Unix(int64(in.Chgtime_), int64(in.ChgtimeNsec))
}
func (in *SetattrIn) Flags() uint32 {
return in.Flags_
}
func openFlags(flags uint32) OpenFlags {
return OpenFlags(flags)
}
type GetxattrIn struct {
getxattrInCommon
// OS X only
Position uint32
Padding uint32
}
func (g *GetxattrIn) GetPosition() uint32 {
return g.Position
}
type SetxattrIn struct {
setxattrInCommon
// OS X only
Position uint32
Padding uint32
}
func (s *SetxattrIn) GetPosition() uint32 {
return s.Position
}
package fuseshim
import "time"
type attr struct {
Ino uint64
Size uint64
Blocks uint64
Atime uint64
Mtime uint64
Ctime uint64
AtimeNsec uint32
MtimeNsec uint32
CtimeNsec uint32
Mode uint32
Nlink uint32
Uid uint32
Gid uint32
Rdev uint32
Blksize uint32
padding uint32
}
func (a *attr) Crtime() time.Time {
return time.Time{}
}
func (a *attr) SetCrtime(s uint64, ns uint32) {
// ignored on freebsd
}
func (a *attr) SetFlags(f uint32) {
// ignored on freebsd
}
type setattrIn struct {
setattrInCommon
}
func (in *setattrIn) BkupTime() time.Time {
return time.Time{}
}
func (in *setattrIn) Chgtime() time.Time {
return time.Time{}
}
func (in *setattrIn) Flags() uint32 {
return 0
}
func openFlags(flags uint32) OpenFlags {
return OpenFlags(flags)
}
type getxattrIn struct {
getxattrInCommon
}
type setxattrIn struct {
setxattrInCommon
}
package fuseshim
import "time"
type attr struct {
Ino uint64
Size uint64
Blocks uint64
Atime uint64
Mtime uint64
Ctime uint64
AtimeNsec uint32
MtimeNsec uint32
CtimeNsec uint32
Mode uint32
Nlink uint32
Uid uint32
Gid uint32
Rdev uint32
Blksize uint32
padding uint32
}
func (a *attr) Crtime() time.Time {
return time.Time{}
}
func (a *attr) SetCrtime(s uint64, ns uint32) {
// Ignored on Linux.
}
func (a *attr) SetFlags(f uint32) {
// Ignored on Linux.
}
type setattrIn struct {
setattrInCommon
}
func (in *setattrIn) BkupTime() time.Time {
return time.Time{}
}
func (in *setattrIn) Chgtime() time.Time {
return time.Time{}
}
func (in *setattrIn) Flags() uint32 {
return 0
}
func openFlags(flags uint32) OpenFlags {
// on amd64, the 32-bit O_LARGEFILE flag is always seen;
// on i386, the flag probably depends on the app
// requesting, but in any case should be utterly
// uninteresting to us here; our kernel protocol messages
// are not directly related to the client app's kernel
// API/ABI
flags &^= 0x8000
return OpenFlags(flags)
}
type getxattrIn struct {
getxattrInCommon
}
type setxattrIn struct {
setxattrInCommon
}
package fusekernel_test
import (
"os"
"testing"
fuse "github.com/jacobsa/bazilfuse"
)
func TestOpenFlagsAccmodeMask(t *testing.T) {
var f = fuse.OpenFlags(os.O_RDWR | os.O_SYNC)
if g, e := f&fuse.OpenAccessModeMask, fuse.OpenReadWrite; g != e {
t.Fatalf("OpenAccessModeMask behaves wrong: %v: %o != %o", f, g, e)
}
if f.IsReadOnly() {
t.Fatalf("IsReadOnly is wrong: %v", f)
}
if f.IsWriteOnly() {
t.Fatalf("IsWriteOnly is wrong: %v", f)
}
if !f.IsReadWrite() {
t.Fatalf("IsReadWrite is wrong: %v", f)
}
}
func TestOpenFlagsString(t *testing.T) {
var f = fuse.OpenFlags(os.O_RDWR | os.O_SYNC | os.O_APPEND)
if g, e := f.String(), "OpenReadWrite+OpenAppend+OpenSync"; g != e {
t.Fatalf("OpenFlags.String: %q != %q", g, e)
}
}
package fusekernel
import (
"fmt"
)
// Protocol is a FUSE protocol version number.
type Protocol struct {
Major uint32
Minor uint32
}
func (p Protocol) String() string {
return fmt.Sprintf("%d.%d", p.Major, p.Minor)
}
// LT returns whether a is less than b.
func (a Protocol) LT(b Protocol) bool {
return a.Major < b.Major ||
(a.Major == b.Major && a.Minor < b.Minor)
}
// GE returns whether a is greater than or equal to b.
func (a Protocol) GE(b Protocol) bool {
return a.Major > b.Major ||
(a.Major == b.Major && a.Minor >= b.Minor)
}
func (a Protocol) is79() bool {
return a.GE(Protocol{7, 9})
}
// HasAttrBlockSize returns whether Attr.BlockSize is respected by the
// kernel.
func (a Protocol) HasAttrBlockSize() bool {
return a.is79()
}
// HasReadWriteFlags returns whether ReadRequest/WriteRequest
// fields Flags and FileFlags are valid.
func (a Protocol) HasReadWriteFlags() bool {
return a.is79()
}
// HasGetattrFlags returns whether GetattrRequest field Flags is
// valid.
func (a Protocol) HasGetattrFlags() bool {
return a.is79()
}
func (a Protocol) is710() bool {
return a.GE(Protocol{7, 10})
}
// HasOpenNonSeekable returns whether OpenResponse field Flags flag
// OpenNonSeekable is supported.
func (a Protocol) HasOpenNonSeekable() bool {
return a.is710()
}
func (a Protocol) is712() bool {
return a.GE(Protocol{7, 12})
}
// HasUmask returns whether CreateRequest/MkdirRequest/MknodRequest
// field Umask is valid.
func (a Protocol) HasUmask() bool {
return a.is712()
}
// HasInvalidate returns whether InvalidateNode/InvalidateEntry are
// supported.
func (a Protocol) HasInvalidate() bool {
return a.is712()
}
package fuseshim
import (
"unsafe"
"github.com/jacobsa/fuse/internal/fusekernel"
)
// buffer provides a mechanism for constructing a message from
// multiple segments.
type buffer []byte
// alloc allocates size bytes and returns a pointer to the new
// segment.
func (w *buffer) alloc(size uintptr) unsafe.Pointer {
s := int(size)
if len(*w)+s > cap(*w) {
old := *w
*w = make([]byte, len(*w), 2*cap(*w)+s)
copy(*w, old)
}
l := len(*w)
*w = (*w)[:l+s]
return unsafe.Pointer(&(*w)[l])
}
// reset clears out the contents of the buffer.
func (w *buffer) reset() {
for i := range (*w)[:cap(*w)] {
(*w)[i] = 0
}
*w = (*w)[:0]
}
func newBuffer(extra uintptr) buffer {
const hdrSize = unsafe.Sizeof(fusekernel.OutHeader{})
buf := make(buffer, hdrSize, hdrSize+extra)
return buf
}
This diff is collapsed.
package fuseshim
import (
"bytes"
"errors"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"syscall"
)
// OS X appears to cap the size of writes to 1 MiB. This constant is also used
// for sizing receive buffers, so make it as small as it can be without
// limiting write sizes.
const maxWrite = 1 << 20
var errNoAvail = errors.New("no available fuse devices")
var errNotLoaded = errors.New("osxfusefs is not loaded")
func loadOSXFUSE() error {
cmd := exec.Command("/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs")
cmd.Dir = "/"
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
return err
}
func openOSXFUSEDev() (*os.File, error) {
var f *os.File
var err error
for i := uint64(0); ; i++ {
path := "/dev/osxfuse" + strconv.FormatUint(i, 10)
f, err = os.OpenFile(path, os.O_RDWR, 0000)
if os.IsNotExist(err) {
if i == 0 {
// not even the first device was found -> fuse is not loaded
return nil, errNotLoaded
}
// we've run out of kernel-provided devices
return nil, errNoAvail
}
if err2, ok := err.(*os.PathError); ok && err2.Err == syscall.EBUSY {
// try the next one
continue
}
if err != nil {
return nil, err
}
return f, nil
}
}
func callMount(dir string, conf *mountConfig, f *os.File, ready chan<- struct{}, errp *error) error {
bin := "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs"
for k, v := range conf.options {
if strings.Contains(k, ",") || strings.Contains(v, ",") {
// Silly limitation but the mount helper does not
// understand any escaping. See TestMountOptionCommaError.
return fmt.Errorf("mount options cannot contain commas on darwin: %q=%q", k, v)
}
}
cmd := exec.Command(
bin,
"-o", conf.getOptions(),
// Tell osxfuse-kext how large our buffer is. It must split
// writes larger than this into multiple writes.
//
// OSXFUSE seems to ignore InitResponse.MaxWrite, and uses
// this instead.
"-o", "iosize="+strconv.FormatUint(maxWrite, 10),
// refers to fd passed in cmd.ExtraFiles
"3",
dir,
)
cmd.ExtraFiles = []*os.File{f}
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=")
// TODO this is used for fs typenames etc, let app influence it
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_DAEMON_PATH="+bin)
var buf bytes.Buffer
cmd.Stdout = &buf
cmd.Stderr = &buf
err := cmd.Start()
if err != nil {
return err
}
go func() {
err := cmd.Wait()
if err != nil {
if buf.Len() > 0 {
output := buf.Bytes()
output = bytes.TrimRight(output, "\n")
msg := err.Error() + ": " + string(output)
err = errors.New(msg)
}
}
*errp = err
close(ready)
}()
return err
}
func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
f, err := openOSXFUSEDev()
if err == errNotLoaded {
err = loadOSXFUSE()
if err != nil {
return nil, err
}
// try again
f, err = openOSXFUSEDev()
}
if err != nil {
return nil, err
}
err = callMount(dir, conf, f, ready, errp)
if err != nil {
f.Close()
return nil, err
}
return f, nil
}
package fuseshim
import (
"fmt"
"os"
"os/exec"
"strings"
)
// Maximum file write size we are prepared to receive from the kernel.
const maxWrite = 128 * 1024
func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
for k, v := range conf.options {
if strings.Contains(k, ",") || strings.Contains(v, ",") {
// Silly limitation but the mount helper does not
// understand any escaping. See TestMountOptionCommaError.
return nil, fmt.Errorf("mount options cannot contain commas on FreeBSD: %q=%q", k, v)
}
}
f, err := os.OpenFile("/dev/fuse", os.O_RDWR, 0000)
if err != nil {
*errp = err
return nil, err
}
cmd := exec.Command(
"/sbin/mount_fusefs",
"--safe",
"-o", conf.getOptions(),
"3",
dir,
)
cmd.ExtraFiles = []*os.File{f}
out, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("mount_fusefs: %q, %v", out, err)
}
close(ready)
return f, nil
}
package fuseshim
import (
"bufio"
"fmt"
"io"
"log"
"net"
"os"
"os/exec"
"sync"
"syscall"
)
// Maximum file write size we are prepared to receive from the kernel. Linux
// appears to limit writes to 128 KiB.
const maxWrite = 128 * 1024
func lineLogger(wg *sync.WaitGroup, prefix string, r io.ReadCloser) {
defer wg.Done()
scanner := bufio.NewScanner(r)
for scanner.Scan() {
switch line := scanner.Text(); line {
case `fusermount: failed to open /etc/fuse.conf: Permission denied`:
// Silence this particular message, it occurs way too
// commonly and isn't very relevant to whether the mount
// succeeds or not.
continue
default:
log.Printf("%s: %s", prefix, line)
}
}
if err := scanner.Err(); err != nil {
log.Printf("%s, error reading: %v", prefix, err)
}
}
func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) {
// linux mount is never delayed
close(ready)
fds, err := syscall.Socketpair(syscall.AF_FILE, syscall.SOCK_STREAM, 0)
if err != nil {
return nil, fmt.Errorf("socketpair error: %v", err)
}
writeFile := os.NewFile(uintptr(fds[0]), "fusermount-child-writes")
defer writeFile.Close()
readFile := os.NewFile(uintptr(fds[1]), "fusermount-parent-reads")
defer readFile.Close()
cmd := exec.Command(
"fusermount",
"-o", conf.getOptions(),
"--",
dir,
)
cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3")
cmd.ExtraFiles = []*os.File{writeFile}
var wg sync.WaitGroup
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("setting up fusermount stderr: %v", err)
}
stderr, err := cmd.StderrPipe()
if err != nil {
return nil, fmt.Errorf("setting up fusermount stderr: %v", err)
}
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("fusermount: %v", err)
}
wg.Add(2)
go lineLogger(&wg, "mount helper output", stdout)
go lineLogger(&wg, "mount helper error", stderr)
wg.Wait()
if err := cmd.Wait(); err != nil {
return nil, fmt.Errorf("fusermount: %v", err)
}
c, err := net.FileConn(readFile)
if err != nil {
return nil, fmt.Errorf("FileConn from fusermount socket: %v", err)
}
defer c.Close()
uc, ok := c.(*net.UnixConn)
if !ok {
return nil, fmt.Errorf("unexpected FileConn type; expected UnixConn, got %T", c)
}
buf := make([]byte, 32) // expect 1 byte
oob := make([]byte, 32) // expect 24 bytes
_, oobn, _, _, err := uc.ReadMsgUnix(buf, oob)
scms, err := syscall.ParseSocketControlMessage(oob[:oobn])
if err != nil {
return nil, fmt.Errorf("ParseSocketControlMessage: %v", err)
}
if len(scms) != 1 {
return nil, fmt.Errorf("expected 1 SocketControlMessage; got scms = %#v", scms)
}
scm := scms[0]
gotFds, err := syscall.ParseUnixRights(&scm)
if err != nil {
return nil, fmt.Errorf("syscall.ParseUnixRights: %v", err)
}
if len(gotFds) != 1 {
return nil, fmt.Errorf("wanted 1 fd; got %#v", gotFds)
}
f := os.NewFile(uintptr(gotFds[0]), "/dev/fuse")
return f, nil
}
package fuseshim
import (
"errors"
"strings"
"github.com/jacobsa/fuse/internal/fusekernel"
)
func dummyOption(conf *mountConfig) error {
return nil
}
// mountConfig holds the configuration for a mount operation.
// Use it by passing MountOption values to Mount.
type mountConfig struct {
options map[string]string
maxReadahead uint32
initFlags fusekernel.InitFlags
}
func escapeComma(s string) string {
s = strings.Replace(s, `\`, `\\`, -1)
s = strings.Replace(s, `,`, `\,`, -1)
return s
}
// getOptions makes a string of options suitable for passing to FUSE
// mount flag `-o`. Returns an empty string if no options were set.
// Any platform specific adjustments should happen before the call.
func (m *mountConfig) getOptions() string {
var opts []string
for k, v := range m.options {
k = escapeComma(k)
if v != "" {
k += "=" + escapeComma(v)
}
opts = append(opts, k)
}
return strings.Join(opts, ",")
}
type mountOption func(*mountConfig) error
// MountOption is passed to Mount to change the behavior of the mount.
type MountOption mountOption
// FSName sets the file system name (also called source) that is
// visible in the list of mounted file systems.
//
// FreeBSD ignores this option.
func FSName(name string) MountOption {
return func(conf *mountConfig) error {
conf.options["fsname"] = name
return nil
}
}
// Subtype sets the subtype of the mount. The main type is always
// `fuse`. The type in a list of mounted file systems will look like
// `fuse.foo`.
//
// OS X ignores this option.
// FreeBSD ignores this option.
func Subtype(fstype string) MountOption {
return func(conf *mountConfig) error {
conf.options["subtype"] = fstype
return nil
}
}
// LocalVolume sets the volume to be local (instead of network),
// changing the behavior of Finder, Spotlight, and such.
//
// OS X only. Others ignore this option.
func LocalVolume() MountOption {
return localVolume
}
// VolumeName sets the volume name shown in Finder.
//
// OS X only. Others ignore this option.
func VolumeName(name string) MountOption {
return volumeName(name)
}
var ErrCannotCombineAllowOtherAndAllowRoot = errors.New("cannot combine AllowOther and AllowRoot")
// AllowOther allows other users to access the file system.
//
// Only one of AllowOther or AllowRoot can be used.
func AllowOther() MountOption {
return func(conf *mountConfig) error {
if _, ok := conf.options["allow_root"]; ok {
return ErrCannotCombineAllowOtherAndAllowRoot
}
conf.options["allow_other"] = ""
return nil
}
}
// AllowRoot allows other users to access the file system.
//
// Only one of AllowOther or AllowRoot can be used.
//
// FreeBSD ignores this option.
func AllowRoot() MountOption {
return func(conf *mountConfig) error {
if _, ok := conf.options["allow_other"]; ok {
return ErrCannotCombineAllowOtherAndAllowRoot
}
conf.options["allow_root"] = ""
return nil
}
}
// DefaultPermissions makes the kernel enforce access control based on
// the file mode (as in chmod).
//
// Without this option, the Node itself decides what is and is not
// allowed. This is normally ok because FUSE file systems cannot be
// accessed by other users without AllowOther/AllowRoot.
//
// FreeBSD ignores this option.
func DefaultPermissions() MountOption {
return func(conf *mountConfig) error {
conf.options["default_permissions"] = ""
return nil
}
}
// Set the supplied arbitrary (key, value) pair in the "-o" argument to the
// fuse mount binary, overriding any previous setting for the key. If value is
// empty, the '=' will be omitted from the argument.
func SetOption(key, value string) MountOption {
return func(conf *mountConfig) error {
conf.options[key] = value
return nil
}
}
// ReadOnly makes the mount read-only.
func ReadOnly() MountOption {
return func(conf *mountConfig) error {
conf.options["ro"] = ""
return nil
}
}
// MaxReadahead sets the number of bytes that can be prefetched for
// sequential reads. The kernel can enforce a maximum value lower than
// this.
//
// This setting makes the kernel perform speculative reads that do not
// originate from any client process. This usually tremendously
// improves read performance.
func MaxReadahead(n uint32) MountOption {
return func(conf *mountConfig) error {
conf.maxReadahead = n
return nil
}
}
// AsyncRead enables multiple outstanding read requests for the same
// handle. Without this, there is at most one request in flight at a
// time.
func AsyncRead() MountOption {
return func(conf *mountConfig) error {
conf.initFlags |= fusekernel.InitAsyncRead
return nil
}
}
// WritebackCache enables the kernel to buffer writes before sending
// them to the FUSE server. Without this, writethrough caching is
// used.
func WritebackCache() MountOption {
return func(conf *mountConfig) error {
conf.initFlags |= fusekernel.InitWritebackCache
return nil
}
}
package fuseshim
func localVolume(conf *mountConfig) error {
conf.options["local"] = ""
return nil
}
func volumeName(name string) MountOption {
return func(conf *mountConfig) error {
conf.options["volname"] = name
return nil
}
}
package fuseshim
func localVolume(conf *mountConfig) error {
return nil
}
func volumeName(name string) MountOption {
return dummyOption
}
package fuseshim
// for TestMountOptionCommaError
func ForTestSetMountOption(k, v string) MountOption {
fn := func(conf *mountConfig) error {
conf.options[k] = v
return nil
}
return fn
}
package fuseshim
func localVolume(conf *mountConfig) error {
return nil
}
func volumeName(name string) MountOption {
return dummyOption
}
// This file contains tests for platforms that have no escape
// mechanism for including commas in mount options.
//
// +build darwin
package fuseshim_test
import (
"runtime"
"testing"
fuse "github.com/jacobsa/bazilfuse"
"github.com/jacobsa/bazilfuse/fs/fstestutil"
)
func TestMountOptionCommaError(t *testing.T) {
t.Parallel()
// this test is not tied to any specific option, it just needs
// some string content
var evil = "FuseTest,Marker"
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}}, nil,
fuse.ForTestSetMountOption("fusetest", evil),
)
if err == nil {
mnt.Close()
t.Fatal("expected an error about commas")
}
if g, e := err.Error(), `mount options cannot contain commas on `+runtime.GOOS+`: "fusetest"="FuseTest,Marker"`; g != e {
t.Fatalf("wrong error: %q != %q", g, e)
}
}
package fuseshim_test
import (
"os"
"runtime"
"syscall"
"testing"
fuse "github.com/jacobsa/bazilfuse"
"github.com/jacobsa/bazilfuse/fs"
"github.com/jacobsa/bazilfuse/fs/fstestutil"
"golang.org/x/net/context"
)
func init() {
fstestutil.DebugByDefault()
}
func TestMountOptionFSName(t *testing.T) {
if runtime.GOOS == "freebsd" {
t.Skip("FreeBSD does not support FSName")
}
t.Parallel()
const name = "FuseTestMarker"
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}}, nil,
fuse.FSName(name),
)
if err != nil {
t.Fatal(err)
}
defer mnt.Close()
info, err := fstestutil.GetMountInfo(mnt.Dir)
if err != nil {
t.Fatal(err)
}
if g, e := info.FSName, name; g != e {
t.Errorf("wrong FSName: %q != %q", g, e)
}
}
func testMountOptionFSNameEvil(t *testing.T, evil string) {
if runtime.GOOS == "freebsd" {
t.Skip("FreeBSD does not support FSName")
}
t.Parallel()
var name = "FuseTest" + evil + "Marker"
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}}, nil,
fuse.FSName(name),
)
if err != nil {
t.Fatal(err)
}
defer mnt.Close()
info, err := fstestutil.GetMountInfo(mnt.Dir)
if err != nil {
t.Fatal(err)
}
if g, e := info.FSName, name; g != e {
t.Errorf("wrong FSName: %q != %q", g, e)
}
}
func TestMountOptionFSNameEvilComma(t *testing.T) {
if runtime.GOOS == "darwin" {
// see TestMountOptionCommaError for a test that enforces we
// at least give a nice error, instead of corrupting the mount
// options
t.Skip("TODO: OS X gets this wrong, commas in mount options cannot be escaped at all")
}
testMountOptionFSNameEvil(t, ",")
}
func TestMountOptionFSNameEvilSpace(t *testing.T) {
testMountOptionFSNameEvil(t, " ")
}
func TestMountOptionFSNameEvilTab(t *testing.T) {
testMountOptionFSNameEvil(t, "\t")
}
func TestMountOptionFSNameEvilNewline(t *testing.T) {
testMountOptionFSNameEvil(t, "\n")
}
func TestMountOptionFSNameEvilBackslash(t *testing.T) {
testMountOptionFSNameEvil(t, `\`)
}
func TestMountOptionFSNameEvilBackslashDouble(t *testing.T) {
// catch double-unescaping, if it were to happen
testMountOptionFSNameEvil(t, `\\`)
}
func TestMountOptionSubtype(t *testing.T) {
if runtime.GOOS == "darwin" {
t.Skip("OS X does not support Subtype")
}
if runtime.GOOS == "freebsd" {
t.Skip("FreeBSD does not support Subtype")
}
t.Parallel()
const name = "FuseTestMarker"
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}}, nil,
fuse.Subtype(name),
)
if err != nil {
t.Fatal(err)
}
defer mnt.Close()
info, err := fstestutil.GetMountInfo(mnt.Dir)
if err != nil {
t.Fatal(err)
}
if g, e := info.Type, "fuse."+name; g != e {
t.Errorf("wrong Subtype: %q != %q", g, e)
}
}
// TODO test LocalVolume
// TODO test AllowOther; hard because needs system-level authorization
func TestMountOptionAllowOtherThenAllowRoot(t *testing.T) {
t.Parallel()
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}}, nil,
fuse.AllowOther(),
fuse.AllowRoot(),
)
if err == nil {
mnt.Close()
}
if g, e := err, fuse.ErrCannotCombineAllowOtherAndAllowRoot; g != e {
t.Fatalf("wrong error: %v != %v", g, e)
}
}
// TODO test AllowRoot; hard because needs system-level authorization
func TestMountOptionAllowRootThenAllowOther(t *testing.T) {
t.Parallel()
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}}, nil,
fuse.AllowRoot(),
fuse.AllowOther(),
)
if err == nil {
mnt.Close()
}
if g, e := err, fuse.ErrCannotCombineAllowOtherAndAllowRoot; g != e {
t.Fatalf("wrong error: %v != %v", g, e)
}
}
type unwritableFile struct{}
func (f unwritableFile) Attr(ctx context.Context, a *fuse.Attr) error {
a.Mode = 0000
return nil
}
func TestMountOptionDefaultPermissions(t *testing.T) {
if runtime.GOOS == "freebsd" {
t.Skip("FreeBSD does not support DefaultPermissions")
}
t.Parallel()
mnt, err := fstestutil.MountedT(t,
fstestutil.SimpleFS{
&fstestutil.ChildMap{"child": unwritableFile{}},
},
nil,
fuse.DefaultPermissions(),
)
if err != nil {
t.Fatal(err)
}
defer mnt.Close()
// This will be prevented by kernel-level access checking when
// DefaultPermissions is used.
f, err := os.OpenFile(mnt.Dir+"/child", os.O_WRONLY, 0000)
if err == nil {
f.Close()
t.Fatal("expected an error")
}
if !os.IsPermission(err) {
t.Fatalf("expected a permission error, got %T: %v", err, err)
}
}
type createrDir struct {
fstestutil.Dir
}
var _ fs.NodeCreater = createrDir{}
func (createrDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
// pick a really distinct error, to identify it later
return nil, nil, fuse.Errno(syscall.ENAMETOOLONG)
}
func TestMountOptionReadOnly(t *testing.T) {
t.Parallel()
mnt, err := fstestutil.MountedT(t,
fstestutil.SimpleFS{createrDir{}},
nil,
fuse.ReadOnly(),
)
if err != nil {
t.Fatal(err)
}
defer mnt.Close()
// This will be prevented by kernel-level access checking when
// ReadOnly is used.
f, err := os.Create(mnt.Dir + "/child")
if err == nil {
f.Close()
t.Fatal("expected an error")
}
perr, ok := err.(*os.PathError)
if !ok {
t.Fatalf("expected PathError, got %T: %v", err, err)
}
if perr.Err != syscall.EROFS {
t.Fatalf("expected EROFS, got %T: %v", err, err)
}
}
package fuseshim
// Unmount tries to unmount the filesystem mounted at dir.
func Unmount(dir string) error {
return unmount(dir)
}
package bazilfuse
import (
"bytes"
"errors"
"os/exec"
)
func unmount(dir string) error {
cmd := exec.Command("fusermount", "-u", dir)
output, err := cmd.CombinedOutput()
if err != nil {
if len(output) > 0 {
output = bytes.TrimRight(output, "\n")
msg := err.Error() + ": " + string(output)
err = errors.New(msg)
}
return err
}
return nil
}
// +build !linux
package fuseshim
import (
"os"
"syscall"
)
func unmount(dir string) error {
err := syscall.Unmount(dir, 0)
if err != nil {
err = &os.PathError{Op: "unmount", Path: dir, Err: err}
return err
}
return nil
}
......@@ -19,7 +19,8 @@ import (
"log"
"runtime"
"github.com/jacobsa/bazilfuse"
"github.com/jacobsa/fuse/internal/fuseshim"
"golang.org/x/net/context"
)
......@@ -111,13 +112,13 @@ type MountConfig struct {
Options map[string]string
}
// Convert to mount options to be passed to package bazilfuse.
func (c *MountConfig) bazilfuseOptions() (opts []bazilfuse.MountOption) {
// Convert to mount options to be passed to package fuseshim.
func (c *MountConfig) bazilfuseOptions() (opts []fuseshim.MountOption) {
isDarwin := runtime.GOOS == "darwin"
// Enable permissions checking in the kernel. See the comments on
// InodeAttributes.Mode.
opts = append(opts, bazilfuse.SetOption("default_permissions", ""))
opts = append(opts, fuseshim.SetOption("default_permissions", ""))
// HACK(jacobsa): Work around what appears to be a bug in systemd v219, as
// shipped in Ubuntu 15.04, where it automatically unmounts any file system
......@@ -135,17 +136,17 @@ func (c *MountConfig) bazilfuseOptions() (opts []bazilfuse.MountOption) {
// Special file system name?
if fsname != "" {
opts = append(opts, bazilfuse.FSName(fsname))
opts = append(opts, fuseshim.FSName(fsname))
}
// Read only?
if c.ReadOnly {
opts = append(opts, bazilfuse.ReadOnly())
opts = append(opts, fuseshim.ReadOnly())
}
// OS X: set novncache when appropriate.
if isDarwin && !c.EnableVnodeCaching {
opts = append(opts, bazilfuse.SetOption("novncache", ""))
opts = append(opts, fuseshim.SetOption("novncache", ""))
}
// OS X: disable the use of "Apple Double" (._foo and .DS_Store) files, which
......@@ -154,7 +155,7 @@ func (c *MountConfig) bazilfuseOptions() (opts []bazilfuse.MountOption) {
//
// Cf. https://github.com/osxfuse/osxfuse/wiki/Mount-options
if isDarwin {
opts = append(opts, bazilfuse.SetOption("noappledouble", ""))
opts = append(opts, fuseshim.SetOption("noappledouble", ""))
}
// Ask the Linux kernel for larger read requests.
......@@ -175,11 +176,11 @@ func (c *MountConfig) bazilfuseOptions() (opts []bazilfuse.MountOption) {
//
// Reading a page at a time is a drag. Ask for a larger size.
const maxReadahead = 1 << 20
opts = append(opts, bazilfuse.MaxReadahead(maxReadahead))
opts = append(opts, fuseshim.MaxReadahead(maxReadahead))
// Last but not least: other user-supplied options.
for k, v := range c.Options {
opts = append(opts, bazilfuse.SetOption(k, v))
opts = append(opts, fuseshim.SetOption(k, v))
}
return
......@@ -198,10 +199,10 @@ func Mount(
joinStatusAvailable: make(chan struct{}),
}
// Open a bazilfuse connection.
bfConn, err := bazilfuse.Mount(mfs.dir, config.bazilfuseOptions()...)
// Open a fuseshim connection.
bfConn, err := fuseshim.Mount(mfs.dir, config.bazilfuseOptions()...)
if err != nil {
err = fmt.Errorf("bazilfuse.Mount: %v", err)
err = fmt.Errorf("fuseshim.Mount: %v", err)
return
}
......
......@@ -30,9 +30,9 @@ import (
"golang.org/x/sys/unix"
"github.com/jacobsa/bazilfuse"
"github.com/jacobsa/fuse/fsutil"
"github.com/jacobsa/fuse/fusetesting"
"github.com/jacobsa/fuse/internal/fuseshim"
"github.com/jacobsa/fuse/samples"
. "github.com/jacobsa/oglematchers"
. "github.com/jacobsa/ogletest"
......@@ -58,8 +58,8 @@ type flushFSTest struct {
func (t *flushFSTest) setUp(
ti *TestInfo,
flushErr bazilfuse.Errno,
fsyncErr bazilfuse.Errno,
flushErr fuseshim.Errno,
fsyncErr fuseshim.Errno,
readOnly bool) {
var err error
......@@ -810,7 +810,7 @@ func init() { RegisterTestSuite(&FlushErrorTest{}) }
func (t *FlushErrorTest) SetUp(ti *TestInfo) {
const noErr = 0
t.flushFSTest.setUp(ti, bazilfuse.ENOENT, noErr, false)
t.flushFSTest.setUp(ti, fuseshim.ENOENT, noErr, false)
}
func (t *FlushErrorTest) Close() {
......@@ -890,7 +890,7 @@ func init() { RegisterTestSuite(&FsyncErrorTest{}) }
func (t *FsyncErrorTest) SetUp(ti *TestInfo) {
const noErr = 0
t.flushFSTest.setUp(ti, noErr, bazilfuse.ENOENT, false)
t.flushFSTest.setUp(ti, noErr, fuseshim.ENOENT, false)
}
func (t *FsyncErrorTest) Fsync() {
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment