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
GOFILES=zipfs.go \
multizip.go \
memtree.go
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
// TODO. locks?
me.zfs.zips[me.Basename] = fs
me.zfs.dirZipFileMap[me.Basename] = zipFile
me.zfs.pendingZips[me.Basename] = false, false
me.zfs = nil
......@@ -74,18 +75,18 @@ func (me *zipCreateFile) Write(input *fuse.WriteIn, nameBytes []byte) (uint32, f
type MultiZipFs struct {
Connector *fuse.FileSystemConnector
lock sync.RWMutex
zips map[string]*ZipArchiveFileSystem
zips map[string]*MemTreeFileSystem
pendingZips map[string]bool
zipFileNames map[string]string
dirZipFileMap map[string]string
fuse.DefaultFileSystem
}
func NewMultiZipFs() *MultiZipFs {
m := new(MultiZipFs)
m.zips = make(map[string]*ZipArchiveFileSystem)
m.zips = make(map[string]*MemTreeFileSystem)
m.pendingZips = make(map[string]bool)
m.zipFileNames = make(map[string]string)
m.dirZipFileMap = make(map[string]string)
m.Connector = fuse.NewFileSystemConnector(m, nil)
return m
}
......@@ -155,9 +156,8 @@ func (me *MultiZipFs) GetAttr(name string) (*os.FileInfo, fuse.Status) {
defer me.lock.RUnlock()
a.Mode = submode
entry, hasDir := me.zips[base]
_, hasDir := me.zips[base]
if hasDir {
a.Size = int64(len(entry.ZipFileName))
return a, fuse.OK
}
_, hasDir = me.pendingZips[base]
......@@ -177,6 +177,7 @@ func (me *MultiZipFs) Unlink(name string) (code fuse.Status) {
_, ok := me.zips[basename]
if ok {
me.zips[basename] = nil, false
me.dirZipFileMap[basename] = "", false
return fuse.OK
} else {
return fuse.ENOENT
......@@ -195,12 +196,12 @@ func (me *MultiZipFs) Open(name string, flags uint32) (file fuse.File, code fuse
me.lock.RLock()
defer me.lock.RUnlock()
entry, ok := me.zips[basename]
orig, ok := me.dirZipFileMap[basename]
if !ok {
return nil, fuse.ENOENT
}
return fuse.NewReadOnlyFile([]byte(entry.ZipFileName)), fuse.OK
return fuse.NewReadOnlyFile([]byte(orig)), fuse.OK
}
return nil, fuse.ENOENT
......
package zipfs
import (
"github.com/hanwen/go-fuse/fuse"
"archive/zip"
"fmt"
"os"
"strings"
"path/filepath"
"syscall"
"log"
)
var _ = log.Printf
type ZipDirTree struct {
subdirs map[string]*ZipDirTree
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
type ZipFile struct {
*zip.File
}
func (me *ZipDirTree) 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 *ZipFile) Stat() *os.FileInfo {
// TODO - do something intelligent with timestamps.
return &os.FileInfo{
Mode: syscall.S_IFREG | 0444,
Size: int64(me.File.UncompressedSize),
}
}
func (me *ZipDirTree) Lookup(name string) (*ZipDirTree, *zip.File) {
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
func (me *ZipFile) Data() []byte {
data := make([]byte, me.UncompressedSize)
zf := (*me)
rc, err := zf.Open()
if err != nil {
panic("zip open")
}
return parent.subdirs[base], nil
}
func (me *ZipDirTree) FindDir(name string) *ZipDirTree {
s, ok := me.subdirs[name]
if !ok {
s = NewZipDirTree()
me.subdirs[name] = s
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 s
return data
}
type ZipArchiveFileSystem struct {
zipReader *zip.ReadCloser
tree *ZipDirTree
ZipFileName string
fuse.DefaultFileSystem
}
func zipFilesToTree(files []*zip.File) *ZipDirTree {
t := NewZipDirTree()
func zipFilesToTree(files []*zip.File) *MemTree {
t := NewMemTree()
for _, f := range files {
parent := t
comps := strings.Split(filepath.Clean(f.Name), "/", -1)
......@@ -94,112 +63,20 @@ func zipFilesToTree(files []*zip.File) *ZipDirTree {
parent = parent.FindDir(c)
}
if base != "" {
parent.files[base] = f
parent.files[base] = &ZipFile{File: f}
}
}
return t
}
// NewZipArchiveFileSystem creates a new file-system for the
// zip file named name.
func NewZipArchiveFileSystem(name string) (*ZipArchiveFileSystem, os.Error) {
z := new(ZipArchiveFileSystem)
func NewZipArchiveFileSystem(name string) (*MemTreeFileSystem, os.Error) {
r, err := zip.OpenReader(name)
if err != nil {
return nil, err
}
z.ZipFileName = name
z.zipReader = r
z.tree = zipFilesToTree(r.File)
z := NewMemTreeFileSystem(zipFilesToTree(r.File))
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) {
state.Mount(mountPoint)
defer state.Unmount()
state.Debug = true
go state.Loop(false)
d, err := os.Open(mountPoint)
......@@ -55,7 +56,7 @@ func TestZipFs(t *testing.T) {
n, err := f.Read(b)
b = b[:n]
if string(b[:n]) != "hello\n" {
if string(b) != "hello\n" {
t.Error("content fail", b[:n])
}
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