...

Source file src/k8s.io/utils/mount/mount_helper_unix.go

Documentation: k8s.io/utils/mount

     1  //go:build !windows
     2  // +build !windows
     3  
     4  /*
     5  Copyright 2019 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package mount
    21  
    22  import (
    23  	"fmt"
    24  	"os"
    25  	"strconv"
    26  	"strings"
    27  	"syscall"
    28  
    29  	utilio "k8s.io/utils/io"
    30  )
    31  
    32  const (
    33  	// At least number of fields per line in /proc/<pid>/mountinfo.
    34  	expectedAtLeastNumFieldsPerMountInfo = 10
    35  	// How many times to retry for a consistent read of /proc/mounts.
    36  	maxListTries = 3
    37  )
    38  
    39  // IsCorruptedMnt return true if err is about corrupted mount point
    40  func IsCorruptedMnt(err error) bool {
    41  	if err == nil {
    42  		return false
    43  	}
    44  	var underlyingError error
    45  	switch pe := err.(type) {
    46  	case nil:
    47  		return false
    48  	case *os.PathError:
    49  		underlyingError = pe.Err
    50  	case *os.LinkError:
    51  		underlyingError = pe.Err
    52  	case *os.SyscallError:
    53  		underlyingError = pe.Err
    54  	}
    55  
    56  	return underlyingError == syscall.ENOTCONN || underlyingError == syscall.ESTALE || underlyingError == syscall.EIO || underlyingError == syscall.EACCES
    57  }
    58  
    59  // MountInfo represents a single line in /proc/<pid>/mountinfo.
    60  type MountInfo struct { // nolint: golint
    61  	// Unique ID for the mount (maybe reused after umount).
    62  	ID int
    63  	// The ID of the parent mount (or of self for the root of this mount namespace's mount tree).
    64  	ParentID int
    65  	// Major indicates one half of the device ID which identifies the device class
    66  	// (parsed from `st_dev` for files on this filesystem).
    67  	Major int
    68  	// Minor indicates one half of the device ID which identifies a specific
    69  	// instance of device (parsed from `st_dev` for files on this filesystem).
    70  	Minor int
    71  	// The pathname of the directory in the filesystem which forms the root of this mount.
    72  	Root string
    73  	// Mount source, filesystem-specific information. e.g. device, tmpfs name.
    74  	Source string
    75  	// Mount point, the pathname of the mount point.
    76  	MountPoint string
    77  	// Optional fieds, zero or more fields of the form "tag[:value]".
    78  	OptionalFields []string
    79  	// The filesystem type in the form "type[.subtype]".
    80  	FsType string
    81  	// Per-mount options.
    82  	MountOptions []string
    83  	// Per-superblock options.
    84  	SuperOptions []string
    85  }
    86  
    87  // ParseMountInfo parses /proc/xxx/mountinfo.
    88  func ParseMountInfo(filename string) ([]MountInfo, error) {
    89  	content, err := utilio.ConsistentRead(filename, maxListTries)
    90  	if err != nil {
    91  		return []MountInfo{}, err
    92  	}
    93  	contentStr := string(content)
    94  	infos := []MountInfo{}
    95  
    96  	for _, line := range strings.Split(contentStr, "\n") {
    97  		if line == "" {
    98  			// the last split() item is empty string following the last \n
    99  			continue
   100  		}
   101  		// See `man proc` for authoritative description of format of the file.
   102  		fields := strings.Fields(line)
   103  		if len(fields) < expectedAtLeastNumFieldsPerMountInfo {
   104  			return nil, fmt.Errorf("wrong number of fields in (expected at least %d, got %d): %s", expectedAtLeastNumFieldsPerMountInfo, len(fields), line)
   105  		}
   106  		id, err := strconv.Atoi(fields[0])
   107  		if err != nil {
   108  			return nil, err
   109  		}
   110  		parentID, err := strconv.Atoi(fields[1])
   111  		if err != nil {
   112  			return nil, err
   113  		}
   114  		mm := strings.Split(fields[2], ":")
   115  		if len(mm) != 2 {
   116  			return nil, fmt.Errorf("parsing '%s' failed: unexpected minor:major pair %s", line, mm)
   117  		}
   118  		major, err := strconv.Atoi(mm[0])
   119  		if err != nil {
   120  			return nil, fmt.Errorf("parsing '%s' failed: unable to parse major device id, err:%v", mm[0], err)
   121  		}
   122  		minor, err := strconv.Atoi(mm[1])
   123  		if err != nil {
   124  			return nil, fmt.Errorf("parsing '%s' failed: unable to parse minor device id, err:%v", mm[1], err)
   125  		}
   126  
   127  		info := MountInfo{
   128  			ID:           id,
   129  			ParentID:     parentID,
   130  			Major:        major,
   131  			Minor:        minor,
   132  			Root:         fields[3],
   133  			MountPoint:   fields[4],
   134  			MountOptions: strings.Split(fields[5], ","),
   135  		}
   136  		// All fields until "-" are "optional fields".
   137  		i := 6
   138  		for ; i < len(fields) && fields[i] != "-"; i++ {
   139  			info.OptionalFields = append(info.OptionalFields, fields[i])
   140  		}
   141  		// Parse the rest 3 fields.
   142  		i++
   143  		if len(fields)-i < 3 {
   144  			return nil, fmt.Errorf("expect 3 fields in %s, got %d", line, len(fields)-i)
   145  		}
   146  		info.FsType = fields[i]
   147  		info.Source = fields[i+1]
   148  		info.SuperOptions = strings.Split(fields[i+2], ",")
   149  		infos = append(infos, info)
   150  	}
   151  	return infos, nil
   152  }
   153  
   154  // isMountPointMatch returns true if the path in mp is the same as dir.
   155  // Handles case where mountpoint dir has been renamed due to stale NFS mount.
   156  func isMountPointMatch(mp MountPoint, dir string) bool {
   157  	deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
   158  	return ((mp.Path == dir) || (mp.Path == deletedDir))
   159  }
   160  

View as plain text