...

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

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

     1  package client
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io/fs"
     8  	"os"
     9  	"path/filepath"
    10  	"sync"
    11  
    12  	"github.com/theupdateframework/go-tuf/client"
    13  	"github.com/theupdateframework/go-tuf/internal/fsutil"
    14  	"github.com/theupdateframework/go-tuf/util"
    15  )
    16  
    17  const (
    18  	// user:  rwx
    19  	// group: r-x
    20  	// other: ---
    21  	dirCreateMode = os.FileMode(0750)
    22  	// user:  rw-
    23  	// group: r--
    24  	// other: ---
    25  	fileCreateMode = os.FileMode(0640)
    26  )
    27  
    28  // FileJSONStore represents a local metadata cache relying on raw JSON files
    29  // as retrieved from the remote repository.
    30  type FileJSONStore struct {
    31  	mtx     sync.RWMutex
    32  	baseDir string
    33  }
    34  
    35  var _ client.LocalStore = (*FileJSONStore)(nil)
    36  
    37  // NewFileJSONStore returns a new metadata cache, implemented using raw JSON
    38  // files, stored in a directory provided by the client.
    39  // If the provided directory does not exist on disk, it will be created.
    40  // The provided metadata cache is safe for concurrent access.
    41  func NewFileJSONStore(baseDir string) (*FileJSONStore, error) {
    42  	f := &FileJSONStore{
    43  		baseDir: baseDir,
    44  	}
    45  
    46  	// Does the directory exist?
    47  	fi, err := os.Stat(baseDir)
    48  	if err != nil {
    49  		if errors.Is(err, fs.ErrNotExist) {
    50  			// Create the directory
    51  			if err = os.MkdirAll(baseDir, dirCreateMode); err != nil {
    52  				return nil, fmt.Errorf("error creating directory for metadata cache: %w", err)
    53  			}
    54  		} else {
    55  			return nil, fmt.Errorf("error getting FileInfo for %s: %w", baseDir, err)
    56  		}
    57  	} else {
    58  		// Verify that it is a directory
    59  		if !fi.IsDir() {
    60  			return nil, fmt.Errorf("can not open %s, not a directory", baseDir)
    61  		}
    62  		// Verify file mode is not too permissive.
    63  		if err = fsutil.EnsureMaxPermissions(fi, dirCreateMode); err != nil {
    64  			return nil, err
    65  		}
    66  	}
    67  
    68  	return f, nil
    69  }
    70  
    71  // GetMeta returns the currently cached set of metadata files.
    72  func (f *FileJSONStore) GetMeta() (map[string]json.RawMessage, error) {
    73  	f.mtx.RLock()
    74  	defer f.mtx.RUnlock()
    75  
    76  	names, err := os.ReadDir(f.baseDir)
    77  	if err != nil {
    78  		return nil, fmt.Errorf("error reading directory %s: %w", f.baseDir, err)
    79  	}
    80  
    81  	meta := map[string]json.RawMessage{}
    82  	for _, name := range names {
    83  		ok, err := fsutil.IsMetaFile(name)
    84  		if err != nil {
    85  			return nil, err
    86  		}
    87  		if !ok {
    88  			continue
    89  		}
    90  
    91  		// Verify permissions
    92  		info, err := name.Info()
    93  		if err != nil {
    94  			return nil, fmt.Errorf("error retrieving FileInfo for %s: %w", name.Name(), err)
    95  		}
    96  		if err = fsutil.EnsureMaxPermissions(info, fileCreateMode); err != nil {
    97  			return nil, err
    98  		}
    99  
   100  		p := filepath.Join(f.baseDir, name.Name())
   101  		b, err := os.ReadFile(p)
   102  		if err != nil {
   103  			return nil, fmt.Errorf("error reading file %s: %w", name.Name(), err)
   104  		}
   105  		meta[name.Name()] = b
   106  	}
   107  
   108  	return meta, nil
   109  }
   110  
   111  // SetMeta stores a metadata file in the cache. If the metadata file exist,
   112  // it will be overwritten.
   113  func (f *FileJSONStore) SetMeta(name string, meta json.RawMessage) error {
   114  	f.mtx.Lock()
   115  	defer f.mtx.Unlock()
   116  
   117  	if filepath.Ext(name) != ".json" {
   118  		return fmt.Errorf("file %s is not a JSON file", name)
   119  	}
   120  
   121  	p := filepath.Join(f.baseDir, name)
   122  	err := util.AtomicallyWriteFile(p, meta, fileCreateMode)
   123  	return err
   124  }
   125  
   126  // DeleteMeta deletes a metadata file from the cache.
   127  // If the file does not exist, an *os.PathError is returned.
   128  func (f *FileJSONStore) DeleteMeta(name string) error {
   129  	f.mtx.Lock()
   130  	defer f.mtx.Unlock()
   131  
   132  	if filepath.Ext(name) != ".json" {
   133  		return fmt.Errorf("file %s is not a JSON file", name)
   134  	}
   135  
   136  	p := filepath.Join(f.baseDir, name)
   137  	err := os.Remove(p)
   138  	if err == nil {
   139  		return nil
   140  	}
   141  
   142  	return fmt.Errorf("error deleting file %s: %w", name, err)
   143  }
   144  
   145  // Close closes the metadata cache. This is a no-op.
   146  func (f *FileJSONStore) Close() error {
   147  	return nil
   148  }
   149  

View as plain text