hostutil_linux.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. // +build linux
  2. /*
  3. Copyright 2014 The Kubernetes Authors.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package hostutil
  15. import (
  16. "fmt"
  17. "os"
  18. "path"
  19. "path/filepath"
  20. "strings"
  21. "syscall"
  22. "golang.org/x/sys/unix"
  23. "k8s.io/klog"
  24. "k8s.io/utils/mount"
  25. utilpath "k8s.io/utils/path"
  26. )
  27. const (
  28. // Location of the mountinfo file
  29. procMountInfoPath = "/proc/self/mountinfo"
  30. )
  31. // HostUtil implements HostUtils for Linux platforms.
  32. type HostUtil struct {
  33. }
  34. // NewHostUtil returns a struct that implements the HostUtils interface on
  35. // linux platforms
  36. func NewHostUtil() *HostUtil {
  37. return &HostUtil{}
  38. }
  39. // DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
  40. // If pathname is not a device, log and return false with nil error.
  41. // If open returns errno EBUSY, return true with nil error.
  42. // If open returns nil, return false with nil error.
  43. // Otherwise, return false with error
  44. func (hu *HostUtil) DeviceOpened(pathname string) (bool, error) {
  45. return ExclusiveOpenFailsOnDevice(pathname)
  46. }
  47. // PathIsDevice uses FileInfo returned from os.Stat to check if path refers
  48. // to a device.
  49. func (hu *HostUtil) PathIsDevice(pathname string) (bool, error) {
  50. pathType, err := hu.GetFileType(pathname)
  51. isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev
  52. return isDevice, err
  53. }
  54. // ExclusiveOpenFailsOnDevice is shared with NsEnterMounter
  55. func ExclusiveOpenFailsOnDevice(pathname string) (bool, error) {
  56. var isDevice bool
  57. finfo, err := os.Stat(pathname)
  58. if os.IsNotExist(err) {
  59. isDevice = false
  60. }
  61. // err in call to os.Stat
  62. if err != nil {
  63. return false, fmt.Errorf(
  64. "PathIsDevice failed for path %q: %v",
  65. pathname,
  66. err)
  67. }
  68. // path refers to a device
  69. if finfo.Mode()&os.ModeDevice != 0 {
  70. isDevice = true
  71. }
  72. if !isDevice {
  73. klog.Errorf("Path %q is not referring to a device.", pathname)
  74. return false, nil
  75. }
  76. fd, errno := unix.Open(pathname, unix.O_RDONLY|unix.O_EXCL|unix.O_CLOEXEC, 0)
  77. // If the device is in use, open will return an invalid fd.
  78. // When this happens, it is expected that Close will fail and throw an error.
  79. defer unix.Close(fd)
  80. if errno == nil {
  81. // device not in use
  82. return false, nil
  83. } else if errno == unix.EBUSY {
  84. // device is in use
  85. return true, nil
  86. }
  87. // error during call to Open
  88. return false, errno
  89. }
  90. // GetDeviceNameFromMount given a mount point, find the device name from its global mount point
  91. func (hu *HostUtil) GetDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error) {
  92. return getDeviceNameFromMount(mounter, mountPath, pluginMountDir)
  93. }
  94. // getDeviceNameFromMountLinux find the device name from /proc/mounts in which
  95. // the mount path reference should match the given plugin mount directory. In case no mount path reference
  96. // matches, returns the volume name taken from its given mountPath
  97. func getDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error) {
  98. refs, err := mounter.GetMountRefs(mountPath)
  99. if err != nil {
  100. klog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
  101. return "", err
  102. }
  103. if len(refs) == 0 {
  104. klog.V(4).Infof("Directory %s is not mounted", mountPath)
  105. return "", fmt.Errorf("directory %s is not mounted", mountPath)
  106. }
  107. for _, ref := range refs {
  108. if strings.HasPrefix(ref, pluginMountDir) {
  109. volumeID, err := filepath.Rel(pluginMountDir, ref)
  110. if err != nil {
  111. klog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
  112. return "", err
  113. }
  114. return volumeID, nil
  115. }
  116. }
  117. return path.Base(mountPath), nil
  118. }
  119. // MakeRShared checks that given path is on a mount with 'rshared' mount
  120. // propagation. If not, it bind-mounts the path as rshared.
  121. func (hu *HostUtil) MakeRShared(path string) error {
  122. return DoMakeRShared(path, procMountInfoPath)
  123. }
  124. // GetFileType checks for file/directory/socket/block/character devices.
  125. func (hu *HostUtil) GetFileType(pathname string) (FileType, error) {
  126. return getFileType(pathname)
  127. }
  128. // PathExists tests if the given path already exists
  129. // Error is returned on any other error than "file not found".
  130. func (hu *HostUtil) PathExists(pathname string) (bool, error) {
  131. return utilpath.Exists(utilpath.CheckFollowSymlink, pathname)
  132. }
  133. // EvalHostSymlinks returns the path name after evaluating symlinks.
  134. // TODO once the nsenter implementation is removed, this method can be removed
  135. // from the interface and filepath.EvalSymlinks used directly
  136. func (hu *HostUtil) EvalHostSymlinks(pathname string) (string, error) {
  137. return filepath.EvalSymlinks(pathname)
  138. }
  139. // isShared returns true, if given path is on a mount point that has shared
  140. // mount propagation.
  141. func isShared(mount string, mountInfoPath string) (bool, error) {
  142. info, err := findMountInfo(mount, mountInfoPath)
  143. if err != nil {
  144. return false, err
  145. }
  146. // parse optional parameters
  147. for _, opt := range info.OptionalFields {
  148. if strings.HasPrefix(opt, "shared:") {
  149. return true, nil
  150. }
  151. }
  152. return false, nil
  153. }
  154. func findMountInfo(path, mountInfoPath string) (mount.MountInfo, error) {
  155. infos, err := mount.ParseMountInfo(mountInfoPath)
  156. if err != nil {
  157. return mount.MountInfo{}, err
  158. }
  159. // process /proc/xxx/mountinfo in backward order and find the first mount
  160. // point that is prefix of 'path' - that's the mount where path resides
  161. var info *mount.MountInfo
  162. for i := len(infos) - 1; i >= 0; i-- {
  163. if mount.PathWithinBase(path, infos[i].MountPoint) {
  164. info = &infos[i]
  165. break
  166. }
  167. }
  168. if info == nil {
  169. return mount.MountInfo{}, fmt.Errorf("cannot find mount point for %q", path)
  170. }
  171. return *info, nil
  172. }
  173. // DoMakeRShared is common implementation of MakeRShared on Linux. It checks if
  174. // path is shared and bind-mounts it as rshared if needed. mountCmd and
  175. // mountArgs are expected to contain mount-like command, DoMakeRShared will add
  176. // '--bind <path> <path>' and '--make-rshared <path>' to mountArgs.
  177. func DoMakeRShared(path string, mountInfoFilename string) error {
  178. shared, err := isShared(path, mountInfoFilename)
  179. if err != nil {
  180. return err
  181. }
  182. if shared {
  183. klog.V(4).Infof("Directory %s is already on a shared mount", path)
  184. return nil
  185. }
  186. klog.V(2).Infof("Bind-mounting %q with shared mount propagation", path)
  187. // mount --bind /var/lib/kubelet /var/lib/kubelet
  188. if err := syscall.Mount(path, path, "" /*fstype*/, syscall.MS_BIND, "" /*data*/); err != nil {
  189. return fmt.Errorf("failed to bind-mount %s: %v", path, err)
  190. }
  191. // mount --make-rshared /var/lib/kubelet
  192. if err := syscall.Mount(path, path, "" /*fstype*/, syscall.MS_SHARED|syscall.MS_REC, "" /*data*/); err != nil {
  193. return fmt.Errorf("failed to make %s rshared: %v", path, err)
  194. }
  195. return nil
  196. }
  197. // GetSELinux is common implementation of GetSELinuxSupport on Linux.
  198. func GetSELinux(path string, mountInfoFilename string) (bool, error) {
  199. info, err := findMountInfo(path, mountInfoFilename)
  200. if err != nil {
  201. return false, err
  202. }
  203. // "seclabel" can be both in mount options and super options.
  204. for _, opt := range info.SuperOptions {
  205. if opt == "seclabel" {
  206. return true, nil
  207. }
  208. }
  209. for _, opt := range info.MountOptions {
  210. if opt == "seclabel" {
  211. return true, nil
  212. }
  213. }
  214. return false, nil
  215. }
  216. // GetSELinuxSupport returns true if given path is on a mount that supports
  217. // SELinux.
  218. func (hu *HostUtil) GetSELinuxSupport(pathname string) (bool, error) {
  219. return GetSELinux(pathname, procMountInfoPath)
  220. }
  221. // GetOwner returns the integer ID for the user and group of the given path
  222. func (hu *HostUtil) GetOwner(pathname string) (int64, int64, error) {
  223. realpath, err := filepath.EvalSymlinks(pathname)
  224. if err != nil {
  225. return -1, -1, err
  226. }
  227. return GetOwnerLinux(realpath)
  228. }
  229. // GetMode returns permissions of the path.
  230. func (hu *HostUtil) GetMode(pathname string) (os.FileMode, error) {
  231. return GetModeLinux(pathname)
  232. }
  233. // GetOwnerLinux is shared between Linux and NsEnterMounter
  234. // pathname must already be evaluated for symlinks
  235. func GetOwnerLinux(pathname string) (int64, int64, error) {
  236. info, err := os.Stat(pathname)
  237. if err != nil {
  238. return -1, -1, err
  239. }
  240. stat := info.Sys().(*syscall.Stat_t)
  241. return int64(stat.Uid), int64(stat.Gid), nil
  242. }
  243. // GetModeLinux is shared between Linux and NsEnterMounter
  244. func GetModeLinux(pathname string) (os.FileMode, error) {
  245. info, err := os.Stat(pathname)
  246. if err != nil {
  247. return 0, err
  248. }
  249. return info.Mode(), nil
  250. }