1
2
3
4 package devicemapper
5
6 import (
7 "fmt"
8 "os"
9 "path"
10 "syscall"
11 "time"
12 "unsafe"
13
14 "golang.org/x/sys/unix"
15
16 "github.com/Microsoft/hcsshim/internal/guest/linux"
17 )
18
19
20 type CreateFlags int
21
22 const (
23
24 CreateReadOnly CreateFlags = 1 << iota
25 )
26
27 var (
28 removeDeviceWrapper = removeDevice
29 openMapperWrapper = openMapper
30 )
31
32
33 const (
34 _DM_IOCTL = 0xfd
35 _DM_IOCTL_SIZE = 312
36 _DM_IOCTL_BASE = linux.IocWRBase | _DM_IOCTL<<linux.IocTypeShift | _DM_IOCTL_SIZE<<linux.IocSizeShift
37
38 _DM_READONLY_FLAG = 1 << 0
39 _DM_SUSPEND_FLAG = 1 << 1
40 _DM_PERSISTENT_DEV_FLAG = 1 << 3
41 )
42
43 const blockSize = 512
44
45
46 const (
47 _DM_VERSION = iota
48 _DM_REMOVE_ALL
49 _DM_LIST_DEVICES
50 _DM_DEV_CREATE
51 _DM_DEV_REMOVE
52 _DM_DEV_RENAME
53 _DM_DEV_SUSPEND
54 _DM_DEV_STATUS
55 _DM_DEV_WAIT
56 _DM_TABLE_LOAD
57 _DM_TABLE_CLEAR
58 _DM_TABLE_DEPS
59 _DM_TABLE_STATUS
60 )
61
62 var dmOpName = []string{
63 "version",
64 "remove all",
65 "list devices",
66 "device create",
67 "device remove",
68 "device rename",
69 "device suspend",
70 "device status",
71 "device wait",
72 "table load",
73 "table clear",
74 "table deps",
75 "table status",
76 }
77
78 type dmIoctl struct {
79 Version [3]uint32
80 DataSize uint32
81 DataStart uint32
82 TargetCount uint32
83 OpenCount int32
84 Flags uint32
85 EventNumber uint32
86 _ uint32
87 Dev uint64
88 Name [128]byte
89 UUID [129]byte
90 _ [7]byte
91 }
92
93 type targetSpec struct {
94 SectorStart int64
95 LengthInBlocks int64
96 Status int32
97 Next uint32
98 Type [16]byte
99 }
100
101
102
103 func initIoctl(d *dmIoctl, size int, name string) {
104 *d = dmIoctl{
105 Version: [3]uint32{4, 0, 0},
106 DataSize: uint32(size),
107 }
108 copy(d.Name[:], name)
109 }
110
111 type dmError struct {
112 Op int
113 Err error
114 }
115
116 func (err *dmError) Error() string {
117 op := "<bad operation>"
118 if err.Op < len(dmOpName) {
119 op = dmOpName[err.Op]
120 }
121 return "device-mapper " + op + ": " + err.Err.Error()
122 }
123
124
125 func devMapperIoctl(f *os.File, code int, data *dmIoctl) error {
126 if err := linux.Ioctl(f, code|_DM_IOCTL_BASE, unsafe.Pointer(data)); err != nil {
127 return &dmError{Op: code, Err: err}
128 }
129 return nil
130 }
131
132
133
134 func openMapper() (f *os.File, err error) {
135 f, err = os.OpenFile("/dev/mapper/control", os.O_RDWR, 0)
136 if err != nil {
137 return nil, err
138 }
139 defer func() {
140 if err != nil {
141 f.Close()
142 }
143 }()
144 var d dmIoctl
145 initIoctl(&d, int(unsafe.Sizeof(d)), "")
146 err = devMapperIoctl(f, _DM_VERSION, &d)
147 if err != nil {
148 return nil, err
149 }
150 return f, nil
151 }
152
153
154 type Target struct {
155 Type string
156 SectorStart int64
157 LengthInBlocks int64
158 Params string
159 }
160
161
162 func (t *Target) sizeof() int {
163
164
165 return (int(unsafe.Sizeof(targetSpec{})) + len(t.Params) + 1 + 7) &^ 7
166 }
167
168
169
170
171
172
173
174
175
176 func LinearTarget(sectorStart, lengthBlocks int64, path string, deviceStart int64) Target {
177 return Target{
178 Type: "linear",
179 SectorStart: sectorStart,
180 LengthInBlocks: lengthBlocks,
181 Params: fmt.Sprintf("%s %d", path, deviceStart),
182 }
183 }
184
185
186
187 func zeroSectorLinearTarget(lengthBytes int64, path string, deviceStartBytes int64) Target {
188 lengthInBlocks := lengthBytes / blockSize
189 startInBlocks := deviceStartBytes / blockSize
190 return LinearTarget(0, lengthInBlocks, path, startInBlocks)
191 }
192
193
194
195 func makeTableIoctl(name string, targets []Target) *dmIoctl {
196 off := int(unsafe.Sizeof(dmIoctl{}))
197 n := off
198 for _, t := range targets {
199 n += t.sizeof()
200 }
201 b := make([]byte, n)
202 d := (*dmIoctl)(unsafe.Pointer(&b[0]))
203 initIoctl(d, n, name)
204 d.DataStart = uint32(off)
205 d.TargetCount = uint32(len(targets))
206 for _, t := range targets {
207 spec := (*targetSpec)(unsafe.Pointer(&b[off]))
208 sn := t.sizeof()
209 spec.SectorStart = t.SectorStart
210 spec.LengthInBlocks = t.LengthInBlocks
211 spec.Next = uint32(sn)
212 copy(spec.Type[:], t.Type)
213 copy(b[off+int(unsafe.Sizeof(*spec)):], t.Params)
214 off += sn
215 }
216 return d
217 }
218
219
220
221 func CreateDevice(name string, flags CreateFlags, targets []Target) (_ string, err error) {
222 f, err := openMapperWrapper()
223 if err != nil {
224 return "", err
225 }
226 defer f.Close()
227
228 var d dmIoctl
229 size := int(unsafe.Sizeof(d))
230 initIoctl(&d, size, name)
231 err = devMapperIoctl(f, _DM_DEV_CREATE, &d)
232 if err != nil {
233 return "", err
234 }
235 defer func() {
236 if err != nil {
237 _ = removeDeviceWrapper(f, name)
238 }
239 }()
240
241 dev := int(d.Dev)
242
243 di := makeTableIoctl(name, targets)
244 if flags&CreateReadOnly != 0 {
245 di.Flags |= _DM_READONLY_FLAG
246 }
247 err = devMapperIoctl(f, _DM_TABLE_LOAD, di)
248 if err != nil {
249 return "", err
250 }
251 initIoctl(&d, size, name)
252 err = devMapperIoctl(f, _DM_DEV_SUSPEND, &d)
253 if err != nil {
254 return "", err
255 }
256
257 p := path.Join("/dev/mapper", name)
258 os.Remove(p)
259 err = unix.Mknod(p, unix.S_IFBLK|0600, dev)
260 if err != nil {
261 return "", nil
262 }
263
264 return p, nil
265 }
266
267
268 func RemoveDevice(name string) (err error) {
269 rm := func() error {
270 f, err := openMapperWrapper()
271 if err != nil {
272 return err
273 }
274 defer f.Close()
275 os.Remove(path.Join("/dev/mapper", name))
276 return removeDeviceWrapper(f, name)
277 }
278
279
280
281 for i := 0; i < 10; i++ {
282 if err = rm(); err != nil {
283 if e, ok := err.(*dmError); !ok || e.Err != syscall.EBUSY {
284 break
285 }
286 time.Sleep(10 * time.Millisecond)
287 continue
288 }
289 break
290 }
291 return err
292 }
293
294 func removeDevice(f *os.File, name string) error {
295 var d dmIoctl
296 initIoctl(&d, int(unsafe.Sizeof(d)), name)
297 err := devMapperIoctl(f, _DM_DEV_REMOVE, &d)
298 if err != nil {
299 return err
300 }
301 return nil
302 }
303
View as plain text