github.com/bpfs/defs@v0.0.15/afero/tarfs/fs.go (about) 1 // package tarfs implements a read-only in-memory representation of a tar archive 2 package tarfs 3 4 import ( 5 "archive/tar" 6 "bytes" 7 "io" 8 "os" 9 "path/filepath" 10 "syscall" 11 "time" 12 13 "github.com/bpfs/defs/afero" 14 ) 15 16 type Fs struct { 17 files map[string]map[string]*File 18 } 19 20 func splitpath(name string) (dir, file string) { 21 name = filepath.ToSlash(name) 22 if len(name) == 0 || name[0] != '/' { 23 name = "/" + name 24 } 25 name = filepath.Clean(name) 26 dir, file = filepath.Split(name) 27 dir = filepath.Clean(dir) 28 return 29 } 30 31 func New(t *tar.Reader) *Fs { 32 fs := &Fs{files: make(map[string]map[string]*File)} 33 for { 34 hdr, err := t.Next() 35 if err == io.EOF { 36 break 37 } 38 if err != nil { 39 return nil 40 } 41 42 d, f := splitpath(hdr.Name) 43 if _, ok := fs.files[d]; !ok { 44 fs.files[d] = make(map[string]*File) 45 } 46 47 var buf bytes.Buffer 48 size, err := buf.ReadFrom(t) 49 if err != nil { 50 panic("tarfs: reading from tar:" + err.Error()) 51 } 52 53 if size != hdr.Size { 54 panic("tarfs: size mismatch") 55 } 56 57 file := &File{ 58 h: hdr, 59 data: bytes.NewReader(buf.Bytes()), 60 fs: fs, 61 } 62 fs.files[d][f] = file 63 64 } 65 66 if fs.files[afero.FilePathSeparator] == nil { 67 fs.files[afero.FilePathSeparator] = make(map[string]*File) 68 } 69 // Add a pseudoroot 70 fs.files[afero.FilePathSeparator][""] = &File{ 71 h: &tar.Header{ 72 Name: afero.FilePathSeparator, 73 Typeflag: tar.TypeDir, 74 Size: 0, 75 }, 76 data: bytes.NewReader(nil), 77 fs: fs, 78 } 79 80 return fs 81 } 82 83 func (fs *Fs) Open(name string) (afero.File, error) { 84 d, f := splitpath(name) 85 if _, ok := fs.files[d]; !ok { 86 return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} 87 } 88 89 file, ok := fs.files[d][f] 90 if !ok { 91 return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} 92 } 93 94 nf := *file 95 96 return &nf, nil 97 } 98 99 func (fs *Fs) Name() string { return "tarfs" } 100 101 func (fs *Fs) Create(name string) (afero.File, error) { return nil, syscall.EROFS } 102 103 func (fs *Fs) Mkdir(name string, perm os.FileMode) error { return syscall.EROFS } 104 105 func (fs *Fs) MkdirAll(path string, perm os.FileMode) error { return syscall.EROFS } 106 107 func (fs *Fs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { 108 if flag != os.O_RDONLY { 109 return nil, &os.PathError{Op: "open", Path: name, Err: syscall.EPERM} 110 } 111 112 return fs.Open(name) 113 } 114 115 func (fs *Fs) Remove(name string) error { return syscall.EROFS } 116 117 func (fs *Fs) RemoveAll(path string) error { return syscall.EROFS } 118 119 func (fs *Fs) Rename(oldname string, newname string) error { return syscall.EROFS } 120 121 func (fs *Fs) Stat(name string) (os.FileInfo, error) { 122 d, f := splitpath(name) 123 if _, ok := fs.files[d]; !ok { 124 return nil, &os.PathError{Op: "stat", Path: name, Err: syscall.ENOENT} 125 } 126 127 file, ok := fs.files[d][f] 128 if !ok { 129 return nil, &os.PathError{Op: "stat", Path: name, Err: syscall.ENOENT} 130 } 131 132 return file.h.FileInfo(), nil 133 } 134 135 func (fs *Fs) Chmod(name string, mode os.FileMode) error { return syscall.EROFS } 136 137 func (fs *Fs) Chown(name string, uid, gid int) error { return syscall.EROFS } 138 139 func (fs *Fs) Chtimes(name string, atime time.Time, mtime time.Time) error { return syscall.EROFS }