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.

157 lines
4.1 KiB

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