571 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			571 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package storage
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"encoding/binary"
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/boltdb/bolt"
 | |
| 	"github.com/containerd/containerd/errdefs"
 | |
| 	"github.com/containerd/containerd/metadata/boltutil"
 | |
| 	"github.com/containerd/containerd/snapshots"
 | |
| 	"github.com/pkg/errors"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	bucketKeyStorageVersion = []byte("v1")
 | |
| 	bucketKeySnapshot       = []byte("snapshots")
 | |
| 	bucketKeyParents        = []byte("parents")
 | |
| 
 | |
| 	bucketKeyID     = []byte("id")
 | |
| 	bucketKeyParent = []byte("parent")
 | |
| 	bucketKeyKind   = []byte("kind")
 | |
| 	bucketKeyInodes = []byte("inodes")
 | |
| 	bucketKeySize   = []byte("size")
 | |
| 
 | |
| 	// ErrNoTransaction is returned when an operation is attempted with
 | |
| 	// a context which is not inside of a transaction.
 | |
| 	ErrNoTransaction = errors.New("no transaction in context")
 | |
| )
 | |
| 
 | |
| // parentKey returns a composite key of the parent and child identifiers. The
 | |
| // parts of the key are separated by a zero byte.
 | |
| func parentKey(parent, child uint64) []byte {
 | |
| 	b := make([]byte, binary.Size([]uint64{parent, child})+1)
 | |
| 	i := binary.PutUvarint(b, parent)
 | |
| 	j := binary.PutUvarint(b[i+1:], child)
 | |
| 	return b[0 : i+j+1]
 | |
| }
 | |
| 
 | |
| // parentPrefixKey returns the parent part of the composite key with the
 | |
| // zero byte separator.
 | |
| func parentPrefixKey(parent uint64) []byte {
 | |
| 	b := make([]byte, binary.Size(parent)+1)
 | |
| 	i := binary.PutUvarint(b, parent)
 | |
| 	return b[0 : i+1]
 | |
| }
 | |
| 
 | |
| // getParentPrefix returns the first part of the composite key which
 | |
| // represents the parent identifier.
 | |
| func getParentPrefix(b []byte) uint64 {
 | |
| 	parent, _ := binary.Uvarint(b)
 | |
| 	return parent
 | |
| }
 | |
| 
 | |
| // GetInfo returns the snapshot Info directly from the metadata. Requires a
 | |
| // context with a storage transaction.
 | |
| func GetInfo(ctx context.Context, key string) (string, snapshots.Info, snapshots.Usage, error) {
 | |
| 	var (
 | |
| 		id uint64
 | |
| 		su snapshots.Usage
 | |
| 		si = snapshots.Info{
 | |
| 			Name: key,
 | |
| 		}
 | |
| 	)
 | |
| 	err := withSnapshotBucket(ctx, key, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
 | |
| 		getUsage(bkt, &su)
 | |
| 		return readSnapshot(bkt, &id, &si)
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return "", snapshots.Info{}, snapshots.Usage{}, err
 | |
| 	}
 | |
| 
 | |
| 	return fmt.Sprintf("%d", id), si, su, nil
 | |
| }
 | |
| 
 | |
| // UpdateInfo updates an existing snapshot info's data
 | |
| func UpdateInfo(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
 | |
| 	updated := snapshots.Info{
 | |
| 		Name: info.Name,
 | |
| 	}
 | |
| 	err := withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
 | |
| 		sbkt := bkt.Bucket([]byte(info.Name))
 | |
| 		if sbkt == nil {
 | |
| 			return errors.Wrap(errdefs.ErrNotFound, "snapshot does not exist")
 | |
| 		}
 | |
| 		if err := readSnapshot(sbkt, nil, &updated); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		if len(fieldpaths) > 0 {
 | |
| 			for _, path := range fieldpaths {
 | |
| 				if strings.HasPrefix(path, "labels.") {
 | |
| 					if updated.Labels == nil {
 | |
| 						updated.Labels = map[string]string{}
 | |
| 					}
 | |
| 
 | |
| 					key := strings.TrimPrefix(path, "labels.")
 | |
| 					updated.Labels[key] = info.Labels[key]
 | |
| 					continue
 | |
| 				}
 | |
| 
 | |
| 				switch path {
 | |
| 				case "labels":
 | |
| 					updated.Labels = info.Labels
 | |
| 				default:
 | |
| 					return errors.Wrapf(errdefs.ErrInvalidArgument, "cannot update %q field on snapshot %q", path, info.Name)
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			// Set mutable fields
 | |
| 			updated.Labels = info.Labels
 | |
| 		}
 | |
| 		updated.Updated = time.Now().UTC()
 | |
| 		if err := boltutil.WriteTimestamps(sbkt, updated.Created, updated.Updated); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		return boltutil.WriteLabels(sbkt, updated.Labels)
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return snapshots.Info{}, err
 | |
| 	}
 | |
| 	return updated, nil
 | |
| }
 | |
| 
 | |
| // WalkInfo iterates through all metadata Info for the stored snapshots and
 | |
| // calls the provided function for each. Requires a context with a storage
 | |
| // transaction.
 | |
| func WalkInfo(ctx context.Context, fn func(context.Context, snapshots.Info) error) error {
 | |
| 	return withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
 | |
| 		return bkt.ForEach(func(k, v []byte) error {
 | |
| 			// skip non buckets
 | |
| 			if v != nil {
 | |
| 				return nil
 | |
| 			}
 | |
| 			var (
 | |
| 				sbkt = bkt.Bucket(k)
 | |
| 				si   = snapshots.Info{
 | |
| 					Name: string(k),
 | |
| 				}
 | |
| 			)
 | |
| 			if err := readSnapshot(sbkt, nil, &si); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 
 | |
| 			return fn(ctx, si)
 | |
| 		})
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // GetSnapshot returns the metadata for the active or view snapshot transaction
 | |
| // referenced by the given key. Requires a context with a storage transaction.
 | |
| func GetSnapshot(ctx context.Context, key string) (s Snapshot, err error) {
 | |
| 	err = withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
 | |
| 		sbkt := bkt.Bucket([]byte(key))
 | |
| 		if sbkt == nil {
 | |
| 			return errors.Wrap(errdefs.ErrNotFound, "snapshot does not exist")
 | |
| 		}
 | |
| 
 | |
| 		s.ID = fmt.Sprintf("%d", readID(sbkt))
 | |
| 		s.Kind = readKind(sbkt)
 | |
| 
 | |
| 		if s.Kind != snapshots.KindActive && s.Kind != snapshots.KindView {
 | |
| 			return errors.Wrapf(errdefs.ErrFailedPrecondition, "requested snapshot %v not active or view", key)
 | |
| 		}
 | |
| 
 | |
| 		if parentKey := sbkt.Get(bucketKeyParent); len(parentKey) > 0 {
 | |
| 			spbkt := bkt.Bucket(parentKey)
 | |
| 			if spbkt == nil {
 | |
| 				return errors.Wrap(errdefs.ErrNotFound, "parent does not exist")
 | |
| 			}
 | |
| 
 | |
| 			s.ParentIDs, err = parents(bkt, spbkt, readID(spbkt))
 | |
| 			if err != nil {
 | |
| 				return errors.Wrap(err, "failed to get parent chain")
 | |
| 			}
 | |
| 		}
 | |
| 		return nil
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return Snapshot{}, err
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // CreateSnapshot inserts a record for an active or view snapshot with the provided parent.
 | |
| func CreateSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts ...snapshots.Opt) (s Snapshot, err error) {
 | |
| 	switch kind {
 | |
| 	case snapshots.KindActive, snapshots.KindView:
 | |
| 	default:
 | |
| 		return Snapshot{}, errors.Wrapf(errdefs.ErrInvalidArgument, "snapshot type %v invalid; only snapshots of type Active or View can be created", kind)
 | |
| 	}
 | |
| 	var base snapshots.Info
 | |
| 	for _, opt := range opts {
 | |
| 		if err := opt(&base); err != nil {
 | |
| 			return Snapshot{}, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	err = createBucketIfNotExists(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
 | |
| 		var (
 | |
| 			spbkt *bolt.Bucket
 | |
| 		)
 | |
| 		if parent != "" {
 | |
| 			spbkt = bkt.Bucket([]byte(parent))
 | |
| 			if spbkt == nil {
 | |
| 				return errors.Wrapf(errdefs.ErrNotFound, "missing parent %q bucket", parent)
 | |
| 			}
 | |
| 
 | |
| 			if readKind(spbkt) != snapshots.KindCommitted {
 | |
| 				return errors.Wrapf(errdefs.ErrInvalidArgument, "parent %q is not committed snapshot", parent)
 | |
| 			}
 | |
| 		}
 | |
| 		sbkt, err := bkt.CreateBucket([]byte(key))
 | |
| 		if err != nil {
 | |
| 			if err == bolt.ErrBucketExists {
 | |
| 				err = errors.Wrapf(errdefs.ErrAlreadyExists, "snapshot %v", key)
 | |
| 			}
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		id, err := bkt.NextSequence()
 | |
| 		if err != nil {
 | |
| 			return errors.Wrapf(err, "unable to get identifier for snapshot %q", key)
 | |
| 		}
 | |
| 
 | |
| 		t := time.Now().UTC()
 | |
| 		si := snapshots.Info{
 | |
| 			Parent:  parent,
 | |
| 			Kind:    kind,
 | |
| 			Labels:  base.Labels,
 | |
| 			Created: t,
 | |
| 			Updated: t,
 | |
| 		}
 | |
| 		if err := putSnapshot(sbkt, id, si); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		if spbkt != nil {
 | |
| 			pid := readID(spbkt)
 | |
| 
 | |
| 			// Store a backlink from the key to the parent. Store the snapshot name
 | |
| 			// as the value to allow following the backlink to the snapshot value.
 | |
| 			if err := pbkt.Put(parentKey(pid, id), []byte(key)); err != nil {
 | |
| 				return errors.Wrapf(err, "failed to write parent link for snapshot %q", key)
 | |
| 			}
 | |
| 
 | |
| 			s.ParentIDs, err = parents(bkt, spbkt, pid)
 | |
| 			if err != nil {
 | |
| 				return errors.Wrapf(err, "failed to get parent chain for snapshot %q", key)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		s.ID = fmt.Sprintf("%d", id)
 | |
| 		s.Kind = kind
 | |
| 		return nil
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return Snapshot{}, err
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Remove removes a snapshot from the metastore. The string identifier for the
 | |
| // snapshot is returned as well as the kind. The provided context must contain a
 | |
| // writable transaction.
 | |
| func Remove(ctx context.Context, key string) (string, snapshots.Kind, error) {
 | |
| 	var (
 | |
| 		id uint64
 | |
| 		si snapshots.Info
 | |
| 	)
 | |
| 
 | |
| 	if err := withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
 | |
| 		sbkt := bkt.Bucket([]byte(key))
 | |
| 		if sbkt == nil {
 | |
| 			return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v", key)
 | |
| 		}
 | |
| 
 | |
| 		if err := readSnapshot(sbkt, &id, &si); err != nil {
 | |
| 			errors.Wrapf(err, "failed to read snapshot %s", key)
 | |
| 		}
 | |
| 
 | |
| 		if pbkt != nil {
 | |
| 			k, _ := pbkt.Cursor().Seek(parentPrefixKey(id))
 | |
| 			if getParentPrefix(k) == id {
 | |
| 				return errors.Wrap(errdefs.ErrFailedPrecondition, "cannot remove snapshot with child")
 | |
| 			}
 | |
| 
 | |
| 			if si.Parent != "" {
 | |
| 				spbkt := bkt.Bucket([]byte(si.Parent))
 | |
| 				if spbkt == nil {
 | |
| 					return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v", key)
 | |
| 				}
 | |
| 
 | |
| 				if err := pbkt.Delete(parentKey(readID(spbkt), id)); err != nil {
 | |
| 					return errors.Wrap(err, "failed to delete parent link")
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if err := bkt.DeleteBucket([]byte(key)); err != nil {
 | |
| 			return errors.Wrap(err, "failed to delete snapshot")
 | |
| 		}
 | |
| 
 | |
| 		return nil
 | |
| 	}); err != nil {
 | |
| 		return "", 0, err
 | |
| 	}
 | |
| 
 | |
| 	return fmt.Sprintf("%d", id), si.Kind, nil
 | |
| }
 | |
| 
 | |
| // CommitActive renames the active snapshot transaction referenced by `key`
 | |
| // as a committed snapshot referenced by `Name`. The resulting snapshot  will be
 | |
| // committed and readonly. The `key` reference will no longer be available for
 | |
| // lookup or removal. The returned string identifier for the committed snapshot
 | |
| // is the same identifier of the original active snapshot. The provided context
 | |
| // must contain a writable transaction.
 | |
| func CommitActive(ctx context.Context, key, name string, usage snapshots.Usage, opts ...snapshots.Opt) (string, error) {
 | |
| 	var (
 | |
| 		id   uint64
 | |
| 		base snapshots.Info
 | |
| 	)
 | |
| 	for _, opt := range opts {
 | |
| 		if err := opt(&base); err != nil {
 | |
| 			return "", err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if err := withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
 | |
| 		dbkt, err := bkt.CreateBucket([]byte(name))
 | |
| 		if err != nil {
 | |
| 			if err == bolt.ErrBucketExists {
 | |
| 				err = errdefs.ErrAlreadyExists
 | |
| 			}
 | |
| 			return errors.Wrapf(err, "committed snapshot %v", name)
 | |
| 		}
 | |
| 		sbkt := bkt.Bucket([]byte(key))
 | |
| 		if sbkt == nil {
 | |
| 			return errors.Wrapf(errdefs.ErrNotFound, "failed to get active snapshot %q", key)
 | |
| 		}
 | |
| 
 | |
| 		var si snapshots.Info
 | |
| 		if err := readSnapshot(sbkt, &id, &si); err != nil {
 | |
| 			return errors.Wrapf(err, "failed to read active snapshot %q", key)
 | |
| 		}
 | |
| 
 | |
| 		if si.Kind != snapshots.KindActive {
 | |
| 			return errors.Wrapf(errdefs.ErrFailedPrecondition, "snapshot %q is not active", key)
 | |
| 		}
 | |
| 		si.Kind = snapshots.KindCommitted
 | |
| 		si.Created = time.Now().UTC()
 | |
| 		si.Updated = si.Created
 | |
| 
 | |
| 		// Replace labels, do not inherit
 | |
| 		si.Labels = base.Labels
 | |
| 
 | |
| 		if err := putSnapshot(dbkt, id, si); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if err := putUsage(dbkt, usage); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if err := bkt.DeleteBucket([]byte(key)); err != nil {
 | |
| 			return errors.Wrapf(err, "failed to delete active snapshot %q", key)
 | |
| 		}
 | |
| 		if si.Parent != "" {
 | |
| 			spbkt := bkt.Bucket([]byte(si.Parent))
 | |
| 			if spbkt == nil {
 | |
| 				return errors.Wrapf(errdefs.ErrNotFound, "missing parent %q of snapshot %q", si.Parent, key)
 | |
| 			}
 | |
| 			pid := readID(spbkt)
 | |
| 
 | |
| 			// Updates parent back link to use new key
 | |
| 			if err := pbkt.Put(parentKey(pid, id), []byte(name)); err != nil {
 | |
| 				return errors.Wrapf(err, "failed to update parent link %q from %q to %q", pid, key, name)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return nil
 | |
| 	}); err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 
 | |
| 	return fmt.Sprintf("%d", id), nil
 | |
| }
 | |
| 
 | |
| func withSnapshotBucket(ctx context.Context, key string, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error {
 | |
| 	tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx)
 | |
| 	if !ok {
 | |
| 		return ErrNoTransaction
 | |
| 	}
 | |
| 	vbkt := tx.Bucket(bucketKeyStorageVersion)
 | |
| 	if vbkt == nil {
 | |
| 		return errors.Wrap(errdefs.ErrNotFound, "bucket does not exist")
 | |
| 	}
 | |
| 	bkt := vbkt.Bucket(bucketKeySnapshot)
 | |
| 	if bkt == nil {
 | |
| 		return errors.Wrap(errdefs.ErrNotFound, "snapshots bucket does not exist")
 | |
| 	}
 | |
| 	bkt = bkt.Bucket([]byte(key))
 | |
| 	if bkt == nil {
 | |
| 		return errors.Wrap(errdefs.ErrNotFound, "snapshot does not exist")
 | |
| 	}
 | |
| 
 | |
| 	return fn(ctx, bkt, vbkt.Bucket(bucketKeyParents))
 | |
| }
 | |
| 
 | |
| func withBucket(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error {
 | |
| 	tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx)
 | |
| 	if !ok {
 | |
| 		return ErrNoTransaction
 | |
| 	}
 | |
| 	bkt := tx.Bucket(bucketKeyStorageVersion)
 | |
| 	if bkt == nil {
 | |
| 		return errors.Wrap(errdefs.ErrNotFound, "bucket does not exist")
 | |
| 	}
 | |
| 	return fn(ctx, bkt.Bucket(bucketKeySnapshot), bkt.Bucket(bucketKeyParents))
 | |
| }
 | |
| 
 | |
| func createBucketIfNotExists(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error {
 | |
| 	tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx)
 | |
| 	if !ok {
 | |
| 		return ErrNoTransaction
 | |
| 	}
 | |
| 
 | |
| 	bkt, err := tx.CreateBucketIfNotExists(bucketKeyStorageVersion)
 | |
| 	if err != nil {
 | |
| 		return errors.Wrap(err, "failed to create version bucket")
 | |
| 	}
 | |
| 	sbkt, err := bkt.CreateBucketIfNotExists(bucketKeySnapshot)
 | |
| 	if err != nil {
 | |
| 		return errors.Wrap(err, "failed to create snapshots bucket")
 | |
| 	}
 | |
| 	pbkt, err := bkt.CreateBucketIfNotExists(bucketKeyParents)
 | |
| 	if err != nil {
 | |
| 		return errors.Wrap(err, "failed to create parents bucket")
 | |
| 	}
 | |
| 	return fn(ctx, sbkt, pbkt)
 | |
| }
 | |
| 
 | |
| func parents(bkt, pbkt *bolt.Bucket, parent uint64) (parents []string, err error) {
 | |
| 	for {
 | |
| 		parents = append(parents, fmt.Sprintf("%d", parent))
 | |
| 
 | |
| 		parentKey := pbkt.Get(bucketKeyParent)
 | |
| 		if len(parentKey) == 0 {
 | |
| 			return
 | |
| 		}
 | |
| 		pbkt = bkt.Bucket(parentKey)
 | |
| 		if pbkt == nil {
 | |
| 			return nil, errors.Wrap(errdefs.ErrNotFound, "missing parent")
 | |
| 		}
 | |
| 
 | |
| 		parent = readID(pbkt)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func readKind(bkt *bolt.Bucket) (k snapshots.Kind) {
 | |
| 	kind := bkt.Get(bucketKeyKind)
 | |
| 	if len(kind) == 1 {
 | |
| 		k = snapshots.Kind(kind[0])
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func readID(bkt *bolt.Bucket) uint64 {
 | |
| 	id, _ := binary.Uvarint(bkt.Get(bucketKeyID))
 | |
| 	return id
 | |
| }
 | |
| 
 | |
| func readSnapshot(bkt *bolt.Bucket, id *uint64, si *snapshots.Info) error {
 | |
| 	if id != nil {
 | |
| 		*id = readID(bkt)
 | |
| 	}
 | |
| 	if si != nil {
 | |
| 		si.Kind = readKind(bkt)
 | |
| 		si.Parent = string(bkt.Get(bucketKeyParent))
 | |
| 
 | |
| 		if err := boltutil.ReadTimestamps(bkt, &si.Created, &si.Updated); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		labels, err := boltutil.ReadLabels(bkt)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		si.Labels = labels
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func putSnapshot(bkt *bolt.Bucket, id uint64, si snapshots.Info) error {
 | |
| 	idEncoded, err := encodeID(id)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	updates := [][2][]byte{
 | |
| 		{bucketKeyID, idEncoded},
 | |
| 		{bucketKeyKind, []byte{byte(si.Kind)}},
 | |
| 	}
 | |
| 	if si.Parent != "" {
 | |
| 		updates = append(updates, [2][]byte{bucketKeyParent, []byte(si.Parent)})
 | |
| 	}
 | |
| 	for _, v := range updates {
 | |
| 		if err := bkt.Put(v[0], v[1]); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	if err := boltutil.WriteTimestamps(bkt, si.Created, si.Updated); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return boltutil.WriteLabels(bkt, si.Labels)
 | |
| }
 | |
| 
 | |
| func getUsage(bkt *bolt.Bucket, usage *snapshots.Usage) {
 | |
| 	usage.Inodes, _ = binary.Varint(bkt.Get(bucketKeyInodes))
 | |
| 	usage.Size, _ = binary.Varint(bkt.Get(bucketKeySize))
 | |
| }
 | |
| 
 | |
| func putUsage(bkt *bolt.Bucket, usage snapshots.Usage) error {
 | |
| 	for _, v := range []struct {
 | |
| 		key   []byte
 | |
| 		value int64
 | |
| 	}{
 | |
| 		{bucketKeyInodes, usage.Inodes},
 | |
| 		{bucketKeySize, usage.Size},
 | |
| 	} {
 | |
| 		e, err := encodeSize(v.value)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if err := bkt.Put(v.key, e); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func encodeSize(size int64) ([]byte, error) {
 | |
| 	var (
 | |
| 		buf         [binary.MaxVarintLen64]byte
 | |
| 		sizeEncoded = buf[:]
 | |
| 	)
 | |
| 	sizeEncoded = sizeEncoded[:binary.PutVarint(sizeEncoded, size)]
 | |
| 
 | |
| 	if len(sizeEncoded) == 0 {
 | |
| 		return nil, fmt.Errorf("failed encoding size = %v", size)
 | |
| 	}
 | |
| 	return sizeEncoded, nil
 | |
| }
 | |
| 
 | |
| func encodeID(id uint64) ([]byte, error) {
 | |
| 	var (
 | |
| 		buf       [binary.MaxVarintLen64]byte
 | |
| 		idEncoded = buf[:]
 | |
| 	)
 | |
| 	idEncoded = idEncoded[:binary.PutUvarint(idEncoded, id)]
 | |
| 
 | |
| 	if len(idEncoded) == 0 {
 | |
| 		return nil, fmt.Errorf("failed encoding id = %v", id)
 | |
| 	}
 | |
| 	return idEncoded, nil
 | |
| }
 | 
