Skip to content

Commit bbb236c

Browse files
authored
Merge pull request #29 from kolyshkin/openat2
mountinfo.Mounted: add fast path for Linux using openat2
2 parents 1d9043a + 9c884dc commit bbb236c

13 files changed

+470
-43
lines changed

.ci/Vagrantfile.fedora32

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# -*- mode: ruby -*-
2+
# vi: set ft=ruby :
3+
4+
Vagrant.configure("2") do |config|
5+
# Fedora box is used for testing cgroup v2 support
6+
config.vm.box = "fedora/32-cloud-base"
7+
config.vm.provider :virtualbox do |v|
8+
v.memory = 2048
9+
v.cpus = 2
10+
end
11+
config.vm.provider :libvirt do |v|
12+
v.memory = 2048
13+
v.cpus = 2
14+
end
15+
config.vm.provision "shell", inline: <<-SHELL
16+
set -e -u -o pipefail
17+
# Work around dnf mirror failures by retrying a few times
18+
for i in $(seq 0 2); do
19+
sleep $i
20+
cat << EOF | dnf -y shell && break
21+
config exclude kernel,kernel-core
22+
config install_weak_deps false
23+
update
24+
install make golang-go libseccomp-devel git-core
25+
ts run
26+
EOF
27+
done
28+
dnf clean all
29+
30+
SHELL
31+
end

.ci/install-vagrant.sh

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/bin/bash
2+
set -eux -o pipefail
3+
VAGRANT_VERSION="2.2.10"
4+
5+
# Based on code from https://github.com/opencontainers/runc
6+
DEB="vagrant_${VAGRANT_VERSION}_$(uname -m).deb"
7+
wget "https://releases.hashicorp.com/vagrant/${VAGRANT_VERSION}/$DEB"
8+
apt-get update
9+
apt-get install -q -y \
10+
bridge-utils \
11+
dnsmasq-base \
12+
ebtables \
13+
libvirt-bin \
14+
libvirt-dev \
15+
qemu-kvm \
16+
qemu-utils \
17+
ruby-dev \
18+
./"$DEB"
19+
rm -f "$DEB"
20+
vagrant plugin install vagrant-libvirt

.github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ jobs:
44
test:
55
strategy:
66
matrix:
7-
go-version: [1.13.x, 1.14.x]
7+
go-version: [1.14.x, 1.15.x]
88
platform: [ubuntu-latest, windows-latest]
99
runs-on: ${{ matrix.platform }}
1010
steps:

.travis.yml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
dist: bionic
2+
os: linux
3+
language: minimal
4+
cache:
5+
directories:
6+
- /home/travis/.vagrant.d/boxes
7+
jobs:
8+
include:
9+
- name: "Fedora 32"
10+
before_install:
11+
- sudo .ci/install-vagrant.sh
12+
- ln -sf .ci/Vagrantfile.fedora32 Vagrantfile
13+
- sudo vagrant up && sudo mkdir -p /root/.ssh && sudo sh -c "vagrant ssh-config >> /root/.ssh/config"
14+
script:
15+
- sudo ssh default -t 'cd /vagrant && sudo make'

mountinfo/doc.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@
1414
// parse filters while reading mountinfo. A filter can skip some entries, or stop
1515
// processing the rest of the file once the needed information is found.
1616
//
17-
// For functions that have path as an argument (such as Mounted or various filters),
18-
// the argument must be
19-
// - an absolute path;
17+
// For mountinfo filters that accept path as an argument, the path must be:
18+
// - absolute;
2019
// - having all symlinks resolved;
2120
// - being cleaned.
2221
//

mountinfo/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ module github.com/moby/sys/mountinfo
22

33
go 1.14
44

5-
require golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae
5+
require golang.org/x/sys v0.0.0-20200909081042-eff7692f9009

mountinfo/go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
2-
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1+
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM=
2+
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

mountinfo/mounted_linux.go

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package mountinfo
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
7+
"golang.org/x/sys/unix"
8+
)
9+
10+
// mountedByOpenat2 is a method of detecting a mount that works for all kinds
11+
// of mounts (incl. bind mounts), but requires a recent (v5.6+) linux kernel.
12+
func mountedByOpenat2(path string) (bool, error) {
13+
dir, last := filepath.Split(path)
14+
15+
dirfd, err := unix.Openat2(unix.AT_FDCWD, dir, &unix.OpenHow{
16+
Flags: unix.O_PATH | unix.O_CLOEXEC,
17+
})
18+
if err != nil {
19+
if err == unix.ENOENT { // not a mount
20+
return false, nil
21+
}
22+
return false, &os.PathError{Op: "openat2", Path: dir, Err: err}
23+
}
24+
fd, err := unix.Openat2(dirfd, last, &unix.OpenHow{
25+
Flags: unix.O_PATH | unix.O_CLOEXEC | unix.O_NOFOLLOW,
26+
Resolve: unix.RESOLVE_NO_XDEV,
27+
})
28+
_ = unix.Close(dirfd)
29+
switch err {
30+
case nil: // definitely not a mount
31+
_ = unix.Close(fd)
32+
return false, nil
33+
case unix.EXDEV: // definitely a mount
34+
return true, nil
35+
case unix.ENOENT: // not a mount
36+
return false, nil
37+
}
38+
// not sure
39+
return false, &os.PathError{Op: "openat2", Path: path, Err: err}
40+
}
41+
42+
func mounted(path string) (bool, error) {
43+
// Try a fast path, using openat2() with RESOLVE_NO_XDEV.
44+
mounted, err := mountedByOpenat2(path)
45+
if err == nil {
46+
return mounted, nil
47+
}
48+
// Another fast path: compare st.st_dev fields.
49+
mounted, err = mountedByStat(path)
50+
// This does not work for bind mounts, so false negative
51+
// is possible, therefore only trust if return is true.
52+
if mounted && err == nil {
53+
return mounted, nil
54+
}
55+
56+
// Fallback to parsing mountinfo
57+
return mountedByMountinfo(path)
58+
}

0 commit comments

Comments
 (0)