Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
J
jacobsa-fuse
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
jacobsa-fuse
Commits
ed2ae829
Commit
ed2ae829
authored
Jul 24, 2015
by
Aaron Jacobs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Deleted fuseshim.
parent
8ce082c8
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
0 additions
and
3045 deletions
+0
-3045
internal/fuseshim/buffer.go
internal/fuseshim/buffer.go
+0
-39
internal/fuseshim/fuse.go
internal/fuseshim/fuse.go
+0
-2217
internal/fuseshim/mount_darwin.go
internal/fuseshim/mount_darwin.go
+0
-131
internal/fuseshim/mount_freebsd.go
internal/fuseshim/mount_freebsd.go
+0
-44
internal/fuseshim/mount_linux.go
internal/fuseshim/mount_linux.go
+0
-116
internal/fuseshim/options.go
internal/fuseshim/options.go
+0
-182
internal/fuseshim/options_darwin.go
internal/fuseshim/options_darwin.go
+0
-13
internal/fuseshim/options_freebsd.go
internal/fuseshim/options_freebsd.go
+0
-9
internal/fuseshim/options_helper_test.go
internal/fuseshim/options_helper_test.go
+0
-10
internal/fuseshim/options_linux.go
internal/fuseshim/options_linux.go
+0
-9
internal/fuseshim/options_test.go
internal/fuseshim/options_test.go
+0
-231
internal/fuseshim/unmount.go
internal/fuseshim/unmount.go
+0
-6
internal/fuseshim/unmount_linux.go
internal/fuseshim/unmount_linux.go
+0
-21
internal/fuseshim/unmount_std.go
internal/fuseshim/unmount_std.go
+0
-17
No files found.
internal/fuseshim/buffer.go
deleted
100644 → 0
View file @
8ce082c8
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
)
(
buf
Buffer
)
{
const
hdrSize
=
unsafe
.
Sizeof
(
fusekernel
.
OutHeader
{})
buf
=
make
(
Buffer
,
hdrSize
,
hdrSize
+
extra
)
return
}
internal/fuseshim/fuse.go
deleted
100644 → 0
View file @
8ce082c8
// See the file LICENSE for copyright and licensing information.
// Adapted from Plan 9 from User Space's src/cmd/9pfuse/fuse.c,
// which carries this notice:
//
// The files in this directory are subject to the following license.
//
// The author of this software is Russ Cox.
//
// Copyright (c) 2006 Russ Cox
//
// Permission to use, copy, modify, and distribute this software for any
// purpose without fee is hereby granted, provided that this entire notice
// is included in all copies of any software which is or includes a copy
// or modification of this software and in all copies of the supporting
// documentation for such software.
//
// THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTY. IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION OR WARRANTY
// OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS
// FITNESS FOR ANY PARTICULAR PURPOSE.
// Package fuse enables writing FUSE file systems on Linux, OS X, and FreeBSD.
//
// On OS X, it requires OSXFUSE (http://osxfuse.github.com/).
//
// There are two approaches to writing a FUSE file system. The first is to speak
// the low-level message protocol, reading from a Conn using ReadRequest and
// writing using the various Respond methods. This approach is closest to
// the actual interaction with the kernel and can be the simplest one in contexts
// such as protocol translators.
//
// Servers of synthesized file systems tend to share common
// bookkeeping abstracted away by the second approach, which is to
// call fs.Serve to serve the FUSE protocol using an implementation of
// the service methods in the interfaces FS* (file system), Node* (file
// or directory), and Handle* (opened file or directory).
// There are a daunting number of such methods that can be written,
// but few are required.
// The specific methods are described in the documentation for those interfaces.
//
// The hellofs subdirectory contains a simple illustration of the fs.Serve approach.
//
// Service Methods
//
// The required and optional methods for the FS, Node, and Handle interfaces
// have the general form
//
// Op(ctx context.Context, req *OpRequest, resp *OpResponse) error
//
// where Op is the name of a FUSE operation. Op reads request
// parameters from req and writes results to resp. An operation whose
// only result is the error result omits the resp parameter.
//
// Multiple goroutines may call service methods simultaneously; the
// methods being called are responsible for appropriate
// synchronization.
//
// The operation must not hold on to the request or response,
// including any []byte fields such as WriteRequest.Data or
// SetxattrRequest.Xattr.
//
// Errors
//
// Operations can return errors. The FUSE interface can only
// communicate POSIX errno error numbers to file system clients, the
// message is not visible to file system clients. The returned error
// can implement ErrorNumber to control the errno returned. Without
// ErrorNumber, a generic errno (EIO) is returned.
//
// Error messages will be visible in the debug log as part of the
// response.
//
// Interrupted Operations
//
// In some file systems, some operations
// may take an undetermined amount of time. For example, a Read waiting for
// a network message or a matching Write might wait indefinitely. If the request
// is cancelled and no longer needed, the context will be cancelled.
// Blocking operations should select on a receive from ctx.Done() and attempt to
// abort the operation early if the receive succeeds (meaning the channel is closed).
// To indicate that the operation failed because it was aborted, return fuse.EINTR.
//
// If an operation does not block for an indefinite amount of time, supporting
// cancellation is not necessary.
//
// Authentication
//
// All requests types embed a Header, meaning that the method can
// inspect req.Pid, req.Uid, and req.Gid as necessary to implement
// permission checking. The kernel FUSE layer normally prevents other
// users from accessing the FUSE file system (to change this, see
// AllowOther, AllowRoot), but does not enforce access modes (to
// change this, see DefaultPermissions).
//
// Mount Options
//
// Behavior and metadata of the mounted file system can be changed by
// passing MountOption values to Mount.
//
package
fuseshim
import
(
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"sync"
"syscall"
"time"
"unsafe"
"github.com/jacobsa/fuse/internal/fusekernel"
)
// A Conn represents a connection to a mounted FUSE file system.
type
Conn
struct
{
// Ready is closed when the mount is complete or has failed.
Ready
<-
chan
struct
{}
// MountError stores any error from the mount process. Only valid
// after Ready is closed.
MountError
error
// File handle for kernel communication. Only safe to access if
// Rio or Wio is held.
Dev
*
os
.
File
Wio
sync
.
RWMutex
Rio
sync
.
RWMutex
// Protocol version negotiated with InitRequest/InitResponse.
proto
fusekernel
.
Protocol
}
// Mount mounts a new FUSE connection on the named directory
// and returns a connection for reading and writing FUSE messages.
//
// After a successful return, caller must call Close to free
// resources.
//
// Even on successful return, the new mount is not guaranteed to be
// visible until after Conn.Ready is closed. See Conn.MountError for
// possible errors. Incoming requests on Conn must be served to make
// progress.
func
Mount
(
dir
string
,
options
...
MountOption
)
(
*
Conn
,
error
)
{
conf
:=
mountConfig
{
options
:
make
(
map
[
string
]
string
),
}
for
_
,
option
:=
range
options
{
if
err
:=
option
(
&
conf
);
err
!=
nil
{
return
nil
,
err
}
}
ready
:=
make
(
chan
struct
{},
1
)
c
:=
&
Conn
{
Ready
:
ready
,
}
f
,
err
:=
mount
(
dir
,
&
conf
,
ready
,
&
c
.
MountError
)
if
err
!=
nil
{
return
nil
,
err
}
c
.
Dev
=
f
if
err
:=
InitMount
(
c
,
conf
.
maxReadahead
,
conf
.
initFlags
);
err
!=
nil
{
c
.
Close
()
return
nil
,
err
}
return
c
,
nil
}
type
OldVersionError
struct
{
Kernel
fusekernel
.
Protocol
LibraryMin
fusekernel
.
Protocol
}
func
(
e
*
OldVersionError
)
Error
()
string
{
return
fmt
.
Sprintf
(
"kernel FUSE version is too old: %v < %v"
,
e
.
Kernel
,
e
.
LibraryMin
)
}
func
InitMount
(
c
*
Conn
,
maxReadahead
uint32
,
initFlags
fusekernel
.
InitFlags
)
error
{
req
,
err
:=
c
.
ReadRequest
()
if
err
!=
nil
{
if
err
==
io
.
EOF
{
return
fmt
.
Errorf
(
"missing init, got EOF"
)
}
return
err
}
r
,
ok
:=
req
.
(
*
InitRequest
)
if
!
ok
{
return
fmt
.
Errorf
(
"missing init, got: %T"
,
req
)
}
min
:=
fusekernel
.
Protocol
{
fusekernel
.
ProtoVersionMinMajor
,
fusekernel
.
ProtoVersionMinMinor
}
if
r
.
Kernel
.
LT
(
min
)
{
req
.
RespondError
(
Errno
(
syscall
.
EPROTO
))
c
.
Close
()
return
&
OldVersionError
{
Kernel
:
r
.
Kernel
,
LibraryMin
:
min
,
}
}
proto
:=
fusekernel
.
Protocol
{
fusekernel
.
ProtoVersionMaxMajor
,
fusekernel
.
ProtoVersionMaxMinor
}
if
r
.
Kernel
.
LT
(
proto
)
{
// Kernel doesn't support the latest version we have.
proto
=
r
.
Kernel
}
c
.
proto
=
proto
s
:=
&
InitResponse
{
Library
:
proto
,
MaxReadahead
:
maxReadahead
,
MaxWrite
:
maxWrite
,
Flags
:
fusekernel
.
InitBigWrites
|
initFlags
,
}
r
.
Respond
(
s
)
return
nil
}
// A Request represents a single FUSE request received from the kernel.
// Use a type switch to determine the specific kind.
// A request of unrecognized type will have concrete type *Header.
type
Request
interface
{
// Hdr returns the Header associated with this request.
Hdr
()
*
Header
// RespondError responds to the request with the given error.
RespondError
(
error
)
String
()
string
}
// A RequestID identifies an active FUSE request.
type
RequestID
uint64
// A NodeID is a number identifying a directory or file.
// It must be unique among IDs returned in LookupResponses
// that have not yet been forgotten by ForgetRequests.
type
NodeID
uint64
// A HandleID is a number identifying an open directory or file.
// It only needs to be unique while the directory or file is open.
type
HandleID
uint64
// The RootID identifies the root directory of a FUSE file system.
const
RootID
NodeID
=
fusekernel
.
RootID
// A Header describes the basic information sent in every request.
type
Header
struct
{
Conn
*
Conn
`json:"-"`
// connection this request was received on
ID
RequestID
// unique ID for request
Node
NodeID
// file or directory the request is about
Uid
uint32
// user ID of process making request
Gid
uint32
// group ID of process making request
Pid
uint32
// process ID of process making request
// for returning to reqPool
msg
*
Message
}
func
(
h
*
Header
)
String
()
string
{
return
fmt
.
Sprintf
(
"ID=%#x Node=%#x Uid=%d Gid=%d Pid=%d"
,
h
.
ID
,
h
.
Node
,
h
.
Uid
,
h
.
Gid
,
h
.
Pid
)
}
func
(
h
*
Header
)
Hdr
()
*
Header
{
return
h
}
func
(
h
*
Header
)
noResponse
()
{
putMessage
(
h
.
msg
)
}
func
(
h
*
Header
)
respond
(
msg
[]
byte
)
{
out
:=
(
*
fusekernel
.
OutHeader
)(
unsafe
.
Pointer
(
&
msg
[
0
]))
out
.
Unique
=
uint64
(
h
.
ID
)
h
.
Conn
.
respond
(
msg
)
putMessage
(
h
.
msg
)
}
// An ErrorNumber is an error with a specific error number.
//
// Operations may return an error value that implements ErrorNumber to
// control what specific error number (errno) to return.
type
ErrorNumber
interface
{
// Errno returns the the error number (errno) for this error.
Errno
()
Errno
}
const
(
// ENOSYS indicates that the call is not supported.
ENOSYS
=
Errno
(
syscall
.
ENOSYS
)
// ESTALE is used by Serve to respond to violations of the FUSE protocol.
ESTALE
=
Errno
(
syscall
.
ESTALE
)
ENOENT
=
Errno
(
syscall
.
ENOENT
)
EIO
=
Errno
(
syscall
.
EIO
)
EPERM
=
Errno
(
syscall
.
EPERM
)
// EINTR indicates request was interrupted by an InterruptRequest.
// See also fs.Intr.
EINTR
=
Errno
(
syscall
.
EINTR
)
ERANGE
=
Errno
(
syscall
.
ERANGE
)
ENOTSUP
=
Errno
(
syscall
.
ENOTSUP
)
EEXIST
=
Errno
(
syscall
.
EEXIST
)
)
// DefaultErrno is the errno used when error returned does not
// implement ErrorNumber.
const
DefaultErrno
=
EIO
var
errnoNames
=
map
[
Errno
]
string
{
ENOSYS
:
"ENOSYS"
,
ESTALE
:
"ESTALE"
,
ENOENT
:
"ENOENT"
,
EIO
:
"EIO"
,
EPERM
:
"EPERM"
,
EINTR
:
"EINTR"
,
EEXIST
:
"EEXIST"
,
}
// Errno implements Error and ErrorNumber using a syscall.Errno.
type
Errno
syscall
.
Errno
var
_
=
ErrorNumber
(
Errno
(
0
))
var
_
=
error
(
Errno
(
0
))
func
(
e
Errno
)
Errno
()
Errno
{
return
e
}
func
(
e
Errno
)
String
()
string
{
return
syscall
.
Errno
(
e
)
.
Error
()
}
func
(
e
Errno
)
Error
()
string
{
return
syscall
.
Errno
(
e
)
.
Error
()
}
// ErrnoName returns the short non-numeric identifier for this errno.
// For example, "EIO".
func
(
e
Errno
)
ErrnoName
()
string
{
s
:=
errnoNames
[
e
]
if
s
==
""
{
s
=
fmt
.
Sprint
(
e
.
Errno
())
}
return
s
}
func
(
e
Errno
)
MarshalText
()
([]
byte
,
error
)
{
s
:=
e
.
ErrnoName
()
return
[]
byte
(
s
),
nil
}
func
(
h
*
Header
)
RespondError
(
err
error
)
{
errno
:=
DefaultErrno
if
ferr
,
ok
:=
err
.
(
ErrorNumber
);
ok
{
errno
=
ferr
.
Errno
()
}
// FUSE uses negative errors!
// TODO: File bug report against OSXFUSE: positive error causes kernel panic.
buf
:=
NewBuffer
(
0
)
hOut
:=
(
*
fusekernel
.
OutHeader
)(
unsafe
.
Pointer
(
&
buf
[
0
]))
hOut
.
Error
=
-
int32
(
errno
)
h
.
respond
(
buf
)
}
// All requests read from the kernel, without data, are shorter than
// this.
var
maxRequestSize
=
syscall
.
Getpagesize
()
var
bufSize
=
maxRequestSize
+
maxWrite
// reqPool is a pool of messages.
//
// Lifetime of a logical message is from getMessage to putMessage.
// getMessage is called by ReadRequest. putMessage is called by
// Conn.ReadRequest, Request.Respond, or Request.RespondError.
//
// Messages in the pool are guaranteed to have conn and off zeroed,
// buf allocated and len==bufSize, and Hdr set.
var
reqPool
struct
{
Mu
sync
.
Mutex
Freelist
[]
*
Message
}
func
allocMessage
()
*
Message
{
m
:=
&
Message
{
buf
:
make
([]
byte
,
bufSize
)}
m
.
Hdr
=
(
*
fusekernel
.
InHeader
)(
unsafe
.
Pointer
(
&
m
.
buf
[
0
]))
return
m
}
func
getMessage
(
c
*
Conn
)
(
m
*
Message
)
{
reqPool
.
Mu
.
Lock
()
l
:=
len
(
reqPool
.
Freelist
)
if
l
!=
0
{
m
=
reqPool
.
Freelist
[
l
-
1
]
reqPool
.
Freelist
=
reqPool
.
Freelist
[
:
l
-
1
]
}
reqPool
.
Mu
.
Unlock
()
if
m
==
nil
{
m
=
allocMessage
()
}
m
.
conn
=
c
return
m
}
func
putMessage
(
m
*
Message
)
{
m
.
buf
=
m
.
buf
[
:
bufSize
]
m
.
conn
=
nil
m
.
off
=
0
reqPool
.
Mu
.
Lock
()
reqPool
.
Freelist
=
append
(
reqPool
.
Freelist
,
m
)
reqPool
.
Mu
.
Unlock
()
}
// a message represents the bytes of a single FUSE message
type
Message
struct
{
conn
*
Conn
buf
[]
byte
// all bytes
Hdr
*
fusekernel
.
InHeader
// header
off
int
// offset for reading additional fields
}
func
(
m
*
Message
)
Len
()
uintptr
{
return
uintptr
(
len
(
m
.
buf
)
-
m
.
off
)
}
func
(
m
*
Message
)
Data
()
(
p
unsafe
.
Pointer
)
{
if
m
.
off
<
len
(
m
.
buf
)
{
p
=
unsafe
.
Pointer
(
&
m
.
buf
[
m
.
off
])
}
return
p
}
func
(
m
*
Message
)
Bytes
()
[]
byte
{
return
m
.
buf
[
m
.
off
:
]
}
func
(
m
*
Message
)
Header
()
Header
{
h
:=
m
.
Hdr
return
Header
{
Conn
:
m
.
conn
,
ID
:
RequestID
(
h
.
Unique
),
Node
:
NodeID
(
h
.
Nodeid
),
Uid
:
h
.
Uid
,
Gid
:
h
.
Gid
,
Pid
:
h
.
Pid
,
msg
:
m
,
}
}
// Destroy the message, releasing its resources. The message must not be used
// further.
func
(
m
*
Message
)
Destroy
()
{
putMessage
(
m
)
}
// FileMode returns a Go os.FileMode from a Unix mode.
func
FileMode
(
unixMode
uint32
)
os
.
FileMode
{
mode
:=
os
.
FileMode
(
unixMode
&
0777
)
switch
unixMode
&
syscall
.
S_IFMT
{
case
syscall
.
S_IFREG
:
// nothing
case
syscall
.
S_IFDIR
:
mode
|=
os
.
ModeDir
case
syscall
.
S_IFCHR
:
mode
|=
os
.
ModeCharDevice
|
os
.
ModeDevice
case
syscall
.
S_IFBLK
:
mode
|=
os
.
ModeDevice
case
syscall
.
S_IFIFO
:
mode
|=
os
.
ModeNamedPipe
case
syscall
.
S_IFLNK
:
mode
|=
os
.
ModeSymlink
case
syscall
.
S_IFSOCK
:
mode
|=
os
.
ModeSocket
default
:
// no idea
mode
|=
os
.
ModeDevice
}
if
unixMode
&
syscall
.
S_ISUID
!=
0
{
mode
|=
os
.
ModeSetuid
}
if
unixMode
&
syscall
.
S_ISGID
!=
0
{
mode
|=
os
.
ModeSetgid
}
return
mode
}
type
noOpcode
struct
{
Opcode
uint32
}
func
(
m
noOpcode
)
String
()
string
{
return
fmt
.
Sprintf
(
"No opcode %v"
,
m
.
Opcode
)
}
type
malformedMessage
struct
{
}
func
(
malformedMessage
)
String
()
string
{
return
"malformed message"
}
// Close closes the FUSE connection.
func
(
c
*
Conn
)
Close
()
error
{
c
.
Wio
.
Lock
()
defer
c
.
Wio
.
Unlock
()
c
.
Rio
.
Lock
()
defer
c
.
Rio
.
Unlock
()
return
c
.
Dev
.
Close
()
}
// caller must hold Wio or Rio
func
(
c
*
Conn
)
fd
()
int
{
return
int
(
c
.
Dev
.
Fd
())
}
func
(
c
*
Conn
)
Protocol
()
fusekernel
.
Protocol
{
return
c
.
proto
}
// Read and sanity check a message from the kernel. Return io.EOF when the
// kernel has hung up. The offset will point to the limit of the header.
//
// The message must later be disposed of by calling m.Destroy.
func
(
c
*
Conn
)
ReadMessage
()
(
m
*
Message
,
err
error
)
{
m
=
getMessage
(
c
)
loop
:
c
.
Rio
.
RLock
()
n
,
err
:=
syscall
.
Read
(
c
.
fd
(),
m
.
buf
)
c
.
Rio
.
RUnlock
()
if
err
==
syscall
.
EINTR
{
// OSXFUSE sends EINTR to userspace when a request interrupt
// completed before it got sent to userspace?
goto
loop
}
if
err
!=
nil
&&
err
!=
syscall
.
ENODEV
{
m
.
Destroy
()
return
nil
,
err
}
if
n
<=
0
{
m
.
Destroy
()
return
nil
,
io
.
EOF
}
m
.
buf
=
m
.
buf
[
:
n
]
if
n
<
fusekernel
.
InHeaderSize
{
m
.
Destroy
()
return
nil
,
errors
.
New
(
"fuse: message too short"
)
}
// FreeBSD FUSE sends a short length in the header
// for FUSE_INIT even though the actual read length is correct.
if
n
==
fusekernel
.
InHeaderSize
+
fusekernel
.
InitInSize
&&
m
.
Hdr
.
Opcode
==
fusekernel
.
OpInit
&&
m
.
Hdr
.
Len
<
uint32
(
n
)
{
m
.
Hdr
.
Len
=
uint32
(
n
)
}
// OSXFUSE sometimes sends the wrong m.Hdr.Len in a FUSE_WRITE message.
if
m
.
Hdr
.
Len
<
uint32
(
n
)
&&
m
.
Hdr
.
Len
>=
uint32
(
unsafe
.
Sizeof
(
fusekernel
.
WriteIn
{}))
&&
m
.
Hdr
.
Opcode
==
fusekernel
.
OpWrite
{
m
.
Hdr
.
Len
=
uint32
(
n
)
}
if
m
.
Hdr
.
Len
!=
uint32
(
n
)
{
// prepare error message before returning m to pool
err
:=
fmt
.
Errorf
(
"fuse: read %d opcode %d but expected %d"
,
n
,
m
.
Hdr
.
Opcode
,
m
.
Hdr
.
Len
)
m
.
Destroy
()
return
nil
,
err
}
m
.
off
=
fusekernel
.
InHeaderSize
return
}
// ReadRequest returns the next FUSE request from the kernel.
//
// Caller must call either Request.Respond or Request.RespondError in
// a reasonable time. Caller must not retain Request after that call.
func
(
c
*
Conn
)
ReadRequest
()
(
Request
,
error
)
{
// Read a message.
m
,
err
:=
c
.
ReadMessage
()
if
err
!=
nil
{
return
nil
,
err
}
// Convert to data structures.
// Do not trust kernel to hand us well-formed data.
var
req
Request
switch
m
.
Hdr
.
Opcode
{
default
:
goto
unrecognized
case
fusekernel
.
OpLookup
:
buf
:=
m
.
Bytes
()
n
:=
len
(
buf
)
if
n
==
0
||
buf
[
n
-
1
]
!=
'\x00'
{
goto
corrupt
}
req
=
&
LookupRequest
{
Header
:
m
.
Header
(),
Name
:
string
(
buf
[
:
n
-
1
]),
}
case
fusekernel
.
OpForget
:
in
:=
(
*
fusekernel
.
ForgetIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
req
=
&
ForgetRequest
{
Header
:
m
.
Header
(),
N
:
in
.
Nlookup
,
}
case
fusekernel
.
OpGetattr
:
switch
{
case
c
.
proto
.
LT
(
fusekernel
.
Protocol
{
7
,
9
})
:
req
=
&
GetattrRequest
{
Header
:
m
.
Header
(),
}
default
:
in
:=
(
*
fusekernel
.
GetattrIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
req
=
&
GetattrRequest
{
Header
:
m
.
Header
(),
Flags
:
fusekernel
.
GetattrFlags
(
in
.
GetattrFlags
),
Handle
:
HandleID
(
in
.
Fh
),
}
}
case
fusekernel
.
OpSetattr
:
in
:=
(
*
fusekernel
.
SetattrIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
req
=
&
SetattrRequest
{
Header
:
m
.
Header
(),
Valid
:
fusekernel
.
SetattrValid
(
in
.
Valid
),
Handle
:
HandleID
(
in
.
Fh
),
Size
:
in
.
Size
,
Atime
:
time
.
Unix
(
int64
(
in
.
Atime
),
int64
(
in
.
AtimeNsec
)),
Mtime
:
time
.
Unix
(
int64
(
in
.
Mtime
),
int64
(
in
.
MtimeNsec
)),
Mode
:
FileMode
(
in
.
Mode
),
Uid
:
in
.
Uid
,
Gid
:
in
.
Gid
,
Bkuptime
:
in
.
BkupTime
(),
Chgtime
:
in
.
Chgtime
(),
Flags
:
in
.
Flags
(),
}
case
fusekernel
.
OpReadlink
:
if
len
(
m
.
Bytes
())
>
0
{
goto
corrupt
}
req
=
&
ReadlinkRequest
{
Header
:
m
.
Header
(),
}
case
fusekernel
.
OpSymlink
:
// m.Bytes() is "newName\0target\0"
names
:=
m
.
Bytes
()
if
len
(
names
)
==
0
||
names
[
len
(
names
)
-
1
]
!=
0
{
goto
corrupt
}
i
:=
bytes
.
IndexByte
(
names
,
'\x00'
)
if
i
<
0
{
goto
corrupt
}
newName
,
target
:=
names
[
0
:
i
],
names
[
i
+
1
:
len
(
names
)
-
1
]
req
=
&
SymlinkRequest
{
Header
:
m
.
Header
(),
NewName
:
string
(
newName
),
Target
:
string
(
target
),
}
case
fusekernel
.
OpLink
:
in
:=
(
*
fusekernel
.
LinkIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
newName
:=
m
.
Bytes
()[
unsafe
.
Sizeof
(
*
in
)
:
]
if
len
(
newName
)
<
2
||
newName
[
len
(
newName
)
-
1
]
!=
0
{
goto
corrupt
}
newName
=
newName
[
:
len
(
newName
)
-
1
]
req
=
&
LinkRequest
{
Header
:
m
.
Header
(),
OldNode
:
NodeID
(
in
.
Oldnodeid
),
NewName
:
string
(
newName
),
}
case
fusekernel
.
OpMknod
:
size
:=
fusekernel
.
MknodInSize
(
c
.
proto
)
if
m
.
Len
()
<
size
{
goto
corrupt
}
in
:=
(
*
fusekernel
.
MknodIn
)(
m
.
Data
())
name
:=
m
.
Bytes
()[
size
:
]
if
len
(
name
)
<
2
||
name
[
len
(
name
)
-
1
]
!=
'\x00'
{
goto
corrupt
}
name
=
name
[
:
len
(
name
)
-
1
]
r
:=
&
MknodRequest
{
Header
:
m
.
Header
(),
Mode
:
FileMode
(
in
.
Mode
),
Rdev
:
in
.
Rdev
,
Name
:
string
(
name
),
}
if
c
.
proto
.
GE
(
fusekernel
.
Protocol
{
7
,
12
})
{
r
.
Umask
=
FileMode
(
in
.
Umask
)
&
os
.
ModePerm
}
req
=
r
case
fusekernel
.
OpMkdir
:
size
:=
fusekernel
.
MkdirInSize
(
c
.
proto
)
if
m
.
Len
()
<
size
{
goto
corrupt
}
in
:=
(
*
fusekernel
.
MkdirIn
)(
m
.
Data
())
name
:=
m
.
Bytes
()[
size
:
]
i
:=
bytes
.
IndexByte
(
name
,
'\x00'
)
if
i
<
0
{
goto
corrupt
}
r
:=
&
MkdirRequest
{
Header
:
m
.
Header
(),
Name
:
string
(
name
[
:
i
]),
// observed on Linux: mkdirIn.Mode & syscall.S_IFMT == 0,
// and this causes FileMode to go into it's "no idea"
// code branch; enforce type to directory
Mode
:
FileMode
((
in
.
Mode
&^
syscall
.
S_IFMT
)
|
syscall
.
S_IFDIR
),
}
if
c
.
proto
.
GE
(
fusekernel
.
Protocol
{
7
,
12
})
{
r
.
Umask
=
FileMode
(
in
.
Umask
)
&
os
.
ModePerm
}
req
=
r
case
fusekernel
.
OpUnlink
,
fusekernel
.
OpRmdir
:
buf
:=
m
.
Bytes
()
n
:=
len
(
buf
)
if
n
==
0
||
buf
[
n
-
1
]
!=
'\x00'
{
goto
corrupt
}
req
=
&
RemoveRequest
{
Header
:
m
.
Header
(),
Name
:
string
(
buf
[
:
n
-
1
]),
Dir
:
m
.
Hdr
.
Opcode
==
fusekernel
.
OpRmdir
,
}
case
fusekernel
.
OpRename
:
in
:=
(
*
fusekernel
.
RenameIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
newDirNodeID
:=
NodeID
(
in
.
Newdir
)
oldNew
:=
m
.
Bytes
()[
unsafe
.
Sizeof
(
*
in
)
:
]
// oldNew should be "old\x00new\x00"
if
len
(
oldNew
)
<
4
{
goto
corrupt
}
if
oldNew
[
len
(
oldNew
)
-
1
]
!=
'\x00'
{
goto
corrupt
}
i
:=
bytes
.
IndexByte
(
oldNew
,
'\x00'
)
if
i
<
0
{
goto
corrupt
}
oldName
,
newName
:=
string
(
oldNew
[
:
i
]),
string
(
oldNew
[
i
+
1
:
len
(
oldNew
)
-
1
])
req
=
&
RenameRequest
{
Header
:
m
.
Header
(),
NewDir
:
newDirNodeID
,
OldName
:
oldName
,
NewName
:
newName
,
}
case
fusekernel
.
OpOpendir
,
fusekernel
.
OpOpen
:
in
:=
(
*
fusekernel
.
OpenIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
req
=
&
OpenRequest
{
Header
:
m
.
Header
(),
Dir
:
m
.
Hdr
.
Opcode
==
fusekernel
.
OpOpendir
,
Flags
:
fusekernel
.
OpenFlags
(
in
.
Flags
),
}
case
fusekernel
.
OpRead
,
fusekernel
.
OpReaddir
:
in
:=
(
*
fusekernel
.
ReadIn
)(
m
.
Data
())
if
m
.
Len
()
<
fusekernel
.
ReadInSize
(
c
.
proto
)
{
goto
corrupt
}
r
:=
&
ReadRequest
{
Header
:
m
.
Header
(),
Dir
:
m
.
Hdr
.
Opcode
==
fusekernel
.
OpReaddir
,
Handle
:
HandleID
(
in
.
Fh
),
Offset
:
int64
(
in
.
Offset
),
Size
:
int
(
in
.
Size
),
}
if
c
.
proto
.
GE
(
fusekernel
.
Protocol
{
7
,
9
})
{
r
.
Flags
=
fusekernel
.
ReadFlags
(
in
.
ReadFlags
)
r
.
LockOwner
=
in
.
LockOwner
r
.
FileFlags
=
fusekernel
.
OpenFlags
(
in
.
Flags
)
}
req
=
r
case
fusekernel
.
OpWrite
:
in
:=
(
*
fusekernel
.
WriteIn
)(
m
.
Data
())
if
m
.
Len
()
<
fusekernel
.
WriteInSize
(
c
.
proto
)
{
goto
corrupt
}
r
:=
&
WriteRequest
{
Header
:
m
.
Header
(),
Handle
:
HandleID
(
in
.
Fh
),
Offset
:
int64
(
in
.
Offset
),
Flags
:
fusekernel
.
WriteFlags
(
in
.
WriteFlags
),
}
if
c
.
proto
.
GE
(
fusekernel
.
Protocol
{
7
,
9
})
{
r
.
LockOwner
=
in
.
LockOwner
r
.
FileFlags
=
fusekernel
.
OpenFlags
(
in
.
Flags
)
}
buf
:=
m
.
Bytes
()[
fusekernel
.
WriteInSize
(
c
.
proto
)
:
]
if
uint32
(
len
(
buf
))
<
in
.
Size
{
goto
corrupt
}
r
.
Data
=
buf
req
=
r
case
fusekernel
.
OpStatfs
:
req
=
&
StatfsRequest
{
Header
:
m
.
Header
(),
}
case
fusekernel
.
OpRelease
,
fusekernel
.
OpReleasedir
:
in
:=
(
*
fusekernel
.
ReleaseIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
req
=
&
ReleaseRequest
{
Header
:
m
.
Header
(),
Dir
:
m
.
Hdr
.
Opcode
==
fusekernel
.
OpReleasedir
,
Handle
:
HandleID
(
in
.
Fh
),
Flags
:
fusekernel
.
OpenFlags
(
in
.
Flags
),
ReleaseFlags
:
fusekernel
.
ReleaseFlags
(
in
.
ReleaseFlags
),
LockOwner
:
in
.
LockOwner
,
}
case
fusekernel
.
OpFsync
,
fusekernel
.
OpFsyncdir
:
in
:=
(
*
fusekernel
.
FsyncIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
req
=
&
FsyncRequest
{
Dir
:
m
.
Hdr
.
Opcode
==
fusekernel
.
OpFsyncdir
,
Header
:
m
.
Header
(),
Handle
:
HandleID
(
in
.
Fh
),
Flags
:
in
.
FsyncFlags
,
}
case
fusekernel
.
OpSetxattr
:
in
:=
(
*
fusekernel
.
SetxattrIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
m
.
off
+=
int
(
unsafe
.
Sizeof
(
*
in
))
name
:=
m
.
Bytes
()
i
:=
bytes
.
IndexByte
(
name
,
'\x00'
)
if
i
<
0
{
goto
corrupt
}
xattr
:=
name
[
i
+
1
:
]
if
uint32
(
len
(
xattr
))
<
in
.
Size
{
goto
corrupt
}
xattr
=
xattr
[
:
in
.
Size
]
req
=
&
SetxattrRequest
{
Header
:
m
.
Header
(),
Flags
:
in
.
Flags
,
Position
:
in
.
GetPosition
(),
Name
:
string
(
name
[
:
i
]),
Xattr
:
xattr
,
}
case
fusekernel
.
OpGetxattr
:
in
:=
(
*
fusekernel
.
GetxattrIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
name
:=
m
.
Bytes
()[
unsafe
.
Sizeof
(
*
in
)
:
]
i
:=
bytes
.
IndexByte
(
name
,
'\x00'
)
if
i
<
0
{
goto
corrupt
}
req
=
&
GetxattrRequest
{
Header
:
m
.
Header
(),
Name
:
string
(
name
[
:
i
]),
Size
:
in
.
Size
,
Position
:
in
.
GetPosition
(),
}
case
fusekernel
.
OpListxattr
:
in
:=
(
*
fusekernel
.
GetxattrIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
req
=
&
ListxattrRequest
{
Header
:
m
.
Header
(),
Size
:
in
.
Size
,
Position
:
in
.
GetPosition
(),
}
case
fusekernel
.
OpRemovexattr
:
buf
:=
m
.
Bytes
()
n
:=
len
(
buf
)
if
n
==
0
||
buf
[
n
-
1
]
!=
'\x00'
{
goto
corrupt
}
req
=
&
RemovexattrRequest
{
Header
:
m
.
Header
(),
Name
:
string
(
buf
[
:
n
-
1
]),
}
case
fusekernel
.
OpFlush
:
in
:=
(
*
fusekernel
.
FlushIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
req
=
&
FlushRequest
{
Header
:
m
.
Header
(),
Handle
:
HandleID
(
in
.
Fh
),
Flags
:
in
.
FlushFlags
,
LockOwner
:
in
.
LockOwner
,
}
case
fusekernel
.
OpInit
:
in
:=
(
*
fusekernel
.
InitIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
req
=
&
InitRequest
{
Header
:
m
.
Header
(),
Kernel
:
fusekernel
.
Protocol
{
in
.
Major
,
in
.
Minor
},
MaxReadahead
:
in
.
MaxReadahead
,
Flags
:
fusekernel
.
InitFlags
(
in
.
Flags
),
}
case
fusekernel
.
OpGetlk
:
panic
(
"fusekernel.OpGetlk"
)
case
fusekernel
.
OpSetlk
:
panic
(
"fusekernel.OpSetlk"
)
case
fusekernel
.
OpSetlkw
:
panic
(
"fusekernel.OpSetlkw"
)
case
fusekernel
.
OpAccess
:
in
:=
(
*
fusekernel
.
AccessIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
req
=
&
AccessRequest
{
Header
:
m
.
Header
(),
Mask
:
in
.
Mask
,
}
case
fusekernel
.
OpCreate
:
size
:=
fusekernel
.
CreateInSize
(
c
.
proto
)
if
m
.
Len
()
<
size
{
goto
corrupt
}
in
:=
(
*
fusekernel
.
CreateIn
)(
m
.
Data
())
name
:=
m
.
Bytes
()[
size
:
]
i
:=
bytes
.
IndexByte
(
name
,
'\x00'
)
if
i
<
0
{
goto
corrupt
}
r
:=
&
CreateRequest
{
Header
:
m
.
Header
(),
Flags
:
fusekernel
.
OpenFlags
(
in
.
Flags
),
Mode
:
FileMode
(
in
.
Mode
),
Name
:
string
(
name
[
:
i
]),
}
if
c
.
proto
.
GE
(
fusekernel
.
Protocol
{
7
,
12
})
{
r
.
Umask
=
FileMode
(
in
.
Umask
)
&
os
.
ModePerm
}
req
=
r
case
fusekernel
.
OpInterrupt
:
in
:=
(
*
fusekernel
.
InterruptIn
)(
m
.
Data
())
if
m
.
Len
()
<
unsafe
.
Sizeof
(
*
in
)
{
goto
corrupt
}
req
=
&
InterruptRequest
{
Header
:
m
.
Header
(),
IntrID
:
RequestID
(
in
.
Unique
),
}
case
fusekernel
.
OpBmap
:
panic
(
"fusekernel.OpBmap"
)
case
fusekernel
.
OpDestroy
:
req
=
&
DestroyRequest
{
Header
:
m
.
Header
(),
}
// OS X
case
fusekernel
.
OpSetvolname
:
panic
(
"fusekernel.OpSetvolname"
)
case
fusekernel
.
OpGetxtimes
:
panic
(
"fusekernel.OpGetxtimes"
)
case
fusekernel
.
OpExchange
:
panic
(
"fusekernel.OpExchange"
)
}
return
req
,
nil
corrupt
:
m
.
Destroy
()
return
nil
,
fmt
.
Errorf
(
"fuse: malformed message"
)
unrecognized
:
// Unrecognized message.
// Assume higher-level code will send a "no idea what you mean" error.
h
:=
m
.
Header
()
return
&
h
,
nil
}
type
bugShortKernelWrite
struct
{
Written
int64
Length
int64
Error
string
Stack
string
}
func
(
b
bugShortKernelWrite
)
String
()
string
{
return
fmt
.
Sprintf
(
"short kernel write: written=%d/%d error=%q stack=
\n
%s"
,
b
.
Written
,
b
.
Length
,
b
.
Error
,
b
.
Stack
)
}
type
bugKernelWriteError
struct
{
Error
string
Stack
string
}
func
(
b
bugKernelWriteError
)
String
()
string
{
return
fmt
.
Sprintf
(
"kernel write error: error=%q stack=
\n
%s"
,
b
.
Error
,
b
.
Stack
)
}
// safe to call even with nil error
func
errorString
(
err
error
)
string
{
if
err
==
nil
{
return
""
}
return
err
.
Error
()
}
func
(
c
*
Conn
)
writeToKernel
(
msg
[]
byte
)
error
{
out
:=
(
*
fusekernel
.
OutHeader
)(
unsafe
.
Pointer
(
&
msg
[
0
]))
out
.
Len
=
uint32
(
len
(
msg
))
return
c
.
WriteToKernel
(
msg
)
}
func
(
c
*
Conn
)
WriteToKernel
(
msg
[]
byte
)
error
{
c
.
Wio
.
RLock
()
defer
c
.
Wio
.
RUnlock
()
_
,
err
:=
syscall
.
Write
(
c
.
fd
(),
msg
)
return
err
}
func
(
c
*
Conn
)
respond
(
msg
[]
byte
)
{
c
.
writeToKernel
(
msg
)
}
type
notCachedError
struct
{}
func
(
notCachedError
)
Error
()
string
{
return
"node not cached"
}
var
_
ErrorNumber
=
notCachedError
{}
func
(
notCachedError
)
Errno
()
Errno
{
// Behave just like if the original syscall.ENOENT had been passed
// straight through.
return
ENOENT
}
var
(
ErrNotCached
=
notCachedError
{}
)
// sendInvalidate sends an invalidate notification to kernel.
//
// A returned ENOENT is translated to a friendlier error.
func
(
c
*
Conn
)
sendInvalidate
(
msg
[]
byte
)
error
{
switch
err
:=
c
.
writeToKernel
(
msg
);
err
{
case
syscall
.
ENOENT
:
return
ErrNotCached
default
:
return
err
}
}
// InvalidateNode invalidates the kernel cache of the attributes and a
// range of the data of a node.
//
// Giving offset 0 and size -1 means all data. To invalidate just the
// attributes, give offset 0 and size 0.
//
// Returns ErrNotCached if the kernel is not currently caching the
// node.
func
(
c
*
Conn
)
InvalidateNode
(
nodeID
NodeID
,
off
int64
,
size
int64
)
error
{
buf
:=
NewBuffer
(
unsafe
.
Sizeof
(
fusekernel
.
NotifyInvalInodeOut
{}))
h
:=
(
*
fusekernel
.
OutHeader
)(
unsafe
.
Pointer
(
&
buf
[
0
]))
// h.Unique is 0
h
.
Error
=
fusekernel
.
NotifyCodeInvalInode
out
:=
(
*
fusekernel
.
NotifyInvalInodeOut
)(
buf
.
Alloc
(
unsafe
.
Sizeof
(
fusekernel
.
NotifyInvalInodeOut
{})))
out
.
Ino
=
uint64
(
nodeID
)
out
.
Off
=
off
out
.
Len
=
size
return
c
.
sendInvalidate
(
buf
)
}
// InvalidateEntry invalidates the kernel cache of the directory entry
// identified by parent directory node ID and entry basename.
//
// Kernel may or may not cache directory listings. To invalidate
// those, use InvalidateNode to invalidate all of the data for a
// directory. (As of 2015-06, Linux FUSE does not cache directory
// listings.)
//
// Returns ErrNotCached if the kernel is not currently caching the
// node.
func
(
c
*
Conn
)
InvalidateEntry
(
parent
NodeID
,
name
string
)
error
{
const
maxUint32
=
^
uint32
(
0
)
if
uint64
(
len
(
name
))
>
uint64
(
maxUint32
)
{
// very unlikely, but we don't want to silently truncate
return
syscall
.
ENAMETOOLONG
}
buf
:=
NewBuffer
(
unsafe
.
Sizeof
(
fusekernel
.
NotifyInvalEntryOut
{})
+
uintptr
(
len
(
name
))
+
1
)
h
:=
(
*
fusekernel
.
OutHeader
)(
unsafe
.
Pointer
(
&
buf
[
0
]))
// h.Unique is 0
h
.
Error
=
fusekernel
.
NotifyCodeInvalEntry
out
:=
(
*
fusekernel
.
NotifyInvalEntryOut
)(
buf
.
Alloc
(
unsafe
.
Sizeof
(
fusekernel
.
NotifyInvalEntryOut
{})))
out
.
Parent
=
uint64
(
parent
)
out
.
Namelen
=
uint32
(
len
(
name
))
buf
=
append
(
buf
,
name
...
)
buf
=
append
(
buf
,
'\x00'
)
return
c
.
sendInvalidate
(
buf
)
}
// An InitRequest is the first request sent on a FUSE file system.
type
InitRequest
struct
{
Header
`json:"-"`
Kernel
fusekernel
.
Protocol
// Maximum readahead in bytes that the kernel plans to use.
MaxReadahead
uint32
Flags
fusekernel
.
InitFlags
}
var
_
=
Request
(
&
InitRequest
{})
func
(
r
*
InitRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Init [%s] %v ra=%d fl=%v"
,
&
r
.
Header
,
r
.
Kernel
,
r
.
MaxReadahead
,
r
.
Flags
)
}
// An InitResponse is the response to an InitRequest.
type
InitResponse
struct
{
Library
fusekernel
.
Protocol
// Maximum readahead in bytes that the kernel can use. Ignored if
// greater than InitRequest.MaxReadahead.
MaxReadahead
uint32
Flags
fusekernel
.
InitFlags
// Maximum size of a single write operation.
// Linux enforces a minimum of 4 KiB.
MaxWrite
uint32
}
func
(
r
*
InitResponse
)
String
()
string
{
return
fmt
.
Sprintf
(
"Init %+v"
,
*
r
)
}
// Respond replies to the request with the given response.
func
(
r
*
InitRequest
)
Respond
(
resp
*
InitResponse
)
{
buf
:=
NewBuffer
(
unsafe
.
Sizeof
(
fusekernel
.
InitOut
{}))
out
:=
(
*
fusekernel
.
InitOut
)(
buf
.
Alloc
(
unsafe
.
Sizeof
(
fusekernel
.
InitOut
{})))
out
.
Major
=
resp
.
Library
.
Major
out
.
Minor
=
resp
.
Library
.
Minor
out
.
MaxReadahead
=
resp
.
MaxReadahead
out
.
Flags
=
uint32
(
resp
.
Flags
)
out
.
MaxWrite
=
resp
.
MaxWrite
// MaxWrite larger than our receive buffer would just lead to
// errors on large writes.
if
out
.
MaxWrite
>
maxWrite
{
out
.
MaxWrite
=
maxWrite
}
r
.
respond
(
buf
)
}
// A StatfsRequest requests information about the mounted file system.
type
StatfsRequest
struct
{
Header
`json:"-"`
}
var
_
=
Request
(
&
StatfsRequest
{})
func
(
r
*
StatfsRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Statfs [%s]"
,
&
r
.
Header
)
}
// Respond replies to the request with the given response.
func
(
r
*
StatfsRequest
)
Respond
(
resp
*
StatfsResponse
)
{
buf
:=
NewBuffer
(
unsafe
.
Sizeof
(
fusekernel
.
StatfsOut
{}))
out
:=
(
*
fusekernel
.
StatfsOut
)(
buf
.
Alloc
(
unsafe
.
Sizeof
(
fusekernel
.
StatfsOut
{})))
out
.
St
=
fusekernel
.
Kstatfs
{
Blocks
:
resp
.
Blocks
,
Bfree
:
resp
.
Bfree
,
Bavail
:
resp
.
Bavail
,
Files
:
resp
.
Files
,
Bsize
:
resp
.
Bsize
,
Namelen
:
resp
.
Namelen
,
Frsize
:
resp
.
Frsize
,
}
r
.
respond
(
buf
)
}
// A StatfsResponse is the response to a StatfsRequest.
type
StatfsResponse
struct
{
Blocks
uint64
// Total data blocks in file system.
Bfree
uint64
// Free blocks in file system.
Bavail
uint64
// Free blocks in file system if you're not root.
Files
uint64
// Total files in file system.
Ffree
uint64
// Free files in file system.
Bsize
uint32
// Block size
Namelen
uint32
// Maximum file name length?
Frsize
uint32
// Fragment size, smallest addressable data size in the file system.
}
func
(
r
*
StatfsResponse
)
String
()
string
{
return
fmt
.
Sprintf
(
"Statfs %+v"
,
*
r
)
}
// An AccessRequest asks whether the file can be accessed
// for the purpose specified by the mask.
type
AccessRequest
struct
{
Header
`json:"-"`
Mask
uint32
}
var
_
=
Request
(
&
AccessRequest
{})
func
(
r
*
AccessRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Access [%s] mask=%#x"
,
&
r
.
Header
,
r
.
Mask
)
}
// Respond replies to the request indicating that access is allowed.
// To deny access, use RespondError.
func
(
r
*
AccessRequest
)
Respond
()
{
buf
:=
NewBuffer
(
0
)
r
.
respond
(
buf
)
}
// An Attr is the metadata for a single file or directory.
type
Attr
struct
{
Valid
time
.
Duration
// how long Attr can be cached
Inode
uint64
// inode number
Size
uint64
// size in bytes
Blocks
uint64
// size in 512-byte units
Atime
time
.
Time
// time of last access
Mtime
time
.
Time
// time of last modification
Ctime
time
.
Time
// time of last inode change
Crtime
time
.
Time
// time of creation (OS X only)
Mode
os
.
FileMode
// file mode
Nlink
uint32
// number of links
Uid
uint32
// owner uid
Gid
uint32
// group gid
Rdev
uint32
// device numbers
Flags
uint32
// chflags(2) flags (OS X only)
BlockSize
uint32
// preferred blocksize for filesystem I/O
}
func
unix
(
t
time
.
Time
)
(
sec
uint64
,
nsec
uint32
)
{
nano
:=
t
.
UnixNano
()
sec
=
uint64
(
nano
/
1e9
)
nsec
=
uint32
(
nano
%
1e9
)
return
}
func
(
a
*
Attr
)
attr
(
out
*
fusekernel
.
Attr
,
proto
fusekernel
.
Protocol
)
{
out
.
Ino
=
a
.
Inode
out
.
Size
=
a
.
Size
out
.
Blocks
=
a
.
Blocks
out
.
Atime
,
out
.
AtimeNsec
=
unix
(
a
.
Atime
)
out
.
Mtime
,
out
.
MtimeNsec
=
unix
(
a
.
Mtime
)
out
.
Ctime
,
out
.
CtimeNsec
=
unix
(
a
.
Ctime
)
out
.
SetCrtime
(
unix
(
a
.
Crtime
))
out
.
Mode
=
uint32
(
a
.
Mode
)
&
0777
switch
{
default
:
out
.
Mode
|=
syscall
.
S_IFREG
case
a
.
Mode
&
os
.
ModeDir
!=
0
:
out
.
Mode
|=
syscall
.
S_IFDIR
case
a
.
Mode
&
os
.
ModeDevice
!=
0
:
if
a
.
Mode
&
os
.
ModeCharDevice
!=
0
{
out
.
Mode
|=
syscall
.
S_IFCHR
}
else
{
out
.
Mode
|=
syscall
.
S_IFBLK
}
case
a
.
Mode
&
os
.
ModeNamedPipe
!=
0
:
out
.
Mode
|=
syscall
.
S_IFIFO
case
a
.
Mode
&
os
.
ModeSymlink
!=
0
:
out
.
Mode
|=
syscall
.
S_IFLNK
case
a
.
Mode
&
os
.
ModeSocket
!=
0
:
out
.
Mode
|=
syscall
.
S_IFSOCK
}
if
a
.
Mode
&
os
.
ModeSetuid
!=
0
{
out
.
Mode
|=
syscall
.
S_ISUID
}
if
a
.
Mode
&
os
.
ModeSetgid
!=
0
{
out
.
Mode
|=
syscall
.
S_ISGID
}
out
.
Nlink
=
a
.
Nlink
out
.
Uid
=
a
.
Uid
out
.
Gid
=
a
.
Gid
out
.
Rdev
=
a
.
Rdev
out
.
SetFlags
(
a
.
Flags
)
if
proto
.
GE
(
fusekernel
.
Protocol
{
7
,
9
})
{
out
.
Blksize
=
a
.
BlockSize
}
return
}
// A GetattrRequest asks for the metadata for the file denoted by r.Node.
type
GetattrRequest
struct
{
Header
`json:"-"`
Flags
fusekernel
.
GetattrFlags
Handle
HandleID
}
var
_
=
Request
(
&
GetattrRequest
{})
func
(
r
*
GetattrRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Getattr [%s] %#x fl=%v"
,
&
r
.
Header
,
r
.
Handle
,
r
.
Flags
)
}
// Respond replies to the request with the given response.
func
(
r
*
GetattrRequest
)
Respond
(
resp
*
GetattrResponse
)
{
size
:=
fusekernel
.
AttrOutSize
(
r
.
Header
.
Conn
.
proto
)
buf
:=
NewBuffer
(
size
)
out
:=
(
*
fusekernel
.
AttrOut
)(
buf
.
Alloc
(
size
))
out
.
AttrValid
=
uint64
(
resp
.
Attr
.
Valid
/
time
.
Second
)
out
.
AttrValidNsec
=
uint32
(
resp
.
Attr
.
Valid
%
time
.
Second
/
time
.
Nanosecond
)
resp
.
Attr
.
attr
(
&
out
.
Attr
,
r
.
Header
.
Conn
.
proto
)
r
.
respond
(
buf
)
}
// A GetattrResponse is the response to a GetattrRequest.
type
GetattrResponse
struct
{
Attr
Attr
// file attributes
}
func
(
r
*
GetattrResponse
)
String
()
string
{
return
fmt
.
Sprintf
(
"Getattr %+v"
,
*
r
)
}
// A GetxattrRequest asks for the extended attributes associated with r.Node.
type
GetxattrRequest
struct
{
Header
`json:"-"`
// Maximum size to return.
Size
uint32
// Name of the attribute requested.
Name
string
// Offset within extended attributes.
//
// Only valid for OS X, and then only with the resource fork
// attribute.
Position
uint32
}
var
_
=
Request
(
&
GetxattrRequest
{})
func
(
r
*
GetxattrRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Getxattr [%s] %q %d @%d"
,
&
r
.
Header
,
r
.
Name
,
r
.
Size
,
r
.
Position
)
}
// Respond replies to the request with the given response.
func
(
r
*
GetxattrRequest
)
Respond
(
resp
*
GetxattrResponse
)
{
if
r
.
Size
==
0
{
buf
:=
NewBuffer
(
unsafe
.
Sizeof
(
fusekernel
.
GetxattrOut
{}))
out
:=
(
*
fusekernel
.
GetxattrOut
)(
buf
.
Alloc
(
unsafe
.
Sizeof
(
fusekernel
.
GetxattrOut
{})))
out
.
Size
=
uint32
(
len
(
resp
.
Xattr
))
r
.
respond
(
buf
)
}
else
{
buf
:=
NewBuffer
(
uintptr
(
len
(
resp
.
Xattr
)))
buf
=
append
(
buf
,
resp
.
Xattr
...
)
r
.
respond
(
buf
)
}
}
// A GetxattrResponse is the response to a GetxattrRequest.
type
GetxattrResponse
struct
{
Xattr
[]
byte
}
func
(
r
*
GetxattrResponse
)
String
()
string
{
return
fmt
.
Sprintf
(
"Getxattr %x"
,
r
.
Xattr
)
}
// A ListxattrRequest asks to list the extended attributes associated with r.Node.
type
ListxattrRequest
struct
{
Header
`json:"-"`
Size
uint32
// maximum size to return
Position
uint32
// offset within attribute list
}
var
_
=
Request
(
&
ListxattrRequest
{})
func
(
r
*
ListxattrRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Listxattr [%s] %d @%d"
,
&
r
.
Header
,
r
.
Size
,
r
.
Position
)
}
// Respond replies to the request with the given response.
func
(
r
*
ListxattrRequest
)
Respond
(
resp
*
ListxattrResponse
)
{
if
r
.
Size
==
0
{
buf
:=
NewBuffer
(
unsafe
.
Sizeof
(
fusekernel
.
GetxattrOut
{}))
out
:=
(
*
fusekernel
.
GetxattrOut
)(
buf
.
Alloc
(
unsafe
.
Sizeof
(
fusekernel
.
GetxattrOut
{})))
out
.
Size
=
uint32
(
len
(
resp
.
Xattr
))
r
.
respond
(
buf
)
}
else
{
buf
:=
NewBuffer
(
uintptr
(
len
(
resp
.
Xattr
)))
buf
=
append
(
buf
,
resp
.
Xattr
...
)
r
.
respond
(
buf
)
}
}
// A ListxattrResponse is the response to a ListxattrRequest.
type
ListxattrResponse
struct
{
Xattr
[]
byte
}
func
(
r
*
ListxattrResponse
)
String
()
string
{
return
fmt
.
Sprintf
(
"Listxattr %x"
,
r
.
Xattr
)
}
// Append adds an extended attribute name to the response.
func
(
r
*
ListxattrResponse
)
Append
(
names
...
string
)
{
for
_
,
name
:=
range
names
{
r
.
Xattr
=
append
(
r
.
Xattr
,
name
...
)
r
.
Xattr
=
append
(
r
.
Xattr
,
'\x00'
)
}
}
// A RemovexattrRequest asks to remove an extended attribute associated with r.Node.
type
RemovexattrRequest
struct
{
Header
`json:"-"`
Name
string
// name of extended attribute
}
var
_
=
Request
(
&
RemovexattrRequest
{})
func
(
r
*
RemovexattrRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Removexattr [%s] %q"
,
&
r
.
Header
,
r
.
Name
)
}
// Respond replies to the request, indicating that the attribute was removed.
func
(
r
*
RemovexattrRequest
)
Respond
()
{
buf
:=
NewBuffer
(
0
)
r
.
respond
(
buf
)
}
// A SetxattrRequest asks to set an extended attribute associated with a file.
type
SetxattrRequest
struct
{
Header
`json:"-"`
// Flags can make the request fail if attribute does/not already
// exist. Unfortunately, the constants are platform-specific and
// not exposed by Go1.2. Look for XATTR_CREATE, XATTR_REPLACE.
//
// TODO improve this later
//
// TODO XATTR_CREATE and exist -> EEXIST
//
// TODO XATTR_REPLACE and not exist -> ENODATA
Flags
uint32
// Offset within extended attributes.
//
// Only valid for OS X, and then only with the resource fork
// attribute.
Position
uint32
Name
string
Xattr
[]
byte
}
var
_
=
Request
(
&
SetxattrRequest
{})
func
trunc
(
b
[]
byte
,
max
int
)
([]
byte
,
string
)
{
if
len
(
b
)
>
max
{
return
b
[
:
max
],
"..."
}
return
b
,
""
}
func
(
r
*
SetxattrRequest
)
String
()
string
{
xattr
,
tail
:=
trunc
(
r
.
Xattr
,
16
)
return
fmt
.
Sprintf
(
"Setxattr [%s] %q %x%s fl=%v @%#x"
,
&
r
.
Header
,
r
.
Name
,
xattr
,
tail
,
r
.
Flags
,
r
.
Position
)
}
// Respond replies to the request, indicating that the extended attribute was set.
func
(
r
*
SetxattrRequest
)
Respond
()
{
buf
:=
NewBuffer
(
0
)
r
.
respond
(
buf
)
}
// A LookupRequest asks to look up the given name in the directory named by r.Node.
type
LookupRequest
struct
{
Header
`json:"-"`
Name
string
}
var
_
=
Request
(
&
LookupRequest
{})
func
(
r
*
LookupRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Lookup [%s] %q"
,
&
r
.
Header
,
r
.
Name
)
}
// Respond replies to the request with the given response.
func
(
r
*
LookupRequest
)
Respond
(
resp
*
LookupResponse
)
{
size
:=
fusekernel
.
EntryOutSize
(
r
.
Header
.
Conn
.
proto
)
buf
:=
NewBuffer
(
size
)
out
:=
(
*
fusekernel
.
EntryOut
)(
buf
.
Alloc
(
size
))
out
.
Nodeid
=
uint64
(
resp
.
Node
)
out
.
Generation
=
resp
.
Generation
out
.
EntryValid
=
uint64
(
resp
.
EntryValid
/
time
.
Second
)
out
.
EntryValidNsec
=
uint32
(
resp
.
EntryValid
%
time
.
Second
/
time
.
Nanosecond
)
out
.
AttrValid
=
uint64
(
resp
.
Attr
.
Valid
/
time
.
Second
)
out
.
AttrValidNsec
=
uint32
(
resp
.
Attr
.
Valid
%
time
.
Second
/
time
.
Nanosecond
)
resp
.
Attr
.
attr
(
&
out
.
Attr
,
r
.
Header
.
Conn
.
proto
)
r
.
respond
(
buf
)
}
// A LookupResponse is the response to a LookupRequest.
type
LookupResponse
struct
{
Node
NodeID
Generation
uint64
EntryValid
time
.
Duration
Attr
Attr
}
func
(
r
*
LookupResponse
)
String
()
string
{
return
fmt
.
Sprintf
(
"Lookup %+v"
,
*
r
)
}
// An OpenRequest asks to open a file or directory
type
OpenRequest
struct
{
Header
`json:"-"`
Dir
bool
// is this Opendir?
Flags
fusekernel
.
OpenFlags
}
var
_
=
Request
(
&
OpenRequest
{})
func
(
r
*
OpenRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Open [%s] dir=%v fl=%v"
,
&
r
.
Header
,
r
.
Dir
,
r
.
Flags
)
}
// Respond replies to the request with the given response.
func
(
r
*
OpenRequest
)
Respond
(
resp
*
OpenResponse
)
{
buf
:=
NewBuffer
(
unsafe
.
Sizeof
(
fusekernel
.
OpenOut
{}))
out
:=
(
*
fusekernel
.
OpenOut
)(
buf
.
Alloc
(
unsafe
.
Sizeof
(
fusekernel
.
OpenOut
{})))
out
.
Fh
=
uint64
(
resp
.
Handle
)
out
.
OpenFlags
=
uint32
(
resp
.
Flags
)
r
.
respond
(
buf
)
}
// A OpenResponse is the response to a OpenRequest.
type
OpenResponse
struct
{
Handle
HandleID
Flags
fusekernel
.
OpenResponseFlags
}
func
(
r
*
OpenResponse
)
String
()
string
{
return
fmt
.
Sprintf
(
"Open %+v"
,
*
r
)
}
// A CreateRequest asks to create and open a file (not a directory).
type
CreateRequest
struct
{
Header
`json:"-"`
Name
string
Flags
fusekernel
.
OpenFlags
Mode
os
.
FileMode
Umask
os
.
FileMode
}
var
_
=
Request
(
&
CreateRequest
{})
func
(
r
*
CreateRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Create [%s] %q fl=%v mode=%v umask=%v"
,
&
r
.
Header
,
r
.
Name
,
r
.
Flags
,
r
.
Mode
,
r
.
Umask
)
}
// Respond replies to the request with the given response.
func
(
r
*
CreateRequest
)
Respond
(
resp
*
CreateResponse
)
{
eSize
:=
fusekernel
.
EntryOutSize
(
r
.
Header
.
Conn
.
proto
)
buf
:=
NewBuffer
(
eSize
+
unsafe
.
Sizeof
(
fusekernel
.
OpenOut
{}))
e
:=
(
*
fusekernel
.
EntryOut
)(
buf
.
Alloc
(
eSize
))
e
.
Nodeid
=
uint64
(
resp
.
Node
)
e
.
Generation
=
resp
.
Generation
e
.
EntryValid
=
uint64
(
resp
.
EntryValid
/
time
.
Second
)
e
.
EntryValidNsec
=
uint32
(
resp
.
EntryValid
%
time
.
Second
/
time
.
Nanosecond
)
e
.
AttrValid
=
uint64
(
resp
.
Attr
.
Valid
/
time
.
Second
)
e
.
AttrValidNsec
=
uint32
(
resp
.
Attr
.
Valid
%
time
.
Second
/
time
.
Nanosecond
)
resp
.
Attr
.
attr
(
&
e
.
Attr
,
r
.
Header
.
Conn
.
proto
)
o
:=
(
*
fusekernel
.
OpenOut
)(
buf
.
Alloc
(
unsafe
.
Sizeof
(
fusekernel
.
OpenOut
{})))
o
.
Fh
=
uint64
(
resp
.
Handle
)
o
.
OpenFlags
=
uint32
(
resp
.
Flags
)
r
.
respond
(
buf
)
}
// A CreateResponse is the response to a CreateRequest.
// It describes the created node and opened handle.
type
CreateResponse
struct
{
LookupResponse
OpenResponse
}
func
(
r
*
CreateResponse
)
String
()
string
{
return
fmt
.
Sprintf
(
"Create %+v"
,
*
r
)
}
// A MkdirRequest asks to create (but not open) a directory.
type
MkdirRequest
struct
{
Header
`json:"-"`
Name
string
Mode
os
.
FileMode
Umask
os
.
FileMode
}
var
_
=
Request
(
&
MkdirRequest
{})
func
(
r
*
MkdirRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Mkdir [%s] %q mode=%v umask=%v"
,
&
r
.
Header
,
r
.
Name
,
r
.
Mode
,
r
.
Umask
)
}
// Respond replies to the request with the given response.
func
(
r
*
MkdirRequest
)
Respond
(
resp
*
MkdirResponse
)
{
size
:=
fusekernel
.
EntryOutSize
(
r
.
Header
.
Conn
.
proto
)
buf
:=
NewBuffer
(
size
)
out
:=
(
*
fusekernel
.
EntryOut
)(
buf
.
Alloc
(
size
))
out
.
Nodeid
=
uint64
(
resp
.
Node
)
out
.
Generation
=
resp
.
Generation
out
.
EntryValid
=
uint64
(
resp
.
EntryValid
/
time
.
Second
)
out
.
EntryValidNsec
=
uint32
(
resp
.
EntryValid
%
time
.
Second
/
time
.
Nanosecond
)
out
.
AttrValid
=
uint64
(
resp
.
Attr
.
Valid
/
time
.
Second
)
out
.
AttrValidNsec
=
uint32
(
resp
.
Attr
.
Valid
%
time
.
Second
/
time
.
Nanosecond
)
resp
.
Attr
.
attr
(
&
out
.
Attr
,
r
.
Header
.
Conn
.
proto
)
r
.
respond
(
buf
)
}
// A MkdirResponse is the response to a MkdirRequest.
type
MkdirResponse
struct
{
LookupResponse
}
func
(
r
*
MkdirResponse
)
String
()
string
{
return
fmt
.
Sprintf
(
"Mkdir %+v"
,
*
r
)
}
// A ReadRequest asks to read from an open file.
type
ReadRequest
struct
{
Header
`json:"-"`
Dir
bool
// is this Readdir?
Handle
HandleID
Offset
int64
Size
int
Flags
fusekernel
.
ReadFlags
LockOwner
uint64
FileFlags
fusekernel
.
OpenFlags
}
var
_
=
Request
(
&
ReadRequest
{})
func
(
r
*
ReadRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Read [%s] %#x %d @%#x dir=%v fl=%v lock=%d ffl=%v"
,
&
r
.
Header
,
r
.
Handle
,
r
.
Size
,
r
.
Offset
,
r
.
Dir
,
r
.
Flags
,
r
.
LockOwner
,
r
.
FileFlags
)
}
// Respond replies to the request with the given response.
func
(
r
*
ReadRequest
)
Respond
(
resp
*
ReadResponse
)
{
buf
:=
NewBuffer
(
uintptr
(
len
(
resp
.
Data
)))
buf
=
append
(
buf
,
resp
.
Data
...
)
r
.
respond
(
buf
)
}
// A ReadResponse is the response to a ReadRequest.
type
ReadResponse
struct
{
Data
[]
byte
}
func
(
r
*
ReadResponse
)
String
()
string
{
return
fmt
.
Sprintf
(
"Read %d"
,
len
(
r
.
Data
))
}
type
jsonReadResponse
struct
{
Len
uint64
}
func
(
r
*
ReadResponse
)
MarshalJSON
()
([]
byte
,
error
)
{
j
:=
jsonReadResponse
{
Len
:
uint64
(
len
(
r
.
Data
)),
}
return
json
.
Marshal
(
j
)
}
// A ReleaseRequest asks to release (close) an open file handle.
type
ReleaseRequest
struct
{
Header
`json:"-"`
Dir
bool
// is this Releasedir?
Handle
HandleID
Flags
fusekernel
.
OpenFlags
// flags from OpenRequest
ReleaseFlags
fusekernel
.
ReleaseFlags
LockOwner
uint32
}
var
_
=
Request
(
&
ReleaseRequest
{})
func
(
r
*
ReleaseRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Release [%s] %#x fl=%v rfl=%v owner=%#x"
,
&
r
.
Header
,
r
.
Handle
,
r
.
Flags
,
r
.
ReleaseFlags
,
r
.
LockOwner
)
}
// Respond replies to the request, indicating that the handle has been released.
func
(
r
*
ReleaseRequest
)
Respond
()
{
buf
:=
NewBuffer
(
0
)
r
.
respond
(
buf
)
}
// A DestroyRequest is sent by the kernel when unmounting the file system.
// No more requests will be received after this one, but it should still be
// responded to.
type
DestroyRequest
struct
{
Header
`json:"-"`
}
var
_
=
Request
(
&
DestroyRequest
{})
func
(
r
*
DestroyRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Destroy [%s]"
,
&
r
.
Header
)
}
// Respond replies to the request.
func
(
r
*
DestroyRequest
)
Respond
()
{
buf
:=
NewBuffer
(
0
)
r
.
respond
(
buf
)
}
// A ForgetRequest is sent by the kernel when forgetting about r.Node
// as returned by r.N lookup requests.
type
ForgetRequest
struct
{
Header
`json:"-"`
N
uint64
}
var
_
=
Request
(
&
ForgetRequest
{})
func
(
r
*
ForgetRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Forget [%s] %d"
,
&
r
.
Header
,
r
.
N
)
}
// Respond replies to the request, indicating that the forgetfulness has been recorded.
func
(
r
*
ForgetRequest
)
Respond
()
{
// Don't reply to forget messages.
r
.
noResponse
()
}
// A Dirent represents a single directory entry.
type
Dirent
struct
{
// Inode this entry names.
Inode
uint64
// Type of the entry, for example DT_File.
//
// Setting this is optional. The zero value (DT_Unknown) means
// callers will just need to do a Getattr when the type is
// needed. Providing a type can speed up operations
// significantly.
Type
DirentType
// Name of the entry
Name
string
}
// Type of an entry in a directory listing.
type
DirentType
uint32
const
(
// These don't quite match os.FileMode; especially there's an
// explicit unknown, instead of zero value meaning file. They
// are also not quite syscall.DT_*; nothing says the FUSE
// protocol follows those, and even if they were, we don't
// want each fs to fiddle with syscall.
// The shift by 12 is hardcoded in the FUSE userspace
// low-level C library, so it's safe here.
DT_Unknown
DirentType
=
0
DT_Socket
DirentType
=
syscall
.
S_IFSOCK
>>
12
DT_Link
DirentType
=
syscall
.
S_IFLNK
>>
12
DT_File
DirentType
=
syscall
.
S_IFREG
>>
12
DT_Block
DirentType
=
syscall
.
S_IFBLK
>>
12
DT_Dir
DirentType
=
syscall
.
S_IFDIR
>>
12
DT_Char
DirentType
=
syscall
.
S_IFCHR
>>
12
DT_FIFO
DirentType
=
syscall
.
S_IFIFO
>>
12
)
func
(
t
DirentType
)
String
()
string
{
switch
t
{
case
DT_Unknown
:
return
"unknown"
case
DT_Socket
:
return
"socket"
case
DT_Link
:
return
"link"
case
DT_File
:
return
"file"
case
DT_Block
:
return
"block"
case
DT_Dir
:
return
"dir"
case
DT_Char
:
return
"char"
case
DT_FIFO
:
return
"fifo"
}
return
"invalid"
}
// AppendDirent appends the encoded form of a directory entry to data
// and returns the resulting slice.
func
AppendDirent
(
data
[]
byte
,
dir
Dirent
)
[]
byte
{
de
:=
fusekernel
.
Dirent
{
Ino
:
dir
.
Inode
,
Namelen
:
uint32
(
len
(
dir
.
Name
)),
Type
:
uint32
(
dir
.
Type
),
}
de
.
Off
=
uint64
(
len
(
data
)
+
fusekernel
.
DirentSize
+
(
len
(
dir
.
Name
)
+
7
)
&^
7
)
data
=
append
(
data
,
(
*
[
fusekernel
.
DirentSize
]
byte
)(
unsafe
.
Pointer
(
&
de
))[
:
]
...
)
data
=
append
(
data
,
dir
.
Name
...
)
n
:=
fusekernel
.
DirentSize
+
uintptr
(
len
(
dir
.
Name
))
if
n
%
8
!=
0
{
var
pad
[
8
]
byte
data
=
append
(
data
,
pad
[
:
8
-
n
%
8
]
...
)
}
return
data
}
// A WriteRequest asks to write to an open file.
type
WriteRequest
struct
{
Header
Handle
HandleID
Offset
int64
Data
[]
byte
Flags
fusekernel
.
WriteFlags
LockOwner
uint64
FileFlags
fusekernel
.
OpenFlags
}
var
_
=
Request
(
&
WriteRequest
{})
func
(
r
*
WriteRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Write [%s] %#x %d @%d fl=%v lock=%d ffl=%v"
,
&
r
.
Header
,
r
.
Handle
,
len
(
r
.
Data
),
r
.
Offset
,
r
.
Flags
,
r
.
LockOwner
,
r
.
FileFlags
)
}
type
jsonWriteRequest
struct
{
Handle
HandleID
Offset
int64
Len
uint64
Flags
fusekernel
.
WriteFlags
}
func
(
r
*
WriteRequest
)
MarshalJSON
()
([]
byte
,
error
)
{
j
:=
jsonWriteRequest
{
Handle
:
r
.
Handle
,
Offset
:
r
.
Offset
,
Len
:
uint64
(
len
(
r
.
Data
)),
Flags
:
r
.
Flags
,
}
return
json
.
Marshal
(
j
)
}
// Respond replies to the request with the given response.
func
(
r
*
WriteRequest
)
Respond
(
resp
*
WriteResponse
)
{
buf
:=
NewBuffer
(
unsafe
.
Sizeof
(
fusekernel
.
WriteOut
{}))
out
:=
(
*
fusekernel
.
WriteOut
)(
buf
.
Alloc
(
unsafe
.
Sizeof
(
fusekernel
.
WriteOut
{})))
out
.
Size
=
uint32
(
resp
.
Size
)
r
.
respond
(
buf
)
}
// A WriteResponse replies to a write indicating how many bytes were written.
type
WriteResponse
struct
{
Size
int
}
func
(
r
*
WriteResponse
)
String
()
string
{
return
fmt
.
Sprintf
(
"Write %+v"
,
*
r
)
}
// A SetattrRequest asks to change one or more attributes associated with a file,
// as indicated by Valid.
type
SetattrRequest
struct
{
Header
`json:"-"`
Valid
fusekernel
.
SetattrValid
Handle
HandleID
Size
uint64
Atime
time
.
Time
Mtime
time
.
Time
Mode
os
.
FileMode
Uid
uint32
Gid
uint32
// OS X only
Bkuptime
time
.
Time
Chgtime
time
.
Time
Crtime
time
.
Time
Flags
uint32
// see chflags(2)
}
var
_
=
Request
(
&
SetattrRequest
{})
func
(
r
*
SetattrRequest
)
String
()
string
{
var
buf
bytes
.
Buffer
fmt
.
Fprintf
(
&
buf
,
"Setattr [%s]"
,
&
r
.
Header
)
if
r
.
Valid
.
Mode
()
{
fmt
.
Fprintf
(
&
buf
,
" mode=%v"
,
r
.
Mode
)
}
if
r
.
Valid
.
Uid
()
{
fmt
.
Fprintf
(
&
buf
,
" uid=%d"
,
r
.
Uid
)
}
if
r
.
Valid
.
Gid
()
{
fmt
.
Fprintf
(
&
buf
,
" gid=%d"
,
r
.
Gid
)
}
if
r
.
Valid
.
Size
()
{
fmt
.
Fprintf
(
&
buf
,
" size=%d"
,
r
.
Size
)
}
if
r
.
Valid
.
Atime
()
{
fmt
.
Fprintf
(
&
buf
,
" atime=%v"
,
r
.
Atime
)
}
if
r
.
Valid
.
AtimeNow
()
{
fmt
.
Fprintf
(
&
buf
,
" atime=now"
)
}
if
r
.
Valid
.
Mtime
()
{
fmt
.
Fprintf
(
&
buf
,
" mtime=%v"
,
r
.
Mtime
)
}
if
r
.
Valid
.
MtimeNow
()
{
fmt
.
Fprintf
(
&
buf
,
" mtime=now"
)
}
if
r
.
Valid
.
Handle
()
{
fmt
.
Fprintf
(
&
buf
,
" handle=%#x"
,
r
.
Handle
)
}
else
{
fmt
.
Fprintf
(
&
buf
,
" handle=INVALID-%#x"
,
r
.
Handle
)
}
if
r
.
Valid
.
LockOwner
()
{
fmt
.
Fprintf
(
&
buf
,
" lockowner"
)
}
if
r
.
Valid
.
Crtime
()
{
fmt
.
Fprintf
(
&
buf
,
" crtime=%v"
,
r
.
Crtime
)
}
if
r
.
Valid
.
Chgtime
()
{
fmt
.
Fprintf
(
&
buf
,
" chgtime=%v"
,
r
.
Chgtime
)
}
if
r
.
Valid
.
Bkuptime
()
{
fmt
.
Fprintf
(
&
buf
,
" bkuptime=%v"
,
r
.
Bkuptime
)
}
if
r
.
Valid
.
Flags
()
{
fmt
.
Fprintf
(
&
buf
,
" flags=%#x"
,
r
.
Flags
)
}
return
buf
.
String
()
}
// Respond replies to the request with the given response,
// giving the updated attributes.
func
(
r
*
SetattrRequest
)
Respond
(
resp
*
SetattrResponse
)
{
size
:=
fusekernel
.
AttrOutSize
(
r
.
Header
.
Conn
.
proto
)
buf
:=
NewBuffer
(
size
)
out
:=
(
*
fusekernel
.
AttrOut
)(
buf
.
Alloc
(
size
))
out
.
AttrValid
=
uint64
(
resp
.
Attr
.
Valid
/
time
.
Second
)
out
.
AttrValidNsec
=
uint32
(
resp
.
Attr
.
Valid
%
time
.
Second
/
time
.
Nanosecond
)
resp
.
Attr
.
attr
(
&
out
.
Attr
,
r
.
Header
.
Conn
.
proto
)
r
.
respond
(
buf
)
}
// A SetattrResponse is the response to a SetattrRequest.
type
SetattrResponse
struct
{
Attr
Attr
// file attributes
}
func
(
r
*
SetattrResponse
)
String
()
string
{
return
fmt
.
Sprintf
(
"Setattr %+v"
,
*
r
)
}
// A FlushRequest asks for the current state of an open file to be flushed
// to storage, as when a file descriptor is being closed. A single opened Handle
// may receive multiple FlushRequests over its lifetime.
type
FlushRequest
struct
{
Header
`json:"-"`
Handle
HandleID
Flags
uint32
LockOwner
uint64
}
var
_
=
Request
(
&
FlushRequest
{})
func
(
r
*
FlushRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Flush [%s] %#x fl=%#x lk=%#x"
,
&
r
.
Header
,
r
.
Handle
,
r
.
Flags
,
r
.
LockOwner
)
}
// Respond replies to the request, indicating that the flush succeeded.
func
(
r
*
FlushRequest
)
Respond
()
{
buf
:=
NewBuffer
(
0
)
r
.
respond
(
buf
)
}
// A RemoveRequest asks to remove a file or directory from the
// directory r.Node.
type
RemoveRequest
struct
{
Header
`json:"-"`
Name
string
// name of the entry to remove
Dir
bool
// is this rmdir?
}
var
_
=
Request
(
&
RemoveRequest
{})
func
(
r
*
RemoveRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Remove [%s] %q dir=%v"
,
&
r
.
Header
,
r
.
Name
,
r
.
Dir
)
}
// Respond replies to the request, indicating that the file was removed.
func
(
r
*
RemoveRequest
)
Respond
()
{
buf
:=
NewBuffer
(
0
)
r
.
respond
(
buf
)
}
// A SymlinkRequest is a request to create a symlink making NewName point to Target.
type
SymlinkRequest
struct
{
Header
`json:"-"`
NewName
,
Target
string
}
var
_
=
Request
(
&
SymlinkRequest
{})
func
(
r
*
SymlinkRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Symlink [%s] from %q to target %q"
,
&
r
.
Header
,
r
.
NewName
,
r
.
Target
)
}
// Respond replies to the request, indicating that the symlink was created.
func
(
r
*
SymlinkRequest
)
Respond
(
resp
*
SymlinkResponse
)
{
size
:=
fusekernel
.
EntryOutSize
(
r
.
Header
.
Conn
.
proto
)
buf
:=
NewBuffer
(
size
)
out
:=
(
*
fusekernel
.
EntryOut
)(
buf
.
Alloc
(
size
))
out
.
Nodeid
=
uint64
(
resp
.
Node
)
out
.
Generation
=
resp
.
Generation
out
.
EntryValid
=
uint64
(
resp
.
EntryValid
/
time
.
Second
)
out
.
EntryValidNsec
=
uint32
(
resp
.
EntryValid
%
time
.
Second
/
time
.
Nanosecond
)
out
.
AttrValid
=
uint64
(
resp
.
Attr
.
Valid
/
time
.
Second
)
out
.
AttrValidNsec
=
uint32
(
resp
.
Attr
.
Valid
%
time
.
Second
/
time
.
Nanosecond
)
resp
.
Attr
.
attr
(
&
out
.
Attr
,
r
.
Header
.
Conn
.
proto
)
r
.
respond
(
buf
)
}
// A SymlinkResponse is the response to a SymlinkRequest.
type
SymlinkResponse
struct
{
LookupResponse
}
// A ReadlinkRequest is a request to read a symlink's target.
type
ReadlinkRequest
struct
{
Header
`json:"-"`
}
var
_
=
Request
(
&
ReadlinkRequest
{})
func
(
r
*
ReadlinkRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Readlink [%s]"
,
&
r
.
Header
)
}
func
(
r
*
ReadlinkRequest
)
Respond
(
target
string
)
{
buf
:=
NewBuffer
(
uintptr
(
len
(
target
)))
buf
=
append
(
buf
,
target
...
)
r
.
respond
(
buf
)
}
// A LinkRequest is a request to create a hard link.
type
LinkRequest
struct
{
Header
`json:"-"`
OldNode
NodeID
NewName
string
}
var
_
=
Request
(
&
LinkRequest
{})
func
(
r
*
LinkRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Link [%s] node %d to %q"
,
&
r
.
Header
,
r
.
OldNode
,
r
.
NewName
)
}
func
(
r
*
LinkRequest
)
Respond
(
resp
*
LookupResponse
)
{
size
:=
fusekernel
.
EntryOutSize
(
r
.
Header
.
Conn
.
proto
)
buf
:=
NewBuffer
(
size
)
out
:=
(
*
fusekernel
.
EntryOut
)(
buf
.
Alloc
(
size
))
out
.
Nodeid
=
uint64
(
resp
.
Node
)
out
.
Generation
=
resp
.
Generation
out
.
EntryValid
=
uint64
(
resp
.
EntryValid
/
time
.
Second
)
out
.
EntryValidNsec
=
uint32
(
resp
.
EntryValid
%
time
.
Second
/
time
.
Nanosecond
)
out
.
AttrValid
=
uint64
(
resp
.
Attr
.
Valid
/
time
.
Second
)
out
.
AttrValidNsec
=
uint32
(
resp
.
Attr
.
Valid
%
time
.
Second
/
time
.
Nanosecond
)
resp
.
Attr
.
attr
(
&
out
.
Attr
,
r
.
Header
.
Conn
.
proto
)
r
.
respond
(
buf
)
}
// A RenameRequest is a request to rename a file.
type
RenameRequest
struct
{
Header
`json:"-"`
NewDir
NodeID
OldName
,
NewName
string
}
var
_
=
Request
(
&
RenameRequest
{})
func
(
r
*
RenameRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Rename [%s] from %q to dirnode %d %q"
,
&
r
.
Header
,
r
.
OldName
,
r
.
NewDir
,
r
.
NewName
)
}
func
(
r
*
RenameRequest
)
Respond
()
{
buf
:=
NewBuffer
(
0
)
r
.
respond
(
buf
)
}
type
MknodRequest
struct
{
Header
`json:"-"`
Name
string
Mode
os
.
FileMode
Rdev
uint32
Umask
os
.
FileMode
}
var
_
=
Request
(
&
MknodRequest
{})
func
(
r
*
MknodRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Mknod [%s] Name %q mode=%v umask=%v rdev=%d"
,
&
r
.
Header
,
r
.
Name
,
r
.
Mode
,
r
.
Umask
,
r
.
Rdev
)
}
func
(
r
*
MknodRequest
)
Respond
(
resp
*
LookupResponse
)
{
size
:=
fusekernel
.
EntryOutSize
(
r
.
Header
.
Conn
.
proto
)
buf
:=
NewBuffer
(
size
)
out
:=
(
*
fusekernel
.
EntryOut
)(
buf
.
Alloc
(
size
))
out
.
Nodeid
=
uint64
(
resp
.
Node
)
out
.
Generation
=
resp
.
Generation
out
.
EntryValid
=
uint64
(
resp
.
EntryValid
/
time
.
Second
)
out
.
EntryValidNsec
=
uint32
(
resp
.
EntryValid
%
time
.
Second
/
time
.
Nanosecond
)
out
.
AttrValid
=
uint64
(
resp
.
Attr
.
Valid
/
time
.
Second
)
out
.
AttrValidNsec
=
uint32
(
resp
.
Attr
.
Valid
%
time
.
Second
/
time
.
Nanosecond
)
resp
.
Attr
.
attr
(
&
out
.
Attr
,
r
.
Header
.
Conn
.
proto
)
r
.
respond
(
buf
)
}
type
FsyncRequest
struct
{
Header
`json:"-"`
Handle
HandleID
// TODO bit 1 is datasync, not well documented upstream
Flags
uint32
Dir
bool
}
var
_
=
Request
(
&
FsyncRequest
{})
func
(
r
*
FsyncRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Fsync [%s] Handle %v Flags %v"
,
&
r
.
Header
,
r
.
Handle
,
r
.
Flags
)
}
func
(
r
*
FsyncRequest
)
Respond
()
{
buf
:=
NewBuffer
(
0
)
r
.
respond
(
buf
)
}
// An InterruptRequest is a request to interrupt another pending request. The
// response to that request should return an error status of EINTR.
type
InterruptRequest
struct
{
Header
`json:"-"`
IntrID
RequestID
// ID of the request to be interrupt.
}
var
_
=
Request
(
&
InterruptRequest
{})
func
(
r
*
InterruptRequest
)
Respond
()
{
// nothing to do here
r
.
noResponse
()
}
func
(
r
*
InterruptRequest
)
String
()
string
{
return
fmt
.
Sprintf
(
"Interrupt [%s] ID %v"
,
&
r
.
Header
,
r
.
IntrID
)
}
internal/fuseshim/mount_darwin.go
deleted
100644 → 0
View file @
8ce082c8
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
}
internal/fuseshim/mount_freebsd.go
deleted
100644 → 0
View file @
8ce082c8
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
}
internal/fuseshim/mount_linux.go
deleted
100644 → 0
View file @
8ce082c8
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
}
internal/fuseshim/options.go
deleted
100644 → 0
View file @
8ce082c8
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
}
}
internal/fuseshim/options_darwin.go
deleted
100644 → 0
View file @
8ce082c8
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
}
}
internal/fuseshim/options_freebsd.go
deleted
100644 → 0
View file @
8ce082c8
package
fuseshim
func
localVolume
(
conf
*
mountConfig
)
error
{
return
nil
}
func
volumeName
(
name
string
)
MountOption
{
return
dummyOption
}
internal/fuseshim/options_helper_test.go
deleted
100644 → 0
View file @
8ce082c8
package
fuseshim
// for TestMountOptionCommaError
func
ForTestSetMountOption
(
k
,
v
string
)
MountOption
{
fn
:=
func
(
conf
*
mountConfig
)
error
{
conf
.
options
[
k
]
=
v
return
nil
}
return
fn
}
internal/fuseshim/options_linux.go
deleted
100644 → 0
View file @
8ce082c8
package
fuseshim
func
localVolume
(
conf
*
mountConfig
)
error
{
return
nil
}
func
volumeName
(
name
string
)
MountOption
{
return
dummyOption
}
internal/fuseshim/options_test.go
deleted
100644 → 0
View file @
8ce082c8
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
)
}
}
internal/fuseshim/unmount.go
deleted
100644 → 0
View file @
8ce082c8
package
fuseshim
// Unmount tries to unmount the filesystem mounted at dir.
func
Unmount
(
dir
string
)
error
{
return
unmount
(
dir
)
}
internal/fuseshim/unmount_linux.go
deleted
100644 → 0
View file @
8ce082c8
package
fuseshim
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
}
internal/fuseshim/unmount_std.go
deleted
100644 → 0
View file @
8ce082c8
// +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
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment