1 package zipfs
2
3 import (
4 "archive/zip"
5 "io"
6 "os"
7 "path/filepath"
8 "syscall"
9
10 "github.com/spf13/afero"
11 )
12
13 type File struct {
14 fs *Fs
15 zipfile *zip.File
16 reader io.ReadCloser
17 offset int64
18 isdir, closed bool
19 buf []byte
20 }
21
22 func (f *File) fillBuffer(offset int64) (err error) {
23 if f.reader == nil {
24 if f.reader, err = f.zipfile.Open(); err != nil {
25 return
26 }
27 }
28 if offset > int64(f.zipfile.UncompressedSize64) {
29 offset = int64(f.zipfile.UncompressedSize64)
30 err = io.EOF
31 }
32 if len(f.buf) >= int(offset) {
33 return
34 }
35 buf := make([]byte, int(offset)-len(f.buf))
36 if n, readErr := io.ReadFull(f.reader, buf); n > 0 {
37 f.buf = append(f.buf, buf[:n]...)
38 } else if readErr != nil {
39 err = readErr
40 }
41 return
42 }
43
44 func (f *File) Close() (err error) {
45 f.zipfile = nil
46 f.closed = true
47 f.buf = nil
48 if f.reader != nil {
49 err = f.reader.Close()
50 f.reader = nil
51 }
52 return
53 }
54
55 func (f *File) Read(p []byte) (n int, err error) {
56 if f.isdir {
57 return 0, syscall.EISDIR
58 }
59 if f.closed {
60 return 0, afero.ErrFileClosed
61 }
62 err = f.fillBuffer(f.offset + int64(len(p)))
63 n = copy(p, f.buf[f.offset:])
64 f.offset += int64(n)
65 return
66 }
67
68 func (f *File) ReadAt(p []byte, off int64) (n int, err error) {
69 if f.isdir {
70 return 0, syscall.EISDIR
71 }
72 if f.closed {
73 return 0, afero.ErrFileClosed
74 }
75 err = f.fillBuffer(off + int64(len(p)))
76 n = copy(p, f.buf[int(off):])
77 return
78 }
79
80 func (f *File) Seek(offset int64, whence int) (int64, error) {
81 if f.isdir {
82 return 0, syscall.EISDIR
83 }
84 if f.closed {
85 return 0, afero.ErrFileClosed
86 }
87 switch whence {
88 case io.SeekStart:
89 case io.SeekCurrent:
90 offset += f.offset
91 case io.SeekEnd:
92 offset += int64(f.zipfile.UncompressedSize64)
93 default:
94 return 0, syscall.EINVAL
95 }
96 if offset < 0 || offset > int64(f.zipfile.UncompressedSize64) {
97 return 0, afero.ErrOutOfRange
98 }
99 f.offset = offset
100 return offset, nil
101 }
102
103 func (f *File) Write(p []byte) (n int, err error) { return 0, syscall.EPERM }
104
105 func (f *File) WriteAt(p []byte, off int64) (n int, err error) { return 0, syscall.EPERM }
106
107 func (f *File) Name() string {
108 if f.zipfile == nil {
109 return string(filepath.Separator)
110 }
111 return filepath.Join(splitpath(f.zipfile.Name))
112 }
113
114 func (f *File) getDirEntries() (map[string]*zip.File, error) {
115 if !f.isdir {
116 return nil, syscall.ENOTDIR
117 }
118 name := f.Name()
119 entries, ok := f.fs.files[name]
120 if !ok {
121 return nil, &os.PathError{Op: "readdir", Path: name, Err: syscall.ENOENT}
122 }
123 return entries, nil
124 }
125
126 func (f *File) Readdir(count int) (fi []os.FileInfo, err error) {
127 zipfiles, err := f.getDirEntries()
128 if err != nil {
129 return nil, err
130 }
131 for _, zipfile := range zipfiles {
132 fi = append(fi, zipfile.FileInfo())
133 if count > 0 && len(fi) >= count {
134 break
135 }
136 }
137 return
138 }
139
140 func (f *File) Readdirnames(count int) (names []string, err error) {
141 zipfiles, err := f.getDirEntries()
142 if err != nil {
143 return nil, err
144 }
145 for filename := range zipfiles {
146 names = append(names, filename)
147 if count > 0 && len(names) >= count {
148 break
149 }
150 }
151 return
152 }
153
154 func (f *File) Stat() (os.FileInfo, error) {
155 if f.zipfile == nil {
156 return &pseudoRoot{}, nil
157 }
158 return f.zipfile.FileInfo(), nil
159 }
160
161 func (f *File) Sync() error { return nil }
162
163 func (f *File) Truncate(size int64) error { return syscall.EPERM }
164
165 func (f *File) WriteString(s string) (ret int, err error) { return 0, syscall.EPERM }
166
View as plain text