Skip to content

Commit 976748e

Browse files
committed
libct: add mountViaFDs, simplify mount
1. Simplify mount call by removing the procfd argument, and use the new mount() where procfd is not used. Now, the mount() arguments are the same as for unix.Mount. 2. Introduce a new mountViaFDs function, which is similar to the old mount(), except it can take procfd for both source and target. The new arguments are called srcFD and dstFD. 3. Modify the mount error to show both srcFD and dstFD so it's clear which one is used for which purpose. This fixes the issue of having a somewhat cryptic errors like this: > mount /proc/self/fd/11:/sys/fs/cgroup/systemd (via /proc/self/fd/12), flags: 0x20502f: operation not permitted (in which fd 11 is actually the source, and fd 12 is the target). After this change, it looks like > mount src=/proc/self/fd/11, dst=/sys/fs/cgroup/systemd, dstFD=/proc/self/fd/12, flags=0x20502f: operation not permitted so it's clear that 12 is a destination fd. 4. Fix the mountViaFDs callers to use dstFD (rather than procfd) for the variable name. 5. Use srcFD where mountFd is set. Signed-off-by: Kir Kolyshkin <[email protected]>
1 parent 5a9266b commit 976748e

File tree

4 files changed

+81
-61
lines changed

4 files changed

+81
-61
lines changed

libcontainer/console_linux.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func mountConsole(slavePath string) error {
1818
if f != nil {
1919
f.Close()
2020
}
21-
return mount(slavePath, "/dev/console", "", "bind", unix.MS_BIND, "")
21+
return mount(slavePath, "/dev/console", "bind", unix.MS_BIND, "")
2222
}
2323

2424
// dupStdio opens the slavePath for the console and dups the fds to the current

libcontainer/container_linux.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,8 +1357,8 @@ func (c *Container) prepareCriuRestoreMounts(mounts []*configs.Mount) error {
13571357
// because during initial container creation mounts are
13581358
// set up in the order they are configured.
13591359
if m.Device == "bind" {
1360-
if err := utils.WithProcfd(c.config.Rootfs, m.Destination, func(procfd string) error {
1361-
if err := mount(m.Source, m.Destination, procfd, "", unix.MS_BIND|unix.MS_REC, ""); err != nil {
1360+
if err := utils.WithProcfd(c.config.Rootfs, m.Destination, func(dstFD string) error {
1361+
if err := mountViaFDs(m.Source, "", m.Destination, dstFD, "", unix.MS_BIND|unix.MS_REC, ""); err != nil {
13621362
return err
13631363
}
13641364
return nil
@@ -1411,7 +1411,7 @@ func (c *Container) Restore(process *Process, criuOpts *CriuOpts) error {
14111411
if err != nil {
14121412
return err
14131413
}
1414-
err = mount(c.config.Rootfs, root, "", "", unix.MS_BIND|unix.MS_REC, "")
1414+
err = mount(c.config.Rootfs, root, "", unix.MS_BIND|unix.MS_REC, "")
14151415
if err != nil {
14161416
return err
14171417
}

libcontainer/mount_linux.go

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import (
1010
type mountError struct {
1111
op string
1212
source string
13+
srcFD string
1314
target string
14-
procfd string
15+
dstFD string
1516
flags uintptr
1617
data string
1718
err error
@@ -22,19 +23,21 @@ func (e *mountError) Error() string {
2223
out := e.op + " "
2324

2425
if e.source != "" {
25-
out += e.source + ":" + e.target
26-
} else {
27-
out += e.target
26+
out += "src=" + e.source + ", "
27+
if e.srcFD != "" {
28+
out += "srcFD=" + e.srcFD + ", "
29+
}
2830
}
29-
if e.procfd != "" {
30-
out += " (via " + e.procfd + ")"
31+
out += "dst=" + e.target
32+
if e.dstFD != "" {
33+
out += ", dstFD=" + e.dstFD
3134
}
3235

3336
if e.flags != uintptr(0) {
34-
out += ", flags: 0x" + strconv.FormatUint(uint64(e.flags), 16)
37+
out += ", flags=0x" + strconv.FormatUint(uint64(e.flags), 16)
3538
}
3639
if e.data != "" {
37-
out += ", data: " + e.data
40+
out += ", data=" + e.data
3841
}
3942

4043
out += ": " + e.err.Error()
@@ -47,19 +50,36 @@ func (e *mountError) Unwrap() error {
4750
return e.err
4851
}
4952

50-
// mount is a simple unix.Mount wrapper. If procfd is not empty, it is used
51-
// instead of target (and the target is only used to add context to an error).
52-
func mount(source, target, procfd, fstype string, flags uintptr, data string) error {
53+
// mount is a simple unix.Mount wrapper, returning an error with more context
54+
// in case it failed.
55+
func mount(source, target, fstype string, flags uintptr, data string) error {
56+
return mountViaFDs(source, "", target, "", fstype, flags, data)
57+
}
58+
59+
// mountViaFDs is a unix.Mount wrapper which uses srcFD instead of source,
60+
// and dstFD instead of target, unless those are empty. The *FD arguments,
61+
// if non-empty, are expected to be in the form of a path to an opened file
62+
// descriptor on procfs (i.e. "/proc/self/fd/NN").
63+
//
64+
// If case an FD is used instead of a source or a target path, the
65+
// corresponding path is only used to add context to an error in case
66+
// the mount operation has failed.
67+
func mountViaFDs(source, srcFD, target, dstFD, fstype string, flags uintptr, data string) error {
68+
src := source
69+
if srcFD != "" {
70+
src = srcFD
71+
}
5372
dst := target
54-
if procfd != "" {
55-
dst = procfd
73+
if dstFD != "" {
74+
dst = dstFD
5675
}
57-
if err := unix.Mount(source, dst, fstype, flags, data); err != nil {
76+
if err := unix.Mount(src, dst, fstype, flags, data); err != nil {
5877
return &mountError{
5978
op: "mount",
6079
source: source,
80+
srcFD: srcFD,
6181
target: target,
62-
procfd: procfd,
82+
dstFD: dstFD,
6383
flags: flags,
6484
data: data,
6585
err: err,

libcontainer/rootfs_linux.go

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,10 @@ func prepareTmp(topTmpDir string) (string, error) {
187187
if err != nil {
188188
return "", err
189189
}
190-
if err := mount(tmpdir, tmpdir, "", "bind", unix.MS_BIND, ""); err != nil {
190+
if err := mount(tmpdir, tmpdir, "bind", unix.MS_BIND, ""); err != nil {
191191
return "", err
192192
}
193-
if err := mount("", tmpdir, "", "", uintptr(unix.MS_PRIVATE), ""); err != nil {
193+
if err := mount("", tmpdir, "", uintptr(unix.MS_PRIVATE), ""); err != nil {
194194
return "", err
195195
}
196196
return tmpdir, nil
@@ -262,7 +262,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
262262
if err := os.MkdirAll(subsystemPath, 0o755); err != nil {
263263
return err
264264
}
265-
if err := utils.WithProcfd(c.root, b.Destination, func(procfd string) error {
265+
if err := utils.WithProcfd(c.root, b.Destination, func(dstFD string) error {
266266
flags := defaultMountFlags
267267
if m.Flags&unix.MS_RDONLY != 0 {
268268
flags = flags | unix.MS_RDONLY
@@ -275,7 +275,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
275275
data = cgroups.CgroupNamePrefix + data
276276
source = "systemd"
277277
}
278-
return mount(source, b.Destination, procfd, "cgroup", uintptr(flags), data)
278+
return mountViaFDs(source, "", b.Destination, dstFD, "cgroup", uintptr(flags), data)
279279
}); err != nil {
280280
return err
281281
}
@@ -306,8 +306,8 @@ func mountCgroupV2(m *configs.Mount, c *mountConfig) error {
306306
if err := os.MkdirAll(dest, 0o755); err != nil {
307307
return err
308308
}
309-
err = utils.WithProcfd(c.root, m.Destination, func(procfd string) error {
310-
return mount(m.Source, m.Destination, procfd, "cgroup2", uintptr(m.Flags), m.Data)
309+
err = utils.WithProcfd(c.root, m.Destination, func(dstFD string) error {
310+
return mountViaFDs(m.Source, "", m.Destination, dstFD, "cgroup2", uintptr(m.Flags), m.Data)
311311
})
312312
if err == nil || !(errors.Is(err, unix.EPERM) || errors.Is(err, unix.EBUSY)) {
313313
return err
@@ -373,15 +373,15 @@ func doTmpfsCopyUp(m *configs.Mount, rootfs, mountLabel string) (Err error) {
373373
}
374374
}()
375375

376-
return utils.WithProcfd(rootfs, m.Destination, func(procfd string) (Err error) {
376+
return utils.WithProcfd(rootfs, m.Destination, func(dstFD string) (Err error) {
377377
// Copy the container data to the host tmpdir. We append "/" to force
378378
// CopyDirectory to resolve the symlink rather than trying to copy the
379379
// symlink itself.
380-
if err := fileutils.CopyDirectory(procfd+"/", tmpDir); err != nil {
381-
return fmt.Errorf("tmpcopyup: failed to copy %s to %s (%s): %w", m.Destination, procfd, tmpDir, err)
380+
if err := fileutils.CopyDirectory(dstFD+"/", tmpDir); err != nil {
381+
return fmt.Errorf("tmpcopyup: failed to copy %s to %s (%s): %w", m.Destination, dstFD, tmpDir, err)
382382
}
383383
// Now move the mount into the container.
384-
if err := mount(tmpDir, m.Destination, procfd, "", unix.MS_MOVE, ""); err != nil {
384+
if err := mountViaFDs(tmpDir, "", m.Destination, dstFD, "", unix.MS_MOVE, ""); err != nil {
385385
return fmt.Errorf("tmpcopyup: failed to move mount: %w", err)
386386
}
387387
return nil
@@ -688,8 +688,8 @@ func bindMountDeviceNode(rootfs, dest string, node *devices.Device) error {
688688
if f != nil {
689689
_ = f.Close()
690690
}
691-
return utils.WithProcfd(rootfs, dest, func(procfd string) error {
692-
return mount(node.Path, dest, procfd, "bind", unix.MS_BIND, "")
691+
return utils.WithProcfd(rootfs, dest, func(dstFD string) error {
692+
return mountViaFDs(node.Path, "", dest, dstFD, "bind", unix.MS_BIND, "")
693693
})
694694
}
695695

@@ -786,7 +786,7 @@ func rootfsParentMountPrivate(rootfs string) error {
786786
// shared. Secondly when we bind mount rootfs it will propagate to
787787
// parent namespace and we don't want that to happen.
788788
if sharedMount {
789-
return mount("", parentMount, "", "", unix.MS_PRIVATE, "")
789+
return mount("", parentMount, "", unix.MS_PRIVATE, "")
790790
}
791791

792792
return nil
@@ -797,7 +797,7 @@ func prepareRoot(config *configs.Config) error {
797797
if config.RootPropagation != 0 {
798798
flag = config.RootPropagation
799799
}
800-
if err := mount("", "/", "", "", uintptr(flag), ""); err != nil {
800+
if err := mount("", "/", "", uintptr(flag), ""); err != nil {
801801
return err
802802
}
803803

@@ -808,13 +808,13 @@ func prepareRoot(config *configs.Config) error {
808808
return err
809809
}
810810

811-
return mount(config.Rootfs, config.Rootfs, "", "bind", unix.MS_BIND|unix.MS_REC, "")
811+
return mount(config.Rootfs, config.Rootfs, "bind", unix.MS_BIND|unix.MS_REC, "")
812812
}
813813

814814
func setReadonly() error {
815815
flags := uintptr(unix.MS_BIND | unix.MS_REMOUNT | unix.MS_RDONLY)
816816

817-
err := mount("", "/", "", "", flags, "")
817+
err := mount("", "/", "", flags, "")
818818
if err == nil {
819819
return nil
820820
}
@@ -823,7 +823,7 @@ func setReadonly() error {
823823
return &os.PathError{Op: "statfs", Path: "/", Err: err}
824824
}
825825
flags |= uintptr(s.Flags)
826-
return mount("", "/", "", "", flags, "")
826+
return mount("", "/", "", flags, "")
827827
}
828828

829829
func setupPtmx(config *configs.Config) error {
@@ -881,7 +881,7 @@ func pivotRoot(rootfs string) error {
881881
// known to cause issues due to races where we still have a reference to a
882882
// mount while a process in the host namespace are trying to operate on
883883
// something they think has no mounts (devicemapper in particular).
884-
if err := mount("", ".", "", "", unix.MS_SLAVE|unix.MS_REC, ""); err != nil {
884+
if err := mount("", ".", "", unix.MS_SLAVE|unix.MS_REC, ""); err != nil {
885885
return err
886886
}
887887
// Perform the unmount. MNT_DETACH allows us to unmount /proc/self/cwd.
@@ -930,7 +930,7 @@ func msMoveRoot(rootfs string) error {
930930
for _, info := range mountinfos {
931931
p := info.Mountpoint
932932
// Be sure umount events are not propagated to the host.
933-
if err := mount("", p, "", "", unix.MS_SLAVE|unix.MS_REC, ""); err != nil {
933+
if err := mount("", p, "", unix.MS_SLAVE|unix.MS_REC, ""); err != nil {
934934
if errors.Is(err, unix.ENOENT) {
935935
// If the mountpoint doesn't exist that means that we've
936936
// already blasted away some parent directory of the mountpoint
@@ -945,15 +945,15 @@ func msMoveRoot(rootfs string) error {
945945
} else {
946946
// If we have not privileges for umounting (e.g. rootless), then
947947
// cover the path.
948-
if err := mount("tmpfs", p, "", "tmpfs", 0, ""); err != nil {
948+
if err := mount("tmpfs", p, "tmpfs", 0, ""); err != nil {
949949
return err
950950
}
951951
}
952952
}
953953
}
954954

955955
// Move the rootfs on top of "/" in our mount namespace.
956-
if err := mount(rootfs, "/", "", "", unix.MS_MOVE, ""); err != nil {
956+
if err := mount(rootfs, "/", "", unix.MS_MOVE, ""); err != nil {
957957
return err
958958
}
959959
return chroot()
@@ -991,7 +991,7 @@ func createIfNotExists(path string, isDir bool) error {
991991

992992
// readonlyPath will make a path read only.
993993
func readonlyPath(path string) error {
994-
if err := mount(path, path, "", "", unix.MS_BIND|unix.MS_REC, ""); err != nil {
994+
if err := mount(path, path, "", unix.MS_BIND|unix.MS_REC, ""); err != nil {
995995
if errors.Is(err, os.ErrNotExist) {
996996
return nil
997997
}
@@ -1004,7 +1004,7 @@ func readonlyPath(path string) error {
10041004
}
10051005
flags := uintptr(s.Flags) & (unix.MS_NOSUID | unix.MS_NODEV | unix.MS_NOEXEC)
10061006

1007-
if err := mount(path, path, "", "", flags|unix.MS_BIND|unix.MS_REMOUNT|unix.MS_RDONLY, ""); err != nil {
1007+
if err := mount(path, path, "", flags|unix.MS_BIND|unix.MS_REMOUNT|unix.MS_RDONLY, ""); err != nil {
10081008
return err
10091009
}
10101010

@@ -1025,7 +1025,7 @@ func remountReadonly(m *configs.Mount) error {
10251025
// nosuid, etc.). So, let's use that case so that we can do
10261026
// this re-mount without failing in a userns.
10271027
flags |= unix.MS_REMOUNT | unix.MS_BIND | unix.MS_RDONLY
1028-
if err := mount("", dest, "", "", uintptr(flags), ""); err != nil {
1028+
if err := mount("", dest, "", uintptr(flags), ""); err != nil {
10291029
if errors.Is(err, unix.EBUSY) {
10301030
time.Sleep(100 * time.Millisecond)
10311031
continue
@@ -1043,9 +1043,9 @@ func remountReadonly(m *configs.Mount) error {
10431043
// For files, maskPath bind mounts /dev/null over the top of the specified path.
10441044
// For directories, maskPath mounts read-only tmpfs over the top of the specified path.
10451045
func maskPath(path string, mountLabel string) error {
1046-
if err := mount("/dev/null", path, "", "", unix.MS_BIND, ""); err != nil && !errors.Is(err, os.ErrNotExist) {
1046+
if err := mount("/dev/null", path, "", unix.MS_BIND, ""); err != nil && !errors.Is(err, os.ErrNotExist) {
10471047
if errors.Is(err, unix.ENOTDIR) {
1048-
return mount("tmpfs", path, "", "tmpfs", unix.MS_RDONLY, label.FormatMountLabel("", mountLabel))
1048+
return mount("tmpfs", path, "tmpfs", unix.MS_RDONLY, label.FormatMountLabel("", mountLabel))
10491049
}
10501050
return err
10511051
}
@@ -1060,28 +1060,28 @@ func writeSystemProperty(key, value string) error {
10601060
}
10611061

10621062
func remount(m *configs.Mount, rootfs string, mountFd *int) error {
1063-
source := m.Source
1063+
srcFD := ""
10641064
if mountFd != nil {
1065-
source = "/proc/self/fd/" + strconv.Itoa(*mountFd)
1065+
srcFD = "/proc/self/fd/" + strconv.Itoa(*mountFd)
10661066
}
10671067

1068-
return utils.WithProcfd(rootfs, m.Destination, func(procfd string) error {
1068+
return utils.WithProcfd(rootfs, m.Destination, func(dstFD string) error {
10691069
flags := uintptr(m.Flags | unix.MS_REMOUNT)
1070-
err := mount(source, m.Destination, procfd, m.Device, flags, "")
1070+
err := mountViaFDs(m.Source, srcFD, m.Destination, dstFD, m.Device, flags, "")
10711071
if err == nil {
10721072
return nil
10731073
}
10741074
// Check if the source has ro flag...
10751075
var s unix.Statfs_t
1076-
if err := unix.Statfs(source, &s); err != nil {
1077-
return &os.PathError{Op: "statfs", Path: source, Err: err}
1076+
if err := unix.Statfs(m.Source, &s); err != nil {
1077+
return &os.PathError{Op: "statfs", Path: m.Source, Err: err}
10781078
}
10791079
if s.Flags&unix.MS_RDONLY != unix.MS_RDONLY {
10801080
return err
10811081
}
10821082
// ... and retry the mount with ro flag set.
10831083
flags |= unix.MS_RDONLY
1084-
return mount(source, m.Destination, procfd, m.Device, flags, "")
1084+
return mountViaFDs(m.Source, srcFD, m.Destination, dstFD, m.Device, flags, "")
10851085
})
10861086
}
10871087

@@ -1100,26 +1100,26 @@ func mountPropagate(m *configs.Mount, rootfs string, mountLabel string, mountFd
11001100
flags &= ^unix.MS_RDONLY
11011101
}
11021102

1103+
srcFD := ""
1104+
if mountFd != nil {
1105+
srcFD = "/proc/self/fd/" + strconv.Itoa(*mountFd)
1106+
}
1107+
11031108
// Because the destination is inside a container path which might be
11041109
// mutating underneath us, we verify that we are actually going to mount
11051110
// inside the container with WithProcfd() -- mounting through a procfd
11061111
// mounts on the target.
1107-
source := m.Source
1108-
if mountFd != nil {
1109-
source = "/proc/self/fd/" + strconv.Itoa(*mountFd)
1110-
}
1111-
1112-
if err := utils.WithProcfd(rootfs, m.Destination, func(procfd string) error {
1113-
return mount(source, m.Destination, procfd, m.Device, uintptr(flags), data)
1112+
if err := utils.WithProcfd(rootfs, m.Destination, func(dstFD string) error {
1113+
return mountViaFDs(m.Source, srcFD, m.Destination, dstFD, m.Device, uintptr(flags), data)
11141114
}); err != nil {
11151115
return err
11161116
}
11171117
// We have to apply mount propagation flags in a separate WithProcfd() call
11181118
// because the previous call invalidates the passed procfd -- the mount
11191119
// target needs to be re-opened.
1120-
if err := utils.WithProcfd(rootfs, m.Destination, func(procfd string) error {
1120+
if err := utils.WithProcfd(rootfs, m.Destination, func(dstFD string) error {
11211121
for _, pflag := range m.PropagationFlags {
1122-
if err := mount("", m.Destination, procfd, "", uintptr(pflag), ""); err != nil {
1122+
if err := mountViaFDs("", "", m.Destination, dstFD, "", uintptr(pflag), ""); err != nil {
11231123
return err
11241124
}
11251125
}

0 commit comments

Comments
 (0)