This repository has been archived by the owner on Mar 22, 2023. It is now read-only.
/
formats.go
229 lines (195 loc) · 6.16 KB
/
formats.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
package vdisk
/**
* SPDX-License-Identifier: Apache-2.0
* Copyright 2020 vorteil.io Pty Ltd
*/
import (
"context"
"encoding/json"
"fmt"
"io"
"sort"
"strings"
"github.com/vorteil/vorteil/pkg/elog"
"github.com/vorteil/vorteil/pkg/vcfg"
"github.com/vorteil/vorteil/pkg/vimg"
)
// Format is a string representing a supported disk image format.
type Format string
// Supported disk image formats.
const (
// RAWFormat is a disk type that returns "raw"
RAWFormat Format = "raw"
// VMDKFormat is a disk type that returns "vmdk"
VMDKFormat Format = "vmdk"
// VMDKSparseFormat is a disk type that returns "vmdk-sparse"
VMDKSparseFormat Format = "vmdk-sparse"
// VMDKStreamOptimizedFormat is a disk type that returns "vmdk-stream-optimized"
VMDKStreamOptimizedFormat Format = "vmdk-stream-optimized"
// GCPFArchiveFormat is a disk type that returns "gcp"
GCPFArchiveFormat Format = "gcp"
// XVAFormat is a disk type that returns "xva"
XVAFormat Format = "xva"
// VHDFormat is a disk type that returns "vhd"
VHDFormat Format = "vhd"
// VHDFixedFormat is a disk type that returns "vhd-fixed"
VHDFixedFormat Format = "vhd-fixed"
// VHDDynamicFormat is a disk type that returns "vhd-dynamic"
VHDDynamicFormat Format = "vhd-dynamic"
// QCOW2Format is a disk type that returns "qcow2"
QCOW2Format Format = "qcow2"
)
// AllFormatStrings returns a list of all supported disk image formats.
func AllFormatStrings() []string {
strs := make([]string, len(formats))
i := 0
for k := range formats {
strs[i] = k.String()
i++
}
sort.Strings(strs)
return strs
}
var (
formats = map[Format]string{
RAWFormat: ".raw",
VMDKFormat: ".vmdk",
VMDKSparseFormat: ".vmdk",
VMDKStreamOptimizedFormat: ".vmdk",
GCPFArchiveFormat: ".tar.gz",
XVAFormat: ".xva",
VHDFormat: ".vhd",
VHDFixedFormat: ".vhd",
VHDDynamicFormat: ".vhd",
QCOW2Format: ".qcow2",
}
alignments = map[Format]int64{
RAWFormat: 0x200000,
VMDKFormat: 0x200000,
VMDKSparseFormat: 0x200000,
VMDKStreamOptimizedFormat: 0x200000,
GCPFArchiveFormat: 0x40000000,
XVAFormat: 0x200000,
VHDFormat: 0x200000,
VHDFixedFormat: 0x200000,
VHDDynamicFormat: 0x200000,
QCOW2Format: 0x200000,
}
defaultMTUs = map[Format]uint{
RAWFormat: 1500,
VMDKFormat: 1500,
VMDKSparseFormat: 1500,
VMDKStreamOptimizedFormat: 1500,
GCPFArchiveFormat: 1460,
XVAFormat: 1500,
VHDFormat: 1500,
VHDFixedFormat: 1500,
VHDDynamicFormat: 1500,
QCOW2Format: 1500,
}
buildFuncs = map[Format]BuildWriterInstantiator{
RAWFormat: buildRAW,
VMDKFormat: buildSparseVMDK,
VMDKSparseFormat: buildSparseVMDK,
VMDKStreamOptimizedFormat: buildStreamOptimizedVMDK,
GCPFArchiveFormat: buildGCPArchive,
XVAFormat: buildXVA,
VHDFormat: buildFixedVHD,
VHDFixedFormat: buildFixedVHD,
VHDDynamicFormat: buildDynamicVHD,
QCOW2Format: buildQCOW2,
}
)
// BuildWriterInstantiator is a function that returns a new io.WriteSeeker that
// can be used to handle the writing of a raw image.
type BuildWriterInstantiator func(io.WriteSeeker, *vimg.Builder, *vcfg.VCFG) (io.WriteSeeker, error)
// RegisterNewDiskFormat registers a new disk format that can be used with the vdisk package.
// Example: RegisterNewDiskFormat(Format("vmdk-custom"), ".vmdk", 0x200000, 1500, customVMDKBuilder)
func RegisterNewDiskFormat(format Format, extention string, alignment int64, mtu uint, builderFunc BuildWriterInstantiator) error {
if _, exists := formats[format]; exists {
return fmt.Errorf("refusing to register disk format '%s': already registered", format)
}
formats[format] = extention
alignments[format] = alignment
defaultMTUs[format] = mtu
buildFuncs[format] = builderFunc
return nil
}
// String returns a string representation of the Format.
func (x Format) String() string {
return string(x)
}
// MarshalText implements encoding.TextMarshaler.
func (x Format) MarshalText() (text []byte, err error) {
return []byte(x.String()), nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (x *Format) UnmarshalText(text []byte) error {
var err error
*x, err = ParseFormat(string(text))
if err != nil {
return err
}
return nil
}
// MarshalJSON implements json.Marshaler.
func (x Format) MarshalJSON() ([]byte, error) {
return json.Marshal(x.String())
}
// UnmarshalJSON implements json.Unmarshaler.
func (x *Format) UnmarshalJSON(data []byte) error {
s := string(data)
s = strings.Trim(s, "\"")
var err error
*x, err = ParseFormat(s)
if err != nil {
return err
}
return nil
}
// ParseFormat resolves a string into a Format.
func ParseFormat(s string) (Format, error) {
if s == "" {
return RAWFormat, nil
}
original := s
s = strings.TrimSpace(s)
s = strings.ToLower(s)
f := Format(s)
if _, ok := formats[f]; !ok {
return RAWFormat, fmt.Errorf("unrecognized virtual disk format '%s'", original)
}
return f, nil
}
// Suffix returns an appropriate file extension for files containing the format.
func (x *Format) Suffix() string {
return formats[*x]
}
// Alignment returns a size in bytes that a RAW image must be aligned to (an
// integer multiple of) to be compatible with the virtual disk image format.
func (x *Format) Alignment() int64 {
return alignments[*x]
}
// DefaultMTU returns the default MTU setting for the image format.
func (x *Format) DefaultMTU() uint {
return defaultMTUs[*x]
}
// Build creates the disk for the correct format ...
func (x *Format) Build(ctx context.Context, log elog.View, w io.WriteSeeker, b *vimg.Builder, cfg *vcfg.VCFG) error {
p := log.NewProgress(fmt.Sprintf("Initializing %s image file", x), "", 0)
defer p.Finish(false)
w, err := buildFuncs[*x](w, b, cfg)
if err != nil {
return err
}
closer, ok := w.(io.Closer)
if ok {
defer closer.Close()
}
p.Finish(true)
err = b.Build(ctx, w)
if err != nil {
return err
}
return nil
}