Commit 0a7304c1 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Add SwitchFileSystem.

parent 2752a889
......@@ -23,9 +23,10 @@ MANUAL_GOFILES=api.go \
pathfilesystem.go \
pathops.go \
request.go \
switchfs.go \
timingfs.go \
timingrawfs.go \
types.go\
types.go \
version.go \
xattr.go \
......
package fuse
import (
"path/filepath"
"os"
"sort"
"strings"
"syscall"
)
// SwitchFileSystem construct the union of a set of filesystems, and
// select them by prefix. Consider the traditional Unix file system:
// different parts have different characteristics, eg. "/usr" is
// read-only, while "/home/user" is read/write. Similarly, "/tmp"
// does not need to be persistent and "/dev" is totally unlike a file
// system.
//
// With SwitchFileSystem, you can write filesystems for each of these
// parts separately, and combine them using SwitchFileSystem. This is
// a simpler and less efficient alternative to in-process mounts, but
// it also works if the encompassing file system already has the
// mounted directory.
type SwitchFileSystem struct {
fileSystems SwitchedFileSystems
}
// This is the definition of one member of SwitchedFileSystems.
type SwitchedFileSystem struct {
Prefix string
FileSystem
StripPrefix bool
}
type SwitchedFileSystems []*SwitchedFileSystem
func (p SwitchedFileSystems) Len() int {
return len(p)
}
func (p SwitchedFileSystems) Less(i, j int) bool {
// Invert order, so we get more specific prefixes first.
return p[i].Prefix > p[j].Prefix
}
func (p SwitchedFileSystems) Swap(i, j int) {
swFs := p[i]
p[i] = p[j]
p[j] = swFs
}
func NewSwitchFileSystem(fsMap []SwitchedFileSystem) *SwitchFileSystem {
me := &SwitchFileSystem{}
for _, inSwFs := range fsMap {
swFs := inSwFs
me.fileSystems = append(me.fileSystems, &swFs)
}
sort.Sort(me.fileSystems)
return me
}
// TODO - use binary search. This is inefficient if there are large
// numbers of switched filesystems.
func (me *SwitchFileSystem) findFileSystem(path string) (string, *SwitchedFileSystem) {
for _, swFs := range me.fileSystems {
if swFs.Prefix == "" || swFs.Prefix == path || strings.HasPrefix(path, swFs.Prefix + string(filepath.Separator)) {
if swFs.StripPrefix {
path = strings.TrimLeft(path[len(swFs.Prefix):], string(filepath.Separator))
}
return path, swFs
}
}
return "", nil
}
func (me *SwitchFileSystem) GetAttr(name string) (*os.FileInfo, Status) {
name, fs := me.findFileSystem(name)
if fs == nil {
return nil, ENOENT
}
return fs.FileSystem.GetAttr(name)
}
func (me *SwitchFileSystem) Readlink(name string) (string, Status) {
name, fs := me.findFileSystem(name)
if fs == nil {
return "", ENOENT
}
return fs.FileSystem.Readlink(name)
}
func (me *SwitchFileSystem) Mknod(name string, mode uint32, dev uint32) Status {
name, fs := me.findFileSystem(name)
if fs == nil {
return ENOENT
}
return fs.FileSystem.Mknod(name, mode, dev)
}
func (me *SwitchFileSystem) Mkdir(name string, mode uint32) Status {
name, fs := me.findFileSystem(name)
if fs == nil {
return ENOENT
}
return fs.FileSystem.Mkdir(name, mode)
}
func (me *SwitchFileSystem) Unlink(name string) (code Status) {
name, fs := me.findFileSystem(name)
if fs == nil {
return ENOENT
}
return fs.FileSystem.Unlink(name)
}
func (me *SwitchFileSystem) Rmdir(name string) (code Status) {
name, fs := me.findFileSystem(name)
if fs == nil {
return ENOENT
}
return fs.FileSystem.Rmdir(name)
}
func (me *SwitchFileSystem) Symlink(value string, linkName string) (code Status) {
linkName, fs := me.findFileSystem(linkName)
if fs == nil {
return ENOENT
}
return fs.FileSystem.Symlink(value, linkName)
}
func (me *SwitchFileSystem) Rename(oldName string, newName string) (code Status) {
oldName, fs1 := me.findFileSystem(oldName)
newName, fs2 := me.findFileSystem(newName)
if fs1 != fs2 {
return syscall.EXDEV
}
if fs1 == nil {
return ENOENT
}
return fs1.Rename(oldName, newName)
}
func (me *SwitchFileSystem) Link(oldName string, newName string) (code Status) {
oldName, fs1 := me.findFileSystem(oldName)
newName, fs2 := me.findFileSystem(newName)
if fs1 != fs2 {
return syscall.EXDEV
}
if fs1 == nil {
return ENOENT
}
return fs1.Link(oldName, newName)
}
func (me *SwitchFileSystem) Chmod(name string, mode uint32) (code Status) {
name, fs := me.findFileSystem(name)
if fs == nil {
return ENOENT
}
return fs.FileSystem.Chmod(name, mode)
}
func (me *SwitchFileSystem) Chown(name string, uid uint32, gid uint32) (code Status) {
name, fs := me.findFileSystem(name)
if fs == nil {
return ENOENT
}
return fs.FileSystem.Chown(name, uid, gid)
}
func (me *SwitchFileSystem) Truncate(name string, offset uint64) (code Status) {
name, fs := me.findFileSystem(name)
if fs == nil {
return ENOENT
}
return fs.FileSystem.Truncate(name, offset)
}
func (me *SwitchFileSystem) Open(name string, flags uint32) (file File, code Status) {
name, fs := me.findFileSystem(name)
if fs == nil {
return nil, ENOENT
}
return fs.FileSystem.Open(name, flags)
}
func (me *SwitchFileSystem) OpenDir(name string) (stream chan DirEntry, status Status) {
name, fs := me.findFileSystem(name)
if fs == nil {
return nil, ENOENT
}
return fs.FileSystem.OpenDir(name)
}
func (me *SwitchFileSystem) Mount(conn *FileSystemConnector) {
for _, fs := range me.fileSystems {
fs.FileSystem.Mount(conn)
}
}
func (me *SwitchFileSystem) Unmount() {
for _, fs := range me.fileSystems {
fs.FileSystem.Unmount()
}
}
func (me *SwitchFileSystem) Access(name string, mode uint32) (code Status) {
name, fs := me.findFileSystem(name)
if fs == nil {
return ENOENT
}
return fs.FileSystem.Access(name, mode)
}
func (me *SwitchFileSystem) Create(name string, flags uint32, mode uint32) (file File, code Status) {
name, fs := me.findFileSystem(name)
if fs == nil {
return nil, ENOENT
}
return fs.FileSystem.Create(name, flags, mode)
}
func (me *SwitchFileSystem) Utimens(name string, AtimeNs uint64, CtimeNs uint64) (code Status) {
name, fs := me.findFileSystem(name)
if fs == nil {
return ENOENT
}
return fs.FileSystem.Utimens(name, AtimeNs, CtimeNs)
}
func (me *SwitchFileSystem) GetXAttr(name string, attr string) ([]byte, Status) {
name, fs := me.findFileSystem(name)
if fs == nil {
return nil, ENOENT
}
return fs.FileSystem.GetXAttr(name, attr)
}
func (me *SwitchFileSystem) SetXAttr(name string, attr string, data []byte, flags int) Status {
name, fs := me.findFileSystem(name)
if fs == nil {
return ENOENT
}
return fs.FileSystem.SetXAttr(name, attr, data, flags)
}
func (me *SwitchFileSystem) ListXAttr(name string) ([]string, Status) {
name, fs := me.findFileSystem(name)
if fs == nil {
return nil, ENOENT
}
return fs.FileSystem.ListXAttr(name)
}
func (me *SwitchFileSystem) RemoveXAttr(name string, attr string) Status {
name, fs := me.findFileSystem(name)
if fs == nil {
return ENOENT
}
return fs.FileSystem.RemoveXAttr(name, attr)
}
func (me *SwitchFileSystem) Flush(name string) Status {
name, fs := me.findFileSystem(name)
if fs == nil {
return ENOENT
}
return fs.FileSystem.Flush(name)
}
package fuse
import (
"testing"
)
func TestSwitchFs(t *testing.T) {
fsMap := []SwitchedFileSystem{
SwitchedFileSystem{Prefix: ""},
SwitchedFileSystem{Prefix:"home/foo"},
SwitchedFileSystem{Prefix:"home"},
SwitchedFileSystem{Prefix:"usr"},
}
sfs := NewSwitchFileSystem(fsMap)
for path, expectPrefix := range map[string]string {
"xyz": "",
"home/foo/bar": "home/foo",
"home/fooz/bar": "home",
"home/efg": "home",
"lib": "",
"abc": "",
"usr/local": "usr",
} {
_, fs := sfs.findFileSystem(path)
if fs.Prefix != expectPrefix {
t.Errorf("Mismatch %s %s %v", path, fs.Prefix, expectPrefix)
}
}
}
func TestSwitchFsStrip(t *testing.T) {
fsMap := []SwitchedFileSystem{
SwitchedFileSystem{Prefix: ""},
SwitchedFileSystem{Prefix:"dev", StripPrefix: true},
SwitchedFileSystem{Prefix:"home", StripPrefix: false},
}
sfs := NewSwitchFileSystem(fsMap)
// Don't check for inputs ending in '/' since Go-FUSE never
// generates them.
for path, expectPath := range map[string]string {
"xyz": "xyz",
"home/foo/bar": "home/foo/bar",
"home": "home",
"dev/null": "null",
"dev": "",
} {
stripPath, _ := sfs.findFileSystem(path)
if stripPath != expectPath {
t.Errorf("Mismatch %s %s %v", path, stripPath, expectPath)
}
}
}
func TestSwitchFsApi(t *testing.T) {
var fs FileSystem
fs = &SwitchedFileSystem{}
_ = fs
}
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