123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- package image
- import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "sync"
- "github.com/Sirupsen/logrus"
- "github.com/docker/distribution/digest"
- "github.com/docker/docker/pkg/ioutils"
- )
- // IDWalkFunc is function called by StoreBackend.Walk
- type IDWalkFunc func(id ID) error
- // StoreBackend provides interface for image.Store persistence
- type StoreBackend interface {
- Walk(f IDWalkFunc) error
- Get(id ID) ([]byte, error)
- Set(data []byte) (ID, error)
- Delete(id ID) error
- SetMetadata(id ID, key string, data []byte) error
- GetMetadata(id ID, key string) ([]byte, error)
- DeleteMetadata(id ID, key string) error
- }
- // fs implements StoreBackend using the filesystem.
- type fs struct {
- sync.RWMutex
- root string
- }
- const (
- contentDirName = "content"
- metadataDirName = "metadata"
- )
- // NewFSStoreBackend returns new filesystem based backend for image.Store
- func NewFSStoreBackend(root string) (StoreBackend, error) {
- return newFSStore(root)
- }
- func newFSStore(root string) (*fs, error) {
- s := &fs{
- root: root,
- }
- if err := os.MkdirAll(filepath.Join(root, contentDirName, string(digest.Canonical)), 0700); err != nil {
- return nil, err
- }
- if err := os.MkdirAll(filepath.Join(root, metadataDirName, string(digest.Canonical)), 0700); err != nil {
- return nil, err
- }
- return s, nil
- }
- func (s *fs) contentFile(id ID) string {
- dgst := digest.Digest(id)
- return filepath.Join(s.root, contentDirName, string(dgst.Algorithm()), dgst.Hex())
- }
- func (s *fs) metadataDir(id ID) string {
- dgst := digest.Digest(id)
- return filepath.Join(s.root, metadataDirName, string(dgst.Algorithm()), dgst.Hex())
- }
- // Walk calls the supplied callback for each image ID in the storage backend.
- func (s *fs) Walk(f IDWalkFunc) error {
- // Only Canonical digest (sha256) is currently supported
- s.RLock()
- dir, err := ioutil.ReadDir(filepath.Join(s.root, contentDirName, string(digest.Canonical)))
- s.RUnlock()
- if err != nil {
- return err
- }
- for _, v := range dir {
- dgst := digest.NewDigestFromHex(string(digest.Canonical), v.Name())
- if err := dgst.Validate(); err != nil {
- logrus.Debugf("Skipping invalid digest %s: %s", dgst, err)
- continue
- }
- if err := f(ID(dgst)); err != nil {
- return err
- }
- }
- return nil
- }
- // Get returns the content stored under a given ID.
- func (s *fs) Get(id ID) ([]byte, error) {
- s.RLock()
- defer s.RUnlock()
- return s.get(id)
- }
- func (s *fs) get(id ID) ([]byte, error) {
- content, err := ioutil.ReadFile(s.contentFile(id))
- if err != nil {
- return nil, err
- }
- // todo: maybe optional
- if ID(digest.FromBytes(content)) != id {
- return nil, fmt.Errorf("failed to verify image: %v", id)
- }
- return content, nil
- }
- // Set stores content under a given ID.
- func (s *fs) Set(data []byte) (ID, error) {
- s.Lock()
- defer s.Unlock()
- if len(data) == 0 {
- return "", fmt.Errorf("Invalid empty data")
- }
- id := ID(digest.FromBytes(data))
- if err := ioutils.AtomicWriteFile(s.contentFile(id), data, 0600); err != nil {
- return "", err
- }
- return id, nil
- }
- // Delete removes content and metadata files associated with the ID.
- func (s *fs) Delete(id ID) error {
- s.Lock()
- defer s.Unlock()
- if err := os.RemoveAll(s.metadataDir(id)); err != nil {
- return err
- }
- if err := os.Remove(s.contentFile(id)); err != nil {
- return err
- }
- return nil
- }
- // SetMetadata sets metadata for a given ID. It fails if there's no base file.
- func (s *fs) SetMetadata(id ID, key string, data []byte) error {
- s.Lock()
- defer s.Unlock()
- if _, err := s.get(id); err != nil {
- return err
- }
- baseDir := filepath.Join(s.metadataDir(id))
- if err := os.MkdirAll(baseDir, 0700); err != nil {
- return err
- }
- return ioutils.AtomicWriteFile(filepath.Join(s.metadataDir(id), key), data, 0600)
- }
- // GetMetadata returns metadata for a given ID.
- func (s *fs) GetMetadata(id ID, key string) ([]byte, error) {
- s.RLock()
- defer s.RUnlock()
- if _, err := s.get(id); err != nil {
- return nil, err
- }
- return ioutil.ReadFile(filepath.Join(s.metadataDir(id), key))
- }
- // DeleteMetadata removes the metadata associated with an ID.
- func (s *fs) DeleteMetadata(id ID, key string) error {
- s.Lock()
- defer s.Unlock()
- return os.RemoveAll(filepath.Join(s.metadataDir(id), key))
- }
|