...
1
2
3
4
19
20 package loopback
21
22 import (
23 "bytes"
24 "errors"
25 "fmt"
26 "os"
27 "os/exec"
28 "strings"
29 "syscall"
30
31 "github.com/sirupsen/logrus"
32 )
33
34
35 func New(size int64) (*Loopback, error) {
36
37 file, err := os.CreateTemp("", "containerd-test-loopback")
38 if err != nil {
39 return nil, fmt.Errorf("could not create temporary file for loopback: %w", err)
40 }
41
42 if err := file.Truncate(size); err != nil {
43 file.Close()
44 os.Remove(file.Name())
45 return nil, fmt.Errorf("failed to resize temp file: %w", err)
46 }
47 file.Close()
48
49
50 losetup := exec.Command("losetup", "--find", "--show", file.Name())
51 var stdout, stderr bytes.Buffer
52 losetup.Stdout = &stdout
53 losetup.Stderr = &stderr
54 if err := losetup.Run(); err != nil {
55 os.Remove(file.Name())
56 return nil, fmt.Errorf("loopback setup failed (%v): stdout=%q, stderr=%q: %w", losetup.Args, stdout.String(), stderr.String(), err)
57 }
58
59 deviceName := strings.TrimSpace(stdout.String())
60 logrus.Debugf("Created loop device %s (using %s)", deviceName, file.Name())
61
62 cleanup := func() error {
63
64 logrus.Debugf("Removing loop device %s", deviceName)
65 losetup := exec.Command("losetup", "--detach", deviceName)
66 if out, err := losetup.CombinedOutput(); err != nil {
67 return fmt.Errorf("Could not remove loop device %s (%v): %q: %w", deviceName, losetup.Args, string(out), err)
68 }
69
70
71 logrus.Debugf("Removing temporary file %s", file.Name())
72 return os.Remove(file.Name())
73 }
74
75 l := Loopback{
76 File: file.Name(),
77 Device: deviceName,
78 close: cleanup,
79 }
80 return &l, nil
81 }
82
83
84 type Loopback struct {
85
86 File string
87
88 Device string
89 close func() error
90 }
91
92
93 func (l *Loopback) SoftSize() (int64, error) {
94 st, err := os.Stat(l.File)
95 if err != nil {
96 return 0, err
97 }
98 return st.Size(), nil
99 }
100
101
102 func (l *Loopback) HardSize() (int64, error) {
103 st, err := os.Stat(l.File)
104 if err != nil {
105 return 0, err
106 }
107 st2, ok := st.Sys().(*syscall.Stat_t)
108 if !ok {
109 return 0, errors.New("st.Sys() is not a *syscall.Stat_t")
110 }
111
112 return st2.Blocks * 512, nil
113 }
114
115
116 func (l *Loopback) Close() error {
117 return l.close()
118 }
119
View as plain text