github.com/devdivbcp/moby@v17.12.0-ce-rc1.0.20200726071732-2d4bfdc789ad+incompatible/pkg/mount/mountinfo_linux.go (about)

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