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 (
func main() {
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.")
branchcache_ttl := flag.Float64("branchcache_ttl", 5.0, "Branch cache TTL in seconds.")
deldirname := flag.String(
......@@ -21,8 +21,6 @@ func main() {
fmt.Println("Usage:\n main MOUNTPOINT BASEDIR")
os.Exit(2)
}
mountpoint := flag.Arg(0)
ufsOptions := unionfs.UnionFsOptions{
DeletionCacheTTLSecs: *delcache_ttl,
BranchCacheTTLSecs: *branchcache_ttl,
......@@ -39,16 +37,14 @@ func main() {
}
gofs := unionfs.NewAutoUnionFs(flag.Arg(1), options)
conn := fuse.NewFileSystemConnector(gofs, nil)
conn.Debug = *debug
mountState := fuse.NewMountState(conn)
mountState.Debug = *debug
fmt.Printf("Go-FUSE %v Mounting AutoUnionFs...\n", fuse.Version())
err := mountState.Mount(mountpoint)
state, conn, err := fuse.MountFileSystem(flag.Arg(0), gofs, nil)
if err != nil {
fmt.Printf("Mount fail: %v\n", err)
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
func main() {
// Scans the arg list and sets up flags
debug := flag.Bool("debug", false, "debug on")
flag.Parse()
if flag.NArg() < 1 {
// TODO - where to get program name?
......@@ -21,12 +22,12 @@ func main() {
}
fs := zipfs.NewMultiZipFs()
state := fuse.NewMountState(fs.Connector)
mountPoint := flag.Arg(0)
state.Debug = true
state.Mount(mountPoint)
fmt.Printf("Mounted %s\n", mountPoint)
state, _, err := fuse.MountFileSystem(flag.Arg(0), fs, nil)
if err != nil {
fmt.Printf("Mount fail: %v\n", err)
os.Exit(1)
}
state.Debug = *debug
state.Loop(true)
}
......@@ -21,7 +21,6 @@ func main() {
fmt.Println("Usage:\n main MOUNTPOINT RW-DIRECTORY RO-DIRECTORY ...")
os.Exit(2)
}
mountpoint := flag.Arg(0)
ufsOptions := unionfs.UnionFsOptions{
DeletionCacheTTLSecs: *delcache_ttl,
......@@ -35,15 +34,12 @@ func main() {
}
ufs := unionfs.NewUnionFs("unionfs", fses, ufsOptions)
conn := fuse.NewFileSystemConnector(ufs, nil)
mountState := fuse.NewMountState(conn)
mountState.Debug = *debug
fmt.Printf("Go-FUSE Version %v.\nMounting UnionFs...\n", fuse.Version())
err := mountState.Mount(mountpoint)
mountState, _, err := fuse.MountFileSystem(flag.Arg(0), ufs, nil)
if err != nil {
fmt.Printf("Mount fail: %v\n", err)
os.Exit(1)
}
fmt.Printf("Mounted!\n")
mountState.Debug = *debug
mountState.Loop(*threaded)
}
......@@ -35,19 +35,18 @@ func main() {
fs = debugFs
}
conn := fuse.NewFileSystemConnector(fs, nil)
state := fuse.NewMountState(conn)
state, conn, err := fuse.MountFileSystem(flag.Arg(0), fs, nil)
if err != nil {
fmt.Printf("Mount fail: %v\n", err)
os.Exit(1)
}
if *latencies {
debugFs.AddFileSystemConnector(conn)
debugFs.AddMountState(state)
}
mountPoint := flag.Arg(0)
state.SetRecordStatistics(*latencies)
state.Debug = *debug
state.Mount(mountPoint)
fmt.Printf("Mounted %s - PID %s\n", mountPoint, fuse.MyPID())
state.Loop(true)
}
......@@ -17,6 +17,7 @@ GOFILES=\
loopback.go \
misc.go \
mount.go \
mountstate.go \
opcode.go \
pathdebug.go \
pathfilesystem.go \
......
......@@ -124,14 +124,12 @@ func NewFile() *MutableDataFile {
func TestFSetAttr(t *testing.T) {
fs := &FSetAttrFs{}
c := NewFileSystemConnector(fs, nil)
state := NewMountState(c)
dir := MakeTempDir()
state.Mount(dir)
defer os.RemoveAll(dir)
state, _, err := MountFileSystem(dir, fs, nil)
CheckSuccess(err)
state.Debug = true
defer state.Unmount()
defer os.RemoveAll(dir)
go state.Loop(false)
......
package fuse
import (
"log"
"os"
"syscall"
"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
"fmt"
)
// MountState contains the logic for reading from the FUSE device and
// translating it to RawFileSystem interface calls.
type MountState struct {
// Empty if unmounted.
mountPoint string
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})
}
func MountFileSystem(mountpoint string, fs FileSystem, opts *MountOptions) (*MountState, *FileSystemConnector, os.Error) {
conn := NewFileSystemConnector(fs, opts)
mountState := NewMountState(conn)
fmt.Printf("Go-FUSE Version %v.\nMounting...\n", Version())
err := mountState.Mount(mountpoint)
if err != nil {
log.Printf("writer: Write/Writev %v failed, err: %v. opcode: %v",
req.outHeaderBytes, err, operationName(req.inHeader.opcode))
return nil, nil, err
}
fmt.Println("Mounted!")
return mountState, conn, nil
}
......@@ -92,20 +92,18 @@ func TestXAttrRead(t *testing.T) {
"user.attr1": []byte("val1"),
"user.attr2": []byte("val2")}
xfs := NewXAttrFs(nm, golden)
connector := NewFileSystemConnector(xfs, nil)
mountPoint := MakeTempDir()
defer os.RemoveAll(mountPoint)
state := NewMountState(connector)
state.Mount(mountPoint)
state, _, err := MountFileSystem(mountPoint, xfs, nil)
CheckSuccess(err)
state.Debug = true
defer state.Unmount()
go state.Loop(false)
mounted := filepath.Join(mountPoint, nm)
_, err := os.Lstat(mounted)
_, err = os.Lstat(mounted)
if err != nil {
t.Error("Unexpected stat error", err)
}
......
......@@ -44,9 +44,8 @@ func setup(t *testing.T) (workdir string, cleanup func()) {
WriteFile(wd+"/ro/file2", "file2")
fs := NewAutoUnionFs(wd+"/store", testAOpts)
connector := fuse.NewFileSystemConnector(fs, &testAOpts.MountOptions)
state := fuse.NewMountState(connector)
state.Mount(wd + "/mount")
state, _, err := fuse.MountFileSystem(wd + "/mount", fs, &testAOpts.MountOptions)
CheckSuccess(err)
state.Debug = true
go state.Loop(false)
......@@ -76,7 +75,7 @@ func TestAutoFsSymlink(t *testing.T) {
// Need time for the unmount to be noticed.
log.Println("sleeping...")
time.Sleep(entryTtl * 2e9)
time.Sleep(2 * entryTtl * 1e9)
fi, _ = os.Lstat(wd + "/mount/manual1")
if fi != nil {
......
......@@ -48,9 +48,8 @@ func setupUfs(t *testing.T) (workdir string, cleanup func()) {
NegativeTimeout: entryTtl,
}
connector := fuse.NewFileSystemConnector(ufs, opts)
state := fuse.NewMountState(connector)
state.Mount(wd + "/mount")
state, _, err := fuse.MountFileSystem(wd + "/mount", ufs, opts)
CheckSuccess(err)
state.Debug = true
go state.Loop(false)
......
......@@ -87,10 +87,14 @@ func NewMultiZipFs() *MultiZipFs {
m.zips = make(map[string]*MemTreeFileSystem)
m.pendingZips = make(map[string]bool)
m.dirZipFileMap = make(map[string]string)
m.Connector = fuse.NewFileSystemConnector(m, nil)
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) {
me.lock.RLock()
defer me.lock.RUnlock()
......
......@@ -8,12 +8,8 @@ import (
"time"
)
func CheckSuccess(err os.Error) {
if err != nil {
log.Println(err)
panic("error")
}
}
var _ = log.Printf
var CheckSuccess = fuse.CheckSuccess
func TestMultiZipFs(t *testing.T) {
var err os.Error
......@@ -22,14 +18,13 @@ func TestMultiZipFs(t *testing.T) {
zipFile := wd + "/test.zip"
fs := NewMultiZipFs()
state := fuse.NewMountState(fs.Connector)
mountPoint := fuse.MakeTempDir()
state, _, err := fuse.MountFileSystem(mountPoint, fs, nil)
defer os.RemoveAll(mountPoint)
state.Debug = true
err = state.Mount(mountPoint)
defer state.Unmount()
CheckSuccess(err)
defer state.Unmount()
state.Debug = 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