...

Source file src/github.com/theupdateframework/go-tuf/client/file_store.go

Documentation: github.com/theupdateframework/go-tuf/client

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"io/fs"
     9  )
    10  
    11  // FileRemoteStore provides a RemoteStore interface compatible
    12  // implementation that can be used where the RemoteStore is backed by a
    13  // fs.FS. This is useful for example in air-gapped environments where there's no
    14  // possibility to make outbound network connections.
    15  // By having this be a fs.FS instead of directories allows the repository to
    16  // be backed by something that's not persisted to disk.
    17  func NewFileRemoteStore(fsys fs.FS, targetDir string) (*FileRemoteStore, error) {
    18  	if fsys == nil {
    19  		return nil, errors.New("nil fs.FS")
    20  	}
    21  	t := targetDir
    22  	if t == "" {
    23  		t = "targets"
    24  	}
    25  	// Make sure directory exists
    26  	d, err := fsys.Open(t)
    27  	if err != nil {
    28  		return nil, fmt.Errorf("failed to open targets directory %s: %w", t, err)
    29  	}
    30  	fi, err := d.Stat()
    31  	if err != nil {
    32  		return nil, fmt.Errorf("failed to stat targets directory %s: %w", t, err)
    33  	}
    34  	if !fi.IsDir() {
    35  		return nil, fmt.Errorf("targets directory not a directory %s", t)
    36  	}
    37  
    38  	fsysT, err := fs.Sub(fsys, t)
    39  	if err != nil {
    40  		return nil, fmt.Errorf("failed to open targets directory %s: %w", t, err)
    41  	}
    42  	return &FileRemoteStore{fsys: fsys, targetDir: fsysT}, nil
    43  }
    44  
    45  type FileRemoteStore struct {
    46  	// Meta directory fs
    47  	fsys fs.FS
    48  	// Target directory fs.
    49  	targetDir fs.FS
    50  	// In order to be able to make write operations (create, delete) we can't
    51  	// use fs.FS for it (it's read only), so we have to know the underlying
    52  	// directory that add/delete test methods can use. This is only necessary
    53  	// for testing purposes.
    54  	testDir string
    55  }
    56  
    57  func (f *FileRemoteStore) GetMeta(name string) (io.ReadCloser, int64, error) {
    58  	rc, b, err := f.get(f.fsys, name)
    59  	return handleErrors(name, rc, b, err)
    60  }
    61  
    62  func (f *FileRemoteStore) GetTarget(name string) (io.ReadCloser, int64, error) {
    63  	rc, b, err := f.get(f.targetDir, name)
    64  	return handleErrors(name, rc, b, err)
    65  }
    66  
    67  func (f *FileRemoteStore) get(fsys fs.FS, s string) (io.ReadCloser, int64, error) {
    68  	if !fs.ValidPath(s) {
    69  		return nil, 0, fmt.Errorf("invalid path %s", s)
    70  	}
    71  
    72  	b, err := fs.ReadFile(fsys, s)
    73  	if err != nil {
    74  		return nil, -1, err
    75  	}
    76  	return io.NopCloser(bytes.NewReader(b)), int64(len(b)), nil
    77  }
    78  
    79  // handleErrors converts NotFound errors to something that TUF knows how to
    80  // handle properly. For example, when looking for n+1 root files, this is a
    81  // signal that it will stop looking.
    82  func handleErrors(name string, rc io.ReadCloser, b int64, err error) (io.ReadCloser, int64, error) {
    83  	if err == nil {
    84  		return rc, b, err
    85  	}
    86  	if errors.Is(err, fs.ErrNotExist) {
    87  		return rc, b, ErrNotFound{name}
    88  	}
    89  	return rc, b, err
    90  }
    91  

View as plain text