-
Notifications
You must be signed in to change notification settings - Fork 0
/
zipfs.go
116 lines (102 loc) · 2.38 KB
/
zipfs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package main
import (
"archive/zip"
"bytes"
"fmt"
"io"
"io/ioutil"
"path"
"strings"
"time"
"github.com/hanwen/go-fuse/fuse/pathfs"
)
func findAllPathsInZip(z *zip.Reader) map[string]int {
m := map[string]int{}
for _, f := range z.File {
name := strings.TrimSuffix(f.Name, "/")
name = uncompressedName(unarchivedName(name))
m[name]++
}
return m
}
func newDirFromZip(r io.ReaderAt, size int64) (dir, error) {
zipr, err := zip.NewReader(r, size)
if err != nil {
return nil, err
}
seen := findAllPathsInZip(zipr)
root := newPlainDir("")
for _, f := range zipr.File {
ext := path.Ext(f.Name)
name := notCollidingCompressedName(f.Name, seen)
file := newFile(newZipFile(f, name), ext)
parent := recursiveAddDir(root, path.Dir(f.Name))
if isArchive(f.Name) {
if err := addArchiveToZip(f, parent, seen); err != nil {
return nil, err
}
} else if f.Name[len(f.Name)-1] != '/' {
parent.addFile(file)
}
}
return root, nil
}
func addArchiveToZip(f *zip.File, parent dir, seen map[string]int) error {
rc, err := f.Open()
if err != nil {
return err
}
defer rc.Close()
b, err := ioutil.ReadAll(rc)
if err != nil {
return err
}
br := bytes.NewReader(b)
dir, err := newDirFromArchive(br, int64(len(b)), f.Name)
if err != nil {
return err
}
name := notCollidingArchiveName(f.Name, seen)
dir.setName(path.Base(name))
if parent.addDir(dir) == nil {
return fmt.Errorf("failed to add fs under '%v'", name)
}
return nil
}
func newStaticTreeFsFromZip(r io.ReaderAt, size int64) (*StaticTreeFs, error) {
root, err := newDirFromZip(r, size)
if err != nil {
return nil, err
}
return &StaticTreeFs{pathfs.NewDefaultFileSystem(), root}, nil
}
// NewZipFs returns new filesystem reading zip archive from r of size.
func NewZipFs(r io.ReaderAt, size int64) (pathfs.FileSystem, error) {
zfs, err := newStaticTreeFsFromZip(r, size)
if err != nil {
return nil, err
}
return pathfs.NewLockingFileSystem(zfs), nil
}
type zipFile struct {
*zip.File
n string
}
func (f *zipFile) name() string {
return path.Base(f.n)
}
func (f *zipFile) size() (uint64, error) {
return f.UncompressedSize64, nil
}
func (f *zipFile) modTime() time.Time {
return f.ModTime()
}
func (f *zipFile) readCloser() (io.ReadCloser, error) {
return f.Open()
}
func (f *zipFile) String() string {
return f.name()
}
func newZipFile(z *zip.File, name string) file {
return &zipFile{z, name}
}