storage/storage_image.go
func (s *storageImageDestination) PutBlob(ctx context.Context, stream io.Reader, blobinfo types.BlobInfo, cache types.BlobInfoCache, isConfig bool) (types.BlobInfo, error) {
	// Stores a layer or data blob in our temporary directory, checking that any information
	// in the blobinfo matches the incoming data.
	errorBlobInfo := types.BlobInfo{
		Digest: "",
		Size:   -1,
	}
	// Set up to digest the blob and count its size while saving it to a file.
	hasher := digest.Canonical.Digester()
	if blobinfo.Digest.Validate() == nil {
		if a := blobinfo.Digest.Algorithm(); a.Available() {
			hasher = a.Digester()
		}
	}
	diffID := digest.Canonical.Digester()
	filename := s.computeNextBlobCacheFile()
	file, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_EXCL, 0600)
	if err != nil {
		return errorBlobInfo, errors.Wrapf(err, "error creating temporary file %q", filename)
	}
	defer file.Close()
	counter := ioutils.NewWriteCounter(hasher.Hash())
	reader := io.TeeReader(io.TeeReader(stream, counter), file)
	decompressed, err := archive.DecompressStream(reader)
	if err != nil {
		return errorBlobInfo, errors.Wrap(err, "error setting up to decompress blob")
	}
	// Copy the data to the file.
	// TODO: This can take quite some time, and should ideally be cancellable using ctx.Done().
	_, err = io.Copy(diffID.Hash(), decompressed)
	decompressed.Close()
	if err != nil {
		return errorBlobInfo, errors.Wrapf(err, "error storing blob to file %q", filename)
	}
	// Ensure that any information that we were given about the blob is correct.
	if blobinfo.Digest.Validate() == nil && blobinfo.Digest != hasher.Digest() {
		return errorBlobInfo, ErrBlobDigestMismatch
	}
	if blobinfo.Size >= 0 && blobinfo.Size != counter.Count {
		return errorBlobInfo, ErrBlobSizeMismatch
	}
	// Record information about the blob.
	s.putBlobMutex.Lock()
	s.blobDiffIDs[hasher.Digest()] = diffID.Digest()
	s.fileSizes[hasher.Digest()] = counter.Count
	s.filenames[hasher.Digest()] = filename
	s.putBlobMutex.Unlock()
	blobDigest := blobinfo.Digest
	if blobDigest.Validate() != nil {
		blobDigest = hasher.Digest()
	}
	blobSize := blobinfo.Size
	if blobSize < 0 {
		blobSize = counter.Count
	}
	// This is safe because we have just computed both values ourselves.
	cache.RecordDigestUncompressedPair(blobDigest, diffID.Digest())
	return types.BlobInfo{
		Digest:    blobDigest,
		Size:      blobSize,
		MediaType: blobinfo.MediaType,
	}, nil
}
func (s *storageImageDestination) PutBlob(ctx context.Context, stream io.Reader, blobinfo types.BlobInfo, cache types.BlobInfoCache, isConfig bool) (types.BlobInfo, error) {
	// Stores a layer or data blob in our temporary directory, checking that any information
	// in the blobinfo matches the incoming data.
	errorBlobInfo := types.BlobInfo{
		Digest: "",
		Size:   -1,
	}
	// Set up to digest the blob and count its size while saving it to a file.
	hasher := digest.Canonical.Digester()
	if blobinfo.Digest.Validate() == nil {
		if a := blobinfo.Digest.Algorithm(); a.Available() {
			hasher = a.Digester()
		}
	}
	diffID := digest.Canonical.Digester()
	filename := s.computeNextBlobCacheFile()
	file, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_EXCL, 0600)
	if err != nil {
		return errorBlobInfo, errors.Wrapf(err, "error creating temporary file %q", filename)
	}
	defer file.Close()
	counter := ioutils.NewWriteCounter(hasher.Hash())
	reader := io.TeeReader(io.TeeReader(stream, counter), file)
	decompressed, err := archive.DecompressStream(reader)
	if err != nil {
		return errorBlobInfo, errors.Wrap(err, "error setting up to decompress blob")
	}
	// Copy the data to the file.
	// TODO: This can take quite some time, and should ideally be cancellable using ctx.Done().
	_, err = io.Copy(diffID.Hash(), decompressed)
	decompressed.Close()
	if err != nil {
		return errorBlobInfo, errors.Wrapf(err, "error storing blob to file %q", filename)
	}
	// Ensure that any information that we were given about the blob is correct.
	if blobinfo.Digest.Validate() == nil && blobinfo.Digest != hasher.Digest() {
		return errorBlobInfo, ErrBlobDigestMismatch
	}
	if blobinfo.Size >= 0 && blobinfo.Size != counter.Count {
		return errorBlobInfo, ErrBlobSizeMismatch
	}
	// Record information about the blob.
	s.putBlobMutex.Lock()
	s.blobDiffIDs[hasher.Digest()] = diffID.Digest()
	s.fileSizes[hasher.Digest()] = counter.Count
	s.filenames[hasher.Digest()] = filename
	s.putBlobMutex.Unlock()
	blobDigest := blobinfo.Digest
	if blobDigest.Validate() != nil {
		blobDigest = hasher.Digest()
	}
	blobSize := blobinfo.Size
	if blobSize < 0 {
		blobSize = counter.Count
	}
	// This is safe because we have just computed both values ourselves.
	cache.RecordDigestUncompressedPair(blobDigest, diffID.Digest())
	return types.BlobInfo{
		Digest:    blobDigest,
		Size:      blobSize,
		MediaType: blobinfo.MediaType,
	}, nil
}
func ( s * storageImageDestination ) PutBlob ( ctx context . Context , stream io . Reader , blobinfo types . BlobInfo , cache types . BlobInfoCache , isConfig bool ) ( types . BlobInfo , error ) { // Stores a layer or data blob in our temporary directory, checking that any information // in the blobinfo matches the incoming data. errorBlobInfo := types . BlobInfo { Digest : " " , Size : - 1 , } // Set up to digest the blob and count its size while saving it to a file. hasher := digest . Canonical . Digester ( ) if blobinfo . Digest . Validate ( ) == nil { if a := blobinfo . Digest . Algorithm ( ) ; a . Available ( ) { hasher = a . Digester ( ) } } diffID := digest . Canonical . Digester ( ) filename := s . computeNextBlobCacheFile ( ) file , err := os . OpenFile ( filename , os . O_CREATE | os . O_TRUNC | os . O_WRONLY | os . O_EXCL , 0600 ) if err != nil { return errorBlobInfo , errors . Wrapf ( err , " " , filename ) } defer file . Close ( ) counter := ioutils . NewWriteCounter ( hasher . Hash ( ) ) reader := io . TeeReader ( io . TeeReader ( stream , counter ) , file ) decompressed , err := archive . DecompressStream ( reader ) if err != nil { return errorBlobInfo , errors . Wrap ( err , " " ) } // Copy the data to the file. // TODO: This can take quite some time, and should ideally be cancellable using ctx.Done(). _ , err = io . Copy ( diffID . Hash ( ) , decompressed ) decompressed . Close ( ) if err != nil { return errorBlobInfo , errors . Wrapf ( err , " " , filename ) } // Ensure that any information that we were given about the blob is correct. if blobinfo . Digest . Validate ( ) == nil && blobinfo . Digest != hasher . Digest ( ) { return errorBlobInfo , ErrBlobDigestMismatch } if blobinfo . Size >= 0 && blobinfo . Size != counter . Count { return errorBlobInfo , ErrBlobSizeMismatch } // Record information about the blob. s . putBlobMutex . Lock ( ) s . blobDiffIDs [ hasher . Digest ( ) ] = diffID . Digest ( ) s . fileSizes [ hasher . Digest ( ) ] = counter . Count s . filenames [ hasher . Digest ( ) ] = filename s . putBlobMutex . Unlock ( ) blobDigest := blobinfo . Digest if blobDigest . Validate ( ) != nil { blobDigest = hasher . Digest ( ) } blobSize := blobinfo . Size if blobSize < 0 { blobSize = counter . Count } // This is safe because we have just computed both values ourselves. cache . RecordDigestUncompressedPair ( blobDigest , diffID . Digest ( ) ) return types . BlobInfo { Digest : blobDigest , Size : blobSize , MediaType : blobinfo . MediaType , } , nil }
// PutBlob writes contents of stream and returns data representing the result.
// inputInfo.Digest can be optionally provided if known; it is not mandatory for the implementation to verify it.
// inputInfo.Size is the expected length of stream, if known.
// inputInfo.MediaType describes the blob format, if known.
// May update cache.
// WARNING: The contents of stream are being verified on the fly.  Until stream.Read() returns io.EOF, the contents of the data SHOULD NOT be available
// to any other readers for download using the supplied digest.
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
PutBlob writes contents of stream and returns data representing the result . inputInfo . Digest can be optionally provided if known ; it is not mandatory for the implementation to verify it . inputInfo . Size is the expected length of stream if known . inputInfo . MediaType describes the blob format if known . May update cache . WARNING : The contents of stream are being verified on the fly . Until stream . Read () returns io . EOF the contents of the data SHOULD NOT be available to any other readers for download using the supplied digest . If stream . Read () at any time ESPECIALLY at end of input returns an error PutBlob MUST 1 ) fail and 2 ) delete any data stored so far .