Copied over the juicy bits of bazilfuse, and made them work.
commit
f4231ede4b
|
@ -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
|
||||
}
|
||||
|
|
16
errors.go
16
errors.go
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package fusekernel
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package fuseshim
|
||||
|
||||
func localVolume(conf *mountConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func volumeName(name string) MountOption {
|
||||
return dummyOption
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package fuseshim
|
||||
|
||||
func localVolume(conf *mountConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func volumeName(name string) MountOption {
|
||||
return dummyOption
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package fuseshim
|
||||
|
||||
// Unmount tries to unmount the filesystem mounted at dir.
|
||||
func Unmount(dir string) error {
|
||||
return unmount(dir)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue