github.com/gozelle/afero@v0.0.0-20230510083704-09e2ff18f19e/zipfs/file.go (about) 1 package zipfs 2 3 import ( 4 "archive/zip" 5 "io" 6 "os" 7 "path/filepath" 8 "syscall" 9 10 "github.com/gozelle/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 }