pkg/bundle/fs/root.go (155 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package fs import ( "errors" "fmt" "io" "io/fs" "os" "path/filepath" "strings" "github.com/awnumar/memguard" "google.golang.org/protobuf/proto" bundlev1 "github.com/elastic/harp/api/gen/go/harp/bundle/v1" ) const ( directoryAccess = 0o555 fileAccess = 0o444 ) type bundleFs struct { root *directory } // ----------------------------------------------------------------------------- // FromBundle initializes an fs.FS object from the given bundle. func FromBundle(b *bundlev1.Bundle) (BundleFS, error) { // Check arguments if b == nil { return nil, errors.New("unable to create a filesytem from a nil bundle") } // Prepare vfs root bfs := &bundleFs{ root: &directory{ children: map[string]interface{}{}, }, } // Prepare filesystem for _, p := range b.Packages { if p == nil { // ignore nil package continue } // Serialize package body, err := proto.Marshal(p) if err != nil { return nil, fmt.Errorf("unable to serialize package '%s': %w", p.Name, err) } // Write content if errWrite := bfs.WriteFile(p.Name, body, fileAccess); errWrite != nil { return nil, fmt.Errorf("unable to write package '%s' in filesystem: %w", p.Name, err) } } // Return bundle filesystem return bfs, nil } // ----------------------------------------------------------------------------- func (bfs *bundleFs) Open(name string) (fs.File, error) { // Return root as default if name == "" { return bfs.root, nil } // Validate input path if !fs.ValidPath(name) { return nil, &fs.PathError{ Op: "open", Path: name, Err: fs.ErrInvalid, } } // Create directory tree dirPath, name := filepath.Split(name) dirNames := strings.Split(dirPath, "/") // Browse directory tree currentDirectory := bfs.root for _, dirName := range dirNames { // Skip empty directory name if dirName == "" { continue } it, ok := currentDirectory.children[dirName] if !ok { return nil, fmt.Errorf("directory '%s' not found: %w", dirName, fs.ErrNotExist) } currentDirectory, ok = it.(*directory) if !ok { return nil, errors.New("invalid directory iterator value") } } // Get child h, ok := currentDirectory.children[name] if !ok { return nil, fmt.Errorf("item '%s' not found in directory '%s': %w", name, currentDirectory.name, fs.ErrNotExist) } switch it := h.(type) { case *directory: // Return directory return it, nil case *file: // Open enclave body, err := it.content.Open() if err != nil { return nil, fmt.Errorf("file '%s' could not be opened: %w", name, err) } // Assign body reader it.bodyReader = body.Reader() // Return file return it, nil } return nil, fmt.Errorf("unexpected file type in filesystem %s: %w", name, fs.ErrInvalid) } func (bfs *bundleFs) ReadDir(name string) ([]fs.DirEntry, error) { // Try to open directory h, err := bfs.Open(name) if err != nil { return nil, fmt.Errorf("unable to open directory: %w", err) } // Retrieve directory info fi, err := h.Stat() if err != nil { return nil, fmt.Errorf("unable to retrieve directory info: %w", err) } // Confirm it's a directory if !fi.IsDir() { return nil, fmt.Errorf("path '%s' point to a file", name) } // Convert handle to directory reader dir, ok := h.(fs.ReadDirFile) if !ok { return nil, fmt.Errorf("path '%s' point to a directory but could not be listed", name) } // Delegate to directory list return dir.ReadDir(0) } func (bfs *bundleFs) ReadFile(name string) ([]byte, error) { // Try to open file h, err := bfs.Open(name) if err != nil { return nil, fmt.Errorf("unable to open file: %w", err) } // Delete to file return io.ReadAll(h) } func (bfs *bundleFs) WriteFile(name string, data []byte, perm os.FileMode) error { // Create directory tree dirPath, fname := filepath.Split(name) dirNames := strings.Split(dirPath, "/") // MkDirAll currentDirectory := bfs.root for _, dirName := range dirNames { // Skip empty directory name if dirName == "" { continue } currentDirectory.RLock() it, ok := currentDirectory.children[dirName] currentDirectory.RUnlock() if !ok { it = &directory{ name: dirName, perm: directoryAccess, children: map[string]interface{}{}, } currentDirectory.Lock() currentDirectory.children[dirName] = it currentDirectory.Unlock() } currentDirectory, ok = it.(*directory) if !ok { return errors.New("invalid directory iterator value") } } // Create file entry currentDirectory.Lock() currentDirectory.children[fname] = &file{ name: fname, mode: fileAccess, size: int64(len(data)), content: memguard.NewEnclave(data), } currentDirectory.Unlock() // No error return nil } func (bfs *bundleFs) Stat(name string) (fs.FileInfo, error) { // Try to open file h, err := bfs.Open(name) if err != nil { return nil, fmt.Errorf("unable to open file: %w", err) } // Delegate to file return h.Stat() }