github.com/akerouanton/docker@v1.11.0-rc3/layer/filestore.go (about)

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