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 }