github.com/rhatdan/docker@v0.7.7-0.20180119204836-47a0dcbcd20a/pkg/mount/mountinfo_linux.go (about)

     1  package mount
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"strings"
     9  )
    10  
    11  const (
    12  	/* 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
    13  	   (1)(2)(3)   (4)   (5)      (6)      (7)   (8) (9)   (10)         (11)
    14  
    15  	   (1) mount ID:  unique identifier of the mount (may be reused after umount)
    16  	   (2) parent ID:  ID of parent (or of self for the top of the mount tree)
    17  	   (3) major:minor:  value of st_dev for files on filesystem
    18  	   (4) root:  root of the mount within the filesystem
    19  	   (5) mount point:  mount point relative to the process's root
    20  	   (6) mount options:  per mount options
    21  	   (7) optional fields:  zero or more fields of the form "tag[:value]"
    22  	   (8) separator:  marks the end of the optional fields
    23  	   (9) filesystem type:  name of filesystem of the form "type[.subtype]"
    24  	   (10) mount source:  filesystem specific information or "none"
    25  	   (11) super options:  per super block options*/
    26  	mountinfoFormat = "%d %d %d:%d %s %s %s %s"
    27  )
    28  
    29  // Parse /proc/self/mountinfo because comparing Dev and ino does not work from
    30  // bind mounts
    31  func parseMountTable() ([]*Info, error) {
    32  	f, err := os.Open("/proc/self/mountinfo")
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	defer f.Close()
    37  
    38  	return parseInfoFile(f)
    39  }
    40  
    41  func parseInfoFile(r io.Reader) ([]*Info, error) {
    42  	var (
    43  		s   = bufio.NewScanner(r)
    44  		out = []*Info{}
    45  	)
    46  
    47  	for s.Scan() {
    48  		if err := s.Err(); err != nil {
    49  			return nil, err
    50  		}
    51  
    52  		var (
    53  			p              = &Info{}
    54  			text           = s.Text()
    55  			optionalFields string
    56  		)
    57  
    58  		if _, err := fmt.Sscanf(text, mountinfoFormat,
    59  			&p.ID, &p.Parent, &p.Major, &p.Minor,
    60  			&p.Root, &p.Mountpoint, &p.Opts, &optionalFields); err != nil {
    61  			return nil, fmt.Errorf("Scanning '%s' failed: %s", text, err)
    62  		}
    63  		// Safe as mountinfo encodes mountpoints with spaces as \040.
    64  		index := strings.Index(text, " - ")
    65  		postSeparatorFields := strings.Fields(text[index+3:])
    66  		if len(postSeparatorFields) < 3 {
    67  			return nil, fmt.Errorf("Error found less than 3 fields post '-' in %q", text)
    68  		}
    69  
    70  		if optionalFields != "-" {
    71  			p.Optional = optionalFields
    72  		}
    73  
    74  		p.Fstype = postSeparatorFields[0]
    75  		p.Source = postSeparatorFields[1]
    76  		p.VfsOpts = strings.Join(postSeparatorFields[2:], " ")
    77  		out = append(out, p)
    78  	}
    79  	return out, nil
    80  }
    81  
    82  // PidMountInfo collects the mounts for a specific process ID. If the process
    83  // ID is unknown, it is better to use `GetMounts` which will inspect
    84  // "/proc/self/mountinfo" instead.
    85  func PidMountInfo(pid int) ([]*Info, error) {
    86  	f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	defer f.Close()
    91  
    92  	return parseInfoFile(f)
    93  }