func (s *File) Close() error {
	var errs []string
	s.tmpFiles.Range(func(name, _ interface{}) bool {
		if err := os.Remove(name.(string)); err != nil {
			errs = append(errs, err.Error())
		}
		return true
	})
	return errors.New(strings.Join(errs, "; "))
}
Close frees up resources used by the file store
package content
import (
	"bytes"
	"compress/gzip"
	"context"
	_ "crypto/sha256"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
	"github.com/containerd/containerd/content"
	"github.com/containerd/containerd/errdefs"
	"github.com/containerd/containerd/remotes"
	digest "github.com/opencontainers/go-digest"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
)
type File struct {
	DisableOverwrite          bool
	AllowPathTraversalOnWrite bool
	Reproducible bool
	root         string
	descriptor   *sync.Map  
	pathMap      *sync.Map  
	memoryMap    *sync.Map  
	refMap       *sync.Map  
	tmpFiles     *sync.Map
	ignoreNoName bool
}
func NewFile(rootPath string, opts ...WriterOpt) *File {
	wOpts := DefaultWriterOpts()
	for _, opt := range opts {
		if err := opt(&wOpts); err != nil {
			continue
		}
	}
	return &File{
		root:         rootPath,
		descriptor:   &sync.Map{},
		pathMap:      &sync.Map{},
		memoryMap:    &sync.Map{},
		refMap:       &sync.Map{},
		tmpFiles:     &sync.Map{},
		ignoreNoName: wOpts.IgnoreNoName,
	}
}
func (s *File) Resolver() remotes.Resolver {
	return s
}
func (s *File) Resolve(ctx context.Context, ref string) (name string, desc ocispec.Descriptor, err error) {
	desc, ok := s.getRef(ref)
	if !ok {
		return "", ocispec.Descriptor{}, fmt.Errorf("unknown reference: %s", ref)
	}
	return ref, desc, nil
}
func (s *File) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) {
	if _, ok := s.refMap.Load(ref); !ok {
		return nil, fmt.Errorf("unknown reference: %s", ref)
	}
	return s, nil
}
func (s *File) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) {
	manifest, ok := s.getMemory(desc)
	if ok {
		return ioutil.NopCloser(bytes.NewReader(manifest)), nil
	}
	desc, ok = s.get(desc)
	if !ok {
		return nil, ErrNotFound
	}
	name, ok := ResolveName(desc)
	if !ok {
		return nil, ErrNoName
	}
	path := s.ResolvePath(name)
	return os.Open(path)
}
func (s *File) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) {
	var tag, hash string
	parts := strings.SplitN(ref, "@", 2)
	if len(parts) > 0 {
		tag = parts[0]
	}
	if len(parts) > 1 {
		hash = parts[1]
	}
	return &filePusher{
		store: s,
		ref:   tag,
		hash:  hash,
	}, nil
}
type filePusher struct {
	store *File
	ref   string
	hash  string
}
func (s *filePusher) Push(ctx context.Context, desc ocispec.Descriptor) (content.Writer, error) {
	name, ok := ResolveName(desc)
	now := time.Now()
	if !ok {
		if !s.store.ignoreNoName {
			return nil, ErrNoName
		}
		return NewIoContentWriter(ioutil.Discard, WithOutputHash(desc.Digest)), nil
	}
	path, err := s.store.resolveWritePath(name)
	if err != nil {
		return nil, err
	}
	file, afterCommit, err := s.store.createWritePath(path, desc, name)
	if err != nil {
		return nil, err
	}
	return &fileWriter{
		store:    s.store,
		file:     file,
		desc:     desc,
		digester: digest.Canonical.Digester(),
		status: content.Status{
			Ref:       name,
			Total:     desc.Size,
			StartedAt: now,
			UpdatedAt: now,
		},
		afterCommit: afterCommit,
	}, nil
}
func (s *File) Add(name, mediaType, path string) (ocispec.Descriptor, error) {
	if path == "" {
		path = name
	}
	path = s.MapPath(name, path)
	fileInfo, err := os.Stat(path)
	if err != nil {
		return ocispec.Descriptor{}, err
	}
	var desc ocispec.Descriptor
	if fileInfo.IsDir() {
		desc, err = s.descFromDir(name, mediaType, path)
	} else {
		desc, err = s.descFromFile(fileInfo, mediaType, path)
	}
	if err != nil {
		return ocispec.Descriptor{}, err
	}
	if desc.Annotations == nil {
		desc.Annotations = make(map[string]string)
	}
	desc.Annotations[ocispec.AnnotationTitle] = name
	s.set(desc)
	return desc, nil
}
func (s *File) Load(desc ocispec.Descriptor, data []byte) error {
	s.memoryMap.Store(desc.Digest, data)
	return nil
}
func (s *File) Ref(ref string) (ocispec.Descriptor, []byte, error) {
	desc, ok := s.getRef(ref)
	if !ok {
		return ocispec.Descriptor{}, nil, ErrNotFound
	}
	manifest, ok := s.getMemory(desc)
	if !ok {
		return ocispec.Descriptor{}, nil, ErrNotFound
	}
	return desc, manifest, nil
}
func (s *File) descFromFile(info os.FileInfo, mediaType, path string) (ocispec.Descriptor, error) {
	file, err := os.Open(path)
	if err != nil {
		return ocispec.Descriptor{}, err
	}
	defer file.Close()
	digest, err := digest.FromReader(file)
	if err != nil {
		return ocispec.Descriptor{}, err
	}
	if mediaType == "" {
		mediaType = DefaultBlobMediaType
	}
	return ocispec.Descriptor{
		MediaType: mediaType,
		Digest:    digest,
		Size:      info.Size(),
	}, nil
}
func (s *File) descFromDir(name, mediaType, root string) (ocispec.Descriptor, error) {
	file, err := s.tempFile()
	if err != nil {
		return ocispec.Descriptor{}, err
	}
	defer file.Close()
	s.MapPath(name, file.Name())
	digester := digest.Canonical.Digester()
	zw := gzip.NewWriter(io.MultiWriter(file, digester.Hash()))
	defer zw.Close()
	tarDigester := digest.Canonical.Digester()
	if err := tarDirectory(root, name, io.MultiWriter(zw, tarDigester.Hash()), s.Reproducible); err != nil {
		return ocispec.Descriptor{}, err
	}
	if err := zw.Close(); err != nil {
		return ocispec.Descriptor{}, err
	}
	if err := file.Sync(); err != nil {
		return ocispec.Descriptor{}, err
	}
	if mediaType == "" {
		mediaType = DefaultBlobDirMediaType
	}
	info, err := file.Stat()
	if err != nil {
		return ocispec.Descriptor{}, err
	}
	return ocispec.Descriptor{
		MediaType: mediaType,
		Digest:    digester.Digest(),
		Size:      info.Size(),
		Annotations: map[string]string{
			AnnotationDigest: tarDigester.Digest().String(),
			AnnotationUnpack: "true",
		},
	}, nil
}
func (s *File) tempFile() (*os.File, error) {
	file, err := ioutil.TempFile("", TempFilePattern)
	if err != nil {
		return nil, err
	}
	s.tmpFiles.Store(file.Name(), file)
	return file, nil
}