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  }