Commit 9ec4c8af authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Rewrite zipfs to be a native NodeFileSystem

parent f3e1921c
...@@ -7,6 +7,7 @@ package fuse ...@@ -7,6 +7,7 @@ package fuse
import ( import (
"fmt" "fmt"
"log" "log"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"unsafe" "unsafe"
...@@ -59,6 +60,7 @@ func (me *FileSystemConnector) verify() { ...@@ -59,6 +60,7 @@ func (me *FileSystemConnector) verify() {
func (me *FileSystemConnector) newInode(isDir bool) *Inode { func (me *FileSystemConnector) newInode(isDir bool) *Inode {
data := new(Inode) data := new(Inode)
data.nodeId = me.inodeMap.Register(&data.handled) data.nodeId = me.inodeMap.Register(&data.handled)
data.connector = me
if isDir { if isDir {
data.children = make(map[string]*Inode, initDirSize) data.children = make(map[string]*Inode, initDirSize)
} }
...@@ -66,6 +68,14 @@ func (me *FileSystemConnector) newInode(isDir bool) *Inode { ...@@ -66,6 +68,14 @@ func (me *FileSystemConnector) newInode(isDir bool) *Inode {
return data return data
} }
func (me *FileSystemConnector) createChild(parent *Inode, name string, fi *os.FileInfo, fsi FsNode) (out *EntryOut, child *Inode) {
child = parent.CreateChild(name, fi.IsDirectory(), fsi)
out = parent.mount.fileInfoToEntry(fi)
out.Ino = child.nodeId
out.NodeId = child.nodeId
return out, child
}
func (me *FileSystemConnector) lookupUpdate(parent *Inode, name string, isDir bool, lookupCount int) *Inode { func (me *FileSystemConnector) lookupUpdate(parent *Inode, name string, isDir bool, lookupCount int) *Inode {
defer me.verify() defer me.verify()
......
...@@ -47,7 +47,7 @@ func (me *FileSystemConnector) internalLookup(parent *Inode, name string, lookup ...@@ -47,7 +47,7 @@ func (me *FileSystemConnector) internalLookup(parent *Inode, name string, lookup
} }
var fi *os.FileInfo var fi *os.FileInfo
child := parent.getChild(name) child := parent.GetChild(name)
if child != nil { if child != nil {
fi, code = child.fsInode.GetAttr(nil, nil) fi, code = child.fsInode.GetAttr(nil, nil)
} }
...@@ -199,15 +199,6 @@ func (me *FileSystemConnector) Mknod(header *InHeader, input *MknodIn, name stri ...@@ -199,15 +199,6 @@ func (me *FileSystemConnector) Mknod(header *InHeader, input *MknodIn, name stri
return out, code return out, code
} }
func (me *FileSystemConnector) createChild(parent *Inode, name string, fi *os.FileInfo, fsi FsNode) (out *EntryOut, child *Inode) {
child = parent.createChild(name, fi.IsDirectory(), fsi, me)
out = parent.mount.fileInfoToEntry(fi)
out.Ino = child.nodeId
out.NodeId = child.nodeId
return out, child
}
func (me *FileSystemConnector) Mkdir(header *InHeader, input *MkdirIn, name string) (out *EntryOut, code Status) { func (me *FileSystemConnector) Mkdir(header *InHeader, input *MkdirIn, name string) (out *EntryOut, code Status) {
parent := me.getInodeData(header.NodeId) parent := me.getInodeData(header.NodeId)
fi, fsInode, code := parent.fsInode.Mkdir(name, input.Mode, &header.Context) fi, fsInode, code := parent.fsInode.Mkdir(name, input.Mode, &header.Context)
......
...@@ -41,6 +41,8 @@ type Inode struct { ...@@ -41,6 +41,8 @@ type Inode struct {
// during the lifetime, except upon Unmount() when it is set // during the lifetime, except upon Unmount() when it is set
// to nil. // to nil.
mount *fileSystemMount mount *fileSystemMount
connector *FileSystemConnector
} }
// public methods. // public methods.
...@@ -66,6 +68,21 @@ func (me *Inode) AnyFile() (file File) { ...@@ -66,6 +68,21 @@ func (me *Inode) AnyFile() (file File) {
return file return file
} }
func (me *Inode) Children() (out map[string]*Inode) {
me.treeLock.Lock()
defer me.treeLock.Unlock()
out = map[string]*Inode{}
for k, v := range me.children {
out[k] = v
}
return out
}
func (me *Inode) FsNode() FsNode {
return me.fsInode
}
// Returns an open writable file for the given Inode. // Returns an open writable file for the given Inode.
func (me *Inode) WritableFiles() (files []File) { func (me *Inode) WritableFiles() (files []File) {
me.openFilesMutex.Lock() me.openFilesMutex.Lock()
...@@ -83,7 +100,8 @@ func (me *Inode) IsDir() bool { ...@@ -83,7 +100,8 @@ func (me *Inode) IsDir() bool {
return me.children != nil return me.children != nil
} }
func (me *Inode) createChild(name string, isDir bool, fsi FsNode, conn *FileSystemConnector) *Inode { // Creates an Inode as child.
func (me *Inode) CreateChild(name string, isDir bool, fsi FsNode) *Inode {
me.treeLock.Lock() me.treeLock.Lock()
defer me.treeLock.Unlock() defer me.treeLock.Unlock()
...@@ -91,18 +109,19 @@ func (me *Inode) createChild(name string, isDir bool, fsi FsNode, conn *FileSyst ...@@ -91,18 +109,19 @@ func (me *Inode) createChild(name string, isDir bool, fsi FsNode, conn *FileSyst
if ch != nil { if ch != nil {
panic(fmt.Sprintf("already have a child at %v %q", me.nodeId, name)) panic(fmt.Sprintf("already have a child at %v %q", me.nodeId, name))
} }
ch = conn.newInode(isDir) ch = me.connector.newInode(isDir)
ch.fsInode = fsi ch.fsInode = fsi
fsi.SetInode(ch) fsi.SetInode(ch)
ch.mount = me.mount ch.mount = me.mount
ch.treeLock = me.treeLock ch.treeLock = me.treeLock
ch.lookupCount = 1 ch.lookupCount = 1
ch.connector = me.connector
me.addChild(name, ch) me.addChild(name, ch)
return ch return ch
} }
func (me *Inode) getChild(name string) (child *Inode) { func (me *Inode) GetChild(name string) (child *Inode) {
me.treeLock.Lock() me.treeLock.Lock()
defer me.treeLock.Unlock() defer me.treeLock.Unlock()
......
...@@ -8,8 +8,6 @@ import ( ...@@ -8,8 +8,6 @@ import (
var _ = log.Println var _ = log.Println
type PathNodeFs struct { type PathNodeFs struct {
fs FileSystem fs FileSystem
root *pathInode root *pathInode
......
...@@ -5,7 +5,6 @@ import ( ...@@ -5,7 +5,6 @@ import (
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"os" "os"
"strings" "strings"
"path/filepath"
) )
type MemFile interface { type MemFile interface {
...@@ -13,135 +12,102 @@ type MemFile interface { ...@@ -13,135 +12,102 @@ type MemFile interface {
Data() []byte Data() []byte
} }
type MemTree struct { type memNode struct {
subdirs map[string]*MemTree fuse.DefaultFsNode
file MemFile
}
type MemTreeFs struct {
fuse.DefaultNodeFileSystem
root memNode
files map[string]MemFile files map[string]MemFile
} }
func NewMemTree() *MemTree { func NewMemTreeFs() *MemTreeFs {
d := new(MemTree) d := new(MemTreeFs)
d.subdirs = make(map[string]*MemTree)
d.files = make(map[string]MemFile)
return d return d
} }
func (me *MemTree) Print(indent int) { func (me *MemTreeFs) Mount(conn *fuse.FileSystemConnector) {
for k, v := range me.files {
me.addFile(k, v)
}
me.files = nil
}
func (me *MemTreeFs) Root() fuse.FsNode {
return &me.root
}
func (me *memNode) Print(indent int) {
s := "" s := ""
for i := 0; i < indent; i++ { for i := 0; i < indent; i++ {
s = s + " " s = s + " "
} }
for k, v := range me.subdirs {
children := me.Inode().Children()
for k, v := range children {
if v.IsDir() {
fmt.Println(s + k + ":") fmt.Println(s + k + ":")
v.Print(indent + 2) mn, ok := v.FsNode().(*memNode)
if ok {
mn.Print(indent+2)
} }
for k, _ := range me.files { } else {
fmt.Println(s + k) fmt.Println(s + k)
} }
}
} }
func (me *MemTree) Lookup(name string) (*MemTree, MemFile) { func (me *memNode) OpenDir(context *fuse.Context) (stream chan fuse.DirEntry, code fuse.Status) {
if name == "" { children := me.Inode().Children()
return me, nil stream = make(chan fuse.DirEntry, len(children))
for k, v := range children {
mode := fuse.S_IFREG | 0666
if v.IsDir() {
mode = fuse.S_IFDIR | 0777
} }
parent := me stream <- fuse.DirEntry{
comps := strings.Split(filepath.Clean(name), "/") Name: k,
for _, c := range comps[:len(comps)-1] { Mode: uint32(mode),
parent = parent.subdirs[c]
if parent == nil {
return nil, nil
}
}
base := comps[len(comps)-1]
file, ok := parent.files[base]
if ok {
return parent, file
} }
return parent.subdirs[base], nil
}
func (me *MemTree) FindDir(name string) *MemTree {
s, ok := me.subdirs[name]
if !ok {
s = NewMemTree()
me.subdirs[name] = s
} }
return s close(stream)
} return stream, fuse.OK
////////////////////////////////////////////////////////////////
type MemTreeFileSystem struct {
tree *MemTree
fuse.DefaultFileSystem
} }
func NewMemTreeFileSystem(t *MemTree) *MemTreeFileSystem { func (me *memNode) Open(flags uint32, context *fuse.Context) (fuseFile fuse.File, code fuse.Status) {
return &MemTreeFileSystem{ if flags&fuse.O_ANYWRITE != 0 {
tree: t, return nil, fuse.EPERM
} }
}
const mem_DIRMODE uint32 = fuse.S_IFDIR | 0500
const mem_FILEMODE uint32 = fuse.S_IFREG | 0400
func (me *MemTreeFileSystem) Name() string { return fuse.NewReadOnlyFile(me.file.Data()), fuse.OK
return "MemTreeFileSystem"
} }
func (me *MemTreeFileSystem) GetAttr(name string, context *fuse.Context) (*os.FileInfo, fuse.Status) { func (me *memNode) GetAttr(file fuse.File, context *fuse.Context) (*os.FileInfo, fuse.Status) {
dir, file := me.tree.Lookup(name) if me.Inode().IsDir() {
if dir == nil { return &os.FileInfo{
return nil, fuse.ENOENT Mode: fuse.S_IFDIR | 0777,
}, fuse.OK
} }
a := &os.FileInfo{} return me.file.Stat(), fuse.OK
if file == nil {
a.Mode = mem_DIRMODE
} else {
a = file.Stat()
}
return a, fuse.OK
} }
func (me *MemTreeFileSystem) Open(name string, flags uint32, context *fuse.Context) (fuseFile fuse.File, code fuse.Status) { func (me *MemTreeFs) addFile(name string, f MemFile) {
if flags&fuse.O_ANYWRITE != 0 { comps := strings.Split(name, "/")
return nil, fuse.EPERM
}
// TODO - should complain if it is a directory.
_, file := me.tree.Lookup(name)
if file == nil {
return nil, fuse.ENOENT
}
return fuse.NewReadOnlyFile(file.Data()), fuse.OK
}
func (me *MemTreeFileSystem) OpenDir(name string, context *fuse.Context) (stream chan fuse.DirEntry, code fuse.Status) { node := me.root.Inode()
dir, file := me.tree.Lookup(name) for i, c := range comps {
if dir == nil { ch := node.GetChild(c)
return nil, fuse.ENOENT if ch == nil {
} fsnode := &memNode{}
if file != nil { if i == len(comps)-1 {
return nil, fuse.ENOTDIR fsnode.file = f
} }
stream = make(chan fuse.DirEntry, len(dir.files)+len(dir.subdirs)) ch = node.CreateChild(c, fsnode.file == nil, fsnode)
for k, _ := range dir.files {
stream <- fuse.DirEntry{
Name: k,
Mode: mem_FILEMODE,
} }
node = ch
} }
for k, _ := range dir.subdirs {
stream <- fuse.DirEntry{
Name: k,
Mode: mem_DIRMODE,
}
}
close(stream)
return stream, fuse.OK
} }
...@@ -32,7 +32,7 @@ const ( ...@@ -32,7 +32,7 @@ const (
type MultiZipFs struct { type MultiZipFs struct {
Connector *fuse.FileSystemConnector Connector *fuse.FileSystemConnector
lock sync.RWMutex lock sync.RWMutex
zips map[string]*MemTreeFileSystem zips map[string]*MemTreeFs
dirZipFileMap map[string]string dirZipFileMap map[string]string
fuse.DefaultFileSystem fuse.DefaultFileSystem
...@@ -40,7 +40,7 @@ type MultiZipFs struct { ...@@ -40,7 +40,7 @@ type MultiZipFs struct {
func NewMultiZipFs() *MultiZipFs { func NewMultiZipFs() *MultiZipFs {
m := new(MultiZipFs) m := new(MultiZipFs)
m.zips = make(map[string]*MemTreeFileSystem) m.zips = make(map[string]*MemTreeFs)
m.dirZipFileMap = make(map[string]string) m.dirZipFileMap = make(map[string]string)
return m return m
} }
...@@ -171,7 +171,7 @@ func (me *MultiZipFs) Symlink(value string, linkName string, context *fuse.Conte ...@@ -171,7 +171,7 @@ func (me *MultiZipFs) Symlink(value string, linkName string, context *fuse.Conte
return fuse.EINVAL return fuse.EINVAL
} }
code = me.Connector.Mount("/"+base, fuse.NewPathNodeFs(fs), nil) code = me.Connector.Mount("/"+base, fs, nil)
if !code.Ok() { if !code.Ok() {
return code return code
} }
......
...@@ -16,7 +16,7 @@ const testTtl = 0.1 ...@@ -16,7 +16,7 @@ const testTtl = 0.1
func setupMzfs() (mountPoint string, cleanup func()) { func setupMzfs() (mountPoint string, cleanup func()) {
fs := NewMultiZipFs() fs := NewMultiZipFs()
mountPoint = fuse.MakeTempDir() mountPoint = fuse.MakeTempDir()
state, _, err := fuse.MountFileSystem(mountPoint, fs, &fuse.FileSystemOptions{ state, _, err := fuse.MountPathFileSystem(mountPoint, fs, &fuse.FileSystemOptions{
EntryTimeout: testTtl, EntryTimeout: testTtl,
AttrTimeout: testTtl, AttrTimeout: testTtl,
NegativeTimeout: 0.0, NegativeTimeout: 0.0,
......
...@@ -8,7 +8,6 @@ import ( ...@@ -8,7 +8,6 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"path/filepath"
"strings" "strings"
"syscall" "syscall"
) )
...@@ -45,8 +44,8 @@ func (me *TarFile) Data() []byte { ...@@ -45,8 +44,8 @@ func (me *TarFile) Data() []byte {
return me.data return me.data
} }
func NewTarTree(r io.Reader) *MemTree { func NewTarTree(r io.Reader) (map[string]MemFile) {
tree := NewMemTree() files := map[string]MemFile{}
tr := tar.NewReader(r) tr := tar.NewReader(r)
var longName *string var longName *string
...@@ -73,32 +72,22 @@ func NewTarTree(r io.Reader) *MemTree { ...@@ -73,32 +72,22 @@ func NewTarTree(r io.Reader) *MemTree {
longName = nil longName = nil
} }
comps := strings.Split(filepath.Clean(hdr.Name), "/") if strings.HasSuffix(hdr.Name, "/") {
base := "" continue
if !strings.HasSuffix(hdr.Name, "/") {
base = comps[len(comps)-1]
comps = comps[:len(comps)-1]
}
parent := tree
for _, c := range comps {
parent = parent.FindDir(c)
} }
buf := bytes.NewBuffer(make([]byte, 0, hdr.Size)) buf := bytes.NewBuffer(make([]byte, 0, hdr.Size))
io.Copy(buf, tr) io.Copy(buf, tr)
if base != "" {
b := buf.Bytes() files[hdr.Name] = &TarFile{
parent.files[base] = &TarFile{
Header: *hdr, Header: *hdr,
data: b, data: buf.Bytes(),
}
} }
} }
return tree return files
} }
func NewTarCompressedTree(name string, format string) (*MemTree, os.Error) { func NewTarCompressedTree(name string, format string) (map[string]MemFile, os.Error) {
f, err := os.Open(name) f, err := os.Open(name)
if err != nil { if err != nil {
return nil, err return nil, err
......
...@@ -4,11 +4,11 @@ import ( ...@@ -4,11 +4,11 @@ import (
"archive/zip" "archive/zip"
"bytes" "bytes"
"fmt" "fmt"
"github.com/hanwen/go-fuse/fuse"
"io" "io"
"os" "os"
"strings"
"path/filepath" "path/filepath"
"syscall" "strings"
"log" "log"
) )
...@@ -21,7 +21,7 @@ type ZipFile struct { ...@@ -21,7 +21,7 @@ type ZipFile struct {
func (me *ZipFile) Stat() *os.FileInfo { func (me *ZipFile) Stat() *os.FileInfo {
// TODO - do something intelligent with timestamps. // TODO - do something intelligent with timestamps.
return &os.FileInfo{ return &os.FileInfo{
Mode: syscall.S_IFREG | 0444, Mode: fuse.S_IFREG | 0444,
Size: int64(me.File.UncompressedSize), Size: int64(me.File.UncompressedSize),
} }
} }
...@@ -41,62 +41,51 @@ func (me *ZipFile) Data() []byte { ...@@ -41,62 +41,51 @@ func (me *ZipFile) Data() []byte {
return dest.Bytes() return dest.Bytes()
} }
func zipFilesToTree(files []*zip.File) *MemTree {
t := NewMemTree()
for _, f := range files {
parent := t
comps := strings.Split(filepath.Clean(f.Name), "/")
base := ""
// Ugh - zip files have directories separate.
if !strings.HasSuffix(f.Name, "/") {
base = comps[len(comps)-1]
comps = comps[:len(comps)-1]
}
for _, c := range comps {
parent = parent.FindDir(c)
}
if base != "" {
parent.files[base] = &ZipFile{File: f}
}
}
return t
}
// NewZipTree creates a new file-system for the zip file named name. // NewZipTree creates a new file-system for the zip file named name.
func NewZipTree(name string) (*MemTree, os.Error) { func NewZipTree(name string) (map[string]MemFile, os.Error) {
r, err := zip.OpenReader(name) r, err := zip.OpenReader(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return zipFilesToTree(r.File), nil
out := map[string]MemFile{}
for _, f := range r.File {
if strings.HasSuffix(f.Name, "/") {
continue
}
n := filepath.Clean(f.Name)
zf := &ZipFile{f}
out[n] = zf
}
return out, nil
} }
func NewArchiveFileSystem(name string) (fs *MemTreeFileSystem, err os.Error) { func NewArchiveFileSystem(name string) (mfs *MemTreeFs, err os.Error) {
var tree *MemTree mfs = &MemTreeFs{}
if strings.HasSuffix(name, ".zip") { if strings.HasSuffix(name, ".zip") {
tree, err = NewZipTree(name) mfs.files, err = NewZipTree(name)
} }
if strings.HasSuffix(name, ".tar.gz") { if strings.HasSuffix(name, ".tar.gz") {
tree, err = NewTarCompressedTree(name, "gz") mfs.files, err = NewTarCompressedTree(name, "gz")
} }
if strings.HasSuffix(name, ".tar.bz2") { if strings.HasSuffix(name, ".tar.bz2") {
tree, err = NewTarCompressedTree(name, "bz2") mfs.files, err = NewTarCompressedTree(name, "bz2")
} }
if strings.HasSuffix(name, ".tar") { if strings.HasSuffix(name, ".tar") {
f, err := os.Open(name) f, err := os.Open(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
tree = NewTarTree(f) mfs.files = NewTarTree(f)
} }
if err != nil { if err != nil {
return nil, err return nil, err
} }
if tree == nil { if mfs.files == nil {
return nil, os.NewError(fmt.Sprintf("Unknown type for %v", name)) return nil, os.NewError(fmt.Sprintf("Unknown type for %v", name))
} }
return NewMemTreeFileSystem(tree), nil return mfs, nil
} }
...@@ -24,7 +24,7 @@ func setupZipfs() (mountPoint string, cleanup func()) { ...@@ -24,7 +24,7 @@ func setupZipfs() (mountPoint string, cleanup func()) {
mountPoint = fuse.MakeTempDir() mountPoint = fuse.MakeTempDir()
state, _, err := fuse.MountFileSystem(mountPoint, zfs, nil) state, _, err := fuse.MountNodeFileSystem(mountPoint, zfs, nil)
state.Debug = true state.Debug = true
go state.Loop(false) go state.Loop(false)
......
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