func (o *snapshotter) Walk(ctx context.Context, fn snapshots.WalkFunc, fs ...string) error {
	log.G(ctx).Debugf("Walk (fs: %s)", fs)
	ctx, t, err := o.ms.TransactionContext(ctx, false)
	if err != nil {
		return err
	}
	defer t.Rollback()
	return storage.WalkInfo(ctx, fn, fs...)
}
Walk the snapshots.
package snapshot
import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"unsafe"
	"github.com/containerd/containerd/errdefs"
	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/mount"
	"github.com/containerd/containerd/snapshots"
	"github.com/containerd/containerd/snapshots/storage"
	"github.com/containerd/continuity/fs"
	"github.com/pkg/errors"
)
type storageType int
const (
	storageTypeUnknown storageType = iota
	storageTypeNormal
	storageTypeLocalBlock
	storageTypeRemoteBlock
)
const (
	labelKeyTargetSnapshotRef = "containerd.io/snapshot.ref"
	labelKeyImageRef = "containerd.io/snapshot/image-ref"
	labelKeyOverlayBDBlobDigest = "containerd.io/snapshot/overlaybd/blob-digest"
	labelKeyOverlayBDBlobSize = "containerd.io/snapshot/overlaybd/blob-size"
	labelKeyOverlayBDBlobFsType = "containerd.io/snapshot/overlaybd/blob-fs-type"
	labelKeyAccelerationLayer = "containerd.io/snapshot/overlaybd/acceleration-layer"
	labelKeyRecordTrace = "containerd.io/snapshot/overlaybd/record-trace"
	labelKeyRecordTracePath = "containerd.io/snapshot/overlaybd/record-trace-path"
	labelKeyCriImageRef = "containerd.io/snapshot/cri.image-ref"
)
const (
	LabelSupportReadWriteMode = "containerd.io/snapshot/overlaybd.writable"
	LabelLocalOverlayBDPath = "containerd.io/snapshot/overlaybd.localcommitpath"
	LabelLocalOverlayBDBaseLayer = "containerd.io/snapshot/overlaybd.baselayer"
)
const (
	roDir = iota  
	rwDir         
	rwDev         
)
type SnapshotterConfig struct {
	OverlayBDUtilBinDir string `toml:"overlaybd_util_bin_dir" json:"overlaybd_util_bin_dir"`
}
var defaultConfig = SnapshotterConfig{
	OverlayBDUtilBinDir: "/opt/overlaybd/bin",
}
type Opt func(config *SnapshotterConfig) error
type snapshotter struct {
	root     string
	config   SnapshotterConfig
	ms       *storage.MetaStore
	indexOff bool
}
func NewSnapshotter(root string, opts ...Opt) (snapshots.Snapshotter, error) {
	config := defaultConfig
	for _, opt := range opts {
		if err := opt(&config); err != nil {
			return nil, err
		}
	}
	if err := os.MkdirAll(root, 0700); err != nil {
		return nil, err
	}
	ms, err := storage.NewMetaStore(filepath.Join(root, "metadata.db"))
	if err != nil {
		return nil, err
	}
	if err := os.Mkdir(filepath.Join(root, "snapshots"), 0700); err != nil && !os.IsExist(err) {
		return nil, err
	}
	var indexOff bool
	if _, err = os.Stat("/sys/module/overlay/parameters/index"); err == nil {
		indexOff = true
	}
	return &snapshotter{
		root:     root,
		ms:       ms,
		indexOff: indexOff,
		config:   config,
	}, nil
}
func (o *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) {
	log.G(ctx).Debugf("Stat (key: %s)", key)
	ctx, t, err := o.ms.TransactionContext(ctx, false)
	if err != nil {
		return snapshots.Info{}, err
	}
	defer t.Rollback()
	_, info, _, err := storage.GetInfo(ctx, key)
	if err != nil {
		return snapshots.Info{}, err
	}
	return info, nil
}
func (o *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
	log.G(ctx).Debugf("Update (fieldpaths: %s)", fieldpaths)
	ctx, t, err := o.ms.TransactionContext(ctx, true)
	if err != nil {
		return snapshots.Info{}, err
	}
	info, err = storage.UpdateInfo(ctx, info, fieldpaths...)
	if err != nil {
		t.Rollback()
		return snapshots.Info{}, err
	}
	if err := t.Commit(); err != nil {
		return snapshots.Info{}, err
	}
	return info, nil
}
func (o *snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) {
	log.G(ctx).Debugf("Usage (key: %s)", key)
	ctx, t, err := o.ms.TransactionContext(ctx, false)
	if err != nil {
		return snapshots.Usage{}, err
	}
	id, info, usage, err := storage.GetInfo(ctx, key)
	t.Rollback()
	if err != nil {
		return snapshots.Usage{}, err
	}
	if info.Kind == snapshots.KindActive {
		upperPath := o.upperPath(id)
		du, err := fs.DiskUsage(ctx, upperPath)
		if err != nil {
			return snapshots.Usage{}, err
		}
		usage = snapshots.Usage(du)
	}
	return usage, nil
}
func (o *snapshotter) getWritableType(ctx context.Context, info snapshots.Info) (mode int) {
	defer func() {
		log.G(ctx).Infof("snapshot R/W label: %d", mode)
	}()
	mode = roDir
	m, ok := info.Labels[LabelSupportReadWriteMode]
	if !ok {
		return
	}
	if m == "dir" {
		mode = rwDir
		return
	}
	if m == "dev" {
		mode = rwDev
		return
	}
	return
}
func (o *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) (_ []mount.Mount, retErr error) {
	ctx, t, err := o.ms.TransactionContext(ctx, true)
	if err != nil {
		return nil, err
	}
	log.G(ctx).Debugf("Prepare (key: %s, parent: %s)", key, parent)
	rollback := true
	defer func() {
		if retErr != nil && rollback {
			if rerr := t.Rollback(); rerr != nil {
				log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
			}
		}
	}()
	id, info, err := o.createSnapshot(ctx, snapshots.KindActive, key, parent, opts)
	if err != nil {
		return nil, err
	}
	log.G(ctx).Debugf("Prepare snapshot info(id: %s, info: %v)", id, info.Labels)
	defer func() {
		if retErr != nil && !errdefs.IsAlreadyExists(retErr) {
			if rerr := os.RemoveAll(o.snPath(id)); rerr != nil {
				log.G(ctx).WithError(rerr).Warn("failed to cleanup")
			}
		}
	}()
	s, err := storage.GetSnapshot(ctx, key)
	if err != nil {
		return nil, err
	}
	if targetRef, ok := info.Labels[labelKeyTargetSnapshotRef]; ok {
		isAccelLayer := info.Labels[labelKeyAccelerationLayer]
		log.G(ctx).Debugf("Prepare (targetRefLabel: %s, accelLayerLabel: %s)", targetRef, isAccelLayer)
		if isAccelLayer == "yes" {
			parentID, _, _, err := storage.GetInfo(ctx, parent)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to get info of parent snapshot %s", parent)
			}
			if err := o.constructSpecForAccelLayer(id, parentID); err != nil {
				return nil, errors.Wrapf(err, "constructSpecForAccelLayer failed: id %s", id)
			}
			rollback = false
			if err := t.Commit(); err != nil {
				return nil, err
			}
			return []mount.Mount{{
				Source: o.upperPath(id),
				Type:   "bind",
				Options: []string{
					"rw",
					"bind",
				}},
			}, nil
		}
		stype, err := o.identifySnapshotStorageType(ctx, id, info)
		log.G(ctx).Debugf("Prepare (storageType: %d)", stype)
		if err != nil {
			return nil, err
		}
		if stype == storageTypeRemoteBlock {
			if _, _, err = o.commit(ctx, targetRef, key, opts...); err != nil {
				return nil, err
			}
			if err := o.constructOverlayBDSpec(ctx, targetRef, false); err != nil {
				return nil, err
			}
			rollback = false
			if err := t.Commit(); err != nil {
				return nil, err
			}
			return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "target snapshot %q", targetRef)
		}
	}
	stype := storageTypeNormal
	writeType := o.getWritableType(ctx, info)
	if _, ok := info.Labels[LabelLocalOverlayBDBaseLayer]; ok && parent == "" {
		log.G(ctx).Debugf("prepare base layer")
		if err := o.constructSpecForBaseLayer(ctx, id); err != nil {
			return nil, err
		}
		log.G(ctx).Debugf("construct spec for base layer done")
		fsType, ok := info.Labels[labelKeyOverlayBDBlobFsType]
		if !ok {
			log.G(ctx).Warnf("cannot get fs type from lable, %v", info.Labels)
			fsType = "ext4"
		}
		log.G(ctx).Debugf("attachAndMountBlockDevice (obdID: %s, writeType: %d, fsType %s, targetPath: %s)",
			id, writeType, fsType, o.overlaybdTargetPath(id))
		if err = o.attachAndMountBlockDevice(ctx, id, writeType, fsType, false); err != nil {
			log.G(ctx).Errorf("%v", err)
			return nil, errors.Wrapf(err, "failed to attach and mount for snapshot %v", id)
		}
	}
	if _, ok := info.Labels[labelKeyTargetSnapshotRef]; !ok && parent != "" {
		parentID, parentInfo, _, err := storage.GetInfo(ctx, parent)
		if err != nil {
			return nil, errors.Wrapf(err, "failed to get info of parent snapshot %s", parent)
		}
		log.G(ctx).Debugf("Prepare snapshot parent info(id: %s, info: %v)", parentID, parentInfo.Labels)
		stype, err = o.identifySnapshotStorageType(ctx, parentID, parentInfo)
		if err != nil {
			return nil, err
		}
		switch stype {
		case storageTypeLocalBlock, storageTypeRemoteBlock:
			if writeType != roDir {
				if err := o.constructOverlayBDSpec(ctx, key, true); err != nil {
					return nil, err
				}
			}
			log.G(ctx).Debugf("Prepare rootfs (parentID: %s, parentStorageType: %d, writeType: %d)", parentID, stype, writeType)
			parentIsAccelLayer := parentInfo.Labels[labelKeyAccelerationLayer] == "yes"
			needRecordTrace := info.Labels[labelKeyRecordTrace] == "yes"
			recordTracePath := info.Labels[labelKeyRecordTracePath]
			log.G(ctx).Debugf("Prepare rootfs (parentIsAccelLayer: %t, needRecordTrace: %t, recordTracePath: %s)",
				parentIsAccelLayer, needRecordTrace, recordTracePath)
			if parentIsAccelLayer {
				log.G(ctx).Debugf("get accel-layer in parent (id: %s)", id)
				err = o.updateSpec(parentID, true, "")
				if writeType != roDir {
					o.updateSpec(id, true, "")
				}
			} else if needRecordTrace && recordTracePath != "" {
				err = o.updateSpec(parentID, false, recordTracePath)
			} else {
				err = o.updateSpec(parentID, false, "")
			}
			if err != nil {
				return nil, errors.Wrapf(err, "updateSpec failed for snapshot %s", parentID)
			}
			var obdID string
			var obdInfo *snapshots.Info
			if writeType != roDir {
				obdID = id
				obdInfo = &info
			} else {
				obdID = parentID
				obdInfo = &parentInfo
			}
			fsType, ok := obdInfo.Labels[labelKeyOverlayBDBlobFsType]
			if !ok {
				log.G(ctx).Warnf("cannot get fs type from lable, %v", obdInfo.Labels)
				fsType = "ext4"
			}
			log.G(ctx).Debugf("attachAndMountBlockDevice (obdID: %s, writeType: %d, fsType %s, targetPath: %s)",
				obdID, writeType, fsType, o.overlaybdTargetPath(obdID))
			if err = o.attachAndMountBlockDevice(ctx, obdID, writeType, fsType, true); err != nil {
				log.G(ctx).Errorf("%v", err)
				return nil, errors.Wrapf(err, "failed to attach and mount for snapshot %v", obdID)
			}
			defer func() {
				if retErr != nil && writeType == rwDir {  
					if rerr := mount.Unmount(o.overlaybdMountpoint(obdID), 0); rerr != nil {
						log.G(ctx).WithError(rerr).Warnf("failed to umount writable block %s", o.overlaybdMountpoint(obdID))
					}
				}
			}()
		default:
		}
	}
	rollback = false
	if err := t.Commit(); err != nil {
		return nil, err
	}
	var m []mount.Mount
	switch stype {
	case storageTypeNormal:
		m = o.normalOverlayMount(s)
		log.G(ctx).Debugf("return mount point(R/W mode: %d): %v", writeType, m)
	case storageTypeLocalBlock, storageTypeRemoteBlock:
		m, err = o.basedOnBlockDeviceMount(ctx, s, writeType)
		if err != nil {
			return nil, errors.Wrapf(err, "%s", err.Error())
		}
	default:
		panic("unreachable")
	}
	return m, nil
}
func (o *snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) (_ []mount.Mount, retErr error) {
	log.G(ctx).Debugf("View (key: %s, parent: %s)", key, parent)
	ctx, t, err := o.ms.TransactionContext(ctx, true)
	if err != nil {
		return nil, err
	}
	rollback := true
	defer func() {
		if retErr != nil && rollback {
			if rerr := t.Rollback(); rerr != nil {
				log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
			}
		}
	}()
	id, _, err := o.createSnapshot(ctx, snapshots.KindView, key, parent, opts)
	if err != nil {
		return nil, err
	}
	defer func() {
		if retErr != nil {
			if rerr := os.RemoveAll(o.snPath(id)); rerr != nil {
				log.G(ctx).WithError(rerr).Warn("failed to cleanup")
			}
		}
	}()
	s, err := storage.GetSnapshot(ctx, key)
	if err != nil {
		return nil, err
	}
	stype := storageTypeNormal
	if parent != "" {
		parentID, parentInfo, _, err := storage.GetInfo(ctx, parent)
		if err != nil {
			return nil, errors.Wrapf(err, "failed to get info of parent snapshot %s", parent)
		}
		stype, err = o.identifySnapshotStorageType(ctx, parentID, parentInfo)
		if err != nil {
			return nil, err
		}
		switch stype {
		case storageTypeLocalBlock, storageTypeRemoteBlock:
			fsType, ok := parentInfo.Labels[labelKeyOverlayBDBlobFsType]
			if !ok {
				fsType = "ext4"
			}
			if err := o.attachAndMountBlockDevice(ctx, parentID, roDir, fsType, true); err != nil {
				return nil, errors.Wrapf(err, "failed to attach and mount for snapshot %v", key)
			}
		default:
		}
	}
	rollback = false
	if err := t.Commit(); err != nil {
		return nil, err
	}
	var m []mount.Mount
	switch stype {
	case storageTypeNormal:
		m = o.normalOverlayMount(s)
	case storageTypeLocalBlock, storageTypeRemoteBlock:
		m, retErr = o.basedOnBlockDeviceMount(ctx, s, roDir)
	default:
		panic("unreachable")
	}
	return m, retErr
}
func (o *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
	ctx, t, err := o.ms.TransactionContext(ctx, false)
	if err != nil {
		return nil, err
	}
	defer t.Rollback()
	s, err := storage.GetSnapshot(ctx, key)
	if err != nil {
		return nil, errors.Wrap(err, "failed to get active mount")
	}
	log.G(ctx).Debugf("Mounts (key: %s, id: %s, parentID: %s, kind: %d)", key, s.ID, s.ParentIDs, s.Kind)
	if len(s.ParentIDs) > 0 {
		_, info, _, err := storage.GetInfo(ctx, key)
		if err != nil {
			return nil, errors.Wrap(err, "failed to get info")
		}
		writeType := o.getWritableType(ctx, info)
		if writeType != roDir {
			return o.basedOnBlockDeviceMount(ctx, s, writeType)
		}
		parentID, parentInfo, _, err := storage.GetInfo(ctx, info.Parent)
		if err != nil {
			return nil, errors.Wrapf(err, "failed to get info of parent snapshot %s", info.Parent)
		}
		parentStype, err := o.identifySnapshotStorageType(ctx, parentID, parentInfo)
		if err != nil {
			return nil, errors.Wrapf(err, "failed to identify storage of parent snapshot %s", parentInfo.Name)
		}
		if parentStype == storageTypeRemoteBlock || parentStype == storageTypeLocalBlock {
			return o.basedOnBlockDeviceMount(ctx, s, roDir)
		}
	}
	return o.normalOverlayMount(s), nil
}
func (o *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) (retErr error) {
	log.G(ctx).Debugf("Commit (key: %s, name: %s)", key, name)
	ctx, t, err := o.ms.TransactionContext(ctx, true)
	if err != nil {
		return err
	}
	rollback := true
	defer func() {
		if retErr != nil && rollback {
			if rerr := t.Rollback(); rerr != nil {
				log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
			}
		}
	}()
	id, oinfo, _, err := storage.GetInfo(ctx, key)
	if err != nil {
		return errors.Wrapf(err, "failed to get info of snapshot %s", key)
	}
	if _, writableBD := oinfo.Labels[LabelSupportReadWriteMode]; writableBD {
		_, baselayer := oinfo.Labels[LabelLocalOverlayBDBaseLayer]
		if err := o.unmountAndDetachBlockDevice(ctx, id, key, !baselayer); err != nil {
			return errors.Wrapf(err, "failed to destroy target device for snapshot %s", key)
		}
		if err := o.commitWritableOverlaybd(ctx, id); err != nil {
			return err
		}
		defer func() {
			if retErr != nil {
				return
			}
			os.Remove(o.overlaybdWritableDataPath(id))
			os.Remove(o.overlaybdWritableIndexPath(id))
		}()
		opts = append(opts, snapshots.WithLabels(map[string]string{LabelLocalOverlayBDPath: o.magicFilePath(id)}))
	}
	id, info, err := o.commit(ctx, name, key, opts...)
	if err != nil {
		return err
	}
	stype, err := o.identifySnapshotStorageType(ctx, id, info)
	if err != nil {
		return err
	}
	log.G(ctx).Debugf("Commit info (id: %s, info: %v, stype: %d)", id, info.Labels, stype)
	if stype == storageTypeLocalBlock {
		if err := o.constructOverlayBDSpec(ctx, name, false); err != nil {
			return errors.Wrapf(err, "failed to construct overlaybd config")
		}
		if info.Labels == nil {
			info.Labels = make(map[string]string)
		}
		info.Labels[LabelLocalOverlayBDPath] = o.magicFilePath(id)
		info, err = storage.UpdateInfo(ctx, info, fmt.Sprintf("labels.%s", LabelLocalOverlayBDPath))
		if err != nil {
			return err
		}
		log.G(ctx).Debugf("Commit info (info: %v)", info.Labels)
	}
	rollback = false
	return t.Commit()
}
func (o *snapshotter) commit(ctx context.Context, name, key string, opts ...snapshots.Opt) (string, snapshots.Info, error) {
	id, _, _, err := storage.GetInfo(ctx, key)
	if err != nil {
		return "", snapshots.Info{}, err
	}
	usage, err := fs.DiskUsage(ctx, o.upperPath(id))
	if err != nil {
		return "", snapshots.Info{}, err
	}
	if _, err := storage.CommitActive(ctx, key, name, snapshots.Usage(usage), opts...); err != nil {
		return "", snapshots.Info{}, errors.Wrap(err, "failed to commit snapshot")
	}
	id, info, _, err := storage.GetInfo(ctx, name)
	if err != nil {
		return "", snapshots.Info{}, err
	}
	return id, info, nil
}
func (o *snapshotter) Remove(ctx context.Context, key string) (err error) {
	log.G(ctx).Debugf("Remove (key: %s)", key)
	ctx, t, err := o.ms.TransactionContext(ctx, true)
	if err != nil {
		return err
	}
	rollback := true
	id, info, _, err := storage.GetInfo(ctx, key)
	if err != nil {
		return err
	}
	stype, err := o.identifySnapshotStorageType(ctx, id, info)
	if err != nil {
		return err
	}
	if stype != storageTypeNormal {
		_, err = os.Stat(o.overlaybdBackstoreMarkFile(id))
		if err == nil {
			err = o.unmountAndDetachBlockDevice(ctx, id, key, true)
			if err != nil {
				return errors.Wrapf(err, "failed to destroy target device for snapshot %s", key)
			}
		}
	}
	defer func() {
		if err != nil && rollback {
			if rerr := t.Rollback(); rerr != nil {
				log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
			}
		}
	}()
	_, _, err = storage.Remove(ctx, key)
	if err != nil {
		return errors.Wrap(err, "failed to remove")
	}
	if err := os.RemoveAll(o.snPath(id)); err != nil && !os.IsNotExist(err) {
		return err
	}
	rollback = false
	return t.Commit()
}