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 }