go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/mount/mount.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package mount 5 6 import ( 7 "bufio" 8 "io" 9 "regexp" 10 "strings" 11 12 "github.com/cockroachdb/errors" 13 "github.com/spf13/afero" 14 ) 15 16 func ParseLinuxMountCmd(r io.Reader) []MountPoint { 17 res := []MountPoint{} 18 19 mountEntry := regexp.MustCompile(`^(\S+)\son\s(\S+)\stype\s(\S+)\s\((\S+)\)$`) 20 21 scanner := bufio.NewScanner(r) 22 for scanner.Scan() { 23 line := scanner.Text() 24 m := mountEntry.FindStringSubmatch(line) 25 if len(m) == 5 { 26 res = append(res, MountPoint{ 27 Device: strings.TrimSpace(m[1]), 28 MountPoint: strings.TrimSpace(m[2]), 29 FSType: strings.TrimSpace(m[3]), 30 Options: parseOptions(strings.TrimSpace(m[4])), 31 }) 32 } 33 } 34 35 return res 36 } 37 38 // NOTE: we do not handle `map auto_home` on macos 39 func ParseUnixMountCmd(r io.Reader) []MountPoint { 40 res := []MountPoint{} 41 mountEntry := regexp.MustCompile(`^(\S+)\son\s(\S+)\s\((.*)\)$`) 42 43 scanner := bufio.NewScanner(r) 44 for scanner.Scan() { 45 line := scanner.Text() 46 m := mountEntry.FindStringSubmatch(line) 47 if len(m) == 4 { 48 opts := strings.TrimSpace(m[3]) 49 fstype := "" 50 entries := strings.Split(opts, ",") 51 if len(entries) > 1 { 52 fstype = strings.TrimSpace(entries[0]) 53 } 54 55 res = append(res, MountPoint{ 56 Device: strings.TrimSpace(m[1]), 57 MountPoint: strings.TrimSpace(m[2]), 58 FSType: fstype, 59 Options: parseOptions(opts), 60 }) 61 } 62 } 63 64 return res 65 } 66 67 // see https://stackoverflow.com/questions/18122123/how-to-interpret-proc-mounts 68 func ParseLinuxProcMount(r io.Reader) []MountPoint { 69 res := []MountPoint{} 70 71 procMountEntry := regexp.MustCompile(`^(\S+)\s(\S+)\s(\S+)\s(\S+)\s0\s0$`) 72 73 scanner := bufio.NewScanner(r) 74 for scanner.Scan() { 75 line := scanner.Text() 76 m := procMountEntry.FindStringSubmatch(line) 77 if len(m) == 5 { 78 res = append(res, MountPoint{ 79 Device: strings.TrimSpace(m[1]), 80 MountPoint: strings.TrimSpace(m[2]), 81 FSType: strings.TrimSpace(m[3]), 82 Options: parseOptions(strings.TrimSpace(m[4])), 83 }) 84 } 85 } 86 87 return res 88 } 89 90 func parseOptions(opts string) map[string]string { 91 res := map[string]string{} 92 entries := strings.Split(opts, ",") 93 for i := range entries { 94 entry := entries[i] 95 keyval := strings.Split(entry, "=") 96 if len(keyval) == 2 { 97 res[strings.TrimSpace(keyval[0])] = strings.TrimSpace(keyval[1]) 98 } else { 99 res[strings.TrimSpace(entry)] = "" 100 } 101 } 102 return res 103 } 104 105 func ParseFstab(r io.Reader) ([]MountPoint, error) { 106 res := []MountPoint{} 107 108 scanner := bufio.NewScanner(r) 109 for scanner.Scan() { 110 line := scanner.Text() 111 if line == "" || line[0] == '#' { 112 continue 113 } 114 fields := strings.Fields(line) 115 if len(fields) < 4 { 116 // ignoring bad lines 117 continue 118 } 119 res = append(res, MountPoint{ 120 Device: fields[0], 121 MountPoint: fields[1], 122 FSType: fields[2], 123 Options: parseOptions(fields[3]), 124 }) 125 } 126 return res, scanner.Err() 127 } 128 129 func mountsFromFSLinux(fs afero.Fs) ([]MountPoint, error) { 130 // Check if we have /proc/mounts 131 procMountExists, err := afero.Exists(fs, "/proc/mounts") 132 if err != nil { 133 return nil, err 134 } 135 if procMountExists { 136 f, err := fs.Open("/proc/mounts") 137 if err != nil { 138 return nil, err 139 } 140 defer f.Close() 141 return ParseLinuxProcMount(f), nil 142 } 143 144 fstabExists, err := afero.Exists(fs, "/etc/fstab") 145 if err != nil { 146 return nil, err 147 } 148 if fstabExists { 149 f, err := fs.Open("/etc/fstab") 150 if err != nil { 151 return nil, err 152 } 153 defer f.Close() 154 return ParseFstab(f) 155 } 156 return nil, errors.New("could not find mounts") 157 }