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