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