package snapshot
import (
"errors"
"fmt"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"syscall"
"github.com/openeuler/Conch/internal/snapshot/common"
)
func prepareSnapshotFiles(layout *BootLayout) error {
return os.MkdirAll(layout.SnapDir(), common.DirMode)
}
func listRootfsLayerErofs(rootfsMount string) ([]string, error) {
entries, err := os.ReadDir(rootfsMount)
if err != nil {
return nil, fmt.Errorf("read dir %s: %w", rootfsMount, err)
}
const (
layerPrefix = "layer"
layerSuffix = ".erofs"
)
type layerEntry struct {
name string
index int
}
layers := make([]layerEntry, 0, len(entries))
for _, entry := range entries {
if entry.IsDir() {
continue
}
name := entry.Name()
if !strings.HasPrefix(name, layerPrefix) || !strings.HasSuffix(name, layerSuffix) {
continue
}
numPart := strings.TrimPrefix(name, layerPrefix)
numPart = strings.TrimSuffix(numPart, layerSuffix)
idx, err := strconv.Atoi(numPart)
if err != nil {
continue
}
if idx < 0 {
continue
}
layers = append(layers, layerEntry{name: name, index: idx})
}
if len(layers) == 0 {
return []string{}, nil
}
sort.Slice(layers, func(i, j int) bool {
return layers[i].index < layers[j].index
})
result := make([]string, len(layers))
for i, layer := range layers {
result[i] = layer.name
}
return result, nil
}
func prepareSparseMemfile(layout *BootLayout, targetDir string) error {
memFile := filepath.Join(targetDir, common.MemFileName)
if err := os.MkdirAll(filepath.Dir(memFile), common.DirMode); err != nil {
return err
}
f, err := os.OpenFile(memFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, common.FileMode)
if err != nil {
return fmt.Errorf("open memfile: %w", err)
}
defer f.Close()
if err := f.Truncate(layout.MemorySizeMB * common.MemMB); err != nil {
return fmt.Errorf("truncate memfile: %w", err)
}
return nil
}
func ensureMemFile(layout *BootLayout, memDir string, createIfMissing bool) error {
memFile := filepath.Join(memDir, common.MemFileName)
if _, err := os.Stat(memFile); err == nil {
return nil
}
if !createIfMissing {
return fmt.Errorf("mem.img not found at %s", memFile)
}
return prepareSparseMemfile(layout, memDir)
}
func MemKeyFromRootfs(rootfsKey string) string {
return getMemKeyFromRootfs(rootfsKey)
}
func cleanupEmptySnapshotParents(mountPoint string) error {
dir := filepath.Dir(mountPoint)
for {
base := filepath.Base(dir)
if base == "." || base == string(filepath.Separator) || base == "snapshot" {
return nil
}
if filepath.Base(filepath.Dir(dir)) == "snapshot" {
return nil
}
err := os.Remove(dir)
if err == nil {
dir = filepath.Dir(dir)
continue
}
if os.IsNotExist(err) {
dir = filepath.Dir(dir)
continue
}
if errors.Is(err, syscall.ENOTEMPTY) || errors.Is(err, syscall.EEXIST) {
return nil
}
return err
}
}
func bootLayoutLabels(layout *BootLayout, labels map[string]string) map[string]string {
if labels == nil {
labels = make(map[string]string)
}
labels[common.SnapshotLabel] = "true"
labels[common.SnapshotLabelMemSize] = fmt.Sprintf("%d", layout.MemorySizeMB)
labels[common.SnapshotLabelRootfs] = layout.RootfsMount
labels[common.SnapshotLabelSnapshotDir] = layout.SnapshotDir
return labels
}