1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-06-26 23:53:19 +08:00

updated bazil.org/fuse

This commit is contained in:
Juan Batiz-Benet
2015-01-02 04:48:32 -08:00
parent 12549ca8f9
commit 56867c8b70
21 changed files with 736 additions and 137 deletions

2
Godeps/Godeps.json generated
View File

@ -7,7 +7,7 @@
"Deps": [
{
"ImportPath": "bazil.org/fuse",
"Rev": "a04507d54fc3610d38ee951402d8c4acab56c7b1"
"Rev": "d62a1291477b51b24becf4def173bd843138c4b6"
},
{
"ImportPath": "bitbucket.org/kardianos/osext",

View File

@ -11,4 +11,11 @@ func stack() string {
func nop(msg interface{}) {}
// Debug is called to output debug messages, including protocol
// traces. The default behavior is to do nothing.
//
// The messages have human-friendly string representations and are
// safe to marshal to JSON.
//
// Implementations must not retain msg.
var Debug func(msg interface{}) = nop

View File

@ -0,0 +1 @@
package fstestutil

View File

@ -55,12 +55,12 @@ func (mnt *Mount) Close() {
// workaround).
//
// After successful return, caller must clean up by calling Close.
func Mounted(srv *fs.Server) (*Mount, error) {
func Mounted(srv *fs.Server, options ...fuse.MountOption) (*Mount, error) {
dir, err := ioutil.TempDir("", "fusetest")
if err != nil {
return nil, err
}
c, err := fuse.Mount(dir)
c, err := fuse.Mount(dir, options...)
if err != nil {
return nil, err
}
@ -100,7 +100,7 @@ func Mounted(srv *fs.Server) (*Mount, error) {
//
// The debug log is not enabled by default. Use `-fuse.debug` or call
// DebugByDefault to enable.
func MountedT(t testing.TB, filesys fs.FS) (*Mount, error) {
func MountedT(t testing.TB, filesys fs.FS, options ...fuse.MountOption) (*Mount, error) {
srv := &fs.Server{
FS: filesys,
}
@ -109,5 +109,5 @@ func MountedT(t testing.TB, filesys fs.FS) (*Mount, error) {
t.Logf("FUSE: %s", msg)
}
}
return Mounted(srv)
return Mounted(srv, options...)
}

View File

@ -0,0 +1,14 @@
package fstestutil
// MountInfo describes a mounted file system.
type MountInfo struct {
FSName string
Type string
}
// GetMountInfo finds information about the mount at mnt. It is
// intended for use by tests only, and only fetches information
// relevant to the current tests.
func GetMountInfo(mnt string) (*MountInfo, error) {
return getMountInfo(mnt)
}

View File

@ -0,0 +1,41 @@
package fstestutil
import (
"regexp"
"syscall"
)
// cstr converts a nil-terminated C string into a Go string
func cstr(ca []int8) string {
s := make([]byte, 0, len(ca))
for _, c := range ca {
if c == 0x00 {
break
}
s = append(s, byte(c))
}
return string(s)
}
var re = regexp.MustCompile(`\\(.)`)
// unescape removes backslash-escaping. The escaped characters are not
// mapped in any way; that is, unescape(`\n` ) == `n`.
func unescape(s string) string {
return re.ReplaceAllString(s, `$1`)
}
func getMountInfo(mnt string) (*MountInfo, error) {
var st syscall.Statfs_t
err := syscall.Statfs(mnt, &st)
if err != nil {
return nil, err
}
i := &MountInfo{
// osx getmntent(3) fails to un-escape the data, so we do it..
// this might lead to double-unescaping in the future. fun.
// TestMountOptionFSNameEvilBackslashDouble checks for that.
FSName: unescape(cstr(st.Mntfromname[:])),
}
return i, nil
}

View File

@ -0,0 +1,51 @@
package fstestutil
import (
"errors"
"io/ioutil"
"strings"
)
// Linux /proc/mounts shows current mounts.
// Same format as /etc/fstab. Quoting getmntent(3):
//
// Since fields in the mtab and fstab files are separated by whitespace,
// octal escapes are used to represent the four characters space (\040),
// tab (\011), newline (\012) and backslash (\134) in those files when
// they occur in one of the four strings in a mntent structure.
//
// http://linux.die.net/man/3/getmntent
var fstabUnescape = strings.NewReplacer(
`\040`, "\040",
`\011`, "\011",
`\012`, "\012",
`\134`, "\134",
)
var errNotFound = errors.New("mount not found")
func getMountInfo(mnt string) (*MountInfo, error) {
data, err := ioutil.ReadFile("/proc/mounts")
if err != nil {
return nil, err
}
for _, line := range strings.Split(string(data), "\n") {
fields := strings.Fields(line)
if len(fields) < 3 {
continue
}
// Fields are: fsname dir type opts freq passno
fsname := fstabUnescape.Replace(fields[0])
dir := fstabUnescape.Replace(fields[1])
fstype := fstabUnescape.Replace(fields[2])
if mnt == dir {
info := &MountInfo{
FSName: fsname,
Type: fstype,
}
return info, nil
}
}
return nil, errNotFound
}

View File

@ -0,0 +1,29 @@
package fstestutil
import (
"os"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
)
// SimpleFS is a trivial FS that just implements the Root method.
type SimpleFS struct {
Node fs.Node
}
var _ = fs.FS(SimpleFS{})
func (f SimpleFS) Root() (fs.Node, fuse.Error) {
return f.Node, nil
}
// File can be embedded in a struct to make it look like a file.
type File struct{}
func (f File) Attr() fuse.Attr { return fuse.Attr{Mode: 0666} }
// Dir can be embedded in a struct to make it look like a directory.
type Dir struct{}
func (f Dir) Attr() fuse.Attr { return fuse.Attr{Mode: os.ModeDir | 0777} }

View File

@ -10,7 +10,6 @@ import (
"reflect"
"strings"
"sync"
"syscall"
"time"
)
@ -295,6 +294,8 @@ type Server struct {
// Function to send debug log messages to. If nil, use fuse.Debug.
// Note that changing this or fuse.Debug may not affect existing
// calls to Serve.
//
// See fuse.Debug for the rules that log functions must follow.
Debug func(msg interface{})
}
@ -317,7 +318,7 @@ func (s *Server) Serve(c *fuse.Conn) error {
root, err := sc.fs.Root()
if err != nil {
return fmt.Errorf("cannot obtain root node: %v", syscall.Errno(err.(fuse.Errno)).Error())
return fmt.Errorf("cannot obtain root node: %v", err)
}
sc.node = append(sc.node, nil, &serveNode{inode: 1, node: root, refs: 1})
sc.handle = append(sc.handle, nil)

View File

@ -87,27 +87,6 @@ func (f childMapFS) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
return child, nil
}
// simpleFS is a trivial FS that just implements the Root method.
type simpleFS struct {
node fs.Node
}
var _ = fs.FS(simpleFS{})
func (f simpleFS) Root() (fs.Node, fuse.Error) {
return f.node, nil
}
// file can be embedded in a struct to make it look like a file.
type file struct{}
func (f file) Attr() fuse.Attr { return fuse.Attr{Mode: 0666} }
// dir can be embedded in a struct to make it look like a directory.
type dir struct{}
func (f dir) Attr() fuse.Attr { return fuse.Attr{Mode: os.ModeDir | 0777} }
// symlink can be embedded in a struct to make it look like a symlink.
type symlink struct {
target string
@ -261,7 +240,9 @@ func TestStatRoot(t *testing.T) {
// Test Read calling ReadAll.
type readAll struct{ file }
type readAll struct {
fstestutil.File
}
const hi = "hello, world"
@ -299,7 +280,9 @@ func TestReadAll(t *testing.T) {
// Test Read.
type readWithHandleRead struct{ file }
type readWithHandleRead struct {
fstestutil.File
}
func (readWithHandleRead) Attr() fuse.Attr {
return fuse.Attr{
@ -327,7 +310,7 @@ func TestReadAllWithHandleRead(t *testing.T) {
// Test Release.
type release struct {
file
fstestutil.File
record.ReleaseWaiter
}
@ -353,7 +336,7 @@ func TestRelease(t *testing.T) {
// Test Write calling basic Write, with an fsync thrown in too.
type write struct {
file
fstestutil.File
record.Writes
record.Fsyncs
}
@ -401,7 +384,7 @@ func TestWrite(t *testing.T) {
// Test Write of a larger buffer.
type writeLarge struct {
file
fstestutil.File
record.Writes
}
@ -446,7 +429,7 @@ func TestWriteLarge(t *testing.T) {
// Test Write calling Setattr+Write+Flush.
type writeTruncateFlush struct {
file
fstestutil.File
record.Writes
record.Setattrs
record.Flushes
@ -479,7 +462,7 @@ func TestWriteTruncateFlush(t *testing.T) {
// Test Mkdir.
type mkdir1 struct {
dir
fstestutil.Dir
record.Mkdirs
}
@ -491,7 +474,7 @@ func (f *mkdir1) Mkdir(req *fuse.MkdirRequest, intr fs.Intr) (fs.Node, fuse.Erro
func TestMkdir(t *testing.T) {
t.Parallel()
f := &mkdir1{}
mnt, err := fstestutil.MountedT(t, simpleFS{f})
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
if err != nil {
t.Fatal(err)
}
@ -513,12 +496,12 @@ func TestMkdir(t *testing.T) {
// Test Create (and fsync)
type create1file struct {
file
fstestutil.File
record.Fsyncs
}
type create1 struct {
dir
fstestutil.Dir
f create1file
}
@ -553,7 +536,7 @@ func (f *create1) Create(req *fuse.CreateRequest, resp *fuse.CreateResponse, int
func TestCreate(t *testing.T) {
t.Parallel()
f := &create1{}
mnt, err := fstestutil.MountedT(t, simpleFS{f})
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
if err != nil {
t.Fatal(err)
}
@ -583,12 +566,12 @@ func TestCreate(t *testing.T) {
// Test Create + Write + Remove
type create3file struct {
file
fstestutil.File
record.Writes
}
type create3 struct {
dir
fstestutil.Dir
f create3file
fooCreated record.MarkRecorder
fooRemoved record.MarkRecorder
@ -622,7 +605,7 @@ func (f *create3) Remove(r *fuse.RemoveRequest, intr fs.Intr) fuse.Error {
func TestCreateWriteRemove(t *testing.T) {
t.Parallel()
f := &create3{}
mnt, err := fstestutil.MountedT(t, simpleFS{f})
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
if err != nil {
t.Fatal(err)
}
@ -659,7 +642,7 @@ func (f symlink1link) Readlink(*fuse.ReadlinkRequest, fs.Intr) (string, fuse.Err
}
type symlink1 struct {
dir
fstestutil.Dir
record.Symlinks
}
@ -671,7 +654,7 @@ func (f *symlink1) Symlink(req *fuse.SymlinkRequest, intr fs.Intr) (fs.Node, fus
func TestSymlink(t *testing.T) {
t.Parallel()
f := &symlink1{}
mnt, err := fstestutil.MountedT(t, simpleFS{f})
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
if err != nil {
t.Fatal(err)
}
@ -701,26 +684,26 @@ func TestSymlink(t *testing.T) {
// Test link
type link1 struct {
dir
fstestutil.Dir
record.Links
}
func (f *link1) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
if name == "old" {
return file{}, nil
return fstestutil.File{}, nil
}
return nil, fuse.ENOENT
}
func (f *link1) Link(r *fuse.LinkRequest, old fs.Node, intr fs.Intr) (fs.Node, fuse.Error) {
f.Links.Link(r, old, intr)
return file{}, nil
return fstestutil.File{}, nil
}
func TestLink(t *testing.T) {
t.Parallel()
f := &link1{}
mnt, err := fstestutil.MountedT(t, simpleFS{f})
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
if err != nil {
t.Fatal(err)
}
@ -745,13 +728,13 @@ func TestLink(t *testing.T) {
// Test Rename
type rename1 struct {
dir
fstestutil.Dir
renamed record.Counter
}
func (f *rename1) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
if name == "old" {
return file{}, nil
return fstestutil.File{}, nil
}
return nil, fuse.ENOENT
}
@ -767,7 +750,7 @@ func (f *rename1) Rename(r *fuse.RenameRequest, newDir fs.Node, intr fs.Intr) fu
func TestRename(t *testing.T) {
t.Parallel()
f := &rename1{}
mnt, err := fstestutil.MountedT(t, simpleFS{f})
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
if err != nil {
t.Fatal(err)
}
@ -789,7 +772,7 @@ func TestRename(t *testing.T) {
// Test mknod
type mknod1 struct {
dir
fstestutil.Dir
record.Mknods
}
@ -805,7 +788,7 @@ func TestMknod(t *testing.T) {
}
f := &mknod1{}
mnt, err := fstestutil.MountedT(t, simpleFS{f})
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
if err != nil {
t.Fatal(err)
}
@ -836,7 +819,7 @@ func TestMknod(t *testing.T) {
// Test Read served with DataHandle.
type dataHandleTest struct {
file
fstestutil.File
}
func (dataHandleTest) Attr() fuse.Attr {
@ -872,7 +855,7 @@ func TestDataHandle(t *testing.T) {
// Test interrupt
type interrupt struct {
file
fstestutil.File
// strobes to signal we have a read hanging
hanging chan struct{}
@ -955,7 +938,7 @@ func TestInterrupt(t *testing.T) {
// Test truncate
type truncate struct {
file
fstestutil.File
record.Setattrs
}
@ -996,7 +979,7 @@ func TestTruncate0(t *testing.T) {
// Test ftruncate
type ftruncate struct {
file
fstestutil.File
record.Setattrs
}
@ -1046,7 +1029,7 @@ func TestFtruncate0(t *testing.T) {
// Test opening existing file truncates
type truncateWithOpen struct {
file
fstestutil.File
record.Setattrs
}
@ -1083,7 +1066,7 @@ func TestTruncateWithOpen(t *testing.T) {
// Test readdir
type readdir struct {
dir
fstestutil.Dir
}
func (d *readdir) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
@ -1097,7 +1080,7 @@ func (d *readdir) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
func TestReadDir(t *testing.T) {
t.Parallel()
f := &readdir{}
mnt, err := fstestutil.MountedT(t, simpleFS{f})
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
if err != nil {
t.Fatal(err)
}
@ -1133,7 +1116,7 @@ func TestReadDir(t *testing.T) {
// Test Chmod.
type chmod struct {
file
fstestutil.File
record.Setattrs
}
@ -1169,7 +1152,7 @@ func TestChmod(t *testing.T) {
// Test open
type open struct {
file
fstestutil.File
record.Opens
}
@ -1233,14 +1216,14 @@ func TestOpen(t *testing.T) {
// Test Fsync on a dir
type fsyncDir struct {
dir
fstestutil.Dir
record.Fsyncs
}
func TestFsyncDir(t *testing.T) {
t.Parallel()
f := &fsyncDir{}
mnt, err := fstestutil.MountedT(t, simpleFS{f})
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{f})
if err != nil {
t.Fatal(err)
}
@ -1278,7 +1261,7 @@ func TestFsyncDir(t *testing.T) {
// Test Getxattr
type getxattr struct {
file
fstestutil.File
record.Getxattrs
}
@ -1316,7 +1299,7 @@ func TestGetxattr(t *testing.T) {
// Test Getxattr that has no space to return value
type getxattrTooSmall struct {
file
fstestutil.File
}
func (f *getxattrTooSmall) Getxattr(req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse, intr fs.Intr) fuse.Error {
@ -1347,7 +1330,7 @@ func TestGetxattrTooSmall(t *testing.T) {
// Test Getxattr used to probe result size
type getxattrSize struct {
file
fstestutil.File
}
func (f *getxattrSize) Getxattr(req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse, intr fs.Intr) fuse.Error {
@ -1377,7 +1360,7 @@ func TestGetxattrSize(t *testing.T) {
// Test Listxattr
type listxattr struct {
file
fstestutil.File
record.Listxattrs
}
@ -1418,7 +1401,7 @@ func TestListxattr(t *testing.T) {
// Test Listxattr that has no space to return value
type listxattrTooSmall struct {
file
fstestutil.File
}
func (f *listxattrTooSmall) Listxattr(req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse, intr fs.Intr) fuse.Error {
@ -1449,7 +1432,7 @@ func TestListxattrTooSmall(t *testing.T) {
// Test Listxattr used to probe result size
type listxattrSize struct {
file
fstestutil.File
}
func (f *listxattrSize) Listxattr(req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse, intr fs.Intr) fuse.Error {
@ -1479,11 +1462,16 @@ func TestListxattrSize(t *testing.T) {
// Test Setxattr
type setxattr struct {
file
fstestutil.File
record.Setxattrs
}
func TestSetxattr(t *testing.T) {
func testSetxattr(t *testing.T, size int) {
const linux_XATTR_NAME_MAX = 64 * 1024
if size > linux_XATTR_NAME_MAX && runtime.GOOS == "linux" {
t.Skip("large xattrs are not supported by linux")
}
t.Parallel()
f := &setxattr{}
mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
@ -1492,7 +1480,9 @@ func TestSetxattr(t *testing.T) {
}
defer mnt.Close()
err = syscallx.Setxattr(mnt.Dir+"/child", "greeting", []byte("hello, world"), 0)
const g = "hello, world"
greeting := strings.Repeat(g, size/len(g)+1)[:size]
err = syscallx.Setxattr(mnt.Dir+"/child", "greeting", []byte(greeting), 0)
if err != nil {
t.Errorf("unexpected error: %v", err)
return
@ -1510,15 +1500,27 @@ func TestSetxattr(t *testing.T) {
t.Errorf("Setxattr incorrect flags: %d != %d", g, e)
}
if g, e := string(got.Xattr), "hello, world"; g != e {
if g, e := string(got.Xattr), greeting; g != e {
t.Errorf("Setxattr incorrect data: %q != %q", g, e)
}
}
func TestSetxattr(t *testing.T) {
testSetxattr(t, 20)
}
func TestSetxattr64kB(t *testing.T) {
testSetxattr(t, 64*1024)
}
func TestSetxattr16MB(t *testing.T) {
testSetxattr(t, 16*1024*1024)
}
// Test Removexattr
type removexattr struct {
file
fstestutil.File
record.Removexattrs
}
@ -1546,7 +1548,7 @@ func TestRemovexattr(t *testing.T) {
// Test default error.
type defaultErrno struct {
dir
fstestutil.Dir
}
func (f defaultErrno) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
@ -1555,7 +1557,7 @@ func (f defaultErrno) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
func TestDefaultErrno(t *testing.T) {
t.Parallel()
mnt, err := fstestutil.MountedT(t, simpleFS{defaultErrno{}})
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{defaultErrno{}})
if err != nil {
t.Fatal(err)
}
@ -1580,7 +1582,7 @@ func TestDefaultErrno(t *testing.T) {
// Test custom error.
type customErrNode struct {
dir
fstestutil.Dir
}
type myCustomError struct {
@ -1601,7 +1603,7 @@ func (f customErrNode) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
func TestCustomErrno(t *testing.T) {
t.Parallel()
mnt, err := fstestutil.MountedT(t, simpleFS{customErrNode{}})
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{customErrNode{}})
if err != nil {
t.Fatal(err)
}
@ -1736,7 +1738,9 @@ func TestMmap(t *testing.T) {
// Test direct Read.
type directRead struct{ file }
type directRead struct {
fstestutil.File
}
// explicitly not defining Attr and setting Size

View File

@ -74,7 +74,8 @@
//
// Mount Options
//
// XXX
// Behavior and metadata of the mounted file system can be changed by
// passing MountOption values to Mount.
//
package fuse
@ -120,13 +121,21 @@ type Conn struct {
// 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) (*Conn, error) {
// TODO(rsc): mount options (...string?)
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, ready, &c.MountError)
f, err := mount(dir, &conf, ready, &c.MountError)
if err != nil {
return nil, err
}
@ -170,6 +179,9 @@ type Header struct {
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 {
@ -180,6 +192,20 @@ func (h *Header) Hdr() *Header {
return h
}
func (h *Header) noResponse() {
putMessage(h.msg)
}
func (h *Header) respond(out *outHeader, n uintptr) {
h.Conn.respond(out, n)
putMessage(h.msg)
}
func (h *Header) respondData(out *outHeader, n uintptr, data []byte) {
h.Conn.respondData(out, n, data)
putMessage(h.msg)
}
// An Error is a FUSE error.
//
// Errors messages will be visible in the debug log as part of the
@ -279,17 +305,48 @@ func (h *Header) RespondError(err Error) {
// FUSE uses negative errors!
// TODO: File bug report against OSXFUSE: positive error causes kernel panic.
out := &outHeader{Error: -int32(errno), Unique: uint64(h.ID)}
h.Conn.respond(out, unsafe.Sizeof(*out))
h.respond(out, unsafe.Sizeof(*out))
}
// Maximum file write size we are prepared to receive from the kernel.
const maxWrite = 128 * 1024
const maxWrite = 16 * 1024 * 1024
// 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 = sync.Pool{
New: allocMessage,
}
func allocMessage() interface{} {
m := &message{buf: make([]byte, bufSize)}
m.hdr = (*inHeader)(unsafe.Pointer(&m.buf[0]))
return m
}
func getMessage(c *Conn) *message {
m := reqPool.Get().(*message)
m.conn = c
return m
}
func putMessage(m *message) {
m.buf = m.buf[:bufSize]
m.conn = nil
m.off = 0
reqPool.Put(m)
}
// a message represents the bytes of a single FUSE message
type message struct {
conn *Conn
@ -298,12 +355,6 @@ type message struct {
off int // offset for reading additional fields
}
func newMessage(c *Conn) *message {
m := &message{conn: c, buf: make([]byte, bufSize)}
m.hdr = (*inHeader)(unsafe.Pointer(&m.buf[0]))
return m
}
func (m *message) len() uintptr {
return uintptr(len(m.buf) - m.off)
}
@ -322,7 +373,16 @@ func (m *message) bytes() []byte {
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}
return Header{
Conn: m.conn,
ID: RequestID(h.Unique),
Node: NodeID(h.Nodeid),
Uid: h.Uid,
Gid: h.Gid,
Pid: h.Pid,
msg: m,
}
}
// fileMode returns a Go os.FileMode from a Unix mode.
@ -385,9 +445,12 @@ func (c *Conn) fd() int {
return int(c.dev.Fd())
}
// 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) {
// TODO: Some kind of buffer reuse.
m := newMessage(c)
m := getMessage(c)
loop:
c.rio.RLock()
n, err := syscall.Read(c.fd(), m.buf)
@ -398,14 +461,17 @@ loop:
goto loop
}
if err != nil && err != syscall.ENODEV {
putMessage(m)
return nil, err
}
if n <= 0 {
putMessage(m)
return nil, io.EOF
}
m.buf = m.buf[:n]
if n < inHeaderSize {
putMessage(m)
return nil, errors.New("fuse: message too short")
}
@ -421,7 +487,10 @@ loop:
}
if m.hdr.Len != uint32(n) {
return nil, fmt.Errorf("fuse: read %d opcode %d but expected %d", n, m.hdr.Opcode, m.hdr.Len)
// 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)
putMessage(m)
return nil, err
}
m.off = inHeaderSize
@ -821,6 +890,7 @@ loop:
corrupt:
Debug(malformedMessage{})
putMessage(m)
return nil, fmt.Errorf("fuse: malformed message")
unrecognized:
@ -886,6 +956,8 @@ type InitRequest struct {
Flags InitFlags
}
var _ = Request(&InitRequest{})
func (r *InitRequest) String() string {
return fmt.Sprintf("Init [%s] %d.%d ra=%d fl=%v", &r.Header, r.Major, r.Minor, r.MaxReadahead, r.Flags)
}
@ -920,7 +992,7 @@ func (r *InitRequest) Respond(resp *InitResponse) {
if out.MaxWrite > maxWrite {
out.MaxWrite = maxWrite
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
}
// A StatfsRequest requests information about the mounted file system.
@ -928,8 +1000,10 @@ type StatfsRequest struct {
Header `json:"-"`
}
var _ = Request(&StatfsRequest{})
func (r *StatfsRequest) String() string {
return fmt.Sprintf("Statfs [%s]\n", &r.Header)
return fmt.Sprintf("Statfs [%s]", &r.Header)
}
// Respond replies to the request with the given response.
@ -946,7 +1020,7 @@ func (r *StatfsRequest) Respond(resp *StatfsResponse) {
Frsize: resp.Frsize,
},
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
}
// A StatfsResponse is the response to a StatfsRequest.
@ -972,6 +1046,8 @@ type AccessRequest struct {
Mask uint32
}
var _ = Request(&AccessRequest{})
func (r *AccessRequest) String() string {
return fmt.Sprintf("Access [%s] mask=%#x", &r.Header, r.Mask)
}
@ -980,7 +1056,7 @@ func (r *AccessRequest) String() string {
// To deny access, use RespondError.
func (r *AccessRequest) Respond() {
out := &outHeader{Unique: uint64(r.ID)}
r.Conn.respond(out, unsafe.Sizeof(*out))
r.respond(out, unsafe.Sizeof(*out))
}
// An Attr is the metadata for a single file or directory.
@ -1057,6 +1133,8 @@ type GetattrRequest struct {
Header `json:"-"`
}
var _ = Request(&GetattrRequest{})
func (r *GetattrRequest) String() string {
return fmt.Sprintf("Getattr [%s]", &r.Header)
}
@ -1069,7 +1147,7 @@ func (r *GetattrRequest) Respond(resp *GetattrResponse) {
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(),
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
}
// A GetattrResponse is the response to a GetattrRequest.
@ -1099,6 +1177,8 @@ type GetxattrRequest struct {
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)
}
@ -1110,10 +1190,10 @@ func (r *GetxattrRequest) Respond(resp *GetxattrResponse) {
outHeader: outHeader{Unique: uint64(r.ID)},
Size: uint32(len(resp.Xattr)),
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
} else {
out := &outHeader{Unique: uint64(r.ID)}
r.Conn.respondData(out, unsafe.Sizeof(*out), resp.Xattr)
r.respondData(out, unsafe.Sizeof(*out), resp.Xattr)
}
}
@ -1138,6 +1218,8 @@ type ListxattrRequest struct {
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)
}
@ -1149,10 +1231,10 @@ func (r *ListxattrRequest) Respond(resp *ListxattrResponse) {
outHeader: outHeader{Unique: uint64(r.ID)},
Size: uint32(len(resp.Xattr)),
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
} else {
out := &outHeader{Unique: uint64(r.ID)}
r.Conn.respondData(out, unsafe.Sizeof(*out), resp.Xattr)
r.respondData(out, unsafe.Sizeof(*out), resp.Xattr)
}
}
@ -1179,6 +1261,8 @@ type RemovexattrRequest struct {
Name string // name of extended attribute
}
var _ = Request(&RemovexattrRequest{})
func (r *RemovexattrRequest) String() string {
return fmt.Sprintf("Removexattr [%s] %q", &r.Header, r.Name)
}
@ -1186,7 +1270,7 @@ func (r *RemovexattrRequest) String() string {
// Respond replies to the request, indicating that the attribute was removed.
func (r *RemovexattrRequest) Respond() {
out := &outHeader{Unique: uint64(r.ID)}
r.Conn.respond(out, unsafe.Sizeof(*out))
r.respond(out, unsafe.Sizeof(*out))
}
func (r *RemovexattrRequest) RespondError(err Error) {
@ -1219,14 +1303,24 @@ type SetxattrRequest struct {
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 {
return fmt.Sprintf("Setxattr [%s] %q %x fl=%v @%#x", &r.Header, r.Name, r.Xattr, r.Flags, r.Position)
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() {
out := &outHeader{Unique: uint64(r.ID)}
r.Conn.respond(out, unsafe.Sizeof(*out))
r.respond(out, unsafe.Sizeof(*out))
}
func (r *SetxattrRequest) RespondError(err Error) {
@ -1240,6 +1334,8 @@ type LookupRequest struct {
Name string
}
var _ = Request(&LookupRequest{})
func (r *LookupRequest) String() string {
return fmt.Sprintf("Lookup [%s] %q", &r.Header, r.Name)
}
@ -1256,7 +1352,7 @@ func (r *LookupRequest) Respond(resp *LookupResponse) {
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(),
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
}
// A LookupResponse is the response to a LookupRequest.
@ -1279,6 +1375,8 @@ type OpenRequest struct {
Flags OpenFlags
}
var _ = Request(&OpenRequest{})
func (r *OpenRequest) String() string {
return fmt.Sprintf("Open [%s] dir=%v fl=%v", &r.Header, r.Dir, r.Flags)
}
@ -1290,7 +1388,7 @@ func (r *OpenRequest) Respond(resp *OpenResponse) {
Fh: uint64(resp.Handle),
OpenFlags: uint32(resp.Flags),
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
}
// A OpenResponse is the response to a OpenRequest.
@ -1311,6 +1409,8 @@ type CreateRequest struct {
Mode os.FileMode
}
var _ = Request(&CreateRequest{})
func (r *CreateRequest) String() string {
return fmt.Sprintf("Create [%s] %q fl=%v mode=%v", &r.Header, r.Name, r.Flags, r.Mode)
}
@ -1331,7 +1431,7 @@ func (r *CreateRequest) Respond(resp *CreateResponse) {
Fh: uint64(resp.Handle),
OpenFlags: uint32(resp.Flags),
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
}
// A CreateResponse is the response to a CreateRequest.
@ -1352,6 +1452,8 @@ type MkdirRequest struct {
Mode os.FileMode
}
var _ = Request(&MkdirRequest{})
func (r *MkdirRequest) String() string {
return fmt.Sprintf("Mkdir [%s] %q mode=%v", &r.Header, r.Name, r.Mode)
}
@ -1368,7 +1470,7 @@ func (r *MkdirRequest) Respond(resp *MkdirResponse) {
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(),
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
}
// A MkdirResponse is the response to a MkdirRequest.
@ -1389,6 +1491,8 @@ type ReadRequest struct {
Size int
}
var _ = Request(&ReadRequest{})
func (r *ReadRequest) String() string {
return fmt.Sprintf("Read [%s] %#x %d @%#x dir=%v", &r.Header, r.Handle, r.Size, r.Offset, r.Dir)
}
@ -1396,7 +1500,7 @@ func (r *ReadRequest) String() string {
// Respond replies to the request with the given response.
func (r *ReadRequest) Respond(resp *ReadResponse) {
out := &outHeader{Unique: uint64(r.ID)}
r.Conn.respondData(out, unsafe.Sizeof(*out), resp.Data)
r.respondData(out, unsafe.Sizeof(*out), resp.Data)
}
// A ReadResponse is the response to a ReadRequest.
@ -1429,6 +1533,8 @@ type ReleaseRequest struct {
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)
}
@ -1436,7 +1542,7 @@ func (r *ReleaseRequest) String() string {
// Respond replies to the request, indicating that the handle has been released.
func (r *ReleaseRequest) Respond() {
out := &outHeader{Unique: uint64(r.ID)}
r.Conn.respond(out, unsafe.Sizeof(*out))
r.respond(out, unsafe.Sizeof(*out))
}
// A DestroyRequest is sent by the kernel when unmounting the file system.
@ -1446,6 +1552,8 @@ type DestroyRequest struct {
Header `json:"-"`
}
var _ = Request(&DestroyRequest{})
func (r *DestroyRequest) String() string {
return fmt.Sprintf("Destroy [%s]", &r.Header)
}
@ -1453,7 +1561,7 @@ func (r *DestroyRequest) String() string {
// Respond replies to the request.
func (r *DestroyRequest) Respond() {
out := &outHeader{Unique: uint64(r.ID)}
r.Conn.respond(out, unsafe.Sizeof(*out))
r.respond(out, unsafe.Sizeof(*out))
}
// A ForgetRequest is sent by the kernel when forgetting about r.Node
@ -1463,6 +1571,8 @@ type ForgetRequest struct {
N uint64
}
var _ = Request(&ForgetRequest{})
func (r *ForgetRequest) String() string {
return fmt.Sprintf("Forget [%s] %d", &r.Header, r.N)
}
@ -1470,6 +1580,7 @@ func (r *ForgetRequest) String() string {
// 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.
@ -1562,6 +1673,8 @@ type WriteRequest struct {
Flags WriteFlags
}
var _ = Request(&WriteRequest{})
func (r *WriteRequest) String() string {
return fmt.Sprintf("Write [%s] %#x %d @%d fl=%v", &r.Header, r.Handle, len(r.Data), r.Offset, r.Flags)
}
@ -1589,7 +1702,7 @@ func (r *WriteRequest) Respond(resp *WriteResponse) {
outHeader: outHeader{Unique: uint64(r.ID)},
Size: uint32(resp.Size),
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
}
// A WriteResponse replies to a write indicating how many bytes were written.
@ -1621,6 +1734,8 @@ type SetattrRequest struct {
Flags uint32 // see chflags(2)
}
var _ = Request(&SetattrRequest{})
func (r *SetattrRequest) String() string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "Setattr [%s]", &r.Header)
@ -1680,7 +1795,7 @@ func (r *SetattrRequest) Respond(resp *SetattrResponse) {
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(),
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
}
// A SetattrResponse is the response to a SetattrRequest.
@ -1703,6 +1818,8 @@ type FlushRequest struct {
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)
}
@ -1710,16 +1827,19 @@ func (r *FlushRequest) String() string {
// Respond replies to the request, indicating that the flush succeeded.
func (r *FlushRequest) Respond() {
out := &outHeader{Unique: uint64(r.ID)}
r.Conn.respond(out, unsafe.Sizeof(*out))
r.respond(out, unsafe.Sizeof(*out))
}
// A RemoveRequest asks to remove a file or directory.
// A RemoveRequest asks to remove a file or directory from the
// directory r.Node.
type RemoveRequest struct {
Header `json:"-"`
Name string // name of extended attribute
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)
}
@ -1727,7 +1847,7 @@ func (r *RemoveRequest) String() string {
// Respond replies to the request, indicating that the file was removed.
func (r *RemoveRequest) Respond() {
out := &outHeader{Unique: uint64(r.ID)}
r.Conn.respond(out, unsafe.Sizeof(*out))
r.respond(out, unsafe.Sizeof(*out))
}
// A SymlinkRequest is a request to create a symlink making NewName point to Target.
@ -1736,6 +1856,8 @@ type SymlinkRequest struct {
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)
}
@ -1752,7 +1874,7 @@ func (r *SymlinkRequest) Respond(resp *SymlinkResponse) {
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(),
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
}
// A SymlinkResponse is the response to a SymlinkRequest.
@ -1765,13 +1887,15 @@ 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) {
out := &outHeader{Unique: uint64(r.ID)}
r.Conn.respondData(out, unsafe.Sizeof(*out), []byte(target))
r.respondData(out, unsafe.Sizeof(*out), []byte(target))
}
// A LinkRequest is a request to create a hard link.
@ -1781,6 +1905,8 @@ type LinkRequest struct {
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)
}
@ -1796,7 +1922,7 @@ func (r *LinkRequest) Respond(resp *LookupResponse) {
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(),
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
}
// A RenameRequest is a request to rename a file.
@ -1806,13 +1932,15 @@ type RenameRequest struct {
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() {
out := &outHeader{Unique: uint64(r.ID)}
r.Conn.respond(out, unsafe.Sizeof(*out))
r.respond(out, unsafe.Sizeof(*out))
}
type MknodRequest struct {
@ -1822,6 +1950,8 @@ type MknodRequest struct {
Rdev uint32
}
var _ = Request(&MknodRequest{})
func (r *MknodRequest) String() string {
return fmt.Sprintf("Mknod [%s] Name %q mode %v rdev %d", &r.Header, r.Name, r.Mode, r.Rdev)
}
@ -1837,7 +1967,7 @@ func (r *MknodRequest) Respond(resp *LookupResponse) {
AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
Attr: resp.Attr.attr(),
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
}
type FsyncRequest struct {
@ -1848,13 +1978,15 @@ type FsyncRequest struct {
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() {
out := &outHeader{Unique: uint64(r.ID)}
r.Conn.respond(out, unsafe.Sizeof(*out))
r.respond(out, unsafe.Sizeof(*out))
}
// An InterruptRequest is a request to interrupt another pending request. The
@ -1864,8 +1996,11 @@ type InterruptRequest struct {
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 {
@ -1880,6 +2015,8 @@ type XXXRequest struct {
xxx
}
var _ = Request(&XXXRequest{})
func (r *XXXRequest) String() string {
return fmt.Sprintf("XXX [%s] xxx", &r.Header)
}
@ -1890,7 +2027,7 @@ func (r *XXXRequest) Respond(resp *XXXResponse) {
outHeader: outHeader{Unique: uint64(r.ID)},
xxx,
}
r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
r.respond(&out.outHeader, unsafe.Sizeof(*out))
}
// A XXXResponse is the response to a XXXRequest.

View File

@ -513,7 +513,7 @@ type writeOut struct {
Padding uint32
}
// The WriteFlags are returned in the WriteResponse.
// The WriteFlags are passed in WriteRequest.
type WriteFlags uint32
func (fl WriteFlags) String() string {

View File

@ -28,7 +28,13 @@ func main() {
}
mountpoint := flag.Arg(0)
c, err := fuse.Mount(mountpoint)
c, err := fuse.Mount(
mountpoint,
fuse.FSName("helloworld"),
fuse.Subtype("hellofs"),
fuse.LocalVolume(),
fuse.VolumeName("Hello world!"),
)
if err != nil {
log.Fatal(err)
}

BIN
Godeps/_workspace/src/bazil.org/fuse/hellofs/hellofs generated vendored Normal file

Binary file not shown.

View File

@ -3,9 +3,11 @@ package fuse
import (
"bytes"
"errors"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"syscall"
)
@ -50,15 +52,22 @@ func openOSXFUSEDev() (*os.File, error) {
}
}
func callMount(dir string, f *os.File, ready chan<- struct{}, errp *error) error {
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 OS X: %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.
//
// TODO add buffer reuse, bump this up significantly
//
// OSXFUSE seems to ignore InitResponse.MaxWrite, and uses
// this instead.
"-o", "iosize="+strconv.FormatUint(maxWrite, 10),
@ -95,7 +104,7 @@ func callMount(dir string, f *os.File, ready chan<- struct{}, errp *error) error
return err
}
func mount(dir string, ready chan<- struct{}, errp *error) (*os.File, error) {
func mount(dir string, conf *MountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
f, err := openOSXFUSEDev()
if err == errNotLoaded {
err = loadOSXFUSE()
@ -108,7 +117,7 @@ func mount(dir string, ready chan<- struct{}, errp *error) (*os.File, error) {
if err != nil {
return nil, err
}
err = callMount(dir, f, ready, errp)
err = callMount(dir, conf, f, ready, errp)
if err != nil {
f.Close()
return nil, err

View File

@ -8,7 +8,7 @@ import (
"syscall"
)
func mount(dir string, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) {
func mount(dir string, conf *MountConfig, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) {
// linux mount is never delayed
close(ready)
@ -19,7 +19,12 @@ func mount(dir string, ready chan<- struct{}, errp *error) (fusefd *os.File, err
defer syscall.Close(fds[0])
defer syscall.Close(fds[1])
cmd := exec.Command("fusermount", "--", dir)
cmd := exec.Command(
"fusermount",
"-o", conf.getOptions(),
"--",
dir,
)
cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3")
writeFile := os.NewFile(uintptr(fds[0]), "fusermount-child-writes")

100
Godeps/_workspace/src/bazil.org/fuse/options.go generated vendored Normal file
View File

@ -0,0 +1,100 @@
package fuse
import (
"errors"
"strings"
)
// MountConfig holds the configuration for a mount operation.
// Use it by passing MountOption values to Mount.
type MountConfig struct {
options map[string]string
}
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, ",")
}
// MountOption is passed to Mount to change the behavior of the mount.
type MountOption func(*MountConfig) error
// FSName sets the file system name (also called source) that is
// visible in the list of mounted file systems.
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.
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.
func AllowRoot() MountOption {
return func(conf *MountConfig) error {
if _, ok := conf.options["allow_other"]; ok {
return ErrCannotCombineAllowOtherAndAllowRoot
}
conf.options["allow_root"] = ""
return nil
}
}

13
Godeps/_workspace/src/bazil.org/fuse/options_darwin.go generated vendored Normal file
View File

@ -0,0 +1,13 @@
package fuse
func localVolume(conf *MountConfig) error {
conf.options["local"] = ""
return nil
}
func volumeName(name string) MountOption {
return func(conf *MountConfig) error {
conf.options["volname"] = name
return nil
}
}

View File

@ -0,0 +1,27 @@
package fuse_test
import (
"testing"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil"
)
func TestMountOptionCommaError(t *testing.T) {
t.Parallel()
// this test is not tied to FSName, but needs just some option
// with string content
var name = "FuseTest,Marker"
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
fuse.FSName(name),
)
switch {
case err == nil:
mnt.Close()
t.Fatal("expected an error about commas")
case err.Error() == `mount options cannot contain commas on OS X: "fsname"="FuseTest,Marker"`:
// all good
default:
t.Fatalf("expected an error about commas, got: %v", err)
}
}

13
Godeps/_workspace/src/bazil.org/fuse/options_linux.go generated vendored Normal file
View File

@ -0,0 +1,13 @@
package fuse
func dummyOption(conf *MountConfig) error {
return nil
}
func localVolume(conf *MountConfig) error {
return nil
}
func volumeName(name string) MountOption {
return dummyOption
}

141
Godeps/_workspace/src/bazil.org/fuse/options_test.go generated vendored Normal file
View File

@ -0,0 +1,141 @@
package fuse_test
import (
"runtime"
"testing"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil"
)
func init() {
fstestutil.DebugByDefault()
}
func TestMountOptionFSName(t *testing.T) {
t.Parallel()
const name = "FuseTestMarker"
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
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) {
t.Parallel()
var name = "FuseTest" + evil + "Marker"
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
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")
}
t.Parallel()
const name = "FuseTestMarker"
mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
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{}},
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{}},
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)
}
}