Skip to content

Commit c12b091

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 6e2b46e commit c12b091

File tree

4 files changed

+82
-62
lines changed

4 files changed

+82
-62
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
@@ -1354,8 +1354,8 @@ func (c *Container) prepareCriuRestoreMounts(mounts []*configs.Mount) error {
13541354
// because during initial container creation mounts are
13551355
// set up in the order they are configured.
13561356
if m.Device == "bind" {
1357-
if err := utils.WithProcfd(c.config.Rootfs, m.Destination, func(procfd string) error {
1358-
if err := mount(m.Source, m.Destination, procfd, "", unix.MS_BIND|unix.MS_REC, ""); err != nil {
1357+
if err := utils.WithProcfd(c.config.Rootfs, m.Destination, func(dstFD string) error {
1358+
if err := mountViaFDs(m.Source, "", m.Destination, dstFD, "", unix.MS_BIND|unix.MS_REC, ""); err != nil {
13591359
return err
13601360
}
13611361
return nil
@@ -1408,7 +1408,7 @@ func (c *Container) Restore(process *Process, criuOpts *CriuOpts) error {
14081408
if err != nil {
14091409
return err
14101410
}
1411-
err = mount(c.config.Rootfs, root, "", "", unix.MS_BIND|unix.MS_REC, "")
1411+
err = mount(c.config.Rootfs, root, "", unix.MS_BIND|unix.MS_REC, "")
14121412
if err != nil {
14131413
return err
14141414
}

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: 43 additions & 43 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-
return utils.WithProcfd(c.root, m.Destination, func(procfd string) error {
310-
if err := mount(m.Source, m.Destination, procfd, "cgroup2", uintptr(m.Flags), m.Data); err != nil {
309+
return utils.WithProcfd(c.root, m.Destination, func(dstFD string) error {
310+
if err := mountViaFDs(m.Source, "", m.Destination, dstFD, "cgroup2", uintptr(m.Flags), m.Data); err != nil {
311311
// when we are in UserNS but CgroupNS is not unshared, we cannot mount cgroup2 (#2158)
312312
if errors.Is(err, unix.EPERM) || errors.Is(err, unix.EBUSY) {
313313
src := fs2.UnifiedMountpoint
@@ -317,7 +317,7 @@ func mountCgroupV2(m *configs.Mount, c *mountConfig) error {
317317
// the whole /sys/fs/cgroup.
318318
src = c.cgroup2Path
319319
}
320-
err = mount(src, m.Destination, procfd, "", uintptr(m.Flags)|unix.MS_BIND, "")
320+
err = mountViaFDs(src, "", m.Destination, dstFD, "", uintptr(m.Flags)|unix.MS_BIND, "")
321321
if c.rootlessCgroups && errors.Is(err, unix.ENOENT) {
322322
err = nil
323323
}
@@ -358,15 +358,15 @@ func doTmpfsCopyUp(m *configs.Mount, rootfs, mountLabel string) (Err error) {
358358
}
359359
}()
360360

361-
return utils.WithProcfd(rootfs, m.Destination, func(procfd string) (Err error) {
361+
return utils.WithProcfd(rootfs, m.Destination, func(dstFD string) (Err error) {
362362
// Copy the container data to the host tmpdir. We append "/" to force
363363
// CopyDirectory to resolve the symlink rather than trying to copy the
364364
// symlink itself.
365-
if err := fileutils.CopyDirectory(procfd+"/", tmpDir); err != nil {
366-
return fmt.Errorf("tmpcopyup: failed to copy %s to %s (%s): %w", m.Destination, procfd, tmpDir, err)
365+
if err := fileutils.CopyDirectory(dstFD+"/", tmpDir); err != nil {
366+
return fmt.Errorf("tmpcopyup: failed to copy %s to %s (%s): %w", m.Destination, dstFD, tmpDir, err)
367367
}
368368
// Now move the mount into the container.
369-
if err := mount(tmpDir, m.Destination, procfd, "", unix.MS_MOVE, ""); err != nil {
369+
if err := mountViaFDs(tmpDir, "", m.Destination, dstFD, "", unix.MS_MOVE, ""); err != nil {
370370
return fmt.Errorf("tmpcopyup: failed to move mount: %w", err)
371371
}
372372
return nil
@@ -665,8 +665,8 @@ func bindMountDeviceNode(rootfs, dest string, node *devices.Device) error {
665665
if f != nil {
666666
_ = f.Close()
667667
}
668-
return utils.WithProcfd(rootfs, dest, func(procfd string) error {
669-
return mount(node.Path, dest, procfd, "bind", unix.MS_BIND, "")
668+
return utils.WithProcfd(rootfs, dest, func(dstFD string) error {
669+
return mountViaFDs(node.Path, "", dest, dstFD, "bind", unix.MS_BIND, "")
670670
})
671671
}
672672

@@ -763,7 +763,7 @@ func rootfsParentMountPrivate(rootfs string) error {
763763
// shared. Secondly when we bind mount rootfs it will propagate to
764764
// parent namespace and we don't want that to happen.
765765
if sharedMount {
766-
return mount("", parentMount, "", "", unix.MS_PRIVATE, "")
766+
return mount("", parentMount, "", unix.MS_PRIVATE, "")
767767
}
768768

769769
return nil
@@ -774,7 +774,7 @@ func prepareRoot(config *configs.Config) error {
774774
if config.RootPropagation != 0 {
775775
flag = config.RootPropagation
776776
}
777-
if err := mount("", "/", "", "", uintptr(flag), ""); err != nil {
777+
if err := mount("", "/", "", uintptr(flag), ""); err != nil {
778778
return err
779779
}
780780

@@ -785,13 +785,13 @@ func prepareRoot(config *configs.Config) error {
785785
return err
786786
}
787787

788-
return mount(config.Rootfs, config.Rootfs, "", "bind", unix.MS_BIND|unix.MS_REC, "")
788+
return mount(config.Rootfs, config.Rootfs, "bind", unix.MS_BIND|unix.MS_REC, "")
789789
}
790790

791791
func setReadonly() error {
792792
flags := uintptr(unix.MS_BIND | unix.MS_REMOUNT | unix.MS_RDONLY)
793793

794-
err := mount("", "/", "", "", flags, "")
794+
err := mount("", "/", "", flags, "")
795795
if err == nil {
796796
return nil
797797
}
@@ -800,7 +800,7 @@ func setReadonly() error {
800800
return &os.PathError{Op: "statfs", Path: "/", Err: err}
801801
}
802802
flags |= uintptr(s.Flags)
803-
return mount("", "/", "", "", flags, "")
803+
return mount("", "/", "", flags, "")
804804
}
805805

806806
func setupPtmx(config *configs.Config) error {
@@ -858,7 +858,7 @@ func pivotRoot(rootfs string) error {
858858
// known to cause issues due to races where we still have a reference to a
859859
// mount while a process in the host namespace are trying to operate on
860860
// something they think has no mounts (devicemapper in particular).
861-
if err := mount("", ".", "", "", unix.MS_SLAVE|unix.MS_REC, ""); err != nil {
861+
if err := mount("", ".", "", unix.MS_SLAVE|unix.MS_REC, ""); err != nil {
862862
return err
863863
}
864864
// Perform the unmount. MNT_DETACH allows us to unmount /proc/self/cwd.
@@ -907,7 +907,7 @@ func msMoveRoot(rootfs string) error {
907907
for _, info := range mountinfos {
908908
p := info.Mountpoint
909909
// Be sure umount events are not propagated to the host.
910-
if err := mount("", p, "", "", unix.MS_SLAVE|unix.MS_REC, ""); err != nil {
910+
if err := mount("", p, "", unix.MS_SLAVE|unix.MS_REC, ""); err != nil {
911911
if errors.Is(err, unix.ENOENT) {
912912
// If the mountpoint doesn't exist that means that we've
913913
// already blasted away some parent directory of the mountpoint
@@ -922,15 +922,15 @@ func msMoveRoot(rootfs string) error {
922922
} else {
923923
// If we have not privileges for umounting (e.g. rootless), then
924924
// cover the path.
925-
if err := mount("tmpfs", p, "", "tmpfs", 0, ""); err != nil {
925+
if err := mount("tmpfs", p, "tmpfs", 0, ""); err != nil {
926926
return err
927927
}
928928
}
929929
}
930930
}
931931

932932
// Move the rootfs on top of "/" in our mount namespace.
933-
if err := mount(rootfs, "/", "", "", unix.MS_MOVE, ""); err != nil {
933+
if err := mount(rootfs, "/", "", unix.MS_MOVE, ""); err != nil {
934934
return err
935935
}
936936
return chroot()
@@ -968,7 +968,7 @@ func createIfNotExists(path string, isDir bool) error {
968968

969969
// readonlyPath will make a path read only.
970970
func readonlyPath(path string) error {
971-
if err := mount(path, path, "", "", unix.MS_BIND|unix.MS_REC, ""); err != nil {
971+
if err := mount(path, path, "", unix.MS_BIND|unix.MS_REC, ""); err != nil {
972972
if errors.Is(err, os.ErrNotExist) {
973973
return nil
974974
}
@@ -981,7 +981,7 @@ func readonlyPath(path string) error {
981981
}
982982
flags := uintptr(s.Flags) & (unix.MS_NOSUID | unix.MS_NODEV | unix.MS_NOEXEC)
983983

984-
if err := mount(path, path, "", "", flags|unix.MS_BIND|unix.MS_REMOUNT|unix.MS_RDONLY, ""); err != nil {
984+
if err := mount(path, path, "", flags|unix.MS_BIND|unix.MS_REMOUNT|unix.MS_RDONLY, ""); err != nil {
985985
return err
986986
}
987987

@@ -1002,7 +1002,7 @@ func remountReadonly(m *configs.Mount) error {
10021002
// nosuid, etc.). So, let's use that case so that we can do
10031003
// this re-mount without failing in a userns.
10041004
flags |= unix.MS_REMOUNT | unix.MS_BIND | unix.MS_RDONLY
1005-
if err := mount("", dest, "", "", uintptr(flags), ""); err != nil {
1005+
if err := mount("", dest, "", uintptr(flags), ""); err != nil {
10061006
if errors.Is(err, unix.EBUSY) {
10071007
time.Sleep(100 * time.Millisecond)
10081008
continue
@@ -1020,9 +1020,9 @@ func remountReadonly(m *configs.Mount) error {
10201020
// For files, maskPath bind mounts /dev/null over the top of the specified path.
10211021
// For directories, maskPath mounts read-only tmpfs over the top of the specified path.
10221022
func maskPath(path string, mountLabel string) error {
1023-
if err := mount("/dev/null", path, "", "", unix.MS_BIND, ""); err != nil && !errors.Is(err, os.ErrNotExist) {
1023+
if err := mount("/dev/null", path, "", unix.MS_BIND, ""); err != nil && !errors.Is(err, os.ErrNotExist) {
10241024
if errors.Is(err, unix.ENOTDIR) {
1025-
return mount("tmpfs", path, "", "tmpfs", unix.MS_RDONLY, label.FormatMountLabel("", mountLabel))
1025+
return mount("tmpfs", path, "tmpfs", unix.MS_RDONLY, label.FormatMountLabel("", mountLabel))
10261026
}
10271027
return err
10281028
}
@@ -1037,28 +1037,28 @@ func writeSystemProperty(key, value string) error {
10371037
}
10381038

10391039
func remount(m *configs.Mount, rootfs string, mountFd *int) error {
1040-
source := m.Source
1040+
srcFD := ""
10411041
if mountFd != nil {
1042-
source = "/proc/self/fd/" + strconv.Itoa(*mountFd)
1042+
srcFD = "/proc/self/fd/" + strconv.Itoa(*mountFd)
10431043
}
10441044

1045-
return utils.WithProcfd(rootfs, m.Destination, func(procfd string) error {
1045+
return utils.WithProcfd(rootfs, m.Destination, func(dstFD string) error {
10461046
flags := uintptr(m.Flags | unix.MS_REMOUNT)
1047-
err := mount(source, m.Destination, procfd, m.Device, flags, "")
1047+
err := mountViaFDs(m.Source, srcFD, m.Destination, dstFD, m.Device, flags, "")
10481048
if err == nil {
10491049
return nil
10501050
}
10511051
// Check if the source has ro flag...
10521052
var s unix.Statfs_t
1053-
if err := unix.Statfs(source, &s); err != nil {
1054-
return &os.PathError{Op: "statfs", Path: source, Err: err}
1053+
if err := unix.Statfs(m.Source, &s); err != nil {
1054+
return &os.PathError{Op: "statfs", Path: m.Source, Err: err}
10551055
}
10561056
if s.Flags&unix.MS_RDONLY != unix.MS_RDONLY {
10571057
return err
10581058
}
10591059
// ... and retry the mount with ro flag set.
10601060
flags |= unix.MS_RDONLY
1061-
return mount(source, m.Destination, procfd, m.Device, flags, "")
1061+
return mountViaFDs(m.Source, srcFD, m.Destination, dstFD, m.Device, flags, "")
10621062
})
10631063
}
10641064

@@ -1077,26 +1077,26 @@ func mountPropagate(m *configs.Mount, rootfs string, mountLabel string, mountFd
10771077
flags &= ^unix.MS_RDONLY
10781078
}
10791079

1080+
srcFD := ""
1081+
if mountFd != nil {
1082+
srcFD = "/proc/self/fd/" + strconv.Itoa(*mountFd)
1083+
}
1084+
10801085
// Because the destination is inside a container path which might be
10811086
// mutating underneath us, we verify that we are actually going to mount
10821087
// inside the container with WithProcfd() -- mounting through a procfd
10831088
// mounts on the target.
1084-
source := m.Source
1085-
if mountFd != nil {
1086-
source = "/proc/self/fd/" + strconv.Itoa(*mountFd)
1087-
}
1088-
1089-
if err := utils.WithProcfd(rootfs, m.Destination, func(procfd string) error {
1090-
return mount(source, m.Destination, procfd, m.Device, uintptr(flags), data)
1089+
if err := utils.WithProcfd(rootfs, m.Destination, func(dstFD string) error {
1090+
return mountViaFDs(m.Source, srcFD, m.Destination, dstFD, m.Device, uintptr(flags), data)
10911091
}); err != nil {
10921092
return err
10931093
}
10941094
// We have to apply mount propagation flags in a separate WithProcfd() call
10951095
// because the previous call invalidates the passed procfd -- the mount
10961096
// target needs to be re-opened.
1097-
if err := utils.WithProcfd(rootfs, m.Destination, func(procfd string) error {
1097+
if err := utils.WithProcfd(rootfs, m.Destination, func(dstFD string) error {
10981098
for _, pflag := range m.PropagationFlags {
1099-
if err := mount("", m.Destination, procfd, "", uintptr(pflag), ""); err != nil {
1099+
if err := mountViaFDs("", "", m.Destination, dstFD, "", uintptr(pflag), ""); err != nil {
11001100
return err
11011101
}
11021102
}

0 commit comments

Comments
 (0)