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
|
package container
import (
"errors"
"fmt"
"os"
. "syscall"
"hakurei.app/ext"
"hakurei.app/message"
"hakurei.app/vfs"
)
/*
Holding CAP_SYS_ADMIN within the user namespace that owns a process's mount namespace
allows that process to create bind mounts and mount the following types of filesystems:
- /proc (since Linux 3.8)
- /sys (since Linux 3.8)
- devpts (since Linux 3.9)
- tmpfs(5) (since Linux 3.9)
- ramfs (since Linux 3.9)
- mqueue (since Linux 3.9)
- bpf (since Linux 4.4)
- overlayfs (since Linux 5.11)
*/
const (
// zeroString is a zero value string, it represents NULL when passed to mount.
zeroString = ""
// SourceNone is used when the source value is ignored,
// such as when remounting.
SourceNone = "none"
// SourceProc is used when mounting proc.
// Note that any source value is allowed when fstype is [FstypeProc].
SourceProc = "proc"
// SourceDevpts is used when mounting devpts.
// Note that any source value is allowed when fstype is [FstypeDevpts].
SourceDevpts = "devpts"
// SourceMqueue is used when mounting mqueue.
// Note that any source value is allowed when fstype is [FstypeMqueue].
SourceMqueue = "mqueue"
// SourceOverlay is used when mounting overlay.
// Note that any source value is allowed when fstype is [FstypeOverlay].
SourceOverlay = "overlay"
// SourceTmpfs is used when mounting tmpfs.
SourceTmpfs = "tmpfs"
// SourceTmpfsRootfs is used when mounting the tmpfs instance backing the intermediate root.
SourceTmpfsRootfs = "rootfs"
// SourceTmpfsDevtmpfs is used when mounting tmpfs representing a subset of host devtmpfs.
SourceTmpfsDevtmpfs = "devtmpfs"
// SourceTmpfsEphemeral is used when mounting a writable instance of tmpfs.
SourceTmpfsEphemeral = "ephemeral"
// SourceTmpfsReadonly is used when mounting a readonly instance of tmpfs.
SourceTmpfsReadonly = "readonly"
// FstypeNULL is used when the fstype value is ignored,
// such as when bind mounting or remounting.
FstypeNULL = zeroString
// FstypeProc represents the proc pseudo-filesystem.
// A fully visible instance of proc must be available in the mount namespace for proc to be mounted.
FstypeProc = "proc"
// FstypeDevpts represents the devpts pseudo-filesystem.
// This type of filesystem is usually mounted on /dev/pts.
FstypeDevpts = "devpts"
// FstypeTmpfs represents the tmpfs filesystem.
// This filesystem type can be mounted anywhere in the container filesystem.
FstypeTmpfs = "tmpfs"
// FstypeMqueue represents the mqueue pseudo-filesystem.
// This filesystem type is usually mounted on /dev/mqueue.
FstypeMqueue = "mqueue"
// FstypeOverlay represents the overlay pseudo-filesystem.
// This filesystem type can be mounted anywhere in the container filesystem.
FstypeOverlay = "overlay"
// OptionOverlayLowerdir represents the lowerdir option of the overlay pseudo-filesystem.
// Any filesystem, does not need to be on a writable filesystem.
OptionOverlayLowerdir = "lowerdir"
// OptionOverlayUpperdir represents the upperdir option of the overlay pseudo-filesystem.
// The upperdir is normally on a writable filesystem.
OptionOverlayUpperdir = "upperdir"
// OptionOverlayWorkdir represents the workdir option of the overlay pseudo-filesystem.
// The workdir needs to be an empty directory on the same filesystem as upperdir.
OptionOverlayWorkdir = "workdir"
// OptionOverlayUserxattr represents the userxattr option of the overlay pseudo-filesystem.
// Use the "user.overlay." xattr namespace instead of "trusted.overlay.".
OptionOverlayUserxattr = "userxattr"
)
// bindMount mounts source on target and recursively applies flags if MS_REC is set.
func (p *procPaths) bindMount(msg message.Msg, source, target string, flags uintptr) error {
// syscallDispatcher.bindMount and procPaths.remount must not be called from this function
if err := p.k.mount(source, target, FstypeNULL, MS_SILENT|MS_BIND|flags&MS_REC, zeroString); err != nil {
return err
}
return p.k.remount(msg, target, flags)
}
// remount applies flags on target, recursively if MS_REC is set.
func (p *procPaths) remount(msg message.Msg, target string, flags uintptr) error {
// syscallDispatcher methods bindMount, remount must not be called from this function
var targetFinal string
if v, err := p.k.evalSymlinks(target); err != nil {
return err
} else {
targetFinal = v
if targetFinal != target {
msg.Verbosef("target resolves to %q", targetFinal)
}
}
// final target path according to the kernel through proc
var targetKFinal string
{
var destFd int
if err := ext.IgnoringEINTR(func() (err error) {
destFd, err = p.k.open(targetFinal, O_PATH|O_CLOEXEC, 0)
return
}); err != nil {
return &os.PathError{Op: "open", Path: targetFinal, Err: err}
}
if v, err := p.k.readlink(p.fd(destFd)); err != nil {
return err
} else if err = p.k.close(destFd); err != nil {
return &os.PathError{Op: "close", Path: targetFinal, Err: err}
} else {
targetKFinal = v
}
}
mf := MS_NOSUID | flags&MS_NODEV | flags&MS_RDONLY
return p.mountinfo(func(d *vfs.MountInfoDecoder) error {
n, err := d.Unfold(targetKFinal)
if err != nil {
return err
}
if err = remountWithFlags(p.k, msg, n, mf); err != nil {
return err
}
if flags&MS_REC == 0 {
return nil
}
for cur := range n.Collective() {
// avoid remounting twice
if cur == n {
continue
}
if err = remountWithFlags(p.k, msg, cur, mf); err != nil && !errors.Is(err, EACCES) {
return err
}
}
return nil
})
}
// remountWithFlags remounts mount point described by [vfs.MountInfoNode].
func remountWithFlags(k syscallDispatcher, msg message.Msg, n *vfs.MountInfoNode, mf uintptr) error {
// syscallDispatcher methods bindMount, remount must not be called from this function
kf, unmatched := n.Flags()
if len(unmatched) != 0 {
msg.Verbosef("unmatched vfs options: %q", unmatched)
}
if kf&mf != mf {
return k.mount(SourceNone, n.Clean, FstypeNULL, MS_SILENT|MS_BIND|MS_REMOUNT|kf|mf, zeroString)
}
return nil
}
// mountTmpfs mounts tmpfs on target;
// callers who wish to mount to sysroot must pass the return value of toSysroot.
func mountTmpfs(k syscallDispatcher, fsname, target string, flags uintptr, size int, perm os.FileMode) error {
// syscallDispatcher.mountTmpfs must not be called from this function
if err := k.mkdirAll(target, parentPerm(perm)); err != nil {
return err
}
opt := fmt.Sprintf("mode=%#o", perm)
if size > 0 {
opt += fmt.Sprintf(",size=%d", size)
}
return k.mount(fsname, target, FstypeTmpfs, flags, opt)
}
func parentPerm(perm os.FileMode) os.FileMode {
pperm := 0755
if perm&0070 == 0 {
pperm &= ^0050
}
if perm&0007 == 0 {
pperm &= ^0005
}
return os.FileMode(pperm)
}
|