github.com/demonoid81/containerd@v1.3.4/mount/mountinfo_linux.go (about)

     1  // +build linux
     2  
     3  /*
     4     Copyright The containerd Authors.
     5  
     6     Licensed under the Apache License, Version 2.0 (the "License");
     7     you may not use this file except in compliance with the License.
     8     You may obtain a copy of the License at
     9  
    10         http://www.apache.org/licenses/LICENSE-2.0
    11  
    12     Unless required by applicable law or agreed to in writing, software
    13     distributed under the License is distributed on an "AS IS" BASIS,
    14     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15     See the License for the specific language governing permissions and
    16     limitations under the License.
    17  */
    18  
    19  package mount
    20  
    21  import (
    22  	"bufio"
    23  	"fmt"
    24  	"io"
    25  	"os"
    26  	"strconv"
    27  	"strings"
    28  
    29  	"github.com/pkg/errors"
    30  )
    31  
    32  // Self retrieves a list of mounts for the current running process.
    33  func Self() ([]Info, error) {
    34  	f, err := os.Open("/proc/self/mountinfo")
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	defer f.Close()
    39  
    40  	return parseInfoFile(f)
    41  }
    42  
    43  func parseInfoFile(r io.Reader) ([]Info, error) {
    44  	s := bufio.NewScanner(r)
    45  	out := []Info{}
    46  	var err error
    47  	for s.Scan() {
    48  		if err = s.Err(); err != nil {
    49  			return nil, err
    50  		}
    51  
    52  		/*
    53  		   See http://man7.org/linux/man-pages/man5/proc.5.html
    54  
    55  		   36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
    56  		   (1)(2)(3)   (4)   (5)      (6)      (7)   (8) (9)   (10)         (11)
    57  		   (1) mount ID:  unique identifier of the mount (may be reused after umount)
    58  		   (2) parent ID:  ID of parent (or of self for the top of the mount tree)
    59  		   (3) major:minor:  value of st_dev for files on filesystem
    60  		   (4) root:  root of the mount within the filesystem
    61  		   (5) mount point:  mount point relative to the process's root
    62  		   (6) mount options:  per mount options
    63  		   (7) optional fields:  zero or more fields of the form "tag[:value]"
    64  		   (8) separator:  marks the end of the optional fields
    65  		   (9) filesystem type:  name of filesystem of the form "type[.subtype]"
    66  		   (10) mount source:  filesystem specific information or "none"
    67  		   (11) super options:  per super block options
    68  		*/
    69  
    70  		text := s.Text()
    71  		fields := strings.Split(text, " ")
    72  		numFields := len(fields)
    73  		if numFields < 10 {
    74  			// should be at least 10 fields
    75  			return nil, errors.Errorf("parsing '%s' failed: not enough fields (%d)", text, numFields)
    76  		}
    77  		p := Info{}
    78  		// ignore any numbers parsing errors, as there should not be any
    79  		p.ID, _ = strconv.Atoi(fields[0])
    80  		p.Parent, _ = strconv.Atoi(fields[1])
    81  		mm := strings.Split(fields[2], ":")
    82  		if len(mm) != 2 {
    83  			return nil, errors.Errorf("parsing '%s' failed: unexpected minor:major pair %s", text, mm)
    84  		}
    85  		p.Major, _ = strconv.Atoi(mm[0])
    86  		p.Minor, _ = strconv.Atoi(mm[1])
    87  
    88  		p.Root, err = strconv.Unquote(`"` + fields[3] + `"`)
    89  		if err != nil {
    90  			return nil, errors.Wrapf(err, "parsing '%s' failed: unable to unquote root field", fields[3])
    91  		}
    92  		p.Mountpoint, err = strconv.Unquote(`"` + fields[4] + `"`)
    93  		if err != nil {
    94  			return nil, errors.Wrapf(err, "parsing '%s' failed: unable to unquote mount point field", fields[4])
    95  		}
    96  		p.Options = fields[5]
    97  
    98  		// one or more optional fields, when a separator (-)
    99  		i := 6
   100  		for ; i < numFields && fields[i] != "-"; i++ {
   101  			switch i {
   102  			case 6:
   103  				p.Optional = fields[6]
   104  			default:
   105  				/* NOTE there might be more optional fields before the separator
   106  				   such as fields[7]...fields[N] (where N < separatorIndex),
   107  				   although as of Linux kernel 4.15 the only known ones are
   108  				   mount propagation flags in fields[6]. The correct
   109  				   behavior is to ignore any unknown optional fields.
   110  				*/
   111  			}
   112  		}
   113  		if i == numFields {
   114  			return nil, errors.Errorf("parsing '%s' failed: missing separator ('-')", text)
   115  		}
   116  		// There should be 3 fields after the separator...
   117  		if i+4 > numFields {
   118  			return nil, errors.Errorf("parsing '%s' failed: not enough fields after a separator", text)
   119  		}
   120  		// ... but in Linux <= 3.9 mounting a cifs with spaces in a share name
   121  		// (like "//serv/My Documents") _may_ end up having a space in the last field
   122  		// of mountinfo (like "unc=//serv/My Documents"). Since kernel 3.10-rc1, cifs
   123  		// option unc= is ignored,  so a space should not appear. In here we ignore
   124  		// those "extra" fields caused by extra spaces.
   125  		p.FSType = fields[i+1]
   126  		p.Source = fields[i+2]
   127  		p.VFSOptions = fields[i+3]
   128  
   129  		out = append(out, p)
   130  	}
   131  	return out, nil
   132  }
   133  
   134  // PID collects the mounts for a specific process ID. If the process
   135  // ID is unknown, it is better to use `Self` which will inspect
   136  // "/proc/self/mountinfo" instead.
   137  func PID(pid int) ([]Info, error) {
   138  	f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  	defer f.Close()
   143  
   144  	return parseInfoFile(f)
   145  }