1
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/spf13/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
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 }
140
View as plain text