github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/image/fs.go (about)

     1  package image
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"sync"
     9  
    10  	"github.com/Sirupsen/logrus"
    11  	"github.com/docker/distribution/digest"
    12  	"github.com/docker/docker/pkg/ioutils"
    13  )
    14  
    15  // DigestWalkFunc is function called by StoreBackend.Walk
    16  type DigestWalkFunc func(id digest.Digest) error
    17  
    18  // StoreBackend provides interface for image.Store persistence
    19  type StoreBackend interface {
    20  	Walk(f DigestWalkFunc) error
    21  	Get(id digest.Digest) ([]byte, error)
    22  	Set(data []byte) (digest.Digest, error)
    23  	Delete(id digest.Digest) error
    24  	SetMetadata(id digest.Digest, key string, data []byte) error
    25  	GetMetadata(id digest.Digest, key string) ([]byte, error)
    26  	DeleteMetadata(id digest.Digest, key string) error
    27  }
    28  
    29  // fs implements StoreBackend using the filesystem.
    30  type fs struct {
    31  	sync.RWMutex
    32  	root string
    33  }
    34  
    35  const (
    36  	contentDirName  = "content"
    37  	metadataDirName = "metadata"
    38  )
    39  
    40  // NewFSStoreBackend returns new filesystem based backend for image.Store
    41  func NewFSStoreBackend(root string) (StoreBackend, error) {
    42  	return newFSStore(root)
    43  }
    44  
    45  func newFSStore(root string) (*fs, error) {
    46  	s := &fs{
    47  		root: root,
    48  	}
    49  	if err := os.MkdirAll(filepath.Join(root, contentDirName, string(digest.Canonical)), 0700); err != nil {
    50  		return nil, err
    51  	}
    52  	if err := os.MkdirAll(filepath.Join(root, metadataDirName, string(digest.Canonical)), 0700); err != nil {
    53  		return nil, err
    54  	}
    55  	return s, nil
    56  }
    57  
    58  func (s *fs) contentFile(dgst digest.Digest) string {
    59  	return filepath.Join(s.root, contentDirName, string(dgst.Algorithm()), dgst.Hex())
    60  }
    61  
    62  func (s *fs) metadataDir(dgst digest.Digest) string {
    63  	return filepath.Join(s.root, metadataDirName, string(dgst.Algorithm()), dgst.Hex())
    64  }
    65  
    66  // Walk calls the supplied callback for each image ID in the storage backend.
    67  func (s *fs) Walk(f DigestWalkFunc) error {
    68  	// Only Canonical digest (sha256) is currently supported
    69  	s.RLock()
    70  	dir, err := ioutil.ReadDir(filepath.Join(s.root, contentDirName, string(digest.Canonical)))
    71  	s.RUnlock()
    72  	if err != nil {
    73  		return err
    74  	}
    75  	for _, v := range dir {
    76  		dgst := digest.NewDigestFromHex(string(digest.Canonical), v.Name())
    77  		if err := dgst.Validate(); err != nil {
    78  			logrus.Debugf("Skipping invalid digest %s: %s", dgst, err)
    79  			continue
    80  		}
    81  		if err := f(dgst); err != nil {
    82  			return err
    83  		}
    84  	}
    85  	return nil
    86  }
    87  
    88  // Get returns the content stored under a given digest.
    89  func (s *fs) Get(dgst digest.Digest) ([]byte, error) {
    90  	s.RLock()
    91  	defer s.RUnlock()
    92  
    93  	return s.get(dgst)
    94  }
    95  
    96  func (s *fs) get(dgst digest.Digest) ([]byte, error) {
    97  	content, err := ioutil.ReadFile(s.contentFile(dgst))
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101      fmt.Println("image/fs.go get() get byte")
   102  
   103  //    fmt.Println("image/fs.go get() content : ", content)
   104  //    fmt.Println("image/fs.go get() dgst : ", dgst)
   105  	// todo: maybe optional
   106  	if digest.FromBytes(content) != dgst {
   107  //        fmt.Println("image/fs.go get() FromBytes() is err!!!")
   108  		return nil, fmt.Errorf("failed to verify: %v", dgst)
   109  	}
   110  //    fmt.Println("image/fs.go get() end")
   111  
   112  	return content, nil
   113  }
   114  
   115  // Set stores content by checksum.
   116  func (s *fs) Set(data []byte) (digest.Digest, error) {
   117  	s.Lock()
   118  	defer s.Unlock()
   119  
   120  	if len(data) == 0 {
   121  		return "", fmt.Errorf("Invalid empty data")
   122  	}
   123  
   124  	dgst := digest.FromBytes(data)
   125  	if err := ioutils.AtomicWriteFile(s.contentFile(dgst), data, 0600); err != nil {
   126  		return "", err
   127  	}
   128  
   129  	return dgst, nil
   130  }
   131  
   132  // Delete removes content and metadata files associated with the digest.
   133  func (s *fs) Delete(dgst digest.Digest) error {
   134  	s.Lock()
   135  	defer s.Unlock()
   136  
   137  	if err := os.RemoveAll(s.metadataDir(dgst)); err != nil {
   138  		return err
   139  	}
   140  	if err := os.Remove(s.contentFile(dgst)); err != nil {
   141  		return err
   142  	}
   143  	return nil
   144  }
   145  
   146  // SetMetadata sets metadata for a given ID. It fails if there's no base file.
   147  func (s *fs) SetMetadata(dgst digest.Digest, key string, data []byte) error {
   148  	s.Lock()
   149  	defer s.Unlock()
   150  	if _, err := s.get(dgst); err != nil {
   151  		return err
   152  	}
   153  
   154  	baseDir := filepath.Join(s.metadataDir(dgst))
   155  	if err := os.MkdirAll(baseDir, 0700); err != nil {
   156  		return err
   157  	}
   158  	return ioutils.AtomicWriteFile(filepath.Join(s.metadataDir(dgst), key), data, 0600)
   159  }
   160  
   161  // GetMetadata returns metadata for a given digest.
   162  func (s *fs) GetMetadata(dgst digest.Digest, key string) ([]byte, error) {
   163  	s.RLock()
   164  	defer s.RUnlock()
   165  
   166  	if _, err := s.get(dgst); err != nil {
   167  		return nil, err
   168  	}
   169  	return ioutil.ReadFile(filepath.Join(s.metadataDir(dgst), key))
   170  }
   171  
   172  // DeleteMetadata removes the metadata associated with a digest.
   173  func (s *fs) DeleteMetadata(dgst digest.Digest, key string) error {
   174  	s.Lock()
   175  	defer s.Unlock()
   176  
   177  	return os.RemoveAll(filepath.Join(s.metadataDir(dgst), key))
   178  }