github.phpd.cn/cilium/cilium@v1.6.12/pkg/mountinfo/mountinfo.go (about)

     1  // Copyright 2018 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package mountinfo
    16  
    17  import (
    18  	"bufio"
    19  	"fmt"
    20  	"io"
    21  	"os"
    22  	"strconv"
    23  	"strings"
    24  )
    25  
    26  const (
    27  	// FilesystemType names for filesystem which are used in /proc/pid/mountinfo
    28  	FilesystemTypeBPFFS   = "bpf"
    29  	FilesystemTypeCgroup2 = "cgroup2"
    30  
    31  	mountInfoFilepath = "/proc/self/mountinfo"
    32  )
    33  
    34  // MountInfo is a struct representing information from /proc/pid/mountinfo. More
    35  // information about file syntax:
    36  // https://www.kernel.org/doc/Documentation/filesystems/proc.txt
    37  type MountInfo struct {
    38  	MountID        int64
    39  	ParentID       int64
    40  	StDev          string
    41  	Root           string
    42  	MountPoint     string
    43  	MountOptions   string
    44  	OptionalFields []string
    45  	FilesystemType string
    46  	MountSource    string
    47  	SuperOptions   string
    48  }
    49  
    50  // parseMountInfoFile returns a slice of *MountInfo with information parsed from
    51  // the given reader
    52  func parseMountInfoFile(r io.Reader) ([]*MountInfo, error) {
    53  	var result []*MountInfo
    54  
    55  	scanner := bufio.NewScanner(r)
    56  	scanner.Split(bufio.ScanLines)
    57  
    58  	for scanner.Scan() {
    59  		mountInfoRaw := scanner.Text()
    60  
    61  		// Optional fields (which are on the 7th position) are separated
    62  		// from the rest of fields by "-" character. The number of
    63  		// optional fields can be greater or equal to 1.
    64  		mountInfoSeparated := strings.Split(mountInfoRaw, " - ")
    65  		if len(mountInfoSeparated) != 2 {
    66  			return nil, fmt.Errorf("invalid mountinfo entry which has more that one '-' separator: %s", mountInfoRaw)
    67  		}
    68  
    69  		// Extract fields from both sides of mountinfo
    70  		mountInfoLeft := strings.Split(strings.TrimSpace(mountInfoSeparated[0]), " ")
    71  		mountInfoRight := strings.Split(strings.TrimSpace(mountInfoSeparated[1]), " ")
    72  
    73  		// Before '-' separator there should be 6 fields and unknown
    74  		// number of optional fields
    75  		if len(mountInfoLeft) < 6 {
    76  			return nil, fmt.Errorf("invalid mountinfo entry: %s", mountInfoRaw)
    77  		}
    78  		// After '-' separator there should be 3 fields
    79  		if len(mountInfoRight) != 3 {
    80  			return nil, fmt.Errorf("invalid mountinfo entry: %s", mountInfoRaw)
    81  		}
    82  
    83  		mountID, err := strconv.ParseInt(mountInfoLeft[0], 10, 64)
    84  		if err != nil {
    85  			return nil, err
    86  		}
    87  
    88  		parentID, err := strconv.ParseInt(mountInfoLeft[1], 10, 64)
    89  		if err != nil {
    90  			return nil, err
    91  		}
    92  
    93  		// Extract optional fields, which start from 7th position
    94  		var optionalFields []string
    95  		for i := 6; i < len(mountInfoLeft); i++ {
    96  			optionalFields = append(optionalFields, mountInfoLeft[i])
    97  		}
    98  
    99  		result = append(result, &MountInfo{
   100  			MountID:        mountID,
   101  			ParentID:       parentID,
   102  			StDev:          mountInfoLeft[2],
   103  			Root:           mountInfoLeft[3],
   104  			MountPoint:     mountInfoLeft[4],
   105  			MountOptions:   mountInfoLeft[5],
   106  			OptionalFields: optionalFields,
   107  			FilesystemType: mountInfoRight[0],
   108  			MountSource:    mountInfoRight[1],
   109  			SuperOptions:   mountInfoRight[2],
   110  		})
   111  	}
   112  
   113  	return result, nil
   114  }
   115  
   116  // GetMountInfo returns a slice of *MountInfo with information parsed from
   117  // /proc/self/mountinfo
   118  func GetMountInfo() ([]*MountInfo, error) {
   119  	fMounts, err := os.Open(mountInfoFilepath)
   120  	if err != nil {
   121  		return nil, fmt.Errorf("failed to open mount information at %s: %s", mountInfoFilepath, err)
   122  	}
   123  	defer fMounts.Close()
   124  
   125  	return parseMountInfoFile(fMounts)
   126  }
   127  
   128  func isMountFS(mountInfos []*MountInfo, mntType string, mapRoot string) (bool, bool) {
   129  	var mapRootMountInfo *MountInfo
   130  
   131  	for _, mountInfo := range mountInfos {
   132  		if mountInfo.MountPoint == mapRoot {
   133  			mapRootMountInfo = mountInfo
   134  			break
   135  		}
   136  	}
   137  
   138  	if mapRootMountInfo == nil {
   139  		return false, false
   140  	}
   141  
   142  	if mapRootMountInfo.FilesystemType == mntType {
   143  		return true, true
   144  	}
   145  	return true, false
   146  }
   147  
   148  // IsMountFS returns two boolean values:checks whether the current mapRoot:
   149  // - whether the current mapRoot has any mount
   150  // - whether that mount's filesystem is of type mntType
   151  func IsMountFS(mntType string, mapRoot string) (bool, bool, error) {
   152  	mountInfos, err := GetMountInfo()
   153  	if err != nil {
   154  		return false, false, err
   155  	}
   156  
   157  	mounted, mntTypeInstance := isMountFS(mountInfos, mntType, mapRoot)
   158  	return mounted, mntTypeInstance, nil
   159  }