podman

Форк
0
272 строки · 8.1 Кб
1
//go:build freebsd
2
// +build freebsd
3

4
package chroot
5

6
import (
7
	"errors"
8
	"fmt"
9
	"io"
10
	"io/fs"
11
	"os"
12
	"os/exec"
13
	"path/filepath"
14
	"strings"
15
	"syscall"
16

17
	"github.com/containers/buildah/pkg/jail"
18
	"github.com/containers/storage/pkg/fileutils"
19
	"github.com/containers/storage/pkg/mount"
20
	"github.com/containers/storage/pkg/unshare"
21
	"github.com/opencontainers/runtime-spec/specs-go"
22
	"github.com/sirupsen/logrus"
23
	"golang.org/x/sys/unix"
24
)
25

26
var (
27
	rlimitsMap = map[string]int{
28
		"RLIMIT_AS":      unix.RLIMIT_AS,
29
		"RLIMIT_CORE":    unix.RLIMIT_CORE,
30
		"RLIMIT_CPU":     unix.RLIMIT_CPU,
31
		"RLIMIT_DATA":    unix.RLIMIT_DATA,
32
		"RLIMIT_FSIZE":   unix.RLIMIT_FSIZE,
33
		"RLIMIT_MEMLOCK": unix.RLIMIT_MEMLOCK,
34
		"RLIMIT_NOFILE":  unix.RLIMIT_NOFILE,
35
		"RLIMIT_NPROC":   unix.RLIMIT_NPROC,
36
		"RLIMIT_RSS":     unix.RLIMIT_RSS,
37
		"RLIMIT_STACK":   unix.RLIMIT_STACK,
38
	}
39
	rlimitsReverseMap = map[int]string{}
40
)
41

42
type runUsingChrootSubprocOptions struct {
43
	Spec       *specs.Spec
44
	BundlePath string
45
}
46

47
func setPlatformUnshareOptions(spec *specs.Spec, cmd *unshare.Cmd) error {
48
	return nil
49
}
50

51
func setContainerHostname(name string) {
52
	// On FreeBSD, we have to set this later when we create the
53
	// jail below in createPlatformContainer
54
}
55

56
func setSelinuxLabel(spec *specs.Spec) error {
57
	// Ignore this on FreeBSD
58
	return nil
59
}
60

61
func setApparmorProfile(spec *specs.Spec) error {
62
	// FreeBSD doesn't have apparmor`
63
	return nil
64
}
65

66
func setCapabilities(spec *specs.Spec, keepCaps ...string) error {
67
	// FreeBSD capabilities are nothing like Linux
68
	return nil
69
}
70

71
func makeRlimit(limit specs.POSIXRlimit) unix.Rlimit {
72
	return unix.Rlimit{Cur: int64(limit.Soft), Max: int64(limit.Hard)}
73
}
74

75
func createPlatformContainer(options runUsingChrootExecSubprocOptions) error {
76
	path := options.Spec.Root.Path
77
	jconf := jail.NewConfig()
78
	jconf.Set("name", filepath.Base(path)+"-chroot")
79
	jconf.Set("host.hostname", options.Spec.Hostname)
80
	jconf.Set("persist", false)
81
	jconf.Set("path", path)
82
	jconf.Set("ip4", jail.INHERIT)
83
	jconf.Set("ip6", jail.INHERIT)
84
	jconf.Set("allow.raw_sockets", true)
85
	jconf.Set("enforce_statfs", 1)
86
	_, err := jail.CreateAndAttach(jconf)
87
	if err != nil {
88
		return fmt.Errorf("creating jail: %w", err)
89
	}
90
	return nil
91
}
92

93
// logNamespaceDiagnostics knows which namespaces we want to create.
94
// Output debug messages when that differs from what we're being asked to do.
95
func logNamespaceDiagnostics(spec *specs.Spec) {
96
	// Nothing here for FreeBSD
97
}
98

99
func makeReadOnly(mntpoint string, flags uintptr) error {
100
	var fs unix.Statfs_t
101
	// Make sure it's read-only.
102
	if err := unix.Statfs(mntpoint, &fs); err != nil {
103
		return fmt.Errorf("checking if directory %q was bound read-only: %w", mntpoint, err)
104
	}
105
	return nil
106
}
107

108
func saveDir(spec *specs.Spec, path string) string {
109
	id := filepath.Base(spec.Root.Path)
110
	return filepath.Join(filepath.Dir(path), ".save-"+id)
111
}
112

113
func copyFile(source, dest string) error {
114
	in, err := os.Open(source)
115
	if err != nil {
116
		return err
117
	}
118
	defer in.Close()
119

120
	out, err := os.Create(dest)
121
	if err != nil {
122
		return err
123
	}
124
	defer out.Close()
125

126
	_, err = io.Copy(out, in)
127
	if err != nil {
128
		return err
129
	}
130
	return out.Close()
131
}
132

133
type rename struct {
134
	from, to string
135
}
136

137
// setupChrootBindMounts actually bind mounts things under the rootfs, and returns a
138
// callback that will clean up its work.
139
func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func() error, err error) {
140
	renames := []rename{}
141
	unmounts := []string{}
142
	removes := []string{}
143
	undoBinds = func() error {
144
		for _, r := range renames {
145
			if err2 := os.Rename(r.to, r.from); err2 != nil {
146
				logrus.Warnf("pkg/chroot: error renaming %q to %q: %v", r.to, r.from, err2)
147
				if err == nil {
148
					err = err2
149
				}
150
			}
151
		}
152
		for _, path := range unmounts {
153
			if err2 := mount.Unmount(path); err2 != nil {
154
				logrus.Warnf("pkg/chroot: error unmounting %q: %v", spec.Root.Path, err2)
155
				if err == nil {
156
					err = err2
157
				}
158
			}
159
		}
160
		for _, path := range removes {
161
			if err2 := os.Remove(path); err2 != nil {
162
				logrus.Warnf("pkg/chroot: error removing %q: %v", path, err2)
163
				if err == nil {
164
					err = err2
165
				}
166
			}
167
		}
168
		return err
169
	}
170

171
	// Now mount all of those things to be under the rootfs's location in this
172
	// mount namespace.
173
	for _, m := range spec.Mounts {
174
		// If the target is there, we can just mount it.
175
		var srcinfo os.FileInfo
176
		switch m.Type {
177
		case "nullfs":
178
			srcinfo, err = os.Stat(m.Source)
179
			if err != nil {
180
				return undoBinds, fmt.Errorf("examining %q for mounting in mount namespace: %w", m.Source, err)
181
			}
182
		}
183
		target := filepath.Join(spec.Root.Path, m.Destination)
184
		if err := fileutils.Exists(target); err != nil {
185
			// If the target can't be stat()ted, check the error.
186
			if !errors.Is(err, fs.ErrNotExist) {
187
				return undoBinds, fmt.Errorf("examining %q for mounting in mount namespace: %w", target, err)
188
			}
189
			// The target isn't there yet, so create it, and make a
190
			// note to remove it later.
191
			// XXX: This was copied from the linux version which supports bind mounting files.
192
			// Leaving it here since I plan to add this to FreeBSD's nullfs.
193
			if m.Type != "nullfs" || srcinfo.IsDir() {
194
				if err = os.MkdirAll(target, 0111); err != nil {
195
					return undoBinds, fmt.Errorf("creating mountpoint %q in mount namespace: %w", target, err)
196
				}
197
				removes = append(removes, target)
198
			} else {
199
				if err = os.MkdirAll(filepath.Dir(target), 0111); err != nil {
200
					return undoBinds, fmt.Errorf("ensuring parent of mountpoint %q (%q) is present in mount namespace: %w", target, filepath.Dir(target), err)
201
				}
202
				// Don't do this until we can support file mounts in nullfs
203
				/*var file *os.File
204
				if file, err = os.OpenFile(target, os.O_WRONLY|os.O_CREATE, 0); err != nil {
205
					return undoBinds, errors.Wrapf(err, "error creating mountpoint %q in mount namespace", target)
206
				}
207
				file.Close()
208
				removes = append(removes, target)*/
209
			}
210
		}
211
		logrus.Debugf("mount: %v", m)
212
		switch m.Type {
213
		case "nullfs":
214
			// Do the bind mount.
215
			if !srcinfo.IsDir() {
216
				logrus.Debugf("emulating file mount %q on %q", m.Source, target)
217
				err := fileutils.Exists(target)
218
				if err == nil {
219
					save := saveDir(spec, target)
220
					if err := fileutils.Exists(save); err != nil {
221
						if errors.Is(err, fs.ErrNotExist) {
222
							err = os.MkdirAll(save, 0111)
223
						}
224
						if err != nil {
225
							return undoBinds, fmt.Errorf("creating file mount save directory %q: %w", save, err)
226
						}
227
						removes = append(removes, save)
228
					}
229
					savePath := filepath.Join(save, filepath.Base(target))
230
					if err := fileutils.Exists(target); err == nil {
231
						logrus.Debugf("moving %q to %q", target, savePath)
232
						if err := os.Rename(target, savePath); err != nil {
233
							return undoBinds, fmt.Errorf("moving %q to %q: %w", target, savePath, err)
234
						}
235
						renames = append(renames, rename{
236
							from: target,
237
							to:   savePath,
238
						})
239
					}
240
				} else {
241
					removes = append(removes, target)
242
				}
243
				if err := copyFile(m.Source, target); err != nil {
244
					return undoBinds, fmt.Errorf("copying %q to %q: %w", m.Source, target, err)
245
				}
246
			} else {
247
				logrus.Debugf("bind mounting %q on %q", m.Destination, filepath.Join(spec.Root.Path, m.Destination))
248
				if err := mount.Mount(m.Source, target, "nullfs", strings.Join(m.Options, ",")); err != nil {
249
					return undoBinds, fmt.Errorf("bind mounting %q from host to %q in mount namespace (%q): %w", m.Source, m.Destination, target, err)
250
				}
251
				logrus.Debugf("bind mounted %q to %q", m.Source, target)
252
				unmounts = append(unmounts, target)
253
			}
254
		case "devfs", "fdescfs", "tmpfs":
255
			// Mount /dev, /dev/fd.
256
			if err := mount.Mount(m.Source, target, m.Type, strings.Join(m.Options, ",")); err != nil {
257
				return undoBinds, fmt.Errorf("mounting %q to %q in mount namespace (%q, %q): %w", m.Type, m.Destination, target, strings.Join(m.Options, ","), err)
258
			}
259
			logrus.Debugf("mounted a %q to %q", m.Type, target)
260
			unmounts = append(unmounts, target)
261
		}
262
	}
263
	return undoBinds, nil
264
}
265

266
// setPdeathsig sets a parent-death signal for the process
267
func setPdeathsig(cmd *exec.Cmd) {
268
	if cmd.SysProcAttr == nil {
269
		cmd.SysProcAttr = &syscall.SysProcAttr{}
270
	}
271
	cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL
272
}
273

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

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

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

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