github.com/containerd/Containerd@v1.4.13/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 /* 49 See http://man7.org/linux/man-pages/man5/proc.5.html 50 51 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue 52 (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11) 53 (1) mount ID: unique identifier of the mount (may be reused after umount) 54 (2) parent ID: ID of parent (or of self for the top of the mount tree) 55 (3) major:minor: value of st_dev for files on filesystem 56 (4) root: root of the mount within the filesystem 57 (5) mount point: mount point relative to the process's root 58 (6) mount options: per mount options 59 (7) optional fields: zero or more fields of the form "tag[:value]" 60 (8) separator: marks the end of the optional fields 61 (9) filesystem type: name of filesystem of the form "type[.subtype]" 62 (10) mount source: filesystem specific information or "none" 63 (11) super options: per super block options 64 */ 65 66 text := s.Text() 67 fields := strings.Split(text, " ") 68 numFields := len(fields) 69 if numFields < 10 { 70 // should be at least 10 fields 71 return nil, errors.Errorf("parsing '%s' failed: not enough fields (%d)", text, numFields) 72 } 73 p := Info{} 74 // ignore any numbers parsing errors, as there should not be any 75 p.ID, _ = strconv.Atoi(fields[0]) 76 p.Parent, _ = strconv.Atoi(fields[1]) 77 mm := strings.Split(fields[2], ":") 78 if len(mm) != 2 { 79 return nil, errors.Errorf("parsing '%s' failed: unexpected minor:major pair %s", text, mm) 80 } 81 p.Major, _ = strconv.Atoi(mm[0]) 82 p.Minor, _ = strconv.Atoi(mm[1]) 83 84 p.Root, err = strconv.Unquote(`"` + strings.Replace(fields[3], `"`, `\"`, -1) + `"`) 85 if err != nil { 86 return nil, errors.Wrapf(err, "parsing '%s' failed: unable to unquote root field", fields[3]) 87 } 88 p.Mountpoint, err = strconv.Unquote(`"` + strings.Replace(fields[4], `"`, `\"`, -1) + `"`) 89 if err != nil { 90 return nil, errors.Wrapf(err, "parsing '%s' failed: unable to unquote mount point field", fields[4]) 91 } 92 p.Options = fields[5] 93 94 // one or more optional fields, when a separator (-) 95 i := 6 96 for ; i < numFields && fields[i] != "-"; i++ { 97 switch i { 98 case 6: 99 p.Optional = fields[6] 100 default: 101 /* NOTE there might be more optional fields before the separator 102 such as fields[7]...fields[N] (where N < separatorIndex), 103 although as of Linux kernel 4.15 the only known ones are 104 mount propagation flags in fields[6]. The correct 105 behavior is to ignore any unknown optional fields. 106 */ 107 } 108 } 109 if i == numFields { 110 return nil, errors.Errorf("parsing '%s' failed: missing separator ('-')", text) 111 } 112 // There should be 3 fields after the separator... 113 if i+4 > numFields { 114 return nil, errors.Errorf("parsing '%s' failed: not enough fields after a separator", text) 115 } 116 // ... but in Linux <= 3.9 mounting a cifs with spaces in a share name 117 // (like "//serv/My Documents") _may_ end up having a space in the last field 118 // of mountinfo (like "unc=//serv/My Documents"). Since kernel 3.10-rc1, cifs 119 // option unc= is ignored, so a space should not appear. In here we ignore 120 // those "extra" fields caused by extra spaces. 121 p.FSType = fields[i+1] 122 p.Source = fields[i+2] 123 p.VFSOptions = fields[i+3] 124 125 out = append(out, p) 126 } 127 if err = s.Err(); err != nil { 128 return nil, err 129 } 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 }