github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/pkg/mountinfo/mountinfo.go (about)

     1  // Copyright 2016 The rkt Authors
     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  	"errors"
    20  	"fmt"
    21  	"io"
    22  	"os"
    23  	"sort"
    24  	"strconv"
    25  	"strings"
    26  
    27  	"github.com/hashicorp/errwrap"
    28  )
    29  
    30  // HasPrefix returns a FilterFunc which returns true if
    31  // the mountpoint of a given mount has prefix p, else false.
    32  func HasPrefix(p string) FilterFunc {
    33  	return FilterFunc(func(m *Mount) bool {
    34  		return strings.HasPrefix(m.MountPoint, p)
    35  	})
    36  }
    37  
    38  // ParseMounts returns all mountpoints associated with a process mount namespace.
    39  // The special value 0 as pid argument is used to specify the current process.
    40  func ParseMounts(pid uint) (Mounts, error) {
    41  	var procPath string
    42  	if pid == 0 {
    43  		procPath = "/proc/self/mountinfo"
    44  	} else {
    45  		procPath = fmt.Sprintf("/proc/%d/mountinfo", pid)
    46  	}
    47  
    48  	mi, err := os.Open(procPath)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	defer mi.Close()
    53  
    54  	return parseMountinfo(mi)
    55  }
    56  
    57  // parseMountinfo parses mi (/proc/<pid>/mountinfo) and returns mounts information
    58  // according to https://www.kernel.org/doc/Documentation/filesystems/proc.txt
    59  func parseMountinfo(mi io.Reader) (Mounts, error) {
    60  	var podMounts Mounts
    61  	sc := bufio.NewScanner(mi)
    62  	var (
    63  		mountID    int
    64  		parentID   int
    65  		major      int
    66  		minor      int
    67  		root       string
    68  		mountPoint string
    69  		opt        map[string]struct{}
    70  	)
    71  
    72  	for sc.Scan() {
    73  		line := sc.Text()
    74  		columns := strings.Split(line, " ")
    75  		if len(columns) < 7 {
    76  			return nil, fmt.Errorf("Not enough fields from line %q: %+v", line, columns)
    77  		}
    78  
    79  		opt = map[string]struct{}{}
    80  		for i, col := range columns {
    81  			if col == "-" {
    82  				// separator: a single hyphen "-" marks the end of "optional fields"
    83  				break
    84  			}
    85  			var err error
    86  			switch i {
    87  			case 0:
    88  				mountID, err = strconv.Atoi(col)
    89  			case 1:
    90  				parentID, err = strconv.Atoi(col)
    91  			case 2:
    92  				split := strings.Split(col, ":")
    93  				if len(split) != 2 {
    94  					err = fmt.Errorf("found unexpected key:value field with more than two colons: %s", col)
    95  					break
    96  				}
    97  				major, err = strconv.Atoi(split[0])
    98  				if err != nil {
    99  					break
   100  				}
   101  				minor, err = strconv.Atoi(split[1])
   102  				if err != nil {
   103  					break
   104  				}
   105  			case 3:
   106  				root = col
   107  			case 4:
   108  				mountPoint = col
   109  			default:
   110  				split := strings.Split(col, ":")
   111  				switch len(split) {
   112  				case 1:
   113  					// we ignore modes like rw, relatime, etc.
   114  				case 2:
   115  					opt[split[0]] = struct{}{}
   116  				default:
   117  					err = fmt.Errorf("found unexpected key:value field with more than two colons: %s", col)
   118  				}
   119  			}
   120  			if err != nil {
   121  				return nil, errwrap.Wrap(fmt.Errorf("could not parse mountinfo line %q", line), err)
   122  			}
   123  		}
   124  
   125  		mnt := &Mount{
   126  			ID:         mountID,
   127  			Parent:     parentID,
   128  			Major:      major,
   129  			Minor:      minor,
   130  			Root:       root,
   131  			MountPoint: mountPoint,
   132  			Opts:       opt,
   133  		}
   134  		podMounts = append(podMounts, mnt)
   135  	}
   136  	if err := sc.Err(); err != nil {
   137  		return nil, errwrap.Wrap(errors.New("problem parsing mountinfo"), err)
   138  	}
   139  	sort.Sort(podMounts)
   140  	return podMounts, nil
   141  }