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 }