33package file
44
55import (
6+ "archive/zip"
7+ "compress/gzip"
8+ "fmt"
69 "io"
710 "os"
811
12+ "github.com/h2non/filetype"
13+ "github.com/h2non/filetype/matchers"
914 "github.com/taubyte/tau/core/vm"
15+ "github.com/taubyte/tau/pkg/specs/builders/wasm"
1016 "github.com/taubyte/tau/pkg/vm/backend/errors"
1117
1218 ma "github.com/multiformats/go-multiaddr"
1319 resolv "github.com/taubyte/tau/pkg/vm/resolvers/taubyte"
1420)
1521
22+ const headerSize = 512
23+
1624type backend struct {}
1725
1826func New () vm.Backend {
@@ -27,6 +35,28 @@ func (b *backend) Scheme() string {
2735 return resolv .FILE_PROTOCOL_NAME
2836}
2937
38+ // zipEntryReadCloser closes both the zip entry reader and the underlying file.
39+ type zipEntryReadCloser struct {
40+ io.ReadCloser
41+ file * os.File
42+ }
43+
44+ func (z * zipEntryReadCloser ) Close () error {
45+ z .ReadCloser .Close ()
46+ return z .file .Close ()
47+ }
48+
49+ // gzipFileReadCloser closes both the gzip reader and the underlying file.
50+ type gzipFileReadCloser struct {
51+ * gzip.Reader
52+ file * os.File
53+ }
54+
55+ func (g * gzipFileReadCloser ) Close () error {
56+ g .Reader .Close ()
57+ return g .file .Close ()
58+ }
59+
3060func (b * backend ) Get (multiAddr ma.Multiaddr ) (io.ReadCloser , error ) {
3161 protocols := multiAddr .Protocols ()
3262 if protocols [0 ].Code != resolv .P_FILE {
@@ -38,13 +68,61 @@ func (b *backend) Get(multiAddr ma.Multiaddr) (io.ReadCloser, error) {
3868 return nil , errors .ParseProtocol (resolv .FILE_PROTOCOL_NAME , err )
3969 }
4070
41- // remove extra slash
4271 path = path [1 :]
4372
4473 file , err := os .Open (path )
4574 if err != nil {
4675 return nil , errors .RetrieveError (path , err , b )
4776 }
4877
49- return file , nil
78+ header := make ([]byte , headerSize )
79+ n , err := file .Read (header )
80+ if err != nil && err != io .EOF {
81+ file .Close ()
82+ return nil , fmt .Errorf ("reading file header: %w" , err )
83+ }
84+ header = header [:n ]
85+
86+ if _ , err := file .Seek (0 , io .SeekStart ); err != nil {
87+ file .Close ()
88+ return nil , fmt .Errorf ("seeking to start: %w" , err )
89+ }
90+
91+ kind , err := filetype .Match (header )
92+ if err != nil {
93+ file .Close ()
94+ return nil , fmt .Errorf ("matching file type: %w" , err )
95+ }
96+
97+ switch kind {
98+ case matchers .TypeZip :
99+ info , err := file .Stat ()
100+ if err != nil {
101+ file .Close ()
102+ return nil , fmt .Errorf ("stating file: %w" , err )
103+ }
104+ zr , err := zip .NewReader (file , info .Size ())
105+ if err != nil {
106+ file .Close ()
107+ return nil , fmt .Errorf ("opening zip: %w" , err )
108+ }
109+ entryReader , err := zr .Open (wasm .WasmFile )
110+ if err != nil {
111+ entryReader , err = zr .Open (wasm .DeprecatedWasmFile )
112+ if err != nil {
113+ file .Close ()
114+ return nil , fmt .Errorf ("zip has no %q or %q: %w" , wasm .WasmFile , wasm .DeprecatedWasmFile , err )
115+ }
116+ }
117+ return & zipEntryReadCloser {ReadCloser : entryReader , file : file }, nil
118+ case matchers .TypeGz :
119+ gzReader , err := gzip .NewReader (file )
120+ if err != nil {
121+ file .Close ()
122+ return nil , fmt .Errorf ("opening gzip: %w" , err )
123+ }
124+ return & gzipFileReadCloser {Reader : gzReader , file : file }, nil
125+ default :
126+ return file , nil
127+ }
50128}
0 commit comments