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
|
package container
import (
"errors"
"fmt"
"os"
"path/filepath"
. "syscall"
"hakurei.app/container/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"
// 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.
// This filesystem type is usually mounted on /proc.
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"
)
// bindMount mounts source on target and recursively applies flags if MS_REC is set.
func (p *procPaths) bindMount(source, target string, flags uintptr, eq bool) error {
if eq {
msg.Verbosef("resolved %q flags %#x", target, flags)
} else {
msg.Verbosef("resolved %q on %q flags %#x", source, target, flags)
}
if err := Mount(source, target, FstypeNULL, MS_SILENT|MS_BIND|flags&MS_REC, zeroString); err != nil {
return wrapErrSuffix(err,
fmt.Sprintf("cannot mount %q on %q:", source, target))
}
return p.remount(target, flags)
}
// remount applies flags on target, recursively if MS_REC is set.
func (p *procPaths) remount(target string, flags uintptr) error {
var targetFinal string
if v, err := filepath.EvalSymlinks(target); err != nil {
return wrapErrSelf(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 := IgnoringEINTR(func() (err error) {
destFd, err = Open(targetFinal, O_PATH|O_CLOEXEC, 0)
return
}); err != nil {
return wrapErrSuffix(err,
fmt.Sprintf("cannot open %q:", targetFinal))
}
if v, err := os.Readlink(p.fd(destFd)); err != nil {
return wrapErrSelf(err)
} else if err = Close(destFd); err != nil {
return wrapErrSuffix(err,
fmt.Sprintf("cannot close %q:", targetFinal))
} else {
targetKFinal = v
}
}
mf := MS_NOSUID | flags&MS_NODEV | flags&MS_RDONLY
return hostProc.mountinfo(func(d *vfs.MountInfoDecoder) error {
n, err := d.Unfold(targetKFinal)
if err != nil {
if errors.Is(err, ESTALE) {
return msg.WrapErr(err,
fmt.Sprintf("mount point %q never appeared in mountinfo", targetKFinal))
}
return wrapErrSuffix(err,
"cannot unfold mount hierarchy:")
}
if err = remountWithFlags(n, mf); err != nil {
return err
}
if flags&MS_REC == 0 {
return nil
}
for cur := range n.Collective() {
err = remountWithFlags(cur, mf)
if err != nil && !errors.Is(err, EACCES) {
return err
}
}
return nil
})
}
// remountWithFlags remounts mount point described by [vfs.MountInfoNode].
func remountWithFlags(n *vfs.MountInfoNode, mf uintptr) error {
kf, unmatched := n.Flags()
if len(unmatched) != 0 {
msg.Verbosef("unmatched vfs options: %q", unmatched)
}
if kf&mf != mf {
return wrapErrSuffix(
Mount(SourceNone, n.Clean, FstypeNULL, MS_SILENT|MS_BIND|MS_REMOUNT|kf|mf, zeroString),
fmt.Sprintf("cannot remount %q:", n.Clean))
}
return nil
}
func mountTmpfs(fsname, name string, flags uintptr, size int, perm os.FileMode) error {
target := toSysroot(name)
if err := os.MkdirAll(target, parentPerm(perm)); err != nil {
return wrapErrSelf(err)
}
opt := fmt.Sprintf("mode=%#o", perm)
if size > 0 {
opt += fmt.Sprintf(",size=%d", size)
}
return wrapErrSuffix(
Mount(fsname, target, FstypeTmpfs, flags, opt),
fmt.Sprintf("cannot mount tmpfs on %q:", name))
}
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)
}
|