You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
3.9 KiB

  1. package command
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "os"
  7. "strings"
  8. )
  9. const (
  10. /* 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
  11. (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
  12. (1) mount ID: unique identifier of the mount (may be reused after umount)
  13. (2) parent ID: ID of parent (or of self for the top of the mount tree)
  14. (3) major:minor: value of st_dev for files on filesystem
  15. (4) root: root of the mount within the filesystem
  16. (5) mount point: mount point relative to the process's root
  17. (6) mount options: per mount options
  18. (7) optional fields: zero or more fields of the form "tag[:value]"
  19. (8) separator: marks the end of the optional fields
  20. (9) filesystem type: name of filesystem of the form "type[.subtype]"
  21. (10) mount source: filesystem specific information or "none"
  22. (11) super options: per super block options*/
  23. mountinfoFormat = "%d %d %d:%d %s %s %s %s"
  24. )
  25. // Info reveals information about a particular mounted filesystem. This
  26. // struct is populated from the content in the /proc/<pid>/mountinfo file.
  27. type Info struct {
  28. // ID is a unique identifier of the mount (may be reused after umount).
  29. ID int
  30. // Parent indicates the ID of the mount parent (or of self for the top of the
  31. // mount tree).
  32. Parent int
  33. // Major indicates one half of the device ID which identifies the device class.
  34. Major int
  35. // Minor indicates one half of the device ID which identifies a specific
  36. // instance of device.
  37. Minor int
  38. // Root of the mount within the filesystem.
  39. Root string
  40. // Mountpoint indicates the mount point relative to the process's root.
  41. Mountpoint string
  42. // Opts represents mount-specific options.
  43. Opts string
  44. // Optional represents optional fields.
  45. Optional string
  46. // Fstype indicates the type of filesystem, such as EXT3.
  47. Fstype string
  48. // Source indicates filesystem specific information or "none".
  49. Source string
  50. // VfsOpts represents per super block options.
  51. VfsOpts string
  52. }
  53. // Mounted determines if a specified mountpoint has been mounted.
  54. // On Linux it looks at /proc/self/mountinfo and on Solaris at mnttab.
  55. func mounted(mountPoint string) (bool, error) {
  56. entries, err := parseMountTable()
  57. if err != nil {
  58. return false, err
  59. }
  60. // Search the table for the mountPoint
  61. for _, e := range entries {
  62. if e.Mountpoint == mountPoint {
  63. return true, nil
  64. }
  65. }
  66. return false, nil
  67. }
  68. // Parse /proc/self/mountinfo because comparing Dev and ino does not work from
  69. // bind mounts
  70. func parseMountTable() ([]*Info, error) {
  71. f, err := os.Open("/proc/self/mountinfo")
  72. if err != nil {
  73. return nil, err
  74. }
  75. defer f.Close()
  76. return parseInfoFile(f)
  77. }
  78. func parseInfoFile(r io.Reader) ([]*Info, error) {
  79. var (
  80. s = bufio.NewScanner(r)
  81. out []*Info
  82. )
  83. for s.Scan() {
  84. if err := s.Err(); err != nil {
  85. return nil, err
  86. }
  87. var (
  88. p = &Info{}
  89. text = s.Text()
  90. optionalFields string
  91. )
  92. if _, err := fmt.Sscanf(text, mountinfoFormat,
  93. &p.ID, &p.Parent, &p.Major, &p.Minor,
  94. &p.Root, &p.Mountpoint, &p.Opts, &optionalFields); err != nil {
  95. return nil, fmt.Errorf("Scanning '%s' failed: %s", text, err)
  96. }
  97. // Safe as mountinfo encodes mountpoints with spaces as \040.
  98. index := strings.Index(text, " - ")
  99. postSeparatorFields := strings.Fields(text[index+3:])
  100. if len(postSeparatorFields) < 3 {
  101. return nil, fmt.Errorf("Error found less than 3 fields post '-' in %q", text)
  102. }
  103. if optionalFields != "-" {
  104. p.Optional = optionalFields
  105. }
  106. p.Fstype = postSeparatorFields[0]
  107. p.Source = postSeparatorFields[1]
  108. p.VfsOpts = strings.Join(postSeparatorFields[2:], " ")
  109. out = append(out, p)
  110. }
  111. return out, nil
  112. }
  113. func checkMountPointAvailable(dir string) bool {
  114. mountPoint := dir
  115. if mountPoint != "/" && strings.HasSuffix(mountPoint, "/") {
  116. mountPoint = mountPoint[0 : len(mountPoint)-1]
  117. }
  118. if mounted, err := mounted(mountPoint); err != nil || mounted {
  119. return false
  120. }
  121. return true
  122. }