github.com/thy00/storage@v1.12.8/images.go (about)

     1  package storage
     2  
     3  import (
     4  	"encoding/json"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/containers/storage/pkg/ioutils"
    12  	"github.com/containers/storage/pkg/stringid"
    13  	"github.com/containers/storage/pkg/truncindex"
    14  	digest "github.com/opencontainers/go-digest"
    15  	"github.com/pkg/errors"
    16  )
    17  
    18  const (
    19  	// ImageDigestManifestBigDataNamePrefix is a prefix of big data item
    20  	// names which we consider to be manifests, used for computing a
    21  	// "digest" value for the image as a whole, by which we can locate the
    22  	// image later.
    23  	ImageDigestManifestBigDataNamePrefix = "manifest"
    24  	// ImageDigestBigDataKey is provided for compatibility with older
    25  	// versions of the image library.  It will be removed in the future.
    26  	ImageDigestBigDataKey = "manifest"
    27  )
    28  
    29  // An Image is a reference to a layer and an associated metadata string.
    30  type Image struct {
    31  	// ID is either one which was specified at create-time, or a random
    32  	// value which was generated by the library.
    33  	ID string `json:"id"`
    34  
    35  	// Digest is a digest value that we can use to locate the image, if one
    36  	// was specified at creation-time.
    37  	Digest digest.Digest `json:"digest,omitempty"`
    38  
    39  	// Digests is a list of digest values of the image's manifests, and
    40  	// possibly a manually-specified value, that we can use to locate the
    41  	// image.  If Digest is set, its value is also in this list.
    42  	Digests []digest.Digest `json:"-"`
    43  
    44  	// Names is an optional set of user-defined convenience values.  The
    45  	// image can be referred to by its ID or any of its names.  Names are
    46  	// unique among images, and are often the text representation of tagged
    47  	// or canonical references.
    48  	Names []string `json:"names,omitempty"`
    49  
    50  	// TopLayer is the ID of the topmost layer of the image itself, if the
    51  	// image contains one or more layers.  Multiple images can refer to the
    52  	// same top layer.
    53  	TopLayer string `json:"layer,omitempty"`
    54  
    55  	// MappedTopLayers are the IDs of alternate versions of the top layer
    56  	// which have the same contents and parent, and which differ from
    57  	// TopLayer only in which ID mappings they use.  When the image is
    58  	// to be removed, they should be removed before the TopLayer, as the
    59  	// graph driver may depend on that.
    60  	MappedTopLayers []string `json:"mapped-layers,omitempty"`
    61  
    62  	// Metadata is data we keep for the convenience of the caller.  It is not
    63  	// expected to be large, since it is kept in memory.
    64  	Metadata string `json:"metadata,omitempty"`
    65  
    66  	// BigDataNames is a list of names of data items that we keep for the
    67  	// convenience of the caller.  They can be large, and are only in
    68  	// memory when being read from or written to disk.
    69  	BigDataNames []string `json:"big-data-names,omitempty"`
    70  
    71  	// BigDataSizes maps the names in BigDataNames to the sizes of the data
    72  	// that has been stored, if they're known.
    73  	BigDataSizes map[string]int64 `json:"big-data-sizes,omitempty"`
    74  
    75  	// BigDataDigests maps the names in BigDataNames to the digests of the
    76  	// data that has been stored, if they're known.
    77  	BigDataDigests map[string]digest.Digest `json:"big-data-digests,omitempty"`
    78  
    79  	// Created is the datestamp for when this image was created.  Older
    80  	// versions of the library did not track this information, so callers
    81  	// will likely want to use the IsZero() method to verify that a value
    82  	// is set before using it.
    83  	Created time.Time `json:"created,omitempty"`
    84  
    85  	// ReadOnly is true if this image resides in a read-only layer store.
    86  	ReadOnly bool `json:"-"`
    87  
    88  	Flags map[string]interface{} `json:"flags,omitempty"`
    89  }
    90  
    91  // ROImageStore provides bookkeeping for information about Images.
    92  type ROImageStore interface {
    93  	ROFileBasedStore
    94  	ROMetadataStore
    95  	ROBigDataStore
    96  
    97  	// Exists checks if there is an image with the given ID or name.
    98  	Exists(id string) bool
    99  
   100  	// Get retrieves information about an image given an ID or name.
   101  	Get(id string) (*Image, error)
   102  
   103  	// Lookup attempts to translate a name to an ID.  Most methods do this
   104  	// implicitly.
   105  	Lookup(name string) (string, error)
   106  
   107  	// Images returns a slice enumerating the known images.
   108  	Images() ([]Image, error)
   109  
   110  	// ByDigest returns a slice enumerating the images which have either an
   111  	// explicitly-set digest, or a big data item with a name that starts
   112  	// with ImageDigestManifestBigDataNamePrefix, which matches the
   113  	// specified digest.
   114  	ByDigest(d digest.Digest) ([]*Image, error)
   115  }
   116  
   117  // ImageStore provides bookkeeping for information about Images.
   118  type ImageStore interface {
   119  	ROImageStore
   120  	RWFileBasedStore
   121  	RWMetadataStore
   122  	RWImageBigDataStore
   123  	FlaggableStore
   124  
   125  	// Create creates an image that has a specified ID (or a random one) and
   126  	// optional names, using the specified layer as its topmost (hopefully
   127  	// read-only) layer.  That layer can be referenced by multiple images.
   128  	Create(id string, names []string, layer, metadata string, created time.Time, searchableDigest digest.Digest) (*Image, error)
   129  
   130  	// SetNames replaces the list of names associated with an image with the
   131  	// supplied values.  The values are expected to be valid normalized
   132  	// named image references.
   133  	SetNames(id string, names []string) error
   134  
   135  	// Delete removes the record of the image.
   136  	Delete(id string) error
   137  
   138  	// Wipe removes records of all images.
   139  	Wipe() error
   140  }
   141  
   142  type imageStore struct {
   143  	lockfile Locker
   144  	dir      string
   145  	images   []*Image
   146  	idindex  *truncindex.TruncIndex
   147  	byid     map[string]*Image
   148  	byname   map[string]*Image
   149  	bydigest map[digest.Digest][]*Image
   150  }
   151  
   152  func copyImage(i *Image) *Image {
   153  	return &Image{
   154  		ID:              i.ID,
   155  		Digest:          i.Digest,
   156  		Digests:         copyDigestSlice(i.Digests),
   157  		Names:           copyStringSlice(i.Names),
   158  		TopLayer:        i.TopLayer,
   159  		MappedTopLayers: copyStringSlice(i.MappedTopLayers),
   160  		Metadata:        i.Metadata,
   161  		BigDataNames:    copyStringSlice(i.BigDataNames),
   162  		BigDataSizes:    copyStringInt64Map(i.BigDataSizes),
   163  		BigDataDigests:  copyStringDigestMap(i.BigDataDigests),
   164  		Created:         i.Created,
   165  		ReadOnly:        i.ReadOnly,
   166  		Flags:           copyStringInterfaceMap(i.Flags),
   167  	}
   168  }
   169  
   170  func copyImageSlice(slice []*Image) []*Image {
   171  	if len(slice) > 0 {
   172  		cp := make([]*Image, len(slice))
   173  		for i := range slice {
   174  			cp[i] = copyImage(slice[i])
   175  		}
   176  		return cp
   177  	}
   178  	return nil
   179  }
   180  
   181  func (r *imageStore) Images() ([]Image, error) {
   182  	images := make([]Image, len(r.images))
   183  	for i := range r.images {
   184  		images[i] = *copyImage(r.images[i])
   185  	}
   186  	return images, nil
   187  }
   188  
   189  func (r *imageStore) imagespath() string {
   190  	return filepath.Join(r.dir, "images.json")
   191  }
   192  
   193  func (r *imageStore) datadir(id string) string {
   194  	return filepath.Join(r.dir, id)
   195  }
   196  
   197  func (r *imageStore) datapath(id, key string) string {
   198  	return filepath.Join(r.datadir(id), makeBigDataBaseName(key))
   199  }
   200  
   201  // bigDataNameIsManifest determines if a big data item with the specified name
   202  // is considered to be representative of the image, in that its digest can be
   203  // said to also be the image's digest.  Currently, if its name is, or begins
   204  // with, "manifest", we say that it is.
   205  func bigDataNameIsManifest(name string) bool {
   206  	return strings.HasPrefix(name, ImageDigestManifestBigDataNamePrefix)
   207  }
   208  
   209  // recomputeDigests takes a fixed digest and a name-to-digest map and builds a
   210  // list of the unique values that would identify the image.
   211  func (image *Image) recomputeDigests() error {
   212  	validDigests := make([]digest.Digest, 0, len(image.BigDataDigests)+1)
   213  	digests := make(map[digest.Digest]struct{})
   214  	if image.Digest != "" {
   215  		if err := image.Digest.Validate(); err != nil {
   216  			return errors.Wrapf(err, "error validating image digest %q", string(image.Digest))
   217  		}
   218  		digests[image.Digest] = struct{}{}
   219  		validDigests = append(validDigests, image.Digest)
   220  	}
   221  	for name, digest := range image.BigDataDigests {
   222  		if !bigDataNameIsManifest(name) {
   223  			continue
   224  		}
   225  		if digest.Validate() != nil {
   226  			return errors.Wrapf(digest.Validate(), "error validating digest %q for big data item %q", string(digest), name)
   227  		}
   228  		// Deduplicate the digest values.
   229  		if _, known := digests[digest]; !known {
   230  			digests[digest] = struct{}{}
   231  			validDigests = append(validDigests, digest)
   232  		}
   233  	}
   234  	if image.Digest == "" && len(validDigests) > 0 {
   235  		image.Digest = validDigests[0]
   236  	}
   237  	image.Digests = validDigests
   238  	return nil
   239  }
   240  
   241  func (r *imageStore) Load() error {
   242  	shouldSave := false
   243  	rpath := r.imagespath()
   244  	data, err := ioutil.ReadFile(rpath)
   245  	if err != nil && !os.IsNotExist(err) {
   246  		return err
   247  	}
   248  	images := []*Image{}
   249  	idlist := []string{}
   250  	ids := make(map[string]*Image)
   251  	names := make(map[string]*Image)
   252  	digests := make(map[digest.Digest][]*Image)
   253  	if err = json.Unmarshal(data, &images); len(data) == 0 || err == nil {
   254  		idlist = make([]string, 0, len(images))
   255  		for n, image := range images {
   256  			ids[image.ID] = images[n]
   257  			idlist = append(idlist, image.ID)
   258  			for _, name := range image.Names {
   259  				if conflict, ok := names[name]; ok {
   260  					r.removeName(conflict, name)
   261  					shouldSave = true
   262  				}
   263  			}
   264  			// Compute the digest list.
   265  			err = image.recomputeDigests()
   266  			if err != nil {
   267  				return errors.Wrapf(err, "error computing digests for image with ID %q (%v)", image.ID, image.Names)
   268  			}
   269  			for _, name := range image.Names {
   270  				names[name] = image
   271  			}
   272  			for _, digest := range image.Digests {
   273  				list := digests[digest]
   274  				digests[digest] = append(list, image)
   275  			}
   276  			image.ReadOnly = !r.IsReadWrite()
   277  		}
   278  	}
   279  	if shouldSave && (!r.IsReadWrite() || !r.Locked()) {
   280  		return ErrDuplicateImageNames
   281  	}
   282  	r.images = images
   283  	r.idindex = truncindex.NewTruncIndex(idlist)
   284  	r.byid = ids
   285  	r.byname = names
   286  	r.bydigest = digests
   287  	if shouldSave {
   288  		return r.Save()
   289  	}
   290  	return nil
   291  }
   292  
   293  func (r *imageStore) Save() error {
   294  	if !r.IsReadWrite() {
   295  		return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify the image store at %q", r.imagespath())
   296  	}
   297  	if !r.Locked() {
   298  		return errors.New("image store is not locked for writing")
   299  	}
   300  	rpath := r.imagespath()
   301  	if err := os.MkdirAll(filepath.Dir(rpath), 0700); err != nil {
   302  		return err
   303  	}
   304  	jdata, err := json.Marshal(&r.images)
   305  	if err != nil {
   306  		return err
   307  	}
   308  	defer r.Touch()
   309  	return ioutils.AtomicWriteFile(rpath, jdata, 0600)
   310  }
   311  
   312  func newImageStore(dir string) (ImageStore, error) {
   313  	if err := os.MkdirAll(dir, 0700); err != nil {
   314  		return nil, err
   315  	}
   316  	lockfile, err := GetLockfile(filepath.Join(dir, "images.lock"))
   317  	if err != nil {
   318  		return nil, err
   319  	}
   320  	lockfile.Lock()
   321  	defer lockfile.Unlock()
   322  	istore := imageStore{
   323  		lockfile: lockfile,
   324  		dir:      dir,
   325  		images:   []*Image{},
   326  		byid:     make(map[string]*Image),
   327  		byname:   make(map[string]*Image),
   328  		bydigest: make(map[digest.Digest][]*Image),
   329  	}
   330  	if err := istore.Load(); err != nil {
   331  		return nil, err
   332  	}
   333  	return &istore, nil
   334  }
   335  
   336  func newROImageStore(dir string) (ROImageStore, error) {
   337  	lockfile, err := GetROLockfile(filepath.Join(dir, "images.lock"))
   338  	if err != nil {
   339  		return nil, err
   340  	}
   341  	lockfile.Lock()
   342  	defer lockfile.Unlock()
   343  	istore := imageStore{
   344  		lockfile: lockfile,
   345  		dir:      dir,
   346  		images:   []*Image{},
   347  		byid:     make(map[string]*Image),
   348  		byname:   make(map[string]*Image),
   349  		bydigest: make(map[digest.Digest][]*Image),
   350  	}
   351  	if err := istore.Load(); err != nil {
   352  		return nil, err
   353  	}
   354  	return &istore, nil
   355  }
   356  
   357  func (r *imageStore) lookup(id string) (*Image, bool) {
   358  	if image, ok := r.byid[id]; ok {
   359  		return image, ok
   360  	} else if image, ok := r.byname[id]; ok {
   361  		return image, ok
   362  	} else if longid, err := r.idindex.Get(id); err == nil {
   363  		image, ok := r.byid[longid]
   364  		return image, ok
   365  	}
   366  	return nil, false
   367  }
   368  
   369  func (r *imageStore) ClearFlag(id string, flag string) error {
   370  	if !r.IsReadWrite() {
   371  		return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to clear flags on images at %q", r.imagespath())
   372  	}
   373  	image, ok := r.lookup(id)
   374  	if !ok {
   375  		return ErrImageUnknown
   376  	}
   377  	delete(image.Flags, flag)
   378  	return r.Save()
   379  }
   380  
   381  func (r *imageStore) SetFlag(id string, flag string, value interface{}) error {
   382  	if !r.IsReadWrite() {
   383  		return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to set flags on images at %q", r.imagespath())
   384  	}
   385  	image, ok := r.lookup(id)
   386  	if !ok {
   387  		return ErrImageUnknown
   388  	}
   389  	if image.Flags == nil {
   390  		image.Flags = make(map[string]interface{})
   391  	}
   392  	image.Flags[flag] = value
   393  	return r.Save()
   394  }
   395  
   396  func (r *imageStore) Create(id string, names []string, layer, metadata string, created time.Time, searchableDigest digest.Digest) (image *Image, err error) {
   397  	if !r.IsReadWrite() {
   398  		return nil, errors.Wrapf(ErrStoreIsReadOnly, "not allowed to create new images at %q", r.imagespath())
   399  	}
   400  	if id == "" {
   401  		id = stringid.GenerateRandomID()
   402  		_, idInUse := r.byid[id]
   403  		for idInUse {
   404  			id = stringid.GenerateRandomID()
   405  			_, idInUse = r.byid[id]
   406  		}
   407  	}
   408  	if _, idInUse := r.byid[id]; idInUse {
   409  		return nil, errors.Wrapf(ErrDuplicateID, "an image with ID %q already exists", id)
   410  	}
   411  	names = dedupeNames(names)
   412  	for _, name := range names {
   413  		if image, nameInUse := r.byname[name]; nameInUse {
   414  			return nil, errors.Wrapf(ErrDuplicateName, "image name %q is already associated with image %q", name, image.ID)
   415  		}
   416  	}
   417  	if created.IsZero() {
   418  		created = time.Now().UTC()
   419  	}
   420  	if err == nil {
   421  		image = &Image{
   422  			ID:             id,
   423  			Digest:         searchableDigest,
   424  			Digests:        nil,
   425  			Names:          names,
   426  			TopLayer:       layer,
   427  			Metadata:       metadata,
   428  			BigDataNames:   []string{},
   429  			BigDataSizes:   make(map[string]int64),
   430  			BigDataDigests: make(map[string]digest.Digest),
   431  			Created:        created,
   432  			Flags:          make(map[string]interface{}),
   433  		}
   434  		err := image.recomputeDigests()
   435  		if err != nil {
   436  			return nil, errors.Wrapf(err, "error validating digests for new image")
   437  		}
   438  		r.images = append(r.images, image)
   439  		r.idindex.Add(id)
   440  		r.byid[id] = image
   441  		for _, name := range names {
   442  			r.byname[name] = image
   443  		}
   444  		for _, digest := range image.Digests {
   445  			list := r.bydigest[digest]
   446  			r.bydigest[digest] = append(list, image)
   447  		}
   448  		err = r.Save()
   449  		image = copyImage(image)
   450  	}
   451  	return image, err
   452  }
   453  
   454  func (r *imageStore) addMappedTopLayer(id, layer string) error {
   455  	if image, ok := r.lookup(id); ok {
   456  		image.MappedTopLayers = append(image.MappedTopLayers, layer)
   457  		return r.Save()
   458  	}
   459  	return ErrImageUnknown
   460  }
   461  
   462  func (r *imageStore) Metadata(id string) (string, error) {
   463  	if image, ok := r.lookup(id); ok {
   464  		return image.Metadata, nil
   465  	}
   466  	return "", ErrImageUnknown
   467  }
   468  
   469  func (r *imageStore) SetMetadata(id, metadata string) error {
   470  	if !r.IsReadWrite() {
   471  		return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify image metadata at %q", r.imagespath())
   472  	}
   473  	if image, ok := r.lookup(id); ok {
   474  		image.Metadata = metadata
   475  		return r.Save()
   476  	}
   477  	return ErrImageUnknown
   478  }
   479  
   480  func (r *imageStore) removeName(image *Image, name string) {
   481  	image.Names = stringSliceWithoutValue(image.Names, name)
   482  }
   483  
   484  func (r *imageStore) SetNames(id string, names []string) error {
   485  	if !r.IsReadWrite() {
   486  		return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to change image name assignments at %q", r.imagespath())
   487  	}
   488  	names = dedupeNames(names)
   489  	if image, ok := r.lookup(id); ok {
   490  		for _, name := range image.Names {
   491  			delete(r.byname, name)
   492  		}
   493  		for _, name := range names {
   494  			if otherImage, ok := r.byname[name]; ok {
   495  				r.removeName(otherImage, name)
   496  			}
   497  			r.byname[name] = image
   498  		}
   499  		image.Names = names
   500  		return r.Save()
   501  	}
   502  	return ErrImageUnknown
   503  }
   504  
   505  func (r *imageStore) Delete(id string) error {
   506  	if !r.IsReadWrite() {
   507  		return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to delete images at %q", r.imagespath())
   508  	}
   509  	image, ok := r.lookup(id)
   510  	if !ok {
   511  		return ErrImageUnknown
   512  	}
   513  	id = image.ID
   514  	toDeleteIndex := -1
   515  	for i, candidate := range r.images {
   516  		if candidate.ID == id {
   517  			toDeleteIndex = i
   518  		}
   519  	}
   520  	delete(r.byid, id)
   521  	r.idindex.Delete(id)
   522  	for _, name := range image.Names {
   523  		delete(r.byname, name)
   524  	}
   525  	for _, digest := range image.Digests {
   526  		prunedList := imageSliceWithoutValue(r.bydigest[digest], image)
   527  		if len(prunedList) == 0 {
   528  			delete(r.bydigest, digest)
   529  		} else {
   530  			r.bydigest[digest] = prunedList
   531  		}
   532  	}
   533  	if toDeleteIndex != -1 {
   534  		// delete the image at toDeleteIndex
   535  		if toDeleteIndex == len(r.images)-1 {
   536  			r.images = r.images[:len(r.images)-1]
   537  		} else {
   538  			r.images = append(r.images[:toDeleteIndex], r.images[toDeleteIndex+1:]...)
   539  		}
   540  	}
   541  	if err := r.Save(); err != nil {
   542  		return err
   543  	}
   544  	if err := os.RemoveAll(r.datadir(id)); err != nil {
   545  		return err
   546  	}
   547  	return nil
   548  }
   549  
   550  func (r *imageStore) Get(id string) (*Image, error) {
   551  	if image, ok := r.lookup(id); ok {
   552  		return copyImage(image), nil
   553  	}
   554  	return nil, ErrImageUnknown
   555  }
   556  
   557  func (r *imageStore) Lookup(name string) (id string, err error) {
   558  	if image, ok := r.lookup(name); ok {
   559  		return image.ID, nil
   560  	}
   561  	return "", ErrImageUnknown
   562  }
   563  
   564  func (r *imageStore) Exists(id string) bool {
   565  	_, ok := r.lookup(id)
   566  	return ok
   567  }
   568  
   569  func (r *imageStore) ByDigest(d digest.Digest) ([]*Image, error) {
   570  	if images, ok := r.bydigest[d]; ok {
   571  		return copyImageSlice(images), nil
   572  	}
   573  	return nil, ErrImageUnknown
   574  }
   575  
   576  func (r *imageStore) BigData(id, key string) ([]byte, error) {
   577  	if key == "" {
   578  		return nil, errors.Wrapf(ErrInvalidBigDataName, "can't retrieve image big data value for empty name")
   579  	}
   580  	image, ok := r.lookup(id)
   581  	if !ok {
   582  		return nil, ErrImageUnknown
   583  	}
   584  	return ioutil.ReadFile(r.datapath(image.ID, key))
   585  }
   586  
   587  func (r *imageStore) BigDataSize(id, key string) (int64, error) {
   588  	if key == "" {
   589  		return -1, errors.Wrapf(ErrInvalidBigDataName, "can't retrieve size of image big data with empty name")
   590  	}
   591  	image, ok := r.lookup(id)
   592  	if !ok {
   593  		return -1, ErrImageUnknown
   594  	}
   595  	if image.BigDataSizes == nil {
   596  		image.BigDataSizes = make(map[string]int64)
   597  	}
   598  	if size, ok := image.BigDataSizes[key]; ok {
   599  		return size, nil
   600  	}
   601  	if data, err := r.BigData(id, key); err == nil && data != nil {
   602  		return int64(len(data)), nil
   603  	}
   604  	return -1, ErrSizeUnknown
   605  }
   606  
   607  func (r *imageStore) BigDataDigest(id, key string) (digest.Digest, error) {
   608  	if key == "" {
   609  		return "", errors.Wrapf(ErrInvalidBigDataName, "can't retrieve digest of image big data value with empty name")
   610  	}
   611  	image, ok := r.lookup(id)
   612  	if !ok {
   613  		return "", ErrImageUnknown
   614  	}
   615  	if image.BigDataDigests == nil {
   616  		image.BigDataDigests = make(map[string]digest.Digest)
   617  	}
   618  	if d, ok := image.BigDataDigests[key]; ok {
   619  		return d, nil
   620  	}
   621  	return "", ErrDigestUnknown
   622  }
   623  
   624  func (r *imageStore) BigDataNames(id string) ([]string, error) {
   625  	image, ok := r.lookup(id)
   626  	if !ok {
   627  		return nil, ErrImageUnknown
   628  	}
   629  	return copyStringSlice(image.BigDataNames), nil
   630  }
   631  
   632  func imageSliceWithoutValue(slice []*Image, value *Image) []*Image {
   633  	modified := make([]*Image, 0, len(slice))
   634  	for _, v := range slice {
   635  		if v == value {
   636  			continue
   637  		}
   638  		modified = append(modified, v)
   639  	}
   640  	return modified
   641  }
   642  
   643  func (r *imageStore) SetBigData(id, key string, data []byte, digestManifest func([]byte) (digest.Digest, error)) error {
   644  	if key == "" {
   645  		return errors.Wrapf(ErrInvalidBigDataName, "can't set empty name for image big data item")
   646  	}
   647  	if !r.IsReadWrite() {
   648  		return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to save data items associated with images at %q", r.imagespath())
   649  	}
   650  	image, ok := r.lookup(id)
   651  	if !ok {
   652  		return ErrImageUnknown
   653  	}
   654  	err := os.MkdirAll(r.datadir(image.ID), 0700)
   655  	if err != nil {
   656  		return err
   657  	}
   658  	var newDigest digest.Digest
   659  	if bigDataNameIsManifest(key) {
   660  		if digestManifest == nil {
   661  			return errors.Wrapf(ErrDigestUnknown, "error digesting manifest: no manifest digest callback provided")
   662  		}
   663  		if newDigest, err = digestManifest(data); err != nil {
   664  			return errors.Wrapf(err, "error digesting manifest")
   665  		}
   666  	} else {
   667  		newDigest = digest.Canonical.FromBytes(data)
   668  	}
   669  	err = ioutils.AtomicWriteFile(r.datapath(image.ID, key), data, 0600)
   670  	if err == nil {
   671  		save := false
   672  		if image.BigDataSizes == nil {
   673  			image.BigDataSizes = make(map[string]int64)
   674  		}
   675  		oldSize, sizeOk := image.BigDataSizes[key]
   676  		image.BigDataSizes[key] = int64(len(data))
   677  		if image.BigDataDigests == nil {
   678  			image.BigDataDigests = make(map[string]digest.Digest)
   679  		}
   680  		oldDigest, digestOk := image.BigDataDigests[key]
   681  		image.BigDataDigests[key] = newDigest
   682  		if !sizeOk || oldSize != image.BigDataSizes[key] || !digestOk || oldDigest != newDigest {
   683  			save = true
   684  		}
   685  		addName := true
   686  		for _, name := range image.BigDataNames {
   687  			if name == key {
   688  				addName = false
   689  				break
   690  			}
   691  		}
   692  		if addName {
   693  			image.BigDataNames = append(image.BigDataNames, key)
   694  			save = true
   695  		}
   696  		for _, oldDigest := range image.Digests {
   697  			// remove the image from the list of images in the digest-based index
   698  			if list, ok := r.bydigest[oldDigest]; ok {
   699  				prunedList := imageSliceWithoutValue(list, image)
   700  				if len(prunedList) == 0 {
   701  					delete(r.bydigest, oldDigest)
   702  				} else {
   703  					r.bydigest[oldDigest] = prunedList
   704  				}
   705  			}
   706  		}
   707  		if err = image.recomputeDigests(); err != nil {
   708  			return errors.Wrapf(err, "error loading recomputing image digest information for %s", image.ID)
   709  		}
   710  		for _, newDigest := range image.Digests {
   711  			// add the image to the list of images in the digest-based index which
   712  			// corresponds to the new digest for this item, unless it's already there
   713  			list := r.bydigest[newDigest]
   714  			if len(list) == len(imageSliceWithoutValue(list, image)) {
   715  				// the list isn't shortened by trying to prune this image from it,
   716  				// so it's not in there yet
   717  				r.bydigest[newDigest] = append(list, image)
   718  			}
   719  		}
   720  		if save {
   721  			err = r.Save()
   722  		}
   723  	}
   724  	return err
   725  }
   726  
   727  func (r *imageStore) Wipe() error {
   728  	if !r.IsReadWrite() {
   729  		return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to delete images at %q", r.imagespath())
   730  	}
   731  	ids := make([]string, 0, len(r.byid))
   732  	for id := range r.byid {
   733  		ids = append(ids, id)
   734  	}
   735  	for _, id := range ids {
   736  		if err := r.Delete(id); err != nil {
   737  			return err
   738  		}
   739  	}
   740  	return nil
   741  }
   742  
   743  func (r *imageStore) Lock() {
   744  	r.lockfile.Lock()
   745  }
   746  
   747  func (r *imageStore) RecursiveLock() {
   748  	r.lockfile.RecursiveLock()
   749  }
   750  
   751  func (r *imageStore) RLock() {
   752  	r.lockfile.RLock()
   753  }
   754  
   755  func (r *imageStore) Unlock() {
   756  	r.lockfile.Unlock()
   757  }
   758  
   759  func (r *imageStore) Touch() error {
   760  	return r.lockfile.Touch()
   761  }
   762  
   763  func (r *imageStore) Modified() (bool, error) {
   764  	return r.lockfile.Modified()
   765  }
   766  
   767  func (r *imageStore) IsReadWrite() bool {
   768  	return r.lockfile.IsReadWrite()
   769  }
   770  
   771  func (r *imageStore) TouchedSince(when time.Time) bool {
   772  	return r.lockfile.TouchedSince(when)
   773  }
   774  
   775  func (r *imageStore) Locked() bool {
   776  	return r.lockfile.Locked()
   777  }