github.com/olljanat/moby@v1.13.1/layer/filestore.go (about)

     1  package layer
     2  
     3  import (
     4  	"compress/gzip"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"os"
    11  	"path/filepath"
    12  	"regexp"
    13  	"strconv"
    14  	"strings"
    15  
    16  	"github.com/Sirupsen/logrus"
    17  	"github.com/docker/distribution"
    18  	"github.com/docker/distribution/digest"
    19  	"github.com/docker/docker/pkg/ioutils"
    20  )
    21  
    22  var (
    23  	stringIDRegexp      = regexp.MustCompile(`^[a-f0-9]{64}(-init)?$`)
    24  	supportedAlgorithms = []digest.Algorithm{
    25  		digest.SHA256,
    26  		// digest.SHA384, // Currently not used
    27  		// digest.SHA512, // Currently not used
    28  	}
    29  )
    30  
    31  type fileMetadataStore struct {
    32  	root string
    33  }
    34  
    35  type fileMetadataTransaction struct {
    36  	store *fileMetadataStore
    37  	ws    *ioutils.AtomicWriteSet
    38  }
    39  
    40  // NewFSMetadataStore returns an instance of a metadata store
    41  // which is backed by files on disk using the provided root
    42  // as the root of metadata files.
    43  func NewFSMetadataStore(root string) (MetadataStore, error) {
    44  	if err := os.MkdirAll(root, 0700); err != nil {
    45  		return nil, err
    46  	}
    47  	return &fileMetadataStore{
    48  		root: root,
    49  	}, nil
    50  }
    51  
    52  func (fms *fileMetadataStore) getLayerDirectory(layer ChainID) string {
    53  	dgst := digest.Digest(layer)
    54  	return filepath.Join(fms.root, string(dgst.Algorithm()), dgst.Hex())
    55  }
    56  
    57  func (fms *fileMetadataStore) getLayerFilename(layer ChainID, filename string) string {
    58  	return filepath.Join(fms.getLayerDirectory(layer), filename)
    59  }
    60  
    61  func (fms *fileMetadataStore) getMountDirectory(mount string) string {
    62  	return filepath.Join(fms.root, "mounts", mount)
    63  }
    64  
    65  func (fms *fileMetadataStore) getMountFilename(mount, filename string) string {
    66  	return filepath.Join(fms.getMountDirectory(mount), filename)
    67  }
    68  
    69  func (fms *fileMetadataStore) StartTransaction() (MetadataTransaction, error) {
    70  	tmpDir := filepath.Join(fms.root, "tmp")
    71  	if err := os.MkdirAll(tmpDir, 0755); err != nil {
    72  		return nil, err
    73  	}
    74  	ws, err := ioutils.NewAtomicWriteSet(tmpDir)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	return &fileMetadataTransaction{
    80  		store: fms,
    81  		ws:    ws,
    82  	}, nil
    83  }
    84  
    85  func (fm *fileMetadataTransaction) SetSize(size int64) error {
    86  	content := fmt.Sprintf("%d", size)
    87  	return fm.ws.WriteFile("size", []byte(content), 0644)
    88  }
    89  
    90  func (fm *fileMetadataTransaction) SetParent(parent ChainID) error {
    91  	return fm.ws.WriteFile("parent", []byte(digest.Digest(parent).String()), 0644)
    92  }
    93  
    94  func (fm *fileMetadataTransaction) SetDiffID(diff DiffID) error {
    95  	return fm.ws.WriteFile("diff", []byte(digest.Digest(diff).String()), 0644)
    96  }
    97  
    98  func (fm *fileMetadataTransaction) SetCacheID(cacheID string) error {
    99  	return fm.ws.WriteFile("cache-id", []byte(cacheID), 0644)
   100  }
   101  
   102  func (fm *fileMetadataTransaction) SetDescriptor(ref distribution.Descriptor) error {
   103  	jsonRef, err := json.Marshal(ref)
   104  	if err != nil {
   105  		return err
   106  	}
   107  	return fm.ws.WriteFile("descriptor.json", jsonRef, 0644)
   108  }
   109  
   110  func (fm *fileMetadataTransaction) TarSplitWriter(compressInput bool) (io.WriteCloser, error) {
   111  	f, err := fm.ws.FileWriter("tar-split.json.gz", os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	var wc io.WriteCloser
   116  	if compressInput {
   117  		wc = gzip.NewWriter(f)
   118  	} else {
   119  		wc = f
   120  	}
   121  
   122  	return ioutils.NewWriteCloserWrapper(wc, func() error {
   123  		wc.Close()
   124  		return f.Close()
   125  	}), nil
   126  }
   127  
   128  func (fm *fileMetadataTransaction) Commit(layer ChainID) error {
   129  	finalDir := fm.store.getLayerDirectory(layer)
   130  	if err := os.MkdirAll(filepath.Dir(finalDir), 0755); err != nil {
   131  		return err
   132  	}
   133  
   134  	return fm.ws.Commit(finalDir)
   135  }
   136  
   137  func (fm *fileMetadataTransaction) Cancel() error {
   138  	return fm.ws.Cancel()
   139  }
   140  
   141  func (fm *fileMetadataTransaction) String() string {
   142  	return fm.ws.String()
   143  }
   144  
   145  func (fms *fileMetadataStore) GetSize(layer ChainID) (int64, error) {
   146  	content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "size"))
   147  	if err != nil {
   148  		return 0, err
   149  	}
   150  
   151  	size, err := strconv.ParseInt(string(content), 10, 64)
   152  	if err != nil {
   153  		return 0, err
   154  	}
   155  
   156  	return size, nil
   157  }
   158  
   159  func (fms *fileMetadataStore) GetParent(layer ChainID) (ChainID, error) {
   160  	content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "parent"))
   161  	if err != nil {
   162  		if os.IsNotExist(err) {
   163  			return "", nil
   164  		}
   165  		return "", err
   166  	}
   167  
   168  	dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
   169  	if err != nil {
   170  		return "", err
   171  	}
   172  
   173  	return ChainID(dgst), nil
   174  }
   175  
   176  func (fms *fileMetadataStore) GetDiffID(layer ChainID) (DiffID, error) {
   177  	content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "diff"))
   178  	if err != nil {
   179  		return "", err
   180  	}
   181  
   182  	dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
   183  	if err != nil {
   184  		return "", err
   185  	}
   186  
   187  	return DiffID(dgst), nil
   188  }
   189  
   190  func (fms *fileMetadataStore) GetCacheID(layer ChainID) (string, error) {
   191  	contentBytes, err := ioutil.ReadFile(fms.getLayerFilename(layer, "cache-id"))
   192  	if err != nil {
   193  		return "", err
   194  	}
   195  	content := strings.TrimSpace(string(contentBytes))
   196  
   197  	if !stringIDRegexp.MatchString(content) {
   198  		return "", errors.New("invalid cache id value")
   199  	}
   200  
   201  	return content, nil
   202  }
   203  
   204  func (fms *fileMetadataStore) GetDescriptor(layer ChainID) (distribution.Descriptor, error) {
   205  	content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "descriptor.json"))
   206  	if err != nil {
   207  		if os.IsNotExist(err) {
   208  			// only return empty descriptor to represent what is stored
   209  			return distribution.Descriptor{}, nil
   210  		}
   211  		return distribution.Descriptor{}, err
   212  	}
   213  
   214  	var ref distribution.Descriptor
   215  	err = json.Unmarshal(content, &ref)
   216  	if err != nil {
   217  		return distribution.Descriptor{}, err
   218  	}
   219  	return ref, err
   220  }
   221  
   222  func (fms *fileMetadataStore) TarSplitReader(layer ChainID) (io.ReadCloser, error) {
   223  	fz, err := os.Open(fms.getLayerFilename(layer, "tar-split.json.gz"))
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  	f, err := gzip.NewReader(fz)
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  
   232  	return ioutils.NewReadCloserWrapper(f, func() error {
   233  		f.Close()
   234  		return fz.Close()
   235  	}), nil
   236  }
   237  
   238  func (fms *fileMetadataStore) SetMountID(mount string, mountID string) error {
   239  	if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
   240  		return err
   241  	}
   242  	return ioutil.WriteFile(fms.getMountFilename(mount, "mount-id"), []byte(mountID), 0644)
   243  }
   244  
   245  func (fms *fileMetadataStore) SetInitID(mount string, init string) error {
   246  	if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
   247  		return err
   248  	}
   249  	return ioutil.WriteFile(fms.getMountFilename(mount, "init-id"), []byte(init), 0644)
   250  }
   251  
   252  func (fms *fileMetadataStore) SetMountParent(mount string, parent ChainID) error {
   253  	if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
   254  		return err
   255  	}
   256  	return ioutil.WriteFile(fms.getMountFilename(mount, "parent"), []byte(digest.Digest(parent).String()), 0644)
   257  }
   258  
   259  func (fms *fileMetadataStore) GetMountID(mount string) (string, error) {
   260  	contentBytes, err := ioutil.ReadFile(fms.getMountFilename(mount, "mount-id"))
   261  	if err != nil {
   262  		return "", err
   263  	}
   264  	content := strings.TrimSpace(string(contentBytes))
   265  
   266  	if !stringIDRegexp.MatchString(content) {
   267  		return "", errors.New("invalid mount id value")
   268  	}
   269  
   270  	return content, nil
   271  }
   272  
   273  func (fms *fileMetadataStore) GetInitID(mount string) (string, error) {
   274  	contentBytes, err := ioutil.ReadFile(fms.getMountFilename(mount, "init-id"))
   275  	if err != nil {
   276  		if os.IsNotExist(err) {
   277  			return "", nil
   278  		}
   279  		return "", err
   280  	}
   281  	content := strings.TrimSpace(string(contentBytes))
   282  
   283  	if !stringIDRegexp.MatchString(content) {
   284  		return "", errors.New("invalid init id value")
   285  	}
   286  
   287  	return content, nil
   288  }
   289  
   290  func (fms *fileMetadataStore) GetMountParent(mount string) (ChainID, error) {
   291  	content, err := ioutil.ReadFile(fms.getMountFilename(mount, "parent"))
   292  	if err != nil {
   293  		if os.IsNotExist(err) {
   294  			return "", nil
   295  		}
   296  		return "", err
   297  	}
   298  
   299  	dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
   300  	if err != nil {
   301  		return "", err
   302  	}
   303  
   304  	return ChainID(dgst), nil
   305  }
   306  
   307  func (fms *fileMetadataStore) List() ([]ChainID, []string, error) {
   308  	var ids []ChainID
   309  	for _, algorithm := range supportedAlgorithms {
   310  		fileInfos, err := ioutil.ReadDir(filepath.Join(fms.root, string(algorithm)))
   311  		if err != nil {
   312  			if os.IsNotExist(err) {
   313  				continue
   314  			}
   315  			return nil, nil, err
   316  		}
   317  
   318  		for _, fi := range fileInfos {
   319  			if fi.IsDir() && fi.Name() != "mounts" {
   320  				dgst := digest.NewDigestFromHex(string(algorithm), fi.Name())
   321  				if err := dgst.Validate(); err != nil {
   322  					logrus.Debugf("Ignoring invalid digest %s:%s", algorithm, fi.Name())
   323  				} else {
   324  					ids = append(ids, ChainID(dgst))
   325  				}
   326  			}
   327  		}
   328  	}
   329  
   330  	fileInfos, err := ioutil.ReadDir(filepath.Join(fms.root, "mounts"))
   331  	if err != nil {
   332  		if os.IsNotExist(err) {
   333  			return ids, []string{}, nil
   334  		}
   335  		return nil, nil, err
   336  	}
   337  
   338  	var mounts []string
   339  	for _, fi := range fileInfos {
   340  		if fi.IsDir() {
   341  			mounts = append(mounts, fi.Name())
   342  		}
   343  	}
   344  
   345  	return ids, mounts, nil
   346  }
   347  
   348  func (fms *fileMetadataStore) Remove(layer ChainID) error {
   349  	return os.RemoveAll(fms.getLayerDirectory(layer))
   350  }
   351  
   352  func (fms *fileMetadataStore) RemoveMount(mount string) error {
   353  	return os.RemoveAll(fms.getMountDirectory(mount))
   354  }