Commit 842bb1b0 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Reorganization

* update README

* Split off zipfs into zipfs/ directory

* Remove marginally useful stackfs example
parent a54bca4b
GO-FUSE: native bindings for the FUSE kernel module.
GETTING STARTED
HIGHLIGHTS
* High speed: within a factor 2 of C/C++ based filesystems on
filesystem benchmarks, using the gc compiler. For many real world
applications, the difference will be negligible.
* Supports in-process mounting of different PathFileSystems onto each
other.
* Includes two fleshed out examples, zipfs and unionfs.
EXAMPLES
* examplelib/zipfs.go contains a small and simple read-only filesystem
for zip files. The corresponding command is in example/zipfs/ . For
......@@ -12,6 +24,9 @@ GETTING STARTED
ls /tmp/mountpoint
fusermount -u /tmp/mountpoint
* examplelib/multizipfs.go shows how to use in-process mounts to
combine multiple Go-FUSE filesystems into a larger filesystem.
* fuse/loopback.go supports all the implemented functionality. A
binary to run is in example/loopback/ . For example
......@@ -20,7 +35,21 @@ GETTING STARTED
ls /tmp/mountpoint
fusermount -u /tmp/mountpoint
* unionfs/unionfs.go: implements a union mount using 1 R/W branch, and
multiple R/O branches.
mkdir -p /tmp/mountpoint /tmp/writable
example/unionfs/unionfs /tmp/mountpoint /tmp/writable /usr &
ls /tmp/mountpoint
ls -l /tmp/mountpoint/bin/vi
rm /tmp/mountpoint/bin/vi
ls -l /tmp/mountpoint/bin/vi
cat /tmp/writable/*DELETION*/*
* union/autounionfs.go: creates UnionFs mounts automatically based on
existence of READONLY symlinks.
Tested on:
- x86 32bits (Fedora 14).
......@@ -28,14 +57,6 @@ Tested on:
BENCHMARKS
In benchmarks, Go-fuse's loopback system is less than 2 times slower
than equivalent C++ based FUSE filesystems. In practical applications,
the the file system client processing may mask some of these
performance gaps.
CREDITS
* Inspired by Taru Karttunen's package, https://bitbucket.org/taruti/go-extra.
......
#!/bin/sh
set -eux
for d in fuse examplelib example/loopback example/zipfs \
example/bulkstat example/multizip
for d in fuse zipfs unionfs example/loopback example/zipfs \
example/bulkstat example/multizip example/unionfs \
example/autounionfs ; \
do
gomake -C $d "$@"
done
for d in fuse examplelib
for d in fuse zipfs unionfs
do
(cd $d && gotest )
done
# Use "gomake install" to build and install this package.
include $(GOROOT)/src/Make.inc
TARG=autounionfs
GOFILES=main.go
DEPS=../../fuse ../../unionfs
include $(GOROOT)/src/Make.cmd
package main
import (
"flag"
"fmt"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/unionfs"
"os"
)
func main() {
debug := flag.Bool("debug", false, "debug on")
threaded := flag.Bool("threaded", true, "debug 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(
"deletion_dirname", "GOUNIONFS_DELETIONS", "Directory name to use for deletions.")
flag.Parse()
if len(flag.Args()) < 2 {
fmt.Println("Usage:\n main MOUNTPOINT BASEDIR")
os.Exit(2)
}
mountpoint := flag.Arg(0)
ufsOptions := unionfs.UnionFsOptions{
DeletionCacheTTLSecs: *delcache_ttl,
BranchCacheTTLSecs: *branchcache_ttl,
DeletionDirName: *deldirname,
}
options := unionfs.AutoUnionFsOptions{
UnionFsOptions: ufsOptions,
}
gofs := unionfs.NewAutoUnionFs(flag.Arg(1), options)
conn := fuse.NewPathFileSystemConnector(gofs)
mountState := fuse.NewMountState(conn)
mountState.Debug = *debug
fmt.Printf("Mounting...\n")
err := mountState.Mount(mountpoint)
if err != nil {
fmt.Printf("MountFuse fail: %v\n", err)
os.Exit(1)
}
fmt.Printf("Mounted!\n")
mountState.Loop(*threaded)
}
......@@ -3,7 +3,7 @@ include $(GOROOT)/src/Make.inc
TARG=multizip
GOFILES=multizip.go
DEPS=../../fuse ../../examplelib
DEPS=../../fuse ../../zipfs
include $(GOROOT)/src/Make.cmd
......@@ -2,7 +2,7 @@ package main
import (
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/examplelib"
zipfs "github.com/hanwen/go-fuse/zipfs"
"fmt"
"flag"
"log"
......@@ -20,7 +20,7 @@ func main() {
os.Exit(2)
}
fs := examplelib.NewMultiZipFs()
fs := zipfs.NewMultiZipFs()
state := fuse.NewMountState(fs.Connector)
mountPoint := flag.Arg(0)
......
......@@ -2,8 +2,9 @@
include $(GOROOT)/src/Make.inc
TARG=zipfs
GOFILES=zipfs.go
DEPS=../../fuse ../../examplelib
GOFILES=main.go
DEPS=../../fuse ../../zipfs
include $(GOROOT)/src/Make.cmd
......@@ -2,7 +2,7 @@ package main
import (
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/examplelib"
zipfs "github.com/hanwen/go-fuse/zipfs"
"fmt"
"flag"
"log"
......@@ -21,7 +21,7 @@ func main() {
os.Exit(2)
}
fs := examplelib.NewZipArchiveFileSystem(flag.Arg(1))
fs := zipfs.NewZipArchiveFileSystem(flag.Arg(1))
conn := fuse.NewPathFileSystemConnector(fs)
state := fuse.NewMountState(conn)
......
This diff is collapsed.
package examplelib
import (
"github.com/hanwen/go-fuse/fuse"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"testing"
)
var _ = strings.Join
var _ = log.Println
////////////////
// Create and mount filesystem.
const magicMode uint32 = 0753
type stackFsTestCase struct {
origDir1 string
origDir2 string
mountDir string
testDir string
tester *testing.T
fs *SubmountFileSystem
state *fuse.MountState
}
func (me *stackFsTestCase) Setup(t *testing.T) {
me.tester = t
me.testDir = fuse.MakeTempDir()
me.origDir1 = filepath.Join(me.testDir, "orig1")
me.origDir2 = filepath.Join(me.testDir, "orig2")
me.mountDir = filepath.Join(me.testDir, "mount")
os.Mkdir(me.origDir1, 0700)
os.Mkdir(me.origDir2, 0700)
os.Mkdir(me.mountDir, 0700)
fs1 := fuse.NewPathFileSystemConnector(fuse.NewLoopbackFileSystem(me.origDir1))
fs2 := fuse.NewPathFileSystemConnector(fuse.NewLoopbackFileSystem(me.origDir2))
me.fs = NewSubmountFileSystem()
attr := fuse.Attr{
Mode: uint32(magicMode),
}
me.fs.AddFileSystem("sub1", fs1, attr)
me.fs.AddFileSystem("sub2", fs2, attr)
me.state = fuse.NewMountState(me.fs)
me.state.Mount(me.mountDir)
me.state.Debug = true
fmt.Println("tempdir: ", me.testDir)
// Unthreaded, but in background.
go me.state.Loop(false)
}
// Unmount and del.
func (me *stackFsTestCase) Cleanup() {
fmt.Println("Unmounting.")
err := me.state.Unmount()
CheckSuccess(err)
os.RemoveAll(me.testDir)
}
////////////////
func (me *stackFsTestCase) testReaddir() {
fmt.Println("testReaddir... ")
dir, err := os.Open(me.mountDir)
CheckSuccess(err)
infos, err := dir.Readdir(10)
CheckSuccess(err)
wanted := map[string]bool{
"sub1": true,
"sub2": true,
}
if len(wanted) != len(infos) {
me.tester.Errorf("Length mismatch %v", infos)
} else {
for _, v := range infos {
_, ok := wanted[v.Name]
if !ok {
me.tester.Errorf("Unexpected name %v", v.Name)
}
if v.Mode&0777 != magicMode {
me.tester.Errorf("Unexpected mode %o, %v", v.Mode, v)
}
}
}
dir.Close()
}
func (me *stackFsTestCase) testSubFs() {
fmt.Println("testSubFs... ")
for i := 1; i <= 2; i++ {
// orig := filepath.Join(me.testDir, fmt.Sprintf("orig%d", i))
mount := filepath.Join(me.mountDir, fmt.Sprintf("sub%d", i))
name := "testFile"
mountFile := filepath.Join(mount, name)
f, err := os.OpenFile(mountFile, os.O_WRONLY, 0)
if err == nil {
me.tester.Errorf("Expected error for open write %v", name)
continue
}
content1 := "booh!"
f, err = os.Create(mountFile)
CheckSuccess(err)
f.Write([]byte(content1))
f.Close()
err = os.Chmod(mountFile, magicMode)
CheckSuccess(err)
fi, err := os.Lstat(mountFile)
CheckSuccess(err)
if fi.Mode&0777 != magicMode {
me.tester.Errorf("Mode %o", fi.Mode)
}
g, err := os.Open(mountFile)
CheckSuccess(err)
buf := make([]byte, 1024)
n, err := g.Read(buf)
CheckSuccess(err)
if string(buf[:n]) != content1 {
me.tester.Errorf("content %v", buf[:n])
}
g.Close()
}
}
func (me *stackFsTestCase) testAddRemove() {
me.tester.Log("testAddRemove")
attr := fuse.Attr{
Mode: 0755,
}
conn := fuse.NewPathFileSystemConnector(fuse.NewLoopbackFileSystem(me.origDir1))
ok := me.fs.AddFileSystem("sub1", conn, attr)
if ok {
me.tester.Errorf("AddFileSystem should fail")
return
}
ok = me.fs.AddFileSystem("third", conn, attr)
if !ok {
me.tester.Errorf("AddFileSystem fail")
}
conn.Init(new(fuse.InHeader), new(fuse.InitIn))
fi, err := os.Lstat(filepath.Join(me.mountDir, "third"))
CheckSuccess(err)
if !fi.IsDirectory() {
me.tester.Errorf("not a directory %v", fi)
}
fs := me.fs.RemoveFileSystem("third")
if fs == nil {
me.tester.Errorf("remove fail")
}
dir, err := os.Open(me.mountDir)
CheckSuccess(err)
infos, err := dir.Readdir(10)
CheckSuccess(err)
if len(infos) != 2 {
me.tester.Errorf("lstat expect 2 infos %v", infos)
}
dir.Close()
_, err = os.Open(filepath.Join(me.mountDir, "third"))
if err == nil {
me.tester.Errorf("expect enoent %v", err)
}
}
func TestStackFS(t *testing.T) {
ts := new(stackFsTestCase)
ts.Setup(t)
ts.testReaddir()
ts.testSubFs()
ts.testAddRemove()
ts.Cleanup()
}
......@@ -2,7 +2,6 @@
include $(GOROOT)/src/Make.inc
TARG=github.com/hanwen/go-fuse/fuse
#TARG=fuse
GOFILES=misc.go\
fuse.go\
......
......@@ -60,7 +60,7 @@ func (me *AutoUnionFs) addFs(roots []string) {
var gofs *UnionFs
if me.knownFilesystems[name] == nil {
log.Println("Adding UnionFs for roots", roots)
gofs = NewUnionfs(roots, me.options.UnionFsOptions)
gofs = NewUnionFs(roots, me.options.UnionFsOptions)
me.knownFilesystems[name] = gofs
}
me.lock.Unlock()
......
......@@ -78,7 +78,7 @@ type UnionFsOptions struct {
DeletionDirName string
}
func NewUnionfs(roots []string, options UnionFsOptions) *UnionFs {
func NewUnionFs(roots []string, options UnionFsOptions) *UnionFs {
g := new(UnionFs)
g.options = &options
......
......@@ -38,7 +38,7 @@ func setup(t *testing.T) (workdir string, state *fuse.MountState) {
var roots []string
roots = append(roots, wd+"/rw")
roots = append(roots, wd+"/ro")
ufs := NewUnionfs(roots, testOpts)
ufs := NewUnionFs(roots, testOpts)
connector := fuse.NewPathFileSystemConnector(ufs)
state = fuse.NewMountState(connector)
......
# Use "gomake install" to build and install this package.
include $(GOROOT)/src/Make.inc
TARG=github.com/hanwen/go-fuse/zipfs
DEPS=../fuse
GOFILES=zipfs.go \
multizip.go \
include $(GOROOT)/src/Make.pkg
......@@ -2,11 +2,19 @@ package examplelib
import (
"github.com/hanwen/go-fuse/fuse"
"log"
"os"
"testing"
"time"
)
func CheckSuccess(err os.Error) {
if err != nil {
log.Println(err)
panic("error")
}
}
func TestMultiZipFs(t *testing.T) {
var err os.Error
......@@ -48,7 +56,9 @@ func TestMultiZipFs(t *testing.T) {
CheckSuccess(err)
// Directory exists, but is empty.
if !IsDir(mountPoint + "/zipmount") {
fi, err := os.Lstat(mountPoint + "/zipmount")
CheckSuccess(err)
if !fi.IsDirectory() {
t.Errorf("Expect directory at /zipmount")
}
......@@ -63,13 +73,13 @@ func TestMultiZipFs(t *testing.T) {
err = f.Close()
CheckSuccess(err)
if !IsDir(mountPoint + "/zipmount") {
fi, err = os.Lstat(mountPoint + "/zipmount")
if !fi.IsDirectory() {
t.Errorf("Expect directory at /zipmount")
}
// Check that zipfs itself works.
fi, err := os.Stat(mountPoint + "/zipmount/subdir")
fi, err = os.Stat(mountPoint + "/zipmount/subdir")
CheckSuccess(err)
if !fi.IsDirectory() {
t.Error("directory type", fi)
......
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