Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
lima-vm
GitHub Repository: lima-vm/lima
Path: blob/master/pkg/instance/clone.go
2611 views
1
// SPDX-FileCopyrightText: Copyright The Lima Authors
2
// SPDX-License-Identifier: Apache-2.0
3
4
package instance
5
6
import (
7
"context"
8
"errors"
9
"fmt"
10
"io/fs"
11
"os"
12
"path/filepath"
13
"slices"
14
"strings"
15
16
continuityfs "github.com/containerd/continuity/fs"
17
18
"github.com/lima-vm/lima/v2/pkg/limatype"
19
"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
20
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
21
"github.com/lima-vm/lima/v2/pkg/osutil"
22
"github.com/lima-vm/lima/v2/pkg/store"
23
)
24
25
func CloneOrRename(ctx context.Context, oldInst *limatype.Instance, newInstName string, rename bool) (*limatype.Instance, error) {
26
verb := "clone"
27
if rename {
28
verb = "rename"
29
}
30
if newInstName == "" {
31
return nil, errors.New("got empty instName")
32
}
33
if oldInst.Name == newInstName {
34
return nil, fmt.Errorf("new instance name %q must be different from %q", newInstName, oldInst.Name)
35
}
36
if oldInst.Status == limatype.StatusRunning {
37
return nil, errors.New("cannot " + verb + " a running instance")
38
}
39
40
newInstDir, err := dirnames.InstanceDir(newInstName)
41
if err != nil {
42
return nil, err
43
}
44
45
if _, err = os.Stat(newInstDir); !errors.Is(err, fs.ErrNotExist) {
46
return nil, fmt.Errorf("instance %q already exists", newInstName)
47
}
48
49
// the full path of the socket name must be less than UNIX_PATH_MAX chars.
50
maxSockName := filepath.Join(newInstDir, filenames.LongestSock)
51
if len(maxSockName) >= osutil.UnixPathMax {
52
return nil, fmt.Errorf("instance name %q too long: %q must be less than UNIX_PATH_MAX=%d characters, but is %d",
53
newInstName, maxSockName, osutil.UnixPathMax, len(maxSockName))
54
}
55
56
if err = os.Mkdir(newInstDir, 0o700); err != nil {
57
return nil, err
58
}
59
60
walkDirFn := func(path string, d fs.DirEntry, err error) error {
61
base := filepath.Base(path)
62
if slices.Contains(filenames.SkipOnClone, base) {
63
return nil
64
}
65
for _, ext := range filenames.TmpFileSuffixes {
66
if strings.HasSuffix(path, ext) {
67
return nil
68
}
69
}
70
if err != nil {
71
return err
72
}
73
pathRel, err := filepath.Rel(oldInst.Dir, path)
74
if err != nil {
75
return err
76
}
77
dst := filepath.Join(newInstDir, pathRel)
78
if d.IsDir() {
79
return os.MkdirAll(dst, d.Type().Perm())
80
}
81
// NullifyOnClone contains VzIdentifier.
82
// VzIdentifier file must not be just removed here, as pkg/limayaml depends on
83
// the existence of VzIdentifier for resolving the VM type.
84
if slices.Contains(filenames.NullifyOnClone, base) {
85
return os.WriteFile(dst, nil, 0o666)
86
}
87
if rename {
88
return os.Rename(path, dst)
89
}
90
// CopyFile attempts copy-on-write when supported by the filesystem
91
return continuityfs.CopyFile(dst, path)
92
}
93
94
if err = filepath.WalkDir(oldInst.Dir, walkDirFn); err != nil {
95
return nil, err
96
}
97
if rename {
98
if err = os.RemoveAll(oldInst.Dir); err != nil {
99
return nil, err
100
}
101
}
102
return store.Inspect(ctx, newInstName)
103
}
104
105