talm

Форк
0
/
system.go 
299 строк · 7.7 Кб
1
// This Source Code Form is subject to the terms of the Mozilla Public
2
// License, v. 2.0. If a copy of the MPL was not distributed with this
3
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4

5
package mount
6

7
import (
8
	"context"
9
	"errors"
10
	"fmt"
11
	"log"
12
	"os"
13
	"sync"
14

15
	"github.com/cosi-project/runtime/pkg/state"
16
	"github.com/siderolabs/gen/maps"
17
	"github.com/siderolabs/go-blockdevice/blockdevice"
18
	"github.com/siderolabs/go-blockdevice/blockdevice/filesystem"
19
	"golang.org/x/sys/unix"
20

21
	"github.com/aenix-io/talm/internal/app/machined/pkg/runtime"
22
	"github.com/aenix-io/talm/internal/app/machined/pkg/runtime/disk"
23
	"github.com/aenix-io/talm/internal/pkg/encryption"
24
	"github.com/aenix-io/talm/internal/pkg/partition"
25
	"github.com/siderolabs/talos/pkg/machinery/constants"
26
	runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime"
27
	"github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1"
28
)
29

30
var (
31
	mountpoints      = map[string]*Point{}
32
	mountpointsMutex sync.RWMutex
33
)
34

35
// SystemMountPointsForDevice returns the mountpoints required to boot the system.
36
// This function is called exclusively during installations ( both image
37
// creation and bare metall installs ). This is why we want to look up
38
// device by specified disk as well as why we don't want to grow any
39
// filesystems.
40
func SystemMountPointsForDevice(ctx context.Context, devpath string, opts ...Option) (mountpoints *Points, err error) {
41
	mountpoints = NewMountPoints()
42

43
	bd, err := blockdevice.Open(devpath)
44
	if err != nil {
45
		return nil, err
46
	}
47

48
	defer bd.Close() //nolint:errcheck
49

50
	for _, name := range []string{constants.EphemeralPartitionLabel, constants.BootPartitionLabel, constants.EFIPartitionLabel, constants.StatePartitionLabel} {
51
		mountpoint, err := SystemMountPointForLabel(ctx, bd, name, opts...)
52
		if err != nil {
53
			return nil, err
54
		}
55

56
		mountpoints.Set(name, mountpoint)
57
	}
58

59
	return mountpoints, nil
60
}
61

62
// SystemMountPointForLabel returns a mount point for the specified device and label.
63
//
64
//nolint:gocyclo
65
func SystemMountPointForLabel(ctx context.Context, device *blockdevice.BlockDevice, label string, opts ...Option) (mountpoint *Point, err error) {
66
	var target string
67

68
	switch label {
69
	case constants.EphemeralPartitionLabel:
70
		target = constants.EphemeralMountPoint
71
	case constants.BootPartitionLabel:
72
		target = constants.BootMountPoint
73
	case constants.EFIPartitionLabel:
74
		target = constants.EFIMountPoint
75
	case constants.StatePartitionLabel:
76
		target = constants.StateMountPoint
77
	default:
78
		return nil, fmt.Errorf("unknown label: %q", label)
79
	}
80

81
	part, err := device.GetPartition(label)
82
	if err != nil && !errors.Is(err, os.ErrNotExist) {
83
		return nil, err
84
	}
85

86
	if part == nil {
87
		// A boot partitition is not required.
88
		if label == constants.BootPartitionLabel {
89
			return nil, nil
90
		}
91

92
		return nil, fmt.Errorf("failed to find device with label %s: %w", label, err)
93
	}
94

95
	fsType, err := part.Filesystem()
96
	if err != nil {
97
		return nil, err
98
	}
99

100
	partPath, err := part.Path()
101
	if err != nil {
102
		return nil, err
103
	}
104

105
	o := NewDefaultOptions(opts...)
106

107
	preMountHooks := []Hook{}
108

109
	if o.Encryption != nil {
110
		encryptionHandler, err := encryption.NewHandler(
111
			device,
112
			part,
113
			o.Encryption,
114
			o.SystemInformationGetter,
115
		)
116
		if err != nil {
117
			return nil, err
118
		}
119

120
		preMountHooks = append(preMountHooks,
121
			func(p *Point) error {
122
				var (
123
					err  error
124
					path string
125
				)
126

127
				if path, err = encryptionHandler.Open(ctx); err != nil {
128
					return err
129
				}
130

131
				p.source = path
132

133
				return nil
134
			},
135
		)
136

137
		opts = append(opts,
138
			WithPostUnmountHooks(
139
				func(p *Point) error {
140
					return encryptionHandler.Close()
141
				},
142
			),
143
		)
144
	}
145

146
	// Format the partition if it does not have any filesystem
147
	preMountHooks = append(preMountHooks, func(p *Point) error {
148
		sb, err := filesystem.Probe(p.source)
149
		if err != nil {
150
			return err
151
		}
152

153
		p.fstype = ""
154

155
		// skip formatting the partition if filesystem is detected
156
		// and assign proper fs type to the mountpoint
157
		if sb != nil && sb.Type() != filesystem.Unknown {
158
			p.fstype = sb.Type()
159

160
			return nil
161
		}
162

163
		opts := partition.NewFormatOptions(part.Name)
164
		if opts == nil {
165
			return fmt.Errorf("failed to determine format options for partition label %s", part.Name)
166
		}
167

168
		if !o.MountFlags.Check(SkipIfNoFilesystem) {
169
			p.fstype = opts.FileSystemType
170

171
			return partition.Format(p.source, opts, log.Printf)
172
		}
173

174
		return nil
175
	})
176

177
	opts = append(opts, WithPreMountHooks(preMountHooks...))
178

179
	mountpoint = NewMountPoint(partPath, target, fsType, unix.MS_NOATIME, "", opts...)
180

181
	return mountpoint, nil
182
}
183

184
// SystemPartitionMount mounts a system partition by the label.
185
//
186
//nolint:gocyclo
187
func SystemPartitionMount(ctx context.Context, r runtime.Runtime, logger *log.Logger, label string, opts ...Option) (err error) {
188
	device := r.State().Machine().Disk(disk.WithPartitionLabel(label))
189
	if device == nil {
190
		return fmt.Errorf("failed to find device with partition labeled %s", label)
191
	}
192

193
	if r.Config() != nil && r.Config().Machine() != nil {
194
		encryptionConfig := r.Config().Machine().SystemDiskEncryption().Get(label)
195

196
		if encryptionConfig != nil {
197
			opts = append(opts,
198
				WithEncryptionConfig(encryptionConfig),
199
				WithSystemInformationGetter(r.GetSystemInformation),
200
			)
201
		}
202
	}
203

204
	opts = append(opts, WithLogger(logger))
205

206
	mountpoint, err := SystemMountPointForLabel(ctx, device.BlockDevice, label, opts...)
207
	if err != nil {
208
		return err
209
	}
210

211
	if mountpoint == nil {
212
		return fmt.Errorf("no mountpoints for label %q", label)
213
	}
214

215
	var skipMount bool
216

217
	if skipMount, err = mountMountpoint(mountpoint); err != nil {
218
		return err
219
	}
220

221
	if skipMount {
222
		if logger != nil {
223
			logger.Printf("mount skipped")
224
		}
225

226
		return
227
	}
228

229
	o := NewDefaultOptions(opts...)
230
	encrypted := o.Encryption != nil
231

232
	// record mount as the resource
233
	mountStatus := runtimeres.NewMountStatus(v1alpha1.NamespaceName, label)
234
	mountStatus.TypedSpec().Source = mountpoint.Source()
235
	mountStatus.TypedSpec().Target = mountpoint.Target()
236
	mountStatus.TypedSpec().FilesystemType = mountpoint.Fstype()
237
	mountStatus.TypedSpec().Encrypted = encrypted
238

239
	if encrypted {
240
		encryptionProviders := make(map[string]struct{})
241

242
		for _, cfg := range o.Encryption.Keys() {
243
			switch {
244
			case cfg.Static() != nil:
245
				encryptionProviders[cfg.Static().String()] = struct{}{}
246
			case cfg.NodeID() != nil:
247
				encryptionProviders[cfg.NodeID().String()] = struct{}{}
248
			case cfg.KMS() != nil:
249
				encryptionProviders[cfg.KMS().String()] = struct{}{}
250
			case cfg.TPM() != nil:
251
				encryptionProviders[cfg.TPM().String()] = struct{}{}
252
			}
253
		}
254

255
		mountStatus.TypedSpec().EncryptionProviders = maps.Keys(encryptionProviders)
256
	}
257

258
	// ignore the error if the MountStatus already exists, as many mounts are silently skipped with the flag SkipIfMounted
259
	if err = r.State().V1Alpha2().Resources().Create(context.Background(), mountStatus); err != nil && !state.IsConflictError(err) {
260
		return fmt.Errorf("error creating mount status resource: %w", err)
261
	}
262

263
	mountpointsMutex.Lock()
264
	defer mountpointsMutex.Unlock()
265

266
	mountpoints[label] = mountpoint
267

268
	return nil
269
}
270

271
// SystemPartitionUnmount unmounts a system partition by the label.
272
func SystemPartitionUnmount(r runtime.Runtime, logger *log.Logger, label string) (err error) {
273
	mountpointsMutex.RLock()
274
	mountpoint, ok := mountpoints[label]
275
	mountpointsMutex.RUnlock()
276

277
	if !ok {
278
		if logger != nil {
279
			logger.Printf("unmount skipped")
280
		}
281

282
		return nil
283
	}
284

285
	err = mountpoint.Unmount()
286
	if err != nil {
287
		return err
288
	}
289

290
	if err = r.State().V1Alpha2().Resources().Destroy(context.Background(), runtimeres.NewMountStatus(v1alpha1.NamespaceName, label).Metadata()); err != nil {
291
		return fmt.Errorf("error destroying mount status resource: %w", err)
292
	}
293

294
	mountpointsMutex.Lock()
295
	delete(mountpoints, label)
296
	mountpointsMutex.Unlock()
297

298
	return nil
299
}
300

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.