volume_unix.go 5.0 KB

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