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

geesefs-0-30-9
Aaron Jacobs 2015-07-23 16:27:14 +10:00
commit f4231ede4b
32 changed files with 4273 additions and 139 deletions

View File

@ -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
}

View File

@ -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)
)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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))
}
}

View File

@ -0,0 +1,766 @@
// See the file LICENSE for copyright and licensing information.
// Derived from FUSE's fuse_kernel.h, which carries this notice:
/*
This file defines the kernel interface of FUSE
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This -- and only this -- header file may also be distributed under
the terms of the BSD Licence as follows:
Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
*/
package fusekernel
import (
"fmt"
"syscall"
"unsafe"
)
// The FUSE version implemented by the package.
const (
ProtoVersionMinMajor = 7
ProtoVersionMinMinor = 8
ProtoVersionMaxMajor = 7
ProtoVersionMaxMinor = 12
)
const (
RootID = 1
)
type Kstatfs struct {
Blocks uint64
Bfree uint64
Bavail uint64
Files uint64
Ffree uint64
Bsize uint32
Namelen uint32
Frsize uint32
Padding uint32
Spare [6]uint32
}
type fileLock struct {
Start uint64
End uint64
Type uint32
Pid uint32
}
// GetattrFlags are bit flags that can be seen in GetattrRequest.
type GetattrFlags uint32
const (
// Indicates the handle is valid.
GetattrFh GetattrFlags = 1 << 0
)
var getattrFlagsNames = []flagName{
{uint32(GetattrFh), "GetattrFh"},
}
func (fl GetattrFlags) String() string {
return flagString(uint32(fl), getattrFlagsNames)
}
// The SetattrValid are bit flags describing which fields in the SetattrRequest
// are included in the change.
type SetattrValid uint32
const (
SetattrMode SetattrValid = 1 << 0
SetattrUid SetattrValid = 1 << 1
SetattrGid SetattrValid = 1 << 2
SetattrSize SetattrValid = 1 << 3
SetattrAtime SetattrValid = 1 << 4
SetattrMtime SetattrValid = 1 << 5
SetattrHandle SetattrValid = 1 << 6
// Linux only(?)
SetattrAtimeNow SetattrValid = 1 << 7
SetattrMtimeNow SetattrValid = 1 << 8
SetattrLockOwner SetattrValid = 1 << 9 // http://www.mail-archive.com/git-commits-head@vger.kernel.org/msg27852.html
// OS X only
SetattrCrtime SetattrValid = 1 << 28
SetattrChgtime SetattrValid = 1 << 29
SetattrBkuptime SetattrValid = 1 << 30
SetattrFlags SetattrValid = 1 << 31
)
func (fl SetattrValid) Mode() bool { return fl&SetattrMode != 0 }
func (fl SetattrValid) Uid() bool { return fl&SetattrUid != 0 }
func (fl SetattrValid) Gid() bool { return fl&SetattrGid != 0 }
func (fl SetattrValid) Size() bool { return fl&SetattrSize != 0 }
func (fl SetattrValid) Atime() bool { return fl&SetattrAtime != 0 }
func (fl SetattrValid) Mtime() bool { return fl&SetattrMtime != 0 }
func (fl SetattrValid) Handle() bool { return fl&SetattrHandle != 0 }
func (fl SetattrValid) AtimeNow() bool { return fl&SetattrAtimeNow != 0 }
func (fl SetattrValid) MtimeNow() bool { return fl&SetattrMtimeNow != 0 }
func (fl SetattrValid) LockOwner() bool { return fl&SetattrLockOwner != 0 }
func (fl SetattrValid) Crtime() bool { return fl&SetattrCrtime != 0 }
func (fl SetattrValid) Chgtime() bool { return fl&SetattrChgtime != 0 }
func (fl SetattrValid) Bkuptime() bool { return fl&SetattrBkuptime != 0 }
func (fl SetattrValid) Flags() bool { return fl&SetattrFlags != 0 }
func (fl SetattrValid) String() string {
return flagString(uint32(fl), setattrValidNames)
}
var setattrValidNames = []flagName{
{uint32(SetattrMode), "SetattrMode"},
{uint32(SetattrUid), "SetattrUid"},
{uint32(SetattrGid), "SetattrGid"},
{uint32(SetattrSize), "SetattrSize"},
{uint32(SetattrAtime), "SetattrAtime"},
{uint32(SetattrMtime), "SetattrMtime"},
{uint32(SetattrHandle), "SetattrHandle"},
{uint32(SetattrAtimeNow), "SetattrAtimeNow"},
{uint32(SetattrMtimeNow), "SetattrMtimeNow"},
{uint32(SetattrLockOwner), "SetattrLockOwner"},
{uint32(SetattrCrtime), "SetattrCrtime"},
{uint32(SetattrChgtime), "SetattrChgtime"},
{uint32(SetattrBkuptime), "SetattrBkuptime"},
{uint32(SetattrFlags), "SetattrFlags"},
}
// Flags that can be seen in OpenRequest.Flags.
const (
// Access modes. These are not 1-bit flags, but alternatives where
// only one can be chosen. See the IsReadOnly etc convenience
// methods.
OpenReadOnly OpenFlags = syscall.O_RDONLY
OpenWriteOnly OpenFlags = syscall.O_WRONLY
OpenReadWrite OpenFlags = syscall.O_RDWR
OpenAppend OpenFlags = syscall.O_APPEND
OpenCreate OpenFlags = syscall.O_CREAT
OpenExclusive OpenFlags = syscall.O_EXCL
OpenSync OpenFlags = syscall.O_SYNC
OpenTruncate OpenFlags = syscall.O_TRUNC
)
// OpenAccessModeMask is a bitmask that separates the access mode
// from the other flags in OpenFlags.
const OpenAccessModeMask OpenFlags = syscall.O_ACCMODE
// OpenFlags are the O_FOO flags passed to open/create/etc calls. For
// example, os.O_WRONLY | os.O_APPEND.
type OpenFlags uint32
func (fl OpenFlags) String() string {
// O_RDONLY, O_RWONLY, O_RDWR are not flags
s := accModeName(fl & OpenAccessModeMask)
flags := uint32(fl &^ OpenAccessModeMask)
if flags != 0 {
s = s + "+" + flagString(flags, openFlagNames)
}
return s
}
// Return true if OpenReadOnly is set.
func (fl OpenFlags) IsReadOnly() bool {
return fl&OpenAccessModeMask == OpenReadOnly
}
// Return true if OpenWriteOnly is set.
func (fl OpenFlags) IsWriteOnly() bool {
return fl&OpenAccessModeMask == OpenWriteOnly
}
// Return true if OpenReadWrite is set.
func (fl OpenFlags) IsReadWrite() bool {
return fl&OpenAccessModeMask == OpenReadWrite
}
func accModeName(flags OpenFlags) string {
switch flags {
case OpenReadOnly:
return "OpenReadOnly"
case OpenWriteOnly:
return "OpenWriteOnly"
case OpenReadWrite:
return "OpenReadWrite"
default:
return ""
}
}
var openFlagNames = []flagName{
{uint32(OpenCreate), "OpenCreate"},
{uint32(OpenExclusive), "OpenExclusive"},
{uint32(OpenTruncate), "OpenTruncate"},
{uint32(OpenAppend), "OpenAppend"},
{uint32(OpenSync), "OpenSync"},
}
// The OpenResponseFlags are returned in the OpenResponse.
type OpenResponseFlags uint32
const (
OpenDirectIO OpenResponseFlags = 1 << 0 // bypass page cache for this open file
OpenKeepCache OpenResponseFlags = 1 << 1 // don't invalidate the data cache on open
OpenNonSeekable OpenResponseFlags = 1 << 2 // mark the file as non-seekable (not supported on OS X)
OpenPurgeAttr OpenResponseFlags = 1 << 30 // OS X
OpenPurgeUBC OpenResponseFlags = 1 << 31 // OS X
)
func (fl OpenResponseFlags) String() string {
return flagString(uint32(fl), openResponseFlagNames)
}
var openResponseFlagNames = []flagName{
{uint32(OpenDirectIO), "OpenDirectIO"},
{uint32(OpenKeepCache), "OpenKeepCache"},
{uint32(OpenNonSeekable), "OpenNonSeekable"},
{uint32(OpenPurgeAttr), "OpenPurgeAttr"},
{uint32(OpenPurgeUBC), "OpenPurgeUBC"},
}
// The InitFlags are used in the Init exchange.
type InitFlags uint32
const (
InitAsyncRead InitFlags = 1 << 0
InitPosixLocks InitFlags = 1 << 1
InitFileOps InitFlags = 1 << 2
InitAtomicTrunc InitFlags = 1 << 3
InitExportSupport InitFlags = 1 << 4
InitBigWrites InitFlags = 1 << 5
InitDontMask InitFlags = 1 << 6
InitSpliceWrite InitFlags = 1 << 7
InitSpliceMove InitFlags = 1 << 8
InitSpliceRead InitFlags = 1 << 9
InitFlockLocks InitFlags = 1 << 10
InitHasIoctlDir InitFlags = 1 << 11
InitAutoInvalData InitFlags = 1 << 12
InitDoReaddirplus InitFlags = 1 << 13
InitReaddirplusAuto InitFlags = 1 << 14
InitAsyncDIO InitFlags = 1 << 15
InitWritebackCache InitFlags = 1 << 16
InitNoOpenSupport InitFlags = 1 << 17
InitCaseSensitive InitFlags = 1 << 29 // OS X only
InitVolRename InitFlags = 1 << 30 // OS X only
InitXtimes InitFlags = 1 << 31 // OS X only
)
type flagName struct {
bit uint32
name string
}
var initFlagNames = []flagName{
{uint32(InitAsyncRead), "InitAsyncRead"},
{uint32(InitPosixLocks), "InitPosixLocks"},
{uint32(InitFileOps), "InitFileOps"},
{uint32(InitAtomicTrunc), "InitAtomicTrunc"},
{uint32(InitExportSupport), "InitExportSupport"},
{uint32(InitBigWrites), "InitBigWrites"},
{uint32(InitDontMask), "InitDontMask"},
{uint32(InitSpliceWrite), "InitSpliceWrite"},
{uint32(InitSpliceMove), "InitSpliceMove"},
{uint32(InitSpliceRead), "InitSpliceRead"},
{uint32(InitFlockLocks), "InitFlockLocks"},
{uint32(InitHasIoctlDir), "InitHasIoctlDir"},
{uint32(InitAutoInvalData), "InitAutoInvalData"},
{uint32(InitDoReaddirplus), "InitDoReaddirplus"},
{uint32(InitReaddirplusAuto), "InitReaddirplusAuto"},
{uint32(InitAsyncDIO), "InitAsyncDIO"},
{uint32(InitWritebackCache), "InitWritebackCache"},
{uint32(InitNoOpenSupport), "InitNoOpenSupport"},
{uint32(InitCaseSensitive), "InitCaseSensitive"},
{uint32(InitVolRename), "InitVolRename"},
{uint32(InitXtimes), "InitXtimes"},
}
func (fl InitFlags) String() string {
return flagString(uint32(fl), initFlagNames)
}
func flagString(f uint32, names []flagName) string {
var s string
if f == 0 {
return "0"
}
for _, n := range names {
if f&n.bit != 0 {
s += "+" + n.name
f &^= n.bit
}
}
if f != 0 {
s += fmt.Sprintf("%+#x", f)
}
return s[1:]
}
// The ReleaseFlags are used in the Release exchange.
type ReleaseFlags uint32
const (
ReleaseFlush ReleaseFlags = 1 << 0
)
func (fl ReleaseFlags) String() string {
return flagString(uint32(fl), releaseFlagNames)
}
var releaseFlagNames = []flagName{
{uint32(ReleaseFlush), "ReleaseFlush"},
}
// Opcodes
const (
OpLookup = 1
OpForget = 2 // no reply
OpGetattr = 3
OpSetattr = 4
OpReadlink = 5
OpSymlink = 6
OpMknod = 8
OpMkdir = 9
OpUnlink = 10
OpRmdir = 11
OpRename = 12
OpLink = 13
OpOpen = 14
OpRead = 15
OpWrite = 16
OpStatfs = 17
OpRelease = 18
OpFsync = 20
OpSetxattr = 21
OpGetxattr = 22
OpListxattr = 23
OpRemovexattr = 24
OpFlush = 25
OpInit = 26
OpOpendir = 27
OpReaddir = 28
OpReleasedir = 29
OpFsyncdir = 30
OpGetlk = 31
OpSetlk = 32
OpSetlkw = 33
OpAccess = 34
OpCreate = 35
OpInterrupt = 36
OpBmap = 37
OpDestroy = 38
OpIoctl = 39 // Linux?
OpPoll = 40 // Linux?
// OS X
OpSetvolname = 61
OpGetxtimes = 62
OpExchange = 63
)
type EntryOut struct {
Nodeid uint64 // Inode ID
Generation uint64 // Inode generation
EntryValid uint64 // Cache timeout for the name
AttrValid uint64 // Cache timeout for the attributes
EntryValidNsec uint32
AttrValidNsec uint32
Attr Attr
}
func EntryOutSize(p Protocol) uintptr {
switch {
case p.LT(Protocol{7, 9}):
return unsafe.Offsetof(EntryOut{}.Attr) + unsafe.Offsetof(EntryOut{}.Attr.Blksize)
default:
return unsafe.Sizeof(EntryOut{})
}
}
type ForgetIn struct {
Nlookup uint64
}
type GetattrIn struct {
GetattrFlags uint32
dummy uint32
Fh uint64
}
type AttrOut struct {
AttrValid uint64 // Cache timeout for the attributes
AttrValidNsec uint32
Dummy uint32
Attr Attr
}
func AttrOutSize(p Protocol) uintptr {
switch {
case p.LT(Protocol{7, 9}):
return unsafe.Offsetof(AttrOut{}.Attr) + unsafe.Offsetof(AttrOut{}.Attr.Blksize)
default:
return unsafe.Sizeof(AttrOut{})
}
}
// OS X
type GetxtimesOut struct {
Bkuptime uint64
Crtime uint64
BkuptimeNsec uint32
CrtimeNsec uint32
}
type MknodIn struct {
Mode uint32
Rdev uint32
Umask uint32
padding uint32
// "filename\x00" follows.
}
func MknodInSize(p Protocol) uintptr {
switch {
case p.LT(Protocol{7, 12}):
return unsafe.Offsetof(MknodIn{}.Umask)
default:
return unsafe.Sizeof(MknodIn{})
}
}
type MkdirIn struct {
Mode uint32
Umask uint32
// filename follows
}
func MkdirInSize(p Protocol) uintptr {
switch {
case p.LT(Protocol{7, 12}):
return unsafe.Offsetof(MkdirIn{}.Umask) + 4
default:
return unsafe.Sizeof(MkdirIn{})
}
}
type RenameIn struct {
Newdir uint64
// "oldname\x00newname\x00" follows
}
// OS X
type ExchangeIn struct {
Olddir uint64
Newdir uint64
Options uint64
}
type LinkIn struct {
Oldnodeid uint64
}
type setattrInCommon struct {
Valid uint32
Padding uint32
Fh uint64
Size uint64
LockOwner uint64 // unused on OS X?
Atime uint64
Mtime uint64
Unused2 uint64
AtimeNsec uint32
MtimeNsec uint32
Unused3 uint32
Mode uint32
Unused4 uint32
Uid uint32
Gid uint32
Unused5 uint32
}
type OpenIn struct {
Flags uint32
Unused uint32
}
type OpenOut struct {
Fh uint64
OpenFlags uint32
Padding uint32
}
type CreateIn struct {
Flags uint32
Mode uint32
Umask uint32
padding uint32
}
func CreateInSize(p Protocol) uintptr {
switch {
case p.LT(Protocol{7, 12}):
return unsafe.Offsetof(CreateIn{}.Umask)
default:
return unsafe.Sizeof(CreateIn{})
}
}
type ReleaseIn struct {
Fh uint64
Flags uint32
ReleaseFlags uint32
LockOwner uint32
}
type FlushIn struct {
Fh uint64
FlushFlags uint32
Padding uint32
LockOwner uint64
}
type ReadIn struct {
Fh uint64
Offset uint64
Size uint32
ReadFlags uint32
LockOwner uint64
Flags uint32
padding uint32
}
func ReadInSize(p Protocol) uintptr {
switch {
case p.LT(Protocol{7, 9}):
return unsafe.Offsetof(ReadIn{}.ReadFlags) + 4
default:
return unsafe.Sizeof(ReadIn{})
}
}
// The ReadFlags are passed in ReadRequest.
type ReadFlags uint32
const (
// LockOwner field is valid.
ReadLockOwner ReadFlags = 1 << 1
)
var readFlagNames = []flagName{
{uint32(ReadLockOwner), "ReadLockOwner"},
}
func (fl ReadFlags) String() string {
return flagString(uint32(fl), readFlagNames)
}
type WriteIn struct {
Fh uint64
Offset uint64
Size uint32
WriteFlags uint32
LockOwner uint64
Flags uint32
padding uint32
}
func WriteInSize(p Protocol) uintptr {
switch {
case p.LT(Protocol{7, 9}):
return unsafe.Offsetof(WriteIn{}.LockOwner)
default:
return unsafe.Sizeof(WriteIn{})
}
}
type WriteOut struct {
Size uint32
Padding uint32
}
// The WriteFlags are passed in WriteRequest.
type WriteFlags uint32
const (
WriteCache WriteFlags = 1 << 0
// LockOwner field is valid.
WriteLockOwner WriteFlags = 1 << 1
)
var writeFlagNames = []flagName{
{uint32(WriteCache), "WriteCache"},
{uint32(WriteLockOwner), "WriteLockOwner"},
}
func (fl WriteFlags) String() string {
return flagString(uint32(fl), writeFlagNames)
}
const compatStatfsSize = 48
type StatfsOut struct {
St Kstatfs
}
type FsyncIn struct {
Fh uint64
FsyncFlags uint32
Padding uint32
}
type setxattrInCommon struct {
Size uint32
Flags uint32
}
func (setxattrInCommon) GetPosition() uint32 {
return 0
}
type getxattrInCommon struct {
Size uint32
Padding uint32
}
func (getxattrInCommon) GetPosition() uint32 {
return 0
}
type GetxattrOut struct {
Size uint32
Padding uint32
}
type LkIn struct {
Fh uint64
Owner uint64
Lk fileLock
LkFlags uint32
padding uint32
}
func LkInSize(p Protocol) uintptr {
switch {
case p.LT(Protocol{7, 9}):
return unsafe.Offsetof(LkIn{}.LkFlags)
default:
return unsafe.Sizeof(LkIn{})
}
}
type LkOut struct {
Lk fileLock
}
type AccessIn struct {
Mask uint32
Padding uint32
}
type InitIn struct {
Major uint32
Minor uint32
MaxReadahead uint32
Flags uint32
}
const InitInSize = int(unsafe.Sizeof(InitIn{}))
type InitOut struct {
Major uint32
Minor uint32
MaxReadahead uint32
Flags uint32
Unused uint32
MaxWrite uint32
}
type InterruptIn struct {
Unique uint64
}
type BmapIn struct {
Block uint64
BlockSize uint32
Padding uint32
}
type BmapOut struct {
Block uint64
}
type InHeader struct {
Len uint32
Opcode uint32
Unique uint64
Nodeid uint64
Uid uint32
Gid uint32
Pid uint32
Padding uint32
}
const InHeaderSize = int(unsafe.Sizeof(InHeader{}))
type OutHeader struct {
Len uint32
Error int32
Unique uint64
}
type Dirent struct {
Ino uint64
Off uint64
Namelen uint32
Type uint32
Name [0]byte
}
const DirentSize = 8 + 8 + 4 + 4
const (
NotifyCodePoll int32 = 1
NotifyCodeInvalInode int32 = 2
NotifyCodeInvalEntry int32 = 3
)
type NotifyInvalInodeOut struct {
Ino uint64
Off int64
Len int64
}
type NotifyInvalEntryOut struct {
Parent uint64
Namelen uint32
padding uint32
}

View File

@ -0,0 +1,88 @@
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
}

View File

@ -0,0 +1,62 @@
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
}

View File

@ -0,0 +1,70 @@
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
}

View File

@ -0,0 +1 @@
package fusekernel

View File

@ -0,0 +1,31 @@
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)
}
}

View File

@ -0,0 +1,75 @@
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()
}

View File

@ -0,0 +1,39 @@
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
}

2193
internal/fuseshim/fuse.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,131 @@
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
}

View File

@ -0,0 +1,44 @@
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
}

View File

@ -0,0 +1,116 @@
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
}

View File

@ -0,0 +1,182 @@
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
}
}

View File

@ -0,0 +1,13 @@
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
}
}

View File

@ -0,0 +1,9 @@
package fuseshim
func localVolume(conf *mountConfig) error {
return nil
}
func volumeName(name string) MountOption {
return dummyOption
}

View File

@ -0,0 +1,10 @@
package fuseshim
// for TestMountOptionCommaError
func ForTestSetMountOption(k, v string) MountOption {
fn := func(conf *mountConfig) error {
conf.options[k] = v
return nil
}
return fn
}

View File

@ -0,0 +1,9 @@
package fuseshim
func localVolume(conf *mountConfig) error {
return nil
}
func volumeName(name string) MountOption {
return dummyOption
}

View File

@ -0,0 +1,31 @@
// 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)
}
}

View File

@ -0,0 +1,231 @@
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)
}
}

View File

@ -0,0 +1,6 @@
package fuseshim
// Unmount tries to unmount the filesystem mounted at dir.
func Unmount(dir string) error {
return unmount(dir)
}

View File

@ -0,0 +1,21 @@
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
}

View File

@ -0,0 +1,17 @@
// +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
}

View File

@ -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
}

View File

@ -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() {

View File

@ -24,8 +24,8 @@ import (
"os"
"runtime"
"github.com/jacobsa/bazilfuse"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/internal/fuseshim"
"github.com/jacobsa/fuse/samples/flushfs"
"golang.org/x/net/context"
)
@ -58,11 +58,11 @@ func makeFlushFS() (server fuse.Server, err error) {
var fsyncErr error
if *fFlushError != 0 {
flushErr = bazilfuse.Errno(*fFlushError)
flushErr = fuseshim.Errno(*fFlushError)
}
if *fFsyncError != 0 {
fsyncErr = bazilfuse.Errno(*fFsyncError)
fsyncErr = fuseshim.Errno(*fFsyncError)
}
// Report flushes and fsyncs by writing the contents followed by a newline.

View File

@ -14,10 +14,10 @@
package fuse
import "github.com/jacobsa/bazilfuse"
import "github.com/jacobsa/fuse/internal/fuseshim"
// Attempt to unmount the file system whose mount point is the supplied
// directory.
func Unmount(dir string) error {
return bazilfuse.Unmount(dir)
return fuseshim.Unmount(dir)
}