Commit 3ad26b23 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Add fuse.MountFileSystem() to encapsulate some main() boilerplate.

parent 6d31d546
...@@ -10,7 +10,7 @@ import ( ...@@ -10,7 +10,7 @@ import (
func main() { func main() {
debug := flag.Bool("debug", false, "debug on") debug := flag.Bool("debug", false, "debug on")
threaded := flag.Bool("threaded", true, "debug on") threaded := flag.Bool("threaded", true, "threading on")
delcache_ttl := flag.Float64("deletion_cache_ttl", 5.0, "Deletion cache TTL in seconds.") delcache_ttl := flag.Float64("deletion_cache_ttl", 5.0, "Deletion cache TTL in seconds.")
branchcache_ttl := flag.Float64("branchcache_ttl", 5.0, "Branch cache TTL in seconds.") branchcache_ttl := flag.Float64("branchcache_ttl", 5.0, "Branch cache TTL in seconds.")
deldirname := flag.String( deldirname := flag.String(
...@@ -21,8 +21,6 @@ func main() { ...@@ -21,8 +21,6 @@ func main() {
fmt.Println("Usage:\n main MOUNTPOINT BASEDIR") fmt.Println("Usage:\n main MOUNTPOINT BASEDIR")
os.Exit(2) os.Exit(2)
} }
mountpoint := flag.Arg(0)
ufsOptions := unionfs.UnionFsOptions{ ufsOptions := unionfs.UnionFsOptions{
DeletionCacheTTLSecs: *delcache_ttl, DeletionCacheTTLSecs: *delcache_ttl,
BranchCacheTTLSecs: *branchcache_ttl, BranchCacheTTLSecs: *branchcache_ttl,
...@@ -39,16 +37,14 @@ func main() { ...@@ -39,16 +37,14 @@ func main() {
} }
gofs := unionfs.NewAutoUnionFs(flag.Arg(1), options) gofs := unionfs.NewAutoUnionFs(flag.Arg(1), options)
conn := fuse.NewFileSystemConnector(gofs, nil)
conn.Debug = *debug state, conn, err := fuse.MountFileSystem(flag.Arg(0), gofs, nil)
mountState := fuse.NewMountState(conn)
mountState.Debug = *debug
fmt.Printf("Go-FUSE %v Mounting AutoUnionFs...\n", fuse.Version())
err := mountState.Mount(mountpoint)
if err != nil { if err != nil {
fmt.Printf("Mount fail: %v\n", err) fmt.Printf("Mount fail: %v\n", err)
os.Exit(1) os.Exit(1)
} }
fmt.Printf("Mounted!\n")
mountState.Loop(*threaded) conn.Debug = *debug
state.Debug = *debug
state.Loop(*threaded)
} }
...@@ -13,6 +13,7 @@ var _ = log.Printf ...@@ -13,6 +13,7 @@ var _ = log.Printf
func main() { func main() {
// Scans the arg list and sets up flags // Scans the arg list and sets up flags
debug := flag.Bool("debug", false, "debug on")
flag.Parse() flag.Parse()
if flag.NArg() < 1 { if flag.NArg() < 1 {
// TODO - where to get program name? // TODO - where to get program name?
...@@ -21,12 +22,12 @@ func main() { ...@@ -21,12 +22,12 @@ func main() {
} }
fs := zipfs.NewMultiZipFs() fs := zipfs.NewMultiZipFs()
state := fuse.NewMountState(fs.Connector) state, _, err := fuse.MountFileSystem(flag.Arg(0), fs, nil)
if err != nil {
mountPoint := flag.Arg(0) fmt.Printf("Mount fail: %v\n", err)
state.Debug = true os.Exit(1)
state.Mount(mountPoint) }
fmt.Printf("Mounted %s\n", mountPoint) state.Debug = *debug
state.Loop(true) state.Loop(true)
} }
...@@ -21,7 +21,6 @@ func main() { ...@@ -21,7 +21,6 @@ func main() {
fmt.Println("Usage:\n main MOUNTPOINT RW-DIRECTORY RO-DIRECTORY ...") fmt.Println("Usage:\n main MOUNTPOINT RW-DIRECTORY RO-DIRECTORY ...")
os.Exit(2) os.Exit(2)
} }
mountpoint := flag.Arg(0)
ufsOptions := unionfs.UnionFsOptions{ ufsOptions := unionfs.UnionFsOptions{
DeletionCacheTTLSecs: *delcache_ttl, DeletionCacheTTLSecs: *delcache_ttl,
...@@ -35,15 +34,12 @@ func main() { ...@@ -35,15 +34,12 @@ func main() {
} }
ufs := unionfs.NewUnionFs("unionfs", fses, ufsOptions) ufs := unionfs.NewUnionFs("unionfs", fses, ufsOptions)
conn := fuse.NewFileSystemConnector(ufs, nil) mountState, _, err := fuse.MountFileSystem(flag.Arg(0), ufs, nil)
mountState := fuse.NewMountState(conn)
mountState.Debug = *debug
fmt.Printf("Go-FUSE Version %v.\nMounting UnionFs...\n", fuse.Version())
err := mountState.Mount(mountpoint)
if err != nil { if err != nil {
fmt.Printf("Mount fail: %v\n", err) fmt.Printf("Mount fail: %v\n", err)
os.Exit(1) os.Exit(1)
} }
fmt.Printf("Mounted!\n")
mountState.Debug = *debug
mountState.Loop(*threaded) mountState.Loop(*threaded)
} }
...@@ -35,19 +35,18 @@ func main() { ...@@ -35,19 +35,18 @@ func main() {
fs = debugFs fs = debugFs
} }
conn := fuse.NewFileSystemConnector(fs, nil) state, conn, err := fuse.MountFileSystem(flag.Arg(0), fs, nil)
state := fuse.NewMountState(conn) if err != nil {
fmt.Printf("Mount fail: %v\n", err)
os.Exit(1)
}
if *latencies { if *latencies {
debugFs.AddFileSystemConnector(conn) debugFs.AddFileSystemConnector(conn)
debugFs.AddMountState(state) debugFs.AddMountState(state)
} }
mountPoint := flag.Arg(0)
state.SetRecordStatistics(*latencies) state.SetRecordStatistics(*latencies)
state.Debug = *debug state.Debug = *debug
state.Mount(mountPoint)
fmt.Printf("Mounted %s - PID %s\n", mountPoint, fuse.MyPID())
state.Loop(true) state.Loop(true)
} }
...@@ -17,6 +17,7 @@ GOFILES=\ ...@@ -17,6 +17,7 @@ GOFILES=\
loopback.go \ loopback.go \
misc.go \ misc.go \
mount.go \ mount.go \
mountstate.go \
opcode.go \ opcode.go \
pathdebug.go \ pathdebug.go \
pathfilesystem.go \ pathfilesystem.go \
......
...@@ -124,14 +124,12 @@ func NewFile() *MutableDataFile { ...@@ -124,14 +124,12 @@ func NewFile() *MutableDataFile {
func TestFSetAttr(t *testing.T) { func TestFSetAttr(t *testing.T) {
fs := &FSetAttrFs{} fs := &FSetAttrFs{}
c := NewFileSystemConnector(fs, nil)
state := NewMountState(c)
dir := MakeTempDir() dir := MakeTempDir()
defer os.RemoveAll(dir)
state.Mount(dir) state, _, err := MountFileSystem(dir, fs, nil)
CheckSuccess(err)
state.Debug = true state.Debug = true
defer state.Unmount() defer state.Unmount()
defer os.RemoveAll(dir)
go state.Loop(false) go state.Loop(false)
......
package fuse package fuse
import ( import (
"log"
"os" "os"
"syscall" "fmt"
"time"
)
const (
// bufSize should be a power of two to minimize lossage in
// BufferPool. The minimum is 8k, but it doesn't cost anything to
// use a much larger buffer.
bufSize = (1 << 16)
maxRead = bufSize - PAGESIZE
) )
// MountState contains the logic for reading from the FUSE device and func MountFileSystem(mountpoint string, fs FileSystem, opts *MountOptions) (*MountState, *FileSystemConnector, os.Error) {
// translating it to RawFileSystem interface calls. conn := NewFileSystemConnector(fs, opts)
type MountState struct { mountState := NewMountState(conn)
// Empty if unmounted. fmt.Printf("Go-FUSE Version %v.\nMounting...\n", Version())
mountPoint string err := mountState.Mount(mountpoint)
fileSystem RawFileSystem
// I/O with kernel and daemon.
mountFile *os.File
// Dump debug info onto stdout.
Debug bool
// For efficient reads and writes.
buffers *BufferPoolImpl
*LatencyMap
kernelSettings InitIn
}
func (me *MountState) KernelSettings() InitIn {
return me.kernelSettings
}
func (me *MountState) MountPoint() string {
return me.mountPoint
}
// Mount filesystem on mountPoint.
func (me *MountState) Mount(mountPoint string) os.Error {
file, mp, err := mount(mountPoint)
if err != nil {
return err
}
me.mountPoint = mp
me.mountFile = file
return nil
}
func (me *MountState) SetRecordStatistics(record bool) {
if record {
me.LatencyMap = NewLatencyMap()
} else {
me.LatencyMap = nil
}
}
func (me *MountState) Unmount() os.Error {
// Todo: flush/release all files/dirs?
result := unmount(me.mountPoint)
if result == nil {
me.mountPoint = ""
}
return result
}
func NewMountState(fs RawFileSystem) *MountState {
me := new(MountState)
me.mountPoint = ""
me.fileSystem = fs
me.buffers = NewBufferPool()
return me
}
func (me *MountState) Latencies() map[string]float64 {
return me.LatencyMap.Latencies(1e-3)
}
func (me *MountState) OperationCounts() map[string]int {
return me.LatencyMap.Counts()
}
func (me *MountState) BufferPoolStats() string {
return me.buffers.String()
}
func (me *MountState) newRequest(oldReq *request) *request {
if oldReq != nil {
me.buffers.FreeBuffer(oldReq.flatData)
*oldReq = request{
status: OK,
inputBuf: oldReq.inputBuf[0:bufSize],
}
return oldReq
}
return &request{
status: OK,
inputBuf: me.buffers.AllocBuffer(bufSize),
}
}
func (me *MountState) readRequest(req *request) os.Error {
n, err := me.mountFile.Read(req.inputBuf)
// If we start timing before the read, we may take into
// account waiting for input into the timing.
if me.LatencyMap != nil {
req.startNs = time.Nanoseconds()
}
req.inputBuf = req.inputBuf[0:n]
return err
}
func (me *MountState) recordStats(req *request) {
if me.LatencyMap != nil {
endNs := time.Nanoseconds()
dt := endNs - req.startNs
opname := operationName(req.inHeader.opcode)
me.LatencyMap.AddMany(
[]LatencyArg{
{opname, "", dt},
{opname + "-write", "", endNs - req.preWriteNs}})
}
}
// Loop initiates the FUSE loop. Normally, callers should run Loop()
// and wait for it to exit, but tests will want to run this in a
// goroutine.
//
// If threaded is given, each filesystem operation executes in a
// separate goroutine.
func (me *MountState) Loop(threaded bool) {
// To limit scheduling overhead, we spawn multiple read loops.
// This means that the request once read does not need to be
// assigned to another thread, so it avoids a context switch.
if threaded {
for i := 0; i < _BACKGROUND_TASKS; i++ {
go me.loop()
}
}
me.loop()
me.mountFile.Close()
}
func (me *MountState) loop() {
var lastReq *request
for {
req := me.newRequest(lastReq)
lastReq = req
err := me.readRequest(req)
if err != nil {
errNo := OsErrorToErrno(err)
// Retry.
if errNo == syscall.ENOENT {
continue
}
// According to fuse_chan_receive()
if errNo == syscall.ENODEV {
break
}
// What I see on linux-x86 2.6.35.10.
if errNo == syscall.ENOSYS {
break
}
log.Printf("Failed to read from fuse conn: %v", err)
break
}
me.handleRequest(req)
}
}
func (me *MountState) handleRequest(req *request) {
defer me.recordStats(req)
req.parse()
if req.handler == nil {
req.status = ENOSYS
}
if req.status.Ok() && me.Debug {
log.Println(req.InputDebug())
}
if req.status.Ok() && req.handler.Func == nil {
log.Printf("Unimplemented opcode %v", req.inHeader.opcode)
req.status = ENOSYS
}
if req.status.Ok() {
req.handler.Func(me, req)
}
me.write(req)
}
func (me *MountState) write(req *request) {
// If we try to write OK, nil, we will get
// error: writer: Writev [[16 0 0 0 0 0 0 0 17 0 0 0 0 0 0 0]]
// failed, err: writev: no such file or directory
if req.inHeader.opcode == _OP_FORGET {
return
}
req.serialize()
if me.Debug {
log.Println(req.OutputDebug())
}
if me.LatencyMap != nil {
req.preWriteNs = time.Nanoseconds()
}
if req.outHeaderBytes == nil {
return
}
var err os.Error
if req.flatData == nil {
_, err = me.mountFile.Write(req.outHeaderBytes)
} else {
_, err = Writev(me.mountFile.Fd(),
[][]byte{req.outHeaderBytes, req.flatData})
}
if err != nil { if err != nil {
log.Printf("writer: Write/Writev %v failed, err: %v. opcode: %v", return nil, nil, err
req.outHeaderBytes, err, operationName(req.inHeader.opcode))
} }
fmt.Println("Mounted!")
return mountState, conn, nil
} }
...@@ -92,20 +92,18 @@ func TestXAttrRead(t *testing.T) { ...@@ -92,20 +92,18 @@ func TestXAttrRead(t *testing.T) {
"user.attr1": []byte("val1"), "user.attr1": []byte("val1"),
"user.attr2": []byte("val2")} "user.attr2": []byte("val2")}
xfs := NewXAttrFs(nm, golden) xfs := NewXAttrFs(nm, golden)
connector := NewFileSystemConnector(xfs, nil)
mountPoint := MakeTempDir() mountPoint := MakeTempDir()
defer os.RemoveAll(mountPoint) defer os.RemoveAll(mountPoint)
state := NewMountState(connector) state, _, err := MountFileSystem(mountPoint, xfs, nil)
state.Mount(mountPoint) CheckSuccess(err)
state.Debug = true state.Debug = true
defer state.Unmount() defer state.Unmount()
go state.Loop(false) go state.Loop(false)
mounted := filepath.Join(mountPoint, nm) mounted := filepath.Join(mountPoint, nm)
_, err := os.Lstat(mounted) _, err = os.Lstat(mounted)
if err != nil { if err != nil {
t.Error("Unexpected stat error", err) t.Error("Unexpected stat error", err)
} }
......
...@@ -44,9 +44,8 @@ func setup(t *testing.T) (workdir string, cleanup func()) { ...@@ -44,9 +44,8 @@ func setup(t *testing.T) (workdir string, cleanup func()) {
WriteFile(wd+"/ro/file2", "file2") WriteFile(wd+"/ro/file2", "file2")
fs := NewAutoUnionFs(wd+"/store", testAOpts) fs := NewAutoUnionFs(wd+"/store", testAOpts)
connector := fuse.NewFileSystemConnector(fs, &testAOpts.MountOptions) state, _, err := fuse.MountFileSystem(wd + "/mount", fs, &testAOpts.MountOptions)
state := fuse.NewMountState(connector) CheckSuccess(err)
state.Mount(wd + "/mount")
state.Debug = true state.Debug = true
go state.Loop(false) go state.Loop(false)
...@@ -76,7 +75,7 @@ func TestAutoFsSymlink(t *testing.T) { ...@@ -76,7 +75,7 @@ func TestAutoFsSymlink(t *testing.T) {
// Need time for the unmount to be noticed. // Need time for the unmount to be noticed.
log.Println("sleeping...") log.Println("sleeping...")
time.Sleep(entryTtl * 2e9) time.Sleep(2 * entryTtl * 1e9)
fi, _ = os.Lstat(wd + "/mount/manual1") fi, _ = os.Lstat(wd + "/mount/manual1")
if fi != nil { if fi != nil {
......
...@@ -48,9 +48,8 @@ func setupUfs(t *testing.T) (workdir string, cleanup func()) { ...@@ -48,9 +48,8 @@ func setupUfs(t *testing.T) (workdir string, cleanup func()) {
NegativeTimeout: entryTtl, NegativeTimeout: entryTtl,
} }
connector := fuse.NewFileSystemConnector(ufs, opts) state, _, err := fuse.MountFileSystem(wd + "/mount", ufs, opts)
state := fuse.NewMountState(connector) CheckSuccess(err)
state.Mount(wd + "/mount")
state.Debug = true state.Debug = true
go state.Loop(false) go state.Loop(false)
......
...@@ -87,10 +87,14 @@ func NewMultiZipFs() *MultiZipFs { ...@@ -87,10 +87,14 @@ func NewMultiZipFs() *MultiZipFs {
m.zips = make(map[string]*MemTreeFileSystem) m.zips = make(map[string]*MemTreeFileSystem)
m.pendingZips = make(map[string]bool) m.pendingZips = make(map[string]bool)
m.dirZipFileMap = make(map[string]string) m.dirZipFileMap = make(map[string]string)
m.Connector = fuse.NewFileSystemConnector(m, nil)
return m return m
} }
func (me *MultiZipFs) Mount(connector *fuse.FileSystemConnector) fuse.Status {
me.Connector = connector
return fuse.OK
}
func (me *MultiZipFs) OpenDir(name string) (stream chan fuse.DirEntry, code fuse.Status) { func (me *MultiZipFs) OpenDir(name string) (stream chan fuse.DirEntry, code fuse.Status) {
me.lock.RLock() me.lock.RLock()
defer me.lock.RUnlock() defer me.lock.RUnlock()
......
...@@ -8,12 +8,8 @@ import ( ...@@ -8,12 +8,8 @@ import (
"time" "time"
) )
func CheckSuccess(err os.Error) { var _ = log.Printf
if err != nil { var CheckSuccess = fuse.CheckSuccess
log.Println(err)
panic("error")
}
}
func TestMultiZipFs(t *testing.T) { func TestMultiZipFs(t *testing.T) {
var err os.Error var err os.Error
...@@ -22,14 +18,13 @@ func TestMultiZipFs(t *testing.T) { ...@@ -22,14 +18,13 @@ func TestMultiZipFs(t *testing.T) {
zipFile := wd + "/test.zip" zipFile := wd + "/test.zip"
fs := NewMultiZipFs() fs := NewMultiZipFs()
state := fuse.NewMountState(fs.Connector)
mountPoint := fuse.MakeTempDir() mountPoint := fuse.MakeTempDir()
state, _, err := fuse.MountFileSystem(mountPoint, fs, nil)
defer os.RemoveAll(mountPoint) defer os.RemoveAll(mountPoint)
state.Debug = true
err = state.Mount(mountPoint)
defer state.Unmount()
CheckSuccess(err) CheckSuccess(err)
defer state.Unmount()
state.Debug = true
go state.Loop(true) go state.Loop(true)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment