// Copyright 2019 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package procfs

import (
	
	
	
	
	

	
)

// A MountInfo is a type that describes the details, options
// for each mount, parsed from /proc/self/mountinfo.
// The fields described in each entry of /proc/self/mountinfo
// is described in the following man page.
// http://man7.org/linux/man-pages/man5/proc.5.html
type MountInfo struct {
	// Unique ID for the mount
	MountID int
	// The ID of the parent mount
	ParentID int
	// The value of `st_dev` for the files on this FS
	MajorMinorVer string
	// The pathname of the directory in the FS that forms
	// the root for this mount
	Root string
	// The pathname of the mount point relative to the root
	MountPoint string
	// Mount options
	Options map[string]string
	// Zero or more optional fields
	OptionalFields map[string]string
	// The Filesystem type
	FSType string
	// FS specific information or "none"
	Source string
	// Superblock options
	SuperOptions map[string]string
}

// Reads each line of the mountinfo file, and returns a list of formatted MountInfo structs.
func parseMountInfo( []byte) ([]*MountInfo, error) {
	 := []*MountInfo{}
	 := bufio.NewScanner(bytes.NewReader())
	for .Scan() {
		 := .Text()
		,  := parseMountInfoString()
		if  != nil {
			return nil, 
		}
		 = append(, )
	}

	 := .Err()
	return , 
}

// Parses a mountinfo file line, and converts it to a MountInfo struct.
// An important check here is to see if the hyphen separator, as if it does not exist,
// it means that the line is malformed.
func parseMountInfoString( string) (*MountInfo, error) {
	var  error

	 := strings.Split(, " ")
	 := len()
	if  < 10 {
		return nil, fmt.Errorf("%w: Too few fields in mount string: %s", ErrFileParse, )
	}

	if [-4] != "-" {
		return nil, fmt.Errorf("%w: couldn't find separator in expected field: %s", ErrFileParse, [-4])
	}

	 := &MountInfo{
		MajorMinorVer:  [2],
		Root:           [3],
		MountPoint:     [4],
		Options:        mountOptionsParser([5]),
		OptionalFields: nil,
		FSType:         [-3],
		Source:         [-2],
		SuperOptions:   mountOptionsParser([-1]),
	}

	.MountID,  = strconv.Atoi([0])
	if  != nil {
		return nil, fmt.Errorf("%w: mount ID: %q", ErrFileParse, .MountID)
	}
	.ParentID,  = strconv.Atoi([1])
	if  != nil {
		return nil, fmt.Errorf("%w: parent ID: %q", ErrFileParse, .ParentID)
	}
	// Has optional fields, which is a space separated list of values.
	// Example: shared:2 master:7
	if [6] != "" {
		.OptionalFields,  = mountOptionsParseOptionalFields([6 : -4])
		if  != nil {
			return nil, fmt.Errorf("%w: %w", ErrFileParse, )
		}
	}
	return , nil
}

// mountOptionsIsValidField checks a string against a valid list of optional fields keys.
func mountOptionsIsValidField( string) bool {
	switch  {
	case
		"shared",
		"master",
		"propagate_from",
		"unbindable":
		return true
	}
	return false
}

// mountOptionsParseOptionalFields parses a list of optional fields strings into a double map of strings.
func mountOptionsParseOptionalFields( []string) (map[string]string, error) {
	 := make(map[string]string)
	for ,  := range  {
		 := strings.SplitN(, ":", 2)
		 := ""
		if len() == 2 {
			 = [1]
		}
		if mountOptionsIsValidField([0]) {
			[[0]] = 
		}
	}
	return , nil
}

// mountOptionsParser parses the mount options, superblock options.
func mountOptionsParser( string) map[string]string {
	 := make(map[string]string)
	 := strings.Split(, ",")
	for ,  := range  {
		 := strings.Split(, "=")
		if len() < 2 {
			 := [0]
			[] = ""
		} else {
			,  := [0], [1]
			[] = 
		}
	}
	return 
}

// GetMounts retrieves mountinfo information from `/proc/self/mountinfo`.
func () ([]*MountInfo, error) {
	,  := util.ReadFileNoStat("/proc/self/mountinfo")
	if  != nil {
		return nil, 
	}
	return parseMountInfo()
}

// GetProcMounts retrieves mountinfo information from a processes' `/proc/<pid>/mountinfo`.
func ( int) ([]*MountInfo, error) {
	,  := util.ReadFileNoStat(fmt.Sprintf("/proc/%d/mountinfo", ))
	if  != nil {
		return nil, 
	}
	return parseMountInfo()
}