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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
// package tarfs implements a read-only in-memory representation of a tar archive
package tarfs
import (
"archive/tar"
"bytes"
"io"
"os"
"path/filepath"
"syscall"
"time"
"github.com/spf13/afero"
)
type Fs struct {
files map[string]map[string]*File
}
func splitpath(name string) (dir, file string) {
name = filepath.ToSlash(name)
if len(name) == 0 || name[0] != '/' {
name = "/" + name
}
name = filepath.Clean(name)
dir, file = filepath.Split(name)
dir = filepath.Clean(dir)
return
}
func New(t *tar.Reader) *Fs {
fs := &Fs{files: make(map[string]map[string]*File)}
for {
hdr, err := t.Next()
if err == io.EOF {
break
}
if err != nil {
return nil
}
d, f := splitpath(hdr.Name)
if _, ok := fs.files[d]; !ok {
fs.files[d] = make(map[string]*File)
}
var buf bytes.Buffer
size, err := buf.ReadFrom(t)
if err != nil {
panic("tarfs: reading from tar:" + err.Error())
}
if size != hdr.Size {
panic("tarfs: size mismatch")
}
file := &File{
h: hdr,
data: bytes.NewReader(buf.Bytes()),
fs: fs,
}
fs.files[d][f] = file
}
if fs.files[afero.FilePathSeparator] == nil {
fs.files[afero.FilePathSeparator] = make(map[string]*File)
}
// Add a pseudoroot
fs.files[afero.FilePathSeparator][""] = &File{
h: &tar.Header{
Name: afero.FilePathSeparator,
Typeflag: tar.TypeDir,
Size: 0,
},
data: bytes.NewReader(nil),
fs: fs,
}
return fs
}
func (fs *Fs) Open(name string) (afero.File, error) {
d, f := splitpath(name)
if _, ok := fs.files[d]; !ok {
return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT}
}
file, ok := fs.files[d][f]
if !ok {
return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT}
}
nf := *file
return &nf, nil
}
func (fs *Fs) Name() string { return "tarfs" }
func (fs *Fs) Create(name string) (afero.File, error) { return nil, syscall.EROFS }
func (fs *Fs) Mkdir(name string, perm os.FileMode) error { return syscall.EROFS }
func (fs *Fs) MkdirAll(path string, perm os.FileMode) error { return syscall.EROFS }
func (fs *Fs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
if flag != os.O_RDONLY {
return nil, &os.PathError{Op: "open", Path: name, Err: syscall.EPERM}
}
return fs.Open(name)
}
func (fs *Fs) Remove(name string) error { return syscall.EROFS }
func (fs *Fs) RemoveAll(path string) error { return syscall.EROFS }
func (fs *Fs) Rename(oldname string, newname string) error { return syscall.EROFS }
func (fs *Fs) Stat(name string) (os.FileInfo, error) {
d, f := splitpath(name)
if _, ok := fs.files[d]; !ok {
return nil, &os.PathError{Op: "stat", Path: name, Err: syscall.ENOENT}
}
file, ok := fs.files[d][f]
if !ok {
return nil, &os.PathError{Op: "stat", Path: name, Err: syscall.ENOENT}
}
return file.h.FileInfo(), nil
}
func (fs *Fs) Chmod(name string, mode os.FileMode) error { return syscall.EROFS }
func (fs *Fs) Chown(name string, uid, gid int) error { return syscall.EROFS }
func (fs *Fs) Chtimes(name string, atime time.Time, mtime time.Time) error { return syscall.EROFS }
|