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

Split out tree/file management of zipfs.go into

archive agnostic MemTree and MemTreeFileSystem.
parent 4e9a1e68
...@@ -6,6 +6,7 @@ DEPS=../fuse ...@@ -6,6 +6,7 @@ DEPS=../fuse
GOFILES=zipfs.go \ GOFILES=zipfs.go \
multizip.go \ multizip.go \
memtree.go
include $(GOROOT)/src/Make.pkg include $(GOROOT)/src/Make.pkg
package zipfs
import (
"fmt"
"github.com/hanwen/go-fuse/fuse"
"os"
"strings"
"path/filepath"
)
type MemFile interface {
Stat() *os.FileInfo
Data() []byte
}
type MemTree struct {
subdirs map[string]*MemTree
files map[string]MemFile
}
func NewMemTree() *MemTree {
d := new(MemTree)
d.subdirs = make(map[string]*MemTree)
d.files = make(map[string]MemFile)
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)
}
for k, _ := range me.files {
fmt.Println(s + k)
}
}
func (me *MemTree) Lookup(name string) (*MemTree, MemFile) {
if name == "" {
return me, nil
}
parent := me
comps := strings.Split(filepath.Clean(name), "/", -1)
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 *MemTree) FindDir(name string) *MemTree {
s, ok := me.subdirs[name]
if !ok {
s = NewMemTree()
me.subdirs[name] = s
}
return s
}
////////////////////////////////////////////////////////////////
type MemTreeFileSystem struct {
tree *MemTree
fuse.DefaultFileSystem
}
func NewMemTreeFileSystem(t *MemTree) *MemTreeFileSystem {
return &MemTreeFileSystem{
tree: t,
}
}
const mem_DIRMODE uint32 = fuse.S_IFDIR | 0500
const mem_FILEMODE uint32 = fuse.S_IFREG | 0400
func (me *MemTreeFileSystem) GetAttr(name string) (*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()
}
return a, fuse.OK
}
func (me *MemTreeFileSystem) Open(name string, flags uint32) (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
}
func (me *MemTreeFileSystem) OpenDir(name string) (stream chan fuse.DirEntry, code fuse.Status) {
dir, file := me.tree.Lookup(name)
if file != nil {
return nil, fuse.ENOSYS
}
if dir == nil {
panic("dir")
}
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,
}
}
close(stream)
return stream, fuse.OK
}
...@@ -59,6 +59,7 @@ func (me *zipCreateFile) Write(input *fuse.WriteIn, nameBytes []byte) (uint32, f ...@@ -59,6 +59,7 @@ func (me *zipCreateFile) Write(input *fuse.WriteIn, nameBytes []byte) (uint32, f
// TODO. locks? // TODO. locks?
me.zfs.zips[me.Basename] = fs me.zfs.zips[me.Basename] = fs
me.zfs.dirZipFileMap[me.Basename] = zipFile
me.zfs.pendingZips[me.Basename] = false, false me.zfs.pendingZips[me.Basename] = false, false
me.zfs = nil me.zfs = nil
...@@ -74,18 +75,18 @@ func (me *zipCreateFile) Write(input *fuse.WriteIn, nameBytes []byte) (uint32, f ...@@ -74,18 +75,18 @@ func (me *zipCreateFile) Write(input *fuse.WriteIn, nameBytes []byte) (uint32, f
type MultiZipFs struct { type MultiZipFs struct {
Connector *fuse.FileSystemConnector Connector *fuse.FileSystemConnector
lock sync.RWMutex lock sync.RWMutex
zips map[string]*ZipArchiveFileSystem zips map[string]*MemTreeFileSystem
pendingZips map[string]bool pendingZips map[string]bool
zipFileNames map[string]string dirZipFileMap map[string]string
fuse.DefaultFileSystem fuse.DefaultFileSystem
} }
func NewMultiZipFs() *MultiZipFs { func NewMultiZipFs() *MultiZipFs {
m := new(MultiZipFs) m := new(MultiZipFs)
m.zips = make(map[string]*ZipArchiveFileSystem) m.zips = make(map[string]*MemTreeFileSystem)
m.pendingZips = make(map[string]bool) m.pendingZips = make(map[string]bool)
m.zipFileNames = make(map[string]string) m.dirZipFileMap = make(map[string]string)
m.Connector = fuse.NewFileSystemConnector(m, nil) m.Connector = fuse.NewFileSystemConnector(m, nil)
return m return m
} }
...@@ -155,9 +156,8 @@ func (me *MultiZipFs) GetAttr(name string) (*os.FileInfo, fuse.Status) { ...@@ -155,9 +156,8 @@ func (me *MultiZipFs) GetAttr(name string) (*os.FileInfo, fuse.Status) {
defer me.lock.RUnlock() defer me.lock.RUnlock()
a.Mode = submode a.Mode = submode
entry, hasDir := me.zips[base] _, hasDir := me.zips[base]
if hasDir { if hasDir {
a.Size = int64(len(entry.ZipFileName))
return a, fuse.OK return a, fuse.OK
} }
_, hasDir = me.pendingZips[base] _, hasDir = me.pendingZips[base]
...@@ -177,6 +177,7 @@ func (me *MultiZipFs) Unlink(name string) (code fuse.Status) { ...@@ -177,6 +177,7 @@ func (me *MultiZipFs) Unlink(name string) (code fuse.Status) {
_, ok := me.zips[basename] _, ok := me.zips[basename]
if ok { if ok {
me.zips[basename] = nil, false me.zips[basename] = nil, false
me.dirZipFileMap[basename] = "", false
return fuse.OK return fuse.OK
} else { } else {
return fuse.ENOENT return fuse.ENOENT
...@@ -195,12 +196,12 @@ func (me *MultiZipFs) Open(name string, flags uint32) (file fuse.File, code fuse ...@@ -195,12 +196,12 @@ func (me *MultiZipFs) Open(name string, flags uint32) (file fuse.File, code fuse
me.lock.RLock() me.lock.RLock()
defer me.lock.RUnlock() defer me.lock.RUnlock()
entry, ok := me.zips[basename] orig, ok := me.dirZipFileMap[basename]
if !ok { if !ok {
return nil, fuse.ENOENT return nil, fuse.ENOENT
} }
return fuse.NewReadOnlyFile([]byte(entry.ZipFileName)), fuse.OK return fuse.NewReadOnlyFile([]byte(orig)), fuse.OK
} }
return nil, fuse.ENOENT return nil, fuse.ENOENT
......
package zipfs package zipfs
import ( import (
"github.com/hanwen/go-fuse/fuse"
"archive/zip" "archive/zip"
"fmt" "fmt"
"os" "os"
"strings" "strings"
"path/filepath" "path/filepath"
"syscall"
"log" "log"
) )
var _ = log.Printf var _ = log.Printf
type ZipDirTree struct { type ZipFile struct {
subdirs map[string]*ZipDirTree *zip.File
files map[string]*zip.File
}
func NewZipDirTree() *ZipDirTree {
d := new(ZipDirTree)
d.subdirs = make(map[string]*ZipDirTree)
d.files = make(map[string]*zip.File)
return d
} }
func (me *ZipDirTree) Print(indent int) { func (me *ZipFile) Stat() *os.FileInfo {
s := "" // TODO - do something intelligent with timestamps.
for i := 0; i < indent; i++ { return &os.FileInfo{
s = s + " " Mode: syscall.S_IFREG | 0444,
} Size: int64(me.File.UncompressedSize),
for k, v := range me.subdirs {
fmt.Println(s + k + ":")
v.Print(indent + 2)
}
for k, _ := range me.files {
fmt.Println(s + k)
} }
} }
func (me *ZipDirTree) Lookup(name string) (*ZipDirTree, *zip.File) { func (me *ZipFile) Data() []byte {
if name == "" { data := make([]byte, me.UncompressedSize)
return me, nil zf := (*me)
} rc, err := zf.Open()
parent := me if err != nil {
comps := strings.Split(filepath.Clean(name), "/", -1) panic("zip open")
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 start := 0
} for {
n, err := rc.Read(data[start:])
func (me *ZipDirTree) FindDir(name string) *ZipDirTree { start += n
s, ok := me.subdirs[name] if err == os.EOF {
if !ok { break
s = NewZipDirTree() }
me.subdirs[name] = s if err != nil && err != os.EOF {
panic(fmt.Sprintf("read err: %v, n %v, sz %v", err, n, len(data)))
}
} }
return s return data
} }
type ZipArchiveFileSystem struct {
zipReader *zip.ReadCloser
tree *ZipDirTree
ZipFileName string
fuse.DefaultFileSystem func zipFilesToTree(files []*zip.File) *MemTree {
} t := NewMemTree()
func zipFilesToTree(files []*zip.File) *ZipDirTree {
t := NewZipDirTree()
for _, f := range files { for _, f := range files {
parent := t parent := t
comps := strings.Split(filepath.Clean(f.Name), "/", -1) comps := strings.Split(filepath.Clean(f.Name), "/", -1)
...@@ -94,112 +63,20 @@ func zipFilesToTree(files []*zip.File) *ZipDirTree { ...@@ -94,112 +63,20 @@ func zipFilesToTree(files []*zip.File) *ZipDirTree {
parent = parent.FindDir(c) parent = parent.FindDir(c)
} }
if base != "" { if base != "" {
parent.files[base] = f parent.files[base] = &ZipFile{File: f}
} }
} }
return t return t
} }
// NewZipArchiveFileSystem creates a new file-system for the // NewZipArchiveFileSystem creates a new file-system for the
// zip file named name. // zip file named name.
func NewZipArchiveFileSystem(name string) (*ZipArchiveFileSystem, os.Error) { func NewZipArchiveFileSystem(name string) (*MemTreeFileSystem, os.Error) {
z := new(ZipArchiveFileSystem)
r, err := zip.OpenReader(name) r, err := zip.OpenReader(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
z.ZipFileName = name z := NewMemTreeFileSystem(zipFilesToTree(r.File))
z.zipReader = r
z.tree = zipFilesToTree(r.File)
return z, nil return z, nil
} }
const zip_DIRMODE uint32 = fuse.S_IFDIR | 0700
const zip_FILEMODE uint32 = fuse.S_IFREG | 0600
func (me *ZipArchiveFileSystem) GetAttr(name string) (*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 = zip_DIRMODE
} else {
// TODO - do something intelligent with timestamps.
a.Mode = zip_FILEMODE
a.Size = int64(file.UncompressedSize)
}
return a, fuse.OK
}
func (me *ZipArchiveFileSystem) Open(name string, flags uint32) (file fuse.File, code fuse.Status) {
if flags&fuse.O_ANYWRITE != 0 {
return nil, fuse.EPERM
}
_, zfile := me.tree.Lookup(name)
if zfile == nil {
return nil, fuse.ENOENT
}
return NewZipFile(zfile), fuse.OK
}
func (me *ZipArchiveFileSystem) OpenDir(name string) (stream chan fuse.DirEntry, code fuse.Status) {
zdir, file := me.tree.Lookup(name)
if file != nil {
return nil, fuse.ENOSYS
}
if zdir == nil {
panic("zdir")
}
stream = make(chan fuse.DirEntry)
go func() {
for k, _ := range zdir.files {
stream <- fuse.DirEntry{
Name: k,
Mode: zip_FILEMODE,
}
}
for k, _ := range zdir.subdirs {
stream <- fuse.DirEntry{
Name: k,
Mode: zip_DIRMODE,
}
}
close(stream)
}()
return stream, fuse.OK
}
////////////////////////////////////////////////////////////////
// files & dirs
type ZipFile struct {
data []byte
fuse.DefaultFile
}
func NewZipFile(f *zip.File) fuse.File {
data := make([]byte, f.UncompressedSize)
rc, err := f.Open()
if err != nil {
panic("zip open")
}
start := 0
for {
n, err := rc.Read(data[start:])
start += n
if err == os.EOF {
break
}
if err != nil && err != os.EOF {
panic(fmt.Sprintf("read err: %v, n %v, sz %v", err, n, len(data)))
}
}
return fuse.NewReadOnlyFile(data)
}
...@@ -22,6 +22,7 @@ func TestZipFs(t *testing.T) { ...@@ -22,6 +22,7 @@ func TestZipFs(t *testing.T) {
state.Mount(mountPoint) state.Mount(mountPoint)
defer state.Unmount() defer state.Unmount()
state.Debug = true
go state.Loop(false) go state.Loop(false)
d, err := os.Open(mountPoint) d, err := os.Open(mountPoint)
...@@ -55,7 +56,7 @@ func TestZipFs(t *testing.T) { ...@@ -55,7 +56,7 @@ func TestZipFs(t *testing.T) {
n, err := f.Read(b) n, err := f.Read(b)
b = b[:n] b = b[:n]
if string(b[:n]) != "hello\n" { if string(b) != "hello\n" {
t.Error("content fail", b[:n]) t.Error("content fail", b[:n])
} }
f.Close() f.Close()
......
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