github.com/demonoid81/containerd@v1.3.4/metadata/snapshot.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package metadata
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  	"sync"
    24  	"sync/atomic"
    25  	"time"
    26  
    27  	"github.com/containerd/containerd/errdefs"
    28  	"github.com/containerd/containerd/labels"
    29  	"github.com/containerd/containerd/log"
    30  	"github.com/containerd/containerd/metadata/boltutil"
    31  	"github.com/containerd/containerd/mount"
    32  	"github.com/containerd/containerd/namespaces"
    33  	"github.com/containerd/containerd/snapshots"
    34  	"github.com/pkg/errors"
    35  	bolt "go.etcd.io/bbolt"
    36  )
    37  
    38  const (
    39  	inheritedLabelsPrefix = "containerd.io/snapshot/"
    40  )
    41  
    42  type snapshotter struct {
    43  	snapshots.Snapshotter
    44  	name string
    45  	db   *DB
    46  	l    sync.RWMutex
    47  }
    48  
    49  // newSnapshotter returns a new Snapshotter which namespaces the given snapshot
    50  // using the provided name and database.
    51  func newSnapshotter(db *DB, name string, sn snapshots.Snapshotter) *snapshotter {
    52  	return &snapshotter{
    53  		Snapshotter: sn,
    54  		name:        name,
    55  		db:          db,
    56  	}
    57  }
    58  
    59  func createKey(id uint64, namespace, key string) string {
    60  	return fmt.Sprintf("%s/%d/%s", namespace, id, key)
    61  }
    62  
    63  func getKey(tx *bolt.Tx, ns, name, key string) string {
    64  	bkt := getSnapshotterBucket(tx, ns, name)
    65  	if bkt == nil {
    66  		return ""
    67  	}
    68  	bkt = bkt.Bucket([]byte(key))
    69  	if bkt == nil {
    70  		return ""
    71  	}
    72  	v := bkt.Get(bucketKeyName)
    73  	if len(v) == 0 {
    74  		return ""
    75  	}
    76  	return string(v)
    77  }
    78  
    79  func (s *snapshotter) resolveKey(ctx context.Context, key string) (string, error) {
    80  	ns, err := namespaces.NamespaceRequired(ctx)
    81  	if err != nil {
    82  		return "", err
    83  	}
    84  
    85  	var id string
    86  	if err := view(ctx, s.db, func(tx *bolt.Tx) error {
    87  		id = getKey(tx, ns, s.name, key)
    88  		if id == "" {
    89  			return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", key)
    90  		}
    91  		return nil
    92  	}); err != nil {
    93  		return "", err
    94  	}
    95  
    96  	return id, nil
    97  }
    98  
    99  func (s *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) {
   100  	ns, err := namespaces.NamespaceRequired(ctx)
   101  	if err != nil {
   102  		return snapshots.Info{}, err
   103  	}
   104  
   105  	var (
   106  		bkey  string
   107  		local = snapshots.Info{
   108  			Name: key,
   109  		}
   110  	)
   111  	if err := view(ctx, s.db, func(tx *bolt.Tx) error {
   112  		bkt := getSnapshotterBucket(tx, ns, s.name)
   113  		if bkt == nil {
   114  			return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", key)
   115  		}
   116  		sbkt := bkt.Bucket([]byte(key))
   117  		if sbkt == nil {
   118  			return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", key)
   119  		}
   120  		local.Labels, err = boltutil.ReadLabels(sbkt)
   121  		if err != nil {
   122  			return errors.Wrap(err, "failed to read labels")
   123  		}
   124  		if err := boltutil.ReadTimestamps(sbkt, &local.Created, &local.Updated); err != nil {
   125  			return errors.Wrap(err, "failed to read timestamps")
   126  		}
   127  		bkey = string(sbkt.Get(bucketKeyName))
   128  		local.Parent = string(sbkt.Get(bucketKeyParent))
   129  
   130  		return nil
   131  	}); err != nil {
   132  		return snapshots.Info{}, err
   133  	}
   134  
   135  	info, err := s.Snapshotter.Stat(ctx, bkey)
   136  	if err != nil {
   137  		return snapshots.Info{}, err
   138  	}
   139  
   140  	return overlayInfo(info, local), nil
   141  }
   142  
   143  func (s *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
   144  	s.l.RLock()
   145  	defer s.l.RUnlock()
   146  
   147  	ns, err := namespaces.NamespaceRequired(ctx)
   148  	if err != nil {
   149  		return snapshots.Info{}, err
   150  	}
   151  
   152  	if info.Name == "" {
   153  		return snapshots.Info{}, errors.Wrap(errdefs.ErrInvalidArgument, "")
   154  	}
   155  
   156  	var (
   157  		bkey  string
   158  		local = snapshots.Info{
   159  			Name: info.Name,
   160  		}
   161  	)
   162  	if err := update(ctx, s.db, func(tx *bolt.Tx) error {
   163  		bkt := getSnapshotterBucket(tx, ns, s.name)
   164  		if bkt == nil {
   165  			return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", info.Name)
   166  		}
   167  		sbkt := bkt.Bucket([]byte(info.Name))
   168  		if sbkt == nil {
   169  			return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", info.Name)
   170  		}
   171  
   172  		local.Labels, err = boltutil.ReadLabels(sbkt)
   173  		if err != nil {
   174  			return errors.Wrap(err, "failed to read labels")
   175  		}
   176  		if err := boltutil.ReadTimestamps(sbkt, &local.Created, &local.Updated); err != nil {
   177  			return errors.Wrap(err, "failed to read timestamps")
   178  		}
   179  
   180  		// Handle field updates
   181  		if len(fieldpaths) > 0 {
   182  			for _, path := range fieldpaths {
   183  				if strings.HasPrefix(path, "labels.") {
   184  					if local.Labels == nil {
   185  						local.Labels = map[string]string{}
   186  					}
   187  
   188  					key := strings.TrimPrefix(path, "labels.")
   189  					local.Labels[key] = info.Labels[key]
   190  					continue
   191  				}
   192  
   193  				switch path {
   194  				case "labels":
   195  					local.Labels = info.Labels
   196  				default:
   197  					return errors.Wrapf(errdefs.ErrInvalidArgument, "cannot update %q field on snapshot %q", path, info.Name)
   198  				}
   199  			}
   200  		} else {
   201  			local.Labels = info.Labels
   202  		}
   203  		if err := validateSnapshot(&local); err != nil {
   204  			return err
   205  		}
   206  		local.Updated = time.Now().UTC()
   207  
   208  		if err := boltutil.WriteTimestamps(sbkt, local.Created, local.Updated); err != nil {
   209  			return errors.Wrap(err, "failed to read timestamps")
   210  		}
   211  		if err := boltutil.WriteLabels(sbkt, local.Labels); err != nil {
   212  			return errors.Wrap(err, "failed to read labels")
   213  		}
   214  		bkey = string(sbkt.Get(bucketKeyName))
   215  		local.Parent = string(sbkt.Get(bucketKeyParent))
   216  
   217  		inner := snapshots.Info{
   218  			Name:   bkey,
   219  			Labels: filterInheritedLabels(local.Labels),
   220  		}
   221  
   222  		if _, err := s.Snapshotter.Update(ctx, inner, fieldpaths...); err != nil {
   223  			return err
   224  		}
   225  
   226  		return nil
   227  	}); err != nil {
   228  		return snapshots.Info{}, err
   229  	}
   230  
   231  	info, err = s.Snapshotter.Stat(ctx, bkey)
   232  	if err != nil {
   233  		return snapshots.Info{}, err
   234  	}
   235  
   236  	return overlayInfo(info, local), nil
   237  }
   238  
   239  func overlayInfo(info, overlay snapshots.Info) snapshots.Info {
   240  	// Merge info
   241  	info.Name = overlay.Name
   242  	info.Created = overlay.Created
   243  	info.Updated = overlay.Updated
   244  	info.Parent = overlay.Parent
   245  	if info.Labels == nil {
   246  		info.Labels = overlay.Labels
   247  	} else {
   248  		for k, v := range overlay.Labels {
   249  			info.Labels[k] = v
   250  		}
   251  	}
   252  	return info
   253  }
   254  
   255  func (s *snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) {
   256  	bkey, err := s.resolveKey(ctx, key)
   257  	if err != nil {
   258  		return snapshots.Usage{}, err
   259  	}
   260  	return s.Snapshotter.Usage(ctx, bkey)
   261  }
   262  
   263  func (s *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
   264  	bkey, err := s.resolveKey(ctx, key)
   265  	if err != nil {
   266  		return nil, err
   267  	}
   268  	return s.Snapshotter.Mounts(ctx, bkey)
   269  }
   270  
   271  func (s *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
   272  	return s.createSnapshot(ctx, key, parent, false, opts)
   273  }
   274  
   275  func (s *snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
   276  	return s.createSnapshot(ctx, key, parent, true, opts)
   277  }
   278  
   279  func (s *snapshotter) createSnapshot(ctx context.Context, key, parent string, readonly bool, opts []snapshots.Opt) ([]mount.Mount, error) {
   280  	s.l.RLock()
   281  	defer s.l.RUnlock()
   282  
   283  	ns, err := namespaces.NamespaceRequired(ctx)
   284  	if err != nil {
   285  		return nil, err
   286  	}
   287  
   288  	var base snapshots.Info
   289  	for _, opt := range opts {
   290  		if err := opt(&base); err != nil {
   291  			return nil, err
   292  		}
   293  	}
   294  
   295  	if err := validateSnapshot(&base); err != nil {
   296  		return nil, err
   297  	}
   298  
   299  	var m []mount.Mount
   300  	if err := update(ctx, s.db, func(tx *bolt.Tx) error {
   301  		bkt, err := createSnapshotterBucket(tx, ns, s.name)
   302  		if err != nil {
   303  			return err
   304  		}
   305  
   306  		bbkt, err := bkt.CreateBucket([]byte(key))
   307  		if err != nil {
   308  			if err == bolt.ErrBucketExists {
   309  				err = errors.Wrapf(errdefs.ErrAlreadyExists, "snapshot %q", key)
   310  			}
   311  			return err
   312  		}
   313  		if err := addSnapshotLease(ctx, tx, s.name, key); err != nil {
   314  			return err
   315  		}
   316  
   317  		var bparent string
   318  		if parent != "" {
   319  			pbkt := bkt.Bucket([]byte(parent))
   320  			if pbkt == nil {
   321  				return errors.Wrapf(errdefs.ErrNotFound, "parent snapshot %v does not exist", parent)
   322  			}
   323  			bparent = string(pbkt.Get(bucketKeyName))
   324  
   325  			cbkt, err := pbkt.CreateBucketIfNotExists(bucketKeyChildren)
   326  			if err != nil {
   327  				return err
   328  			}
   329  			if err := cbkt.Put([]byte(key), nil); err != nil {
   330  				return err
   331  			}
   332  
   333  			if err := bbkt.Put(bucketKeyParent, []byte(parent)); err != nil {
   334  				return err
   335  			}
   336  		}
   337  
   338  		sid, err := bkt.NextSequence()
   339  		if err != nil {
   340  			return err
   341  		}
   342  		bkey := createKey(sid, ns, key)
   343  		if err := bbkt.Put(bucketKeyName, []byte(bkey)); err != nil {
   344  			return err
   345  		}
   346  
   347  		ts := time.Now().UTC()
   348  		if err := boltutil.WriteTimestamps(bbkt, ts, ts); err != nil {
   349  			return err
   350  		}
   351  		if err := boltutil.WriteLabels(bbkt, base.Labels); err != nil {
   352  			return err
   353  		}
   354  
   355  		inheritedOpt := snapshots.WithLabels(filterInheritedLabels(base.Labels))
   356  
   357  		// TODO: Consider doing this outside of transaction to lessen
   358  		// metadata lock time
   359  		if readonly {
   360  			m, err = s.Snapshotter.View(ctx, bkey, bparent, inheritedOpt)
   361  		} else {
   362  			m, err = s.Snapshotter.Prepare(ctx, bkey, bparent, inheritedOpt)
   363  		}
   364  		return err
   365  	}); err != nil {
   366  		return nil, err
   367  	}
   368  	return m, nil
   369  }
   370  
   371  func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
   372  	s.l.RLock()
   373  	defer s.l.RUnlock()
   374  
   375  	ns, err := namespaces.NamespaceRequired(ctx)
   376  	if err != nil {
   377  		return err
   378  	}
   379  
   380  	var base snapshots.Info
   381  	for _, opt := range opts {
   382  		if err := opt(&base); err != nil {
   383  			return err
   384  		}
   385  	}
   386  
   387  	if err := validateSnapshot(&base); err != nil {
   388  		return err
   389  	}
   390  
   391  	return update(ctx, s.db, func(tx *bolt.Tx) error {
   392  		bkt := getSnapshotterBucket(tx, ns, s.name)
   393  		if bkt == nil {
   394  			return errors.Wrapf(errdefs.ErrNotFound,
   395  				"can not find snapshotter %q", s.name)
   396  		}
   397  
   398  		bbkt, err := bkt.CreateBucket([]byte(name))
   399  		if err != nil {
   400  			if err == bolt.ErrBucketExists {
   401  				err = errors.Wrapf(errdefs.ErrAlreadyExists, "snapshot %q", name)
   402  			}
   403  			return err
   404  		}
   405  		if err := addSnapshotLease(ctx, tx, s.name, name); err != nil {
   406  			return err
   407  		}
   408  
   409  		obkt := bkt.Bucket([]byte(key))
   410  		if obkt == nil {
   411  			return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", key)
   412  		}
   413  
   414  		bkey := string(obkt.Get(bucketKeyName))
   415  
   416  		sid, err := bkt.NextSequence()
   417  		if err != nil {
   418  			return err
   419  		}
   420  
   421  		nameKey := createKey(sid, ns, name)
   422  
   423  		if err := bbkt.Put(bucketKeyName, []byte(nameKey)); err != nil {
   424  			return err
   425  		}
   426  
   427  		parent := obkt.Get(bucketKeyParent)
   428  		if len(parent) > 0 {
   429  			pbkt := bkt.Bucket(parent)
   430  			if pbkt == nil {
   431  				return errors.Wrapf(errdefs.ErrNotFound, "parent snapshot %v does not exist", string(parent))
   432  			}
   433  
   434  			cbkt, err := pbkt.CreateBucketIfNotExists(bucketKeyChildren)
   435  			if err != nil {
   436  				return err
   437  			}
   438  			if err := cbkt.Delete([]byte(key)); err != nil {
   439  				return err
   440  			}
   441  			if err := cbkt.Put([]byte(name), nil); err != nil {
   442  				return err
   443  			}
   444  
   445  			if err := bbkt.Put(bucketKeyParent, parent); err != nil {
   446  				return err
   447  			}
   448  		}
   449  		ts := time.Now().UTC()
   450  		if err := boltutil.WriteTimestamps(bbkt, ts, ts); err != nil {
   451  			return err
   452  		}
   453  		if err := boltutil.WriteLabels(bbkt, base.Labels); err != nil {
   454  			return err
   455  		}
   456  
   457  		if err := bkt.DeleteBucket([]byte(key)); err != nil {
   458  			return err
   459  		}
   460  		if err := removeSnapshotLease(ctx, tx, s.name, key); err != nil {
   461  			return err
   462  		}
   463  
   464  		inheritedOpt := snapshots.WithLabels(filterInheritedLabels(base.Labels))
   465  
   466  		// TODO: Consider doing this outside of transaction to lessen
   467  		// metadata lock time
   468  		return s.Snapshotter.Commit(ctx, nameKey, bkey, inheritedOpt)
   469  	})
   470  
   471  }
   472  
   473  func (s *snapshotter) Remove(ctx context.Context, key string) error {
   474  	s.l.RLock()
   475  	defer s.l.RUnlock()
   476  
   477  	ns, err := namespaces.NamespaceRequired(ctx)
   478  	if err != nil {
   479  		return err
   480  	}
   481  
   482  	return update(ctx, s.db, func(tx *bolt.Tx) error {
   483  		var sbkt *bolt.Bucket
   484  		bkt := getSnapshotterBucket(tx, ns, s.name)
   485  		if bkt != nil {
   486  			sbkt = bkt.Bucket([]byte(key))
   487  		}
   488  		if sbkt == nil {
   489  			return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", key)
   490  		}
   491  
   492  		cbkt := sbkt.Bucket(bucketKeyChildren)
   493  		if cbkt != nil {
   494  			if child, _ := cbkt.Cursor().First(); child != nil {
   495  				return errors.Wrap(errdefs.ErrFailedPrecondition, "cannot remove snapshot with child")
   496  			}
   497  		}
   498  
   499  		parent := sbkt.Get(bucketKeyParent)
   500  		if len(parent) > 0 {
   501  			pbkt := bkt.Bucket(parent)
   502  			if pbkt == nil {
   503  				return errors.Wrapf(errdefs.ErrNotFound, "parent snapshot %v does not exist", string(parent))
   504  			}
   505  			cbkt := pbkt.Bucket(bucketKeyChildren)
   506  			if cbkt != nil {
   507  				if err := cbkt.Delete([]byte(key)); err != nil {
   508  					return errors.Wrap(err, "failed to remove child link")
   509  				}
   510  			}
   511  		}
   512  
   513  		if err := bkt.DeleteBucket([]byte(key)); err != nil {
   514  			return err
   515  		}
   516  		if err := removeSnapshotLease(ctx, tx, s.name, key); err != nil {
   517  			return err
   518  		}
   519  
   520  		// Mark snapshotter as dirty for triggering garbage collection
   521  		atomic.AddUint32(&s.db.dirty, 1)
   522  		s.db.dirtySS[s.name] = struct{}{}
   523  
   524  		return nil
   525  	})
   526  }
   527  
   528  type infoPair struct {
   529  	bkey string
   530  	info snapshots.Info
   531  }
   532  
   533  func (s *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error) error {
   534  	ns, err := namespaces.NamespaceRequired(ctx)
   535  	if err != nil {
   536  		return err
   537  	}
   538  
   539  	var (
   540  		batchSize = 100
   541  		pairs     = []infoPair{}
   542  		lastKey   string
   543  	)
   544  
   545  	for {
   546  		if err := view(ctx, s.db, func(tx *bolt.Tx) error {
   547  			bkt := getSnapshotterBucket(tx, ns, s.name)
   548  			if bkt == nil {
   549  				return nil
   550  			}
   551  
   552  			c := bkt.Cursor()
   553  
   554  			var k, v []byte
   555  			if lastKey == "" {
   556  				k, v = c.First()
   557  			} else {
   558  				k, v = c.Seek([]byte(lastKey))
   559  			}
   560  
   561  			for k != nil {
   562  				if v == nil {
   563  					if len(pairs) >= batchSize {
   564  						break
   565  					}
   566  					sbkt := bkt.Bucket(k)
   567  
   568  					pair := infoPair{
   569  						bkey: string(sbkt.Get(bucketKeyName)),
   570  						info: snapshots.Info{
   571  							Name:   string(k),
   572  							Parent: string(sbkt.Get(bucketKeyParent)),
   573  						},
   574  					}
   575  
   576  					err := boltutil.ReadTimestamps(sbkt, &pair.info.Created, &pair.info.Updated)
   577  					if err != nil {
   578  						return err
   579  					}
   580  					pair.info.Labels, err = boltutil.ReadLabels(sbkt)
   581  					if err != nil {
   582  						return err
   583  					}
   584  
   585  					pairs = append(pairs, pair)
   586  				}
   587  
   588  				k, v = c.Next()
   589  			}
   590  
   591  			lastKey = string(k)
   592  
   593  			return nil
   594  		}); err != nil {
   595  			return err
   596  		}
   597  
   598  		for _, pair := range pairs {
   599  			info, err := s.Snapshotter.Stat(ctx, pair.bkey)
   600  			if err != nil {
   601  				if errdefs.IsNotFound(err) {
   602  					continue
   603  				}
   604  				return err
   605  			}
   606  
   607  			if err := fn(ctx, overlayInfo(info, pair.info)); err != nil {
   608  				return err
   609  			}
   610  		}
   611  
   612  		if lastKey == "" {
   613  			break
   614  		}
   615  
   616  		pairs = pairs[:0]
   617  
   618  	}
   619  
   620  	return nil
   621  }
   622  
   623  func validateSnapshot(info *snapshots.Info) error {
   624  	for k, v := range info.Labels {
   625  		if err := labels.Validate(k, v); err != nil {
   626  			return errors.Wrapf(err, "info.Labels")
   627  		}
   628  	}
   629  
   630  	return nil
   631  }
   632  
   633  type cleaner interface {
   634  	Cleanup(ctx context.Context) error
   635  }
   636  
   637  func (s *snapshotter) garbageCollect(ctx context.Context) (d time.Duration, err error) {
   638  	s.l.Lock()
   639  	t1 := time.Now()
   640  	defer func() {
   641  		s.l.Unlock()
   642  		if err == nil {
   643  			if c, ok := s.Snapshotter.(cleaner); ok {
   644  				err = c.Cleanup(ctx)
   645  			}
   646  		}
   647  		if err == nil {
   648  			d = time.Since(t1)
   649  		}
   650  	}()
   651  
   652  	seen := map[string]struct{}{}
   653  	if err := s.db.View(func(tx *bolt.Tx) error {
   654  		v1bkt := tx.Bucket(bucketKeyVersion)
   655  		if v1bkt == nil {
   656  			return nil
   657  		}
   658  
   659  		// iterate through each namespace
   660  		v1c := v1bkt.Cursor()
   661  
   662  		for k, v := v1c.First(); k != nil; k, v = v1c.Next() {
   663  			if v != nil {
   664  				continue
   665  			}
   666  
   667  			sbkt := v1bkt.Bucket(k).Bucket(bucketKeyObjectSnapshots)
   668  			if sbkt == nil {
   669  				continue
   670  			}
   671  
   672  			// Load specific snapshotter
   673  			ssbkt := sbkt.Bucket([]byte(s.name))
   674  			if ssbkt == nil {
   675  				continue
   676  			}
   677  
   678  			if err := ssbkt.ForEach(func(sk, sv []byte) error {
   679  				if sv == nil {
   680  					bkey := ssbkt.Bucket(sk).Get(bucketKeyName)
   681  					if len(bkey) > 0 {
   682  						seen[string(bkey)] = struct{}{}
   683  					}
   684  				}
   685  				return nil
   686  			}); err != nil {
   687  				return err
   688  			}
   689  		}
   690  
   691  		return nil
   692  	}); err != nil {
   693  		return 0, err
   694  	}
   695  
   696  	roots, err := s.walkTree(ctx, seen)
   697  	if err != nil {
   698  		return 0, err
   699  	}
   700  
   701  	// TODO: Unlock before removal (once nodes are fully unavailable).
   702  	// This could be achieved through doing prune inside the lock
   703  	// and having a cleanup method which actually performs the
   704  	// deletions on the snapshotters which support it.
   705  
   706  	for _, node := range roots {
   707  		if err := s.pruneBranch(ctx, node); err != nil {
   708  			return 0, err
   709  		}
   710  	}
   711  
   712  	return
   713  }
   714  
   715  type treeNode struct {
   716  	info     snapshots.Info
   717  	remove   bool
   718  	children []*treeNode
   719  }
   720  
   721  func (s *snapshotter) walkTree(ctx context.Context, seen map[string]struct{}) ([]*treeNode, error) {
   722  	roots := []*treeNode{}
   723  	nodes := map[string]*treeNode{}
   724  
   725  	if err := s.Snapshotter.Walk(ctx, func(ctx context.Context, info snapshots.Info) error {
   726  		_, isSeen := seen[info.Name]
   727  		node, ok := nodes[info.Name]
   728  		if !ok {
   729  			node = &treeNode{}
   730  			nodes[info.Name] = node
   731  		}
   732  
   733  		node.remove = !isSeen
   734  		node.info = info
   735  
   736  		if info.Parent == "" {
   737  			roots = append(roots, node)
   738  		} else {
   739  			parent, ok := nodes[info.Parent]
   740  			if !ok {
   741  				parent = &treeNode{}
   742  				nodes[info.Parent] = parent
   743  			}
   744  			parent.children = append(parent.children, node)
   745  		}
   746  
   747  		return nil
   748  	}); err != nil {
   749  		return nil, err
   750  	}
   751  
   752  	return roots, nil
   753  }
   754  
   755  func (s *snapshotter) pruneBranch(ctx context.Context, node *treeNode) error {
   756  	for _, child := range node.children {
   757  		if err := s.pruneBranch(ctx, child); err != nil {
   758  			return err
   759  		}
   760  	}
   761  
   762  	if node.remove {
   763  		logger := log.G(ctx).WithField("snapshotter", s.name)
   764  		if err := s.Snapshotter.Remove(ctx, node.info.Name); err != nil {
   765  			if !errdefs.IsFailedPrecondition(err) {
   766  				return err
   767  			}
   768  			logger.WithError(err).WithField("key", node.info.Name).Warnf("failed to remove snapshot")
   769  		} else {
   770  			logger.WithField("key", node.info.Name).Debug("removed snapshot")
   771  		}
   772  	}
   773  
   774  	return nil
   775  }
   776  
   777  // Close closes s.Snapshotter but not db
   778  func (s *snapshotter) Close() error {
   779  	return s.Snapshotter.Close()
   780  }
   781  
   782  // filterInheritedLabels filters the provided labels by removing any key which doesn't have
   783  // a prefix of "containerd.io/snapshot/".
   784  func filterInheritedLabels(labels map[string]string) map[string]string {
   785  	if labels == nil {
   786  		return nil
   787  	}
   788  
   789  	filtered := make(map[string]string)
   790  	for k, v := range labels {
   791  		if strings.HasPrefix(k, inheritedLabelsPrefix) {
   792  			filtered[k] = v
   793  		}
   794  	}
   795  	return filtered
   796  }