blob: 2f754b675a6e023958a121cb1efa3a8c9f6f3232 [file] [log] [blame]
Lorenz Brun878f5f92020-05-12 16:15:39 +02001Copyright 2020 The Monogon Project Authors.
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License.
14
15
16From 8335005ed1983ca5ac036af15dd04b8717898c35 Mon Sep 17 00:00:00 2001
17From: Lorenz Brun <lorenz@brun.one>
18Date: Mon, 16 Mar 2020 22:13:08 +0100
19Subject: [PATCH 1/3] Provide native mounter implementation for Linux
20
21---
22 BUILD.bazel | 2 +
23 mount/mount_linux.go | 141 ++++++++++++++++++++++-
24 2 files changed, 139 insertions(+), 4 deletions(-)
25
26diff --git a/mount/BUILD b/mount/BUILD.bazel
27index bef3ec2cf55..6f997103dac 100644
28--- a/mount/BUILD.bazel
29+++ b/mount/BUILD.bazel
30@@ -21,6 +21,7 @@ go_library(
31 "//exec:go_default_library",
32 ] + select({
33 "@io_bazel_rules_go//go/platform:android": [
34+ "@org_golang_x_sys//unix:go_default_library",
35 "//io:go_default_library",
36 ],
37 "@io_bazel_rules_go//go/platform:darwin": [
38@@ -36,6 +37,7 @@ go_library(
39 "//io:go_default_library",
40 ],
41 "@io_bazel_rules_go//go/platform:linux": [
42+ "@org_golang_x_sys//unix:go_default_library",
43 "//io:go_default_library",
44 ],
45 "@io_bazel_rules_go//go/platform:nacl": [
46diff --git a/mount/mount_linux.go b/mount/mount_linux.go
47index 41f69efe3f0..01182684653 100644
48--- a/mount/mount_linux.go
49+++ b/mount/mount_linux.go
50@@ -20,6 +20,7 @@ package mount
51
52 import (
53 "fmt"
54+ "io/ioutil"
55 "os"
56 "os/exec"
57 "path/filepath"
58@@ -27,6 +28,7 @@ import (
59 "strings"
60 "syscall"
61
62+ "golang.org/x/sys/unix"
Lorenz Brunb876fc32020-07-14 13:54:01 +020063 "k8s.io/klog/v2"
Lorenz Brun878f5f92020-05-12 16:15:39 +020064 utilexec "k8s.io/utils/exec"
65 utilio "k8s.io/utils/io"
66@@ -49,8 +51,10 @@ const (
67 // for the linux platform. This implementation assumes that the
68 // kubelet is running in the host's root mount namespace.
69 type Mounter struct {
70- mounterPath string
71- withSystemd bool
72+ mounterPath string
73+ withSystemd bool
74+ withLinuxUtils bool
75+ nativeSupportedFstypes map[string]struct{}
76 }
77
78 // New returns a mount.Interface for the current system.
79@@ -58,8 +62,10 @@ type Mounter struct {
80 // mounterPath allows using an alternative to `/bin/mount` for mounting.
81 func New(mounterPath string) Interface {
82 return &Mounter{
83- mounterPath: mounterPath,
84- withSystemd: detectSystemd(),
85+ mounterPath: mounterPath,
86+ withSystemd: detectSystemd(),
87+ withLinuxUtils: detectLinuxUtils(),
88+ nativeSupportedFstypes: detectNativeSupportedFstypes(),
89 }
90 }
91
92@@ -78,6 +84,29 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio
93 // method should be used by callers that pass sensitive material (like
94 // passwords) as mount options.
95 func (mounter *Mounter) MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
96+ if !mounter.withLinuxUtils {
97+ flags, pflags, fsoptions := parseMountOptions(options)
98+ if len(pflags) > 0 {
99+ return fmt.Errorf("the native mounter is active and does not support mount propagation at the moment")
100+ }
101+
102+ if !mounter.nativeSupportsFstype(fstype) && flags&unix.MS_BIND == 0 {
103+ return fmt.Errorf("the native mounter is active and cannot mount filesystems of type \"%v\"", fstype)
104+ }
105+
106+ if flags&unix.MS_BIND != 0 && flags & ^uintptr(unix.MS_BIND) != 0 {
107+ if err := unix.Mount(source, target, "", unix.MS_BIND, ""); err != nil {
108+ return fmt.Errorf("bind pre-mount failed: %w", err)
109+ }
110+ flags |= unix.MS_REMOUNT
111+ }
112+
113+ if err := unix.Mount(source, target, fstype, flags, fsoptions); err != nil {
114+ return fmt.Errorf("failed to mount filesystem: %w", err)
115+ }
116+ return nil
117+ }
118+
119 // Path to mounter binary if containerized mounter is needed. Otherwise, it is set to empty.
120 // All Linux distros are expected to be shipped with a mount utility that a support bind mounts.
121 mounterPath := ""
122@@ -102,6 +131,80 @@ func (mounter *Mounter) MountSensitive(source string, target string, fstype stri
123 return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, options, sensitiveOptions)
124 }
125
126+// nativeSupportsFstype checks if the native mounter can mount the given fstype
127+func (mounter *Mounter) nativeSupportsFstype(fstype string) bool {
128+ _, ok := mounter.nativeSupportedFstypes[fstype]
129+ return ok
130+}
131+
132+// parseMountOptions parses the string and returns the flags, propagation
133+// flags and any mount data that it contains.
134+// Taken from libcontainer/specconv/spec_linux.go (Apache 2.0) and modified
135+func parseMountOptions(options []string) (uintptr, []uintptr, string) {
136+ var (
137+ flag uintptr
138+ pgflag []uintptr
139+ data []string
140+ )
141+ flags := map[string]struct {
142+ clear bool
143+ flag uintptr
144+ }{
145+ "async": {true, syscall.MS_SYNCHRONOUS},
146+ "atime": {true, syscall.MS_NOATIME},
147+ "bind": {false, syscall.MS_BIND},
148+ "defaults": {false, 0},
149+ "dev": {true, syscall.MS_NODEV},
150+ "diratime": {true, syscall.MS_NODIRATIME},
151+ "dirsync": {false, syscall.MS_DIRSYNC},
152+ "exec": {true, syscall.MS_NOEXEC},
153+ "mand": {false, syscall.MS_MANDLOCK},
154+ "noatime": {false, syscall.MS_NOATIME},
155+ "nodev": {false, syscall.MS_NODEV},
156+ "nodiratime": {false, syscall.MS_NODIRATIME},
157+ "noexec": {false, syscall.MS_NOEXEC},
158+ "nomand": {true, syscall.MS_MANDLOCK},
159+ "norelatime": {true, syscall.MS_RELATIME},
160+ "nostrictatime": {true, syscall.MS_STRICTATIME},
161+ "nosuid": {false, syscall.MS_NOSUID},
162+ "rbind": {false, syscall.MS_BIND | syscall.MS_REC},
163+ "relatime": {false, syscall.MS_RELATIME},
164+ "remount": {false, syscall.MS_REMOUNT},
165+ "ro": {false, syscall.MS_RDONLY},
166+ "rw": {true, syscall.MS_RDONLY},
167+ "strictatime": {false, syscall.MS_STRICTATIME},
168+ "suid": {true, syscall.MS_NOSUID},
169+ "sync": {false, syscall.MS_SYNCHRONOUS},
170+ }
171+ propagationFlags := map[string]uintptr{
172+ "private": syscall.MS_PRIVATE,
173+ "shared": syscall.MS_SHARED,
174+ "slave": syscall.MS_SLAVE,
175+ "unbindable": syscall.MS_UNBINDABLE,
176+ "rprivate": syscall.MS_PRIVATE | syscall.MS_REC,
177+ "rshared": syscall.MS_SHARED | syscall.MS_REC,
178+ "rslave": syscall.MS_SLAVE | syscall.MS_REC,
179+ "runbindable": syscall.MS_UNBINDABLE | syscall.MS_REC,
180+ }
181+ for _, o := range options {
182+ // If the option does not exist in the flags table or the flag
183+ // is not supported on the platform,
184+ // then it is a data value for a specific fs type
185+ if f, exists := flags[o]; exists && f.flag != 0 {
186+ if f.clear {
187+ flag &= ^f.flag
188+ } else {
189+ flag |= f.flag
190+ }
191+ } else if f, exists := propagationFlags[o]; exists && f != 0 {
192+ pgflag = append(pgflag, f)
193+ } else {
194+ data = append(data, o)
195+ }
196+ }
197+ return flag, pgflag, strings.Join(data, ",")
198+}
199+
200 // doMount runs the mount command. mounterPath is the path to mounter binary if containerized mounter is used.
201 // sensitiveOptions is an extention of options except they will not be logged (because they may contain sensitive material)
202 func (mounter *Mounter) doMount(mounterPath string, mountCmd string, source string, target string, fstype string, options []string, sensitiveOptions []string) error {
203@@ -179,6 +282,30 @@ func detectSystemd() bool {
204 return true
205 }
206
207+// detectLinuxUtils detects if the host operating system has the mount and unmount commands present
208+func detectLinuxUtils() bool {
209+ _, err := exec.LookPath("mount")
210+ return err == nil
211+}
212+
213+func detectNativeSupportedFstypes() map[string]struct{} {
214+ nativeSupportedFstypes := make(map[string]struct{})
215+ filesystemsRaw, err := ioutil.ReadFile("/proc/filesystems")
216+ if err != nil {
217+ return nativeSupportedFstypes
218+ }
219+ filesystemLines := strings.Split(string(filesystemsRaw), "\n")
220+ for _, line := range filesystemLines {
221+ fields := strings.Fields(line)
222+ if len(fields) != 2 {
223+ continue
224+ }
225+ filesystem := fields[1]
226+ nativeSupportedFstypes[filesystem] = struct{}{}
227+ }
228+ return nativeSupportedFstypes
229+}
230+
231 // MakeMountArgs makes the arguments to the mount(8) command.
232 // options MUST not contain sensitive material (like passwords).
233 func MakeMountArgs(source, target, fstype string, options []string) (mountArgs []string) {
234@@ -236,6 +363,12 @@ func AddSystemdScopeSensitive(systemdRunPath, mountName, command string, args []
235 // Unmount unmounts the target.
236 func (mounter *Mounter) Unmount(target string) error {
237 klog.V(4).Infof("Unmounting %s", target)
238+ if !mounter.withLinuxUtils {
239+ if err := unix.Unmount(target, unix.UMOUNT_NOFOLLOW); err != nil {
240+ return fmt.Errorf("unmount failed: %v", err)
241+ }
242+ return nil
243+ }
244 command := exec.Command("umount", target)
245 output, err := command.CombinedOutput()
246 if err != nil {
247--
2482.25.1
249