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