blob: f011331e8e0b243f16ef62041f9fe31f9b0f3eb9 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package source
import (
"encoding/json"
"os"
"path/filepath"
"sync"
"github.com/flynn/go-tuf/client"
)
// FileStore implements go-tuf/client.LocalStore in terms of a JSON file on the
// local filesystem.
type FileStore struct {
Path string
// mu serializes file read/write operations against this store
mu sync.Mutex
}
var _ client.LocalStore = &FileStore{}
func NewFileStore(path string) (*FileStore, error) {
s := &FileStore{Path: path, mu: sync.Mutex{}}
return s, os.MkdirAll(filepath.Dir(s.Path), 0755)
}
func (s *FileStore) GetMeta() (map[string]json.RawMessage, error) {
s.mu.Lock()
defer s.mu.Unlock()
return s.getMetaLocked()
}
func (s *FileStore) getMetaLocked() (map[string]json.RawMessage, error) {
var m = make(map[string]json.RawMessage)
f, err := os.Open(s.Path)
if err != nil {
if os.IsNotExist(err) {
return m, nil
}
return m, err
}
defer f.Close()
return m, json.NewDecoder(f).Decode(&m)
}
func (s *FileStore) SetMeta(name string, meta json.RawMessage) error {
s.mu.Lock()
defer s.mu.Unlock()
m, err := s.getMetaLocked()
if err != nil {
return err
}
m[name] = meta
newPath := s.Path + ".new"
f, err := os.Create(newPath)
if err != nil {
return err
}
if err := json.NewEncoder(f).Encode(&m); err != nil {
f.Close()
os.Remove(newPath)
return err
}
if err := f.Close(); err != nil {
os.Remove(newPath)
return err
}
return os.Rename(newPath, s.Path)
}