volume_unix.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // +build linux freebsd darwin solaris
  2. package volume
  3. import (
  4. "fmt"
  5. "path/filepath"
  6. "strings"
  7. )
  8. // read-write modes
  9. var rwModes = map[string]bool{
  10. "rw": true,
  11. "ro": true,
  12. }
  13. // label modes
  14. var labelModes = map[string]bool{
  15. "Z": true,
  16. "z": true,
  17. }
  18. // BackwardsCompatible decides whether this mount point can be
  19. // used in old versions of Docker or not.
  20. // Only bind mounts and local volumes can be used in old versions of Docker.
  21. func (m *MountPoint) BackwardsCompatible() bool {
  22. return len(m.Source) > 0 || m.Driver == DefaultDriverName
  23. }
  24. // HasResource checks whether the given absolute path for a container is in
  25. // this mount point. If the relative path starts with `../` then the resource
  26. // is outside of this mount point, but we can't simply check for this prefix
  27. // because it misses `..` which is also outside of the mount, so check both.
  28. func (m *MountPoint) HasResource(absolutePath string) bool {
  29. relPath, err := filepath.Rel(m.Destination, absolutePath)
  30. return err == nil && relPath != ".." && !strings.HasPrefix(relPath, fmt.Sprintf("..%c", filepath.Separator))
  31. }
  32. // ParseMountSpec validates the configuration of mount information is valid.
  33. func ParseMountSpec(spec, volumeDriver string) (*MountPoint, error) {
  34. spec = filepath.ToSlash(spec)
  35. mp := &MountPoint{
  36. RW: true,
  37. Propagation: DefaultPropagationMode,
  38. }
  39. if strings.Count(spec, ":") > 2 {
  40. return nil, errInvalidSpec(spec)
  41. }
  42. arr := strings.SplitN(spec, ":", 3)
  43. if arr[0] == "" {
  44. return nil, errInvalidSpec(spec)
  45. }
  46. switch len(arr) {
  47. case 1:
  48. // Just a destination path in the container
  49. mp.Destination = filepath.Clean(arr[0])
  50. case 2:
  51. if isValid := ValidMountMode(arr[1]); isValid {
  52. // Destination + Mode is not a valid volume - volumes
  53. // cannot include a mode. eg /foo:rw
  54. return nil, errInvalidSpec(spec)
  55. }
  56. // Host Source Path or Name + Destination
  57. mp.Source = arr[0]
  58. mp.Destination = arr[1]
  59. case 3:
  60. // HostSourcePath+DestinationPath+Mode
  61. mp.Source = arr[0]
  62. mp.Destination = arr[1]
  63. mp.Mode = arr[2] // Mode field is used by SELinux to decide whether to apply label
  64. if !ValidMountMode(mp.Mode) {
  65. return nil, errInvalidMode(mp.Mode)
  66. }
  67. mp.RW = ReadWrite(mp.Mode)
  68. mp.Propagation = GetPropagation(mp.Mode)
  69. default:
  70. return nil, errInvalidSpec(spec)
  71. }
  72. //validate the volumes destination path
  73. mp.Destination = filepath.Clean(mp.Destination)
  74. if !filepath.IsAbs(mp.Destination) {
  75. return nil, fmt.Errorf("Invalid volume destination path: '%s' mount path must be absolute.", mp.Destination)
  76. }
  77. // Destination cannot be "/"
  78. if mp.Destination == "/" {
  79. return nil, fmt.Errorf("Invalid specification: destination can't be '/' in '%s'", spec)
  80. }
  81. name, source := ParseVolumeSource(mp.Source)
  82. if len(source) == 0 {
  83. mp.Source = "" // Clear it out as we previously assumed it was not a name
  84. mp.Driver = volumeDriver
  85. // Named volumes can't have propagation properties specified.
  86. // Their defaults will be decided by docker. This is just a
  87. // safeguard. Don't want to get into situations where named
  88. // volumes were mounted as '[r]shared' inside container and
  89. // container does further mounts under that volume and these
  90. // mounts become visible on host and later original volume
  91. // cleanup becomes an issue if container does not unmount
  92. // submounts explicitly.
  93. if HasPropagation(mp.Mode) {
  94. return nil, errInvalidSpec(spec)
  95. }
  96. } else {
  97. mp.Source = filepath.Clean(source)
  98. }
  99. copyData, isSet := getCopyMode(mp.Mode)
  100. // do not allow copy modes on binds
  101. if len(name) == 0 && isSet {
  102. return nil, errInvalidMode(mp.Mode)
  103. }
  104. mp.CopyData = copyData
  105. mp.Name = name
  106. return mp, nil
  107. }
  108. // ParseVolumeSource parses the origin sources that's mounted into the container.
  109. // It returns a name and a source. It looks to see if the spec passed in
  110. // is an absolute file. If it is, it assumes the spec is a source. If not,
  111. // it assumes the spec is a name.
  112. func ParseVolumeSource(spec string) (string, string) {
  113. if !filepath.IsAbs(spec) {
  114. return spec, ""
  115. }
  116. return "", spec
  117. }
  118. // IsVolumeNameValid checks a volume name in a platform specific manner.
  119. func IsVolumeNameValid(name string) (bool, error) {
  120. return true, nil
  121. }
  122. // ValidMountMode will make sure the mount mode is valid.
  123. // returns if it's a valid mount mode or not.
  124. func ValidMountMode(mode string) bool {
  125. rwModeCount := 0
  126. labelModeCount := 0
  127. propagationModeCount := 0
  128. copyModeCount := 0
  129. for _, o := range strings.Split(mode, ",") {
  130. switch {
  131. case rwModes[o]:
  132. rwModeCount++
  133. case labelModes[o]:
  134. labelModeCount++
  135. case propagationModes[o]:
  136. propagationModeCount++
  137. case copyModeExists(o):
  138. copyModeCount++
  139. default:
  140. return false
  141. }
  142. }
  143. // Only one string for each mode is allowed.
  144. if rwModeCount > 1 || labelModeCount > 1 || propagationModeCount > 1 || copyModeCount > 1 {
  145. return false
  146. }
  147. return true
  148. }
  149. // ReadWrite tells you if a mode string is a valid read-write mode or not.
  150. // If there are no specifications w.r.t read write mode, then by default
  151. // it returns true.
  152. func ReadWrite(mode string) bool {
  153. if !ValidMountMode(mode) {
  154. return false
  155. }
  156. for _, o := range strings.Split(mode, ",") {
  157. if o == "ro" {
  158. return false
  159. }
  160. }
  161. return true
  162. }