forked from linuxkit/linuxkit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprovider_cdrom.go
170 lines (154 loc) · 4.71 KB
/
provider_cdrom.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
package main
import (
"fmt"
"io/ioutil"
"path"
"path/filepath"
"strings"
"syscall"
"github.com/diskfs/go-diskfs"
log "github.com/sirupsen/logrus"
)
const (
metadataFile = "meta-data"
userdataFile = "user-data"
userdataFallback = "config"
cdromDevs = "/dev/sr[0-9]*"
blockDevs = "/sys/class/block/*"
)
var (
userdataFiles = []string{userdataFile, userdataFallback}
)
// ProviderCDROM is the type implementing the Provider interface for CDROMs
// It looks for file called 'meta-data', 'user-data' or 'config' in the root
type ProviderCDROM struct {
device string
mountPoint string
err error
userdata, metadata []byte
}
// ListCDROMs lists all the cdroms in the system
func ListCDROMs() []Provider {
cdroms, err := filepath.Glob(cdromDevs)
if err != nil {
// Glob can only error on invalid pattern
panic(fmt.Sprintf("Invalid glob pattern: %s", cdromDevs))
}
log.Debugf("cdrom devices to be checked: %v", cdroms)
// get the devices that match the cloud-init spec
cidevs := FindCIs()
log.Debugf("CIDATA devices to be checked: %v", cidevs)
// merge the two, ensuring that the list is unique
cdroms = append(cidevs, cdroms...)
cdroms = uniqueString(cdroms)
log.Debugf("unique devices to be checked: %v", cdroms)
providers := []Provider{}
for _, device := range cdroms {
providers = append(providers, NewCDROM(device))
}
return providers
}
// FindCIs goes through all known devices. Returns any that are either fat32 or
// iso9660 and have a filesystem label "CIDATA" or "cidata", per the spec
// here https://github.com/canonical/cloud-init/blob/master/doc/rtd/topics/datasources/nocloud.rst
func FindCIs() []string {
devs, err := filepath.Glob(blockDevs)
log.Debugf("block devices found: %v", devs)
if err != nil {
// Glob can only error on invalid pattern
panic(fmt.Sprintf("Invalid glob pattern: %s", blockDevs))
}
foundDevices := []string{}
for _, device := range devs {
// get the base device name
dev := filepath.Base(device)
// ignore loop and ram devices
if strings.HasPrefix(dev, "loop") || strings.HasPrefix(dev, "ram") {
log.Debugf("ignoring loop or ram device: %s", dev)
continue
}
dev = fmt.Sprintf("/dev/%s", dev)
log.Debugf("checking device: %s", dev)
// open readonly, ignore errors
disk, err := diskfs.OpenWithMode(dev, diskfs.ReadOnly)
if err != nil {
log.Debugf("failed to open device read-only: %s: %v", dev, err)
continue
}
fs, err := disk.GetFilesystem(0)
if err != nil {
log.Debugf("failed to get filesystem on partition 0 for device: %s: %v", dev, err)
continue
}
// get the label
label := strings.TrimSpace(fs.Label())
log.Debugf("found trimmed filesystem label for device: %s: '%s'", dev, label)
if label == "cidata" || label == "CIDATA" {
log.Debugf("adding device: %s", dev)
foundDevices = append(foundDevices, dev)
}
}
return foundDevices
}
// NewCDROM returns a new ProviderCDROM
func NewCDROM(device string) *ProviderCDROM {
mountPoint, err := ioutil.TempDir("", "cd")
p := ProviderCDROM{device, mountPoint, err, []byte{}, []byte{}}
if err == nil {
if p.err = p.mount(); p.err == nil {
// read the userdata - we read the spec file and the fallback, but eventually
// will remove the fallback
for _, f := range userdataFiles {
userdata, err := ioutil.ReadFile(path.Join(p.mountPoint, f))
// did we find a file?
if err == nil && userdata != nil {
p.userdata = userdata
break
}
}
if p.userdata == nil {
p.err = fmt.Errorf("no userdata file found at any of %v", userdataFiles)
}
// read the metadata
metadata, err := ioutil.ReadFile(path.Join(p.mountPoint, metadataFile))
// did we find a file?
if err == nil && metadata != nil {
p.metadata = metadata
}
p.unmount()
}
}
return &p
}
func (p *ProviderCDROM) String() string {
return "CDROM " + p.device
}
// Probe checks if the CD has the right file
func (p *ProviderCDROM) Probe() bool {
return len(p.userdata) != 0
}
// Extract gets both the CDROM specific and generic userdata
func (p *ProviderCDROM) Extract() ([]byte, error) {
return p.userdata, p.err
}
// mount mounts a CDROM/DVD device under mountPoint
func (p *ProviderCDROM) mount() error {
// We may need to poll a little for device ready
return syscall.Mount(p.device, p.mountPoint, "iso9660", syscall.MS_RDONLY, "")
}
// unmount removes the mount
func (p *ProviderCDROM) unmount() {
_ = syscall.Unmount(p.mountPoint, 0)
}
// uniqueString returns a unique subset of the string slice provided.
func uniqueString(input []string) []string {
u := make([]string, 0, len(input))
m := make(map[string]bool)
for _, val := range input {
if _, ok := m[val]; !ok {
m[val] = true
u = append(u, val)
}
}
return u
}