@@ -23,27 +23,10 @@ import (
2323 "fmt"
2424 "io"
2525 "os"
26+ "strconv"
2627 "strings"
2728)
2829
29- const (
30- /* 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
31- (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
32-
33- (1) mount ID: unique identifier of the mount (may be reused after umount)
34- (2) parent ID: ID of parent (or of self for the top of the mount tree)
35- (3) major:minor: value of st_dev for files on filesystem
36- (4) root: root of the mount within the filesystem
37- (5) mount point: mount point relative to the process's root
38- (6) mount options: per mount options
39- (7) optional fields: zero or more fields of the form "tag[:value]"
40- (8) separator: marks the end of the optional fields
41- (9) filesystem type: name of filesystem of the form "type[.subtype]"
42- (10) mount source: filesystem specific information or "none"
43- (11) super options: per super block options*/
44- mountinfoFormat = "%d %d %d:%d %s %s %s %s"
45- )
46-
4730// Self retrieves a list of mounts for the current running process.
4831func Self () ([]Info , error ) {
4932 f , err := os .Open ("/proc/self/mountinfo" )
@@ -56,41 +39,83 @@ func Self() ([]Info, error) {
5639}
5740
5841func parseInfoFile (r io.Reader ) ([]Info , error ) {
59- var (
60- s = bufio .NewScanner (r )
61- out = []Info {}
62- )
42+ s := bufio .NewScanner (r )
43+ out := []Info {}
6344
6445 for s .Scan () {
6546 if err := s .Err (); err != nil {
6647 return nil , err
6748 }
6849
69- var (
70- p = Info {}
71- text = s .Text ()
72- optionalFields string
73- )
74-
75- if _ , err := fmt .Sscanf (text , mountinfoFormat ,
76- & p .ID , & p .Parent , & p .Major , & p .Minor ,
77- & p .Root , & p .Mountpoint , & p .Options , & optionalFields ); err != nil {
78- return nil , fmt .Errorf ("Scanning '%s' failed: %s" , text , err )
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 , fmt .Errorf ("Parsing '%s' failed: not enough fields (%d)" , text , numFields )
7972 }
80- // Safe as mountinfo encodes mountpoints with spaces as \040.
81- index := strings .Index (text , " - " )
82- postSeparatorFields := strings .Fields (text [index + 3 :])
83- if len (postSeparatorFields ) < 3 {
84- return nil , fmt .Errorf ("Error found less than 3 fields post '-' in %q" , text )
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 , fmt .Errorf ("Parsing '%s' failed: unexpected minor:major pair %s" , text , mm )
8580 }
86-
87- if optionalFields != "-" {
88- p .Optional = optionalFields
81+ p .Major , _ = strconv .Atoi (mm [0 ])
82+ p .Minor , _ = strconv .Atoi (mm [1 ])
83+
84+ p .Root = fields [3 ]
85+ p .Mountpoint = fields [4 ]
86+ p .Options = fields [5 ]
87+
88+ // one or more optional fields, when a separator (-)
89+ i := 6
90+ for ; i < numFields && fields [i ] != "-" ; i ++ {
91+ switch i {
92+ case 6 :
93+ p .Optional = fields [6 ]
94+ default :
95+ /* NOTE there might be more optional fields before the separator
96+ such as fields[7]...fields[N] (where N < separatorIndex),
97+ although as of Linux kernel 4.15 the only known ones are
98+ mount propagation flags in fields[6]. The correct
99+ behavior is to ignore any unknown optional fields.
100+ */
101+ }
102+ }
103+ if i == numFields {
104+ return nil , fmt .Errorf ("Parsing '%s' failed: missing separator ('-')" , text )
105+ }
106+ // There should be 3 fields after the separator...
107+ if i + 4 > numFields {
108+ return nil , fmt .Errorf ("Parsing '%s' failed: not enough fields after a separator" , text )
89109 }
110+ // ... but in Linux <= 3.9 mounting a cifs with spaces in a share name
111+ // (like "//serv/My Documents") _may_ end up having a space in the last field
112+ // of mountinfo (like "unc=//serv/My Documents"). Since kernel 3.10-rc1, cifs
113+ // option unc= is ignored, so a space should not appear. In here we ignore
114+ // those "extra" fields caused by extra spaces.
115+ p .FSType = fields [i + 1 ]
116+ p .Source = fields [i + 2 ]
117+ p .VFSOptions = fields [i + 3 ]
90118
91- p .FSType = postSeparatorFields [0 ]
92- p .Source = postSeparatorFields [1 ]
93- p .VFSOptions = strings .Join (postSeparatorFields [2 :], " " )
94119 out = append (out , p )
95120 }
96121 return out , nil
0 commit comments