123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- // +build !windows
- /*
- Copyright 2019 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package mount
- import (
- "fmt"
- "os"
- "strconv"
- "strings"
- "syscall"
- utilio "k8s.io/utils/io"
- )
- const (
- // At least number of fields per line in /proc/<pid>/mountinfo.
- expectedAtLeastNumFieldsPerMountInfo = 10
- // How many times to retry for a consistent read of /proc/mounts.
- maxListTries = 3
- )
- // IsCorruptedMnt return true if err is about corrupted mount point
- func IsCorruptedMnt(err error) bool {
- if err == nil {
- return false
- }
- var underlyingError error
- switch pe := err.(type) {
- case nil:
- return false
- case *os.PathError:
- underlyingError = pe.Err
- case *os.LinkError:
- underlyingError = pe.Err
- case *os.SyscallError:
- underlyingError = pe.Err
- }
- return underlyingError == syscall.ENOTCONN || underlyingError == syscall.ESTALE || underlyingError == syscall.EIO || underlyingError == syscall.EACCES
- }
- // MountInfo represents a single line in /proc/<pid>/mountinfo.
- type MountInfo struct {
- // Unique ID for the mount (maybe reused after umount).
- ID int
- // The ID of the parent mount (or of self for the root of this mount namespace's mount tree).
- ParentID int
- // The value of `st_dev` for files on this filesystem.
- MajorMinor string
- // The pathname of the directory in the filesystem which forms the root of this mount.
- Root string
- // Mount source, filesystem-specific information. e.g. device, tmpfs name.
- Source string
- // Mount point, the pathname of the mount point.
- MountPoint string
- // Optional fieds, zero or more fields of the form "tag[:value]".
- OptionalFields []string
- // The filesystem type in the form "type[.subtype]".
- FsType string
- // Per-mount options.
- MountOptions []string
- // Per-superblock options.
- SuperOptions []string
- }
- // ParseMountInfo parses /proc/xxx/mountinfo.
- func ParseMountInfo(filename string) ([]MountInfo, error) {
- content, err := utilio.ConsistentRead(filename, maxListTries)
- if err != nil {
- return []MountInfo{}, err
- }
- contentStr := string(content)
- infos := []MountInfo{}
- for _, line := range strings.Split(contentStr, "\n") {
- if line == "" {
- // the last split() item is empty string following the last \n
- continue
- }
- // See `man proc` for authoritative description of format of the file.
- fields := strings.Fields(line)
- if len(fields) < expectedAtLeastNumFieldsPerMountInfo {
- return nil, fmt.Errorf("wrong number of fields in (expected at least %d, got %d): %s", expectedAtLeastNumFieldsPerMountInfo, len(fields), line)
- }
- id, err := strconv.Atoi(fields[0])
- if err != nil {
- return nil, err
- }
- parentID, err := strconv.Atoi(fields[1])
- if err != nil {
- return nil, err
- }
- info := MountInfo{
- ID: id,
- ParentID: parentID,
- MajorMinor: fields[2],
- Root: fields[3],
- MountPoint: fields[4],
- MountOptions: strings.Split(fields[5], ","),
- }
- // All fields until "-" are "optional fields".
- i := 6
- for ; i < len(fields) && fields[i] != "-"; i++ {
- info.OptionalFields = append(info.OptionalFields, fields[i])
- }
- // Parse the rest 3 fields.
- i++
- if len(fields)-i < 3 {
- return nil, fmt.Errorf("expect 3 fields in %s, got %d", line, len(fields)-i)
- }
- info.FsType = fields[i]
- info.Source = fields[i+1]
- info.SuperOptions = strings.Split(fields[i+2], ",")
- infos = append(infos, info)
- }
- return infos, nil
- }
- // isMountPointMatch returns true if the path in mp is the same as dir.
- // Handles case where mountpoint dir has been renamed due to stale NFS mount.
- func isMountPointMatch(mp MountPoint, dir string) bool {
- deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
- return ((mp.Path == dir) || (mp.Path == deletedDir))
- }
|