volumes.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. package daemon
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "github.com/Sirupsen/logrus"
  9. "github.com/docker/docker/pkg/chrootarchive"
  10. "github.com/docker/docker/pkg/system"
  11. "github.com/docker/docker/runconfig"
  12. "github.com/docker/docker/volume"
  13. "github.com/docker/docker/volume/local"
  14. "github.com/docker/libcontainer/label"
  15. )
  16. type mountPoint struct {
  17. Name string
  18. Destination string
  19. Driver string
  20. RW bool
  21. Volume volume.Volume `json:"-"`
  22. Source string
  23. Relabel string
  24. }
  25. func (m *mountPoint) Setup() (string, error) {
  26. if m.Volume != nil {
  27. return m.Volume.Mount()
  28. }
  29. if len(m.Source) > 0 {
  30. if _, err := os.Stat(m.Source); err != nil {
  31. if !os.IsNotExist(err) {
  32. return "", err
  33. }
  34. if err := system.MkdirAll(m.Source, 0755); err != nil {
  35. return "", err
  36. }
  37. }
  38. return m.Source, nil
  39. }
  40. return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined")
  41. }
  42. func (m *mountPoint) Path() string {
  43. if m.Volume != nil {
  44. return m.Volume.Path()
  45. }
  46. return m.Source
  47. }
  48. // BackwardsCompatible decides whether this mount point can be
  49. // used in old versions of Docker or not.
  50. // Only bind mounts and local volumes can be used in old versions of Docker.
  51. func (m *mountPoint) BackwardsCompatible() bool {
  52. return len(m.Source) > 0 || m.Driver == volume.DefaultDriverName
  53. }
  54. func parseBindMount(spec string, mountLabel string, config *runconfig.Config) (*mountPoint, error) {
  55. bind := &mountPoint{
  56. RW: true,
  57. }
  58. arr := strings.Split(spec, ":")
  59. switch len(arr) {
  60. case 2:
  61. bind.Destination = arr[1]
  62. case 3:
  63. bind.Destination = arr[1]
  64. mode := arr[2]
  65. if !validMountMode(mode) {
  66. return nil, fmt.Errorf("invalid mode for volumes-from: %s", mode)
  67. }
  68. bind.RW = rwModes[mode]
  69. // Relabel will apply a SELinux label, if necessary
  70. bind.Relabel = mode
  71. default:
  72. return nil, fmt.Errorf("Invalid volume specification: %s", spec)
  73. }
  74. name, source, err := parseVolumeSource(arr[0])
  75. if err != nil {
  76. return nil, err
  77. }
  78. if len(source) == 0 {
  79. bind.Driver = config.VolumeDriver
  80. if len(bind.Driver) == 0 {
  81. bind.Driver = volume.DefaultDriverName
  82. }
  83. } else {
  84. bind.Source = filepath.Clean(source)
  85. }
  86. bind.Name = name
  87. bind.Destination = filepath.Clean(bind.Destination)
  88. return bind, nil
  89. }
  90. func parseVolumesFrom(spec string) (string, string, error) {
  91. if len(spec) == 0 {
  92. return "", "", fmt.Errorf("malformed volumes-from specification: %s", spec)
  93. }
  94. specParts := strings.SplitN(spec, ":", 2)
  95. id := specParts[0]
  96. mode := "rw"
  97. if len(specParts) == 2 {
  98. mode = specParts[1]
  99. if !validMountMode(mode) {
  100. return "", "", fmt.Errorf("invalid mode for volumes-from: %s", mode)
  101. }
  102. }
  103. return id, mode, nil
  104. }
  105. // read-write modes
  106. var rwModes = map[string]bool{
  107. "rw": true,
  108. "rw,Z": true,
  109. "rw,z": true,
  110. "z,rw": true,
  111. "Z,rw": true,
  112. "Z": true,
  113. "z": true,
  114. }
  115. // read-only modes
  116. var roModes = map[string]bool{
  117. "ro": true,
  118. "ro,Z": true,
  119. "ro,z": true,
  120. "z,ro": true,
  121. "Z,ro": true,
  122. }
  123. func validMountMode(mode string) bool {
  124. return roModes[mode] || rwModes[mode]
  125. }
  126. func copyExistingContents(source, destination string) error {
  127. volList, err := ioutil.ReadDir(source)
  128. if err != nil {
  129. return err
  130. }
  131. if len(volList) > 0 {
  132. srcList, err := ioutil.ReadDir(destination)
  133. if err != nil {
  134. return err
  135. }
  136. if len(srcList) == 0 {
  137. // If the source volume is empty copy files from the root into the volume
  138. if err := chrootarchive.CopyWithTar(source, destination); err != nil {
  139. return err
  140. }
  141. }
  142. }
  143. return copyOwnership(source, destination)
  144. }
  145. // registerMountPoints initializes the container mount points with the configured volumes and bind mounts.
  146. // It follows the next sequence to decide what to mount in each final destination:
  147. //
  148. // 1. Select the previously configured mount points for the containers, if any.
  149. // 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination.
  150. // 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations.
  151. func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runconfig.HostConfig) error {
  152. binds := map[string]bool{}
  153. mountPoints := map[string]*mountPoint{}
  154. // 1. Read already configured mount points.
  155. for name, point := range container.MountPoints {
  156. mountPoints[name] = point
  157. }
  158. // 2. Read volumes from other containers.
  159. for _, v := range hostConfig.VolumesFrom {
  160. containerID, mode, err := parseVolumesFrom(v)
  161. if err != nil {
  162. return err
  163. }
  164. c, err := daemon.Get(containerID)
  165. if err != nil {
  166. return err
  167. }
  168. for _, m := range c.MountPoints {
  169. cp := &mountPoint{
  170. Name: m.Name,
  171. Source: m.Source,
  172. RW: m.RW && !roModes[mode],
  173. Driver: m.Driver,
  174. Destination: m.Destination,
  175. }
  176. if len(cp.Source) == 0 {
  177. v, err := createVolume(cp.Name, cp.Driver)
  178. if err != nil {
  179. return err
  180. }
  181. cp.Volume = v
  182. }
  183. mountPoints[cp.Destination] = cp
  184. }
  185. }
  186. // 3. Read bind mounts
  187. for _, b := range hostConfig.Binds {
  188. // #10618
  189. bind, err := parseBindMount(b, container.MountLabel, container.Config)
  190. if err != nil {
  191. return err
  192. }
  193. if binds[bind.Destination] {
  194. return fmt.Errorf("Duplicate bind mount %s", bind.Destination)
  195. }
  196. if len(bind.Name) > 0 && len(bind.Driver) > 0 {
  197. // create the volume
  198. v, err := createVolume(bind.Name, bind.Driver)
  199. if err != nil {
  200. return err
  201. }
  202. bind.Volume = v
  203. bind.Source = v.Path()
  204. // Since this is just a named volume and not a typical bind, set to shared mode `z`
  205. if bind.Relabel == "" {
  206. bind.Relabel = "z"
  207. }
  208. }
  209. if err := label.Relabel(bind.Source, container.MountLabel, bind.Relabel); err != nil {
  210. return err
  211. }
  212. binds[bind.Destination] = true
  213. mountPoints[bind.Destination] = bind
  214. }
  215. // Keep backwards compatible structures
  216. bcVolumes := map[string]string{}
  217. bcVolumesRW := map[string]bool{}
  218. for _, m := range mountPoints {
  219. if m.BackwardsCompatible() {
  220. bcVolumes[m.Destination] = m.Path()
  221. bcVolumesRW[m.Destination] = m.RW
  222. }
  223. }
  224. container.Lock()
  225. container.MountPoints = mountPoints
  226. container.Volumes = bcVolumes
  227. container.VolumesRW = bcVolumesRW
  228. container.Unlock()
  229. return nil
  230. }
  231. // TODO Windows. Factor out as not relevant (as Windows daemon support not in pre-1.7)
  232. // verifyVolumesInfo ports volumes configured for the containers pre docker 1.7.
  233. // It reads the container configuration and creates valid mount points for the old volumes.
  234. func (daemon *Daemon) verifyVolumesInfo(container *Container) error {
  235. // Inspect old structures only when we're upgrading from old versions
  236. // to versions >= 1.7 and the MountPoints has not been populated with volumes data.
  237. if len(container.MountPoints) == 0 && len(container.Volumes) > 0 {
  238. for destination, hostPath := range container.Volumes {
  239. vfsPath := filepath.Join(daemon.root, "vfs", "dir")
  240. rw := container.VolumesRW != nil && container.VolumesRW[destination]
  241. if strings.HasPrefix(hostPath, vfsPath) {
  242. id := filepath.Base(hostPath)
  243. if err := migrateVolume(id, hostPath); err != nil {
  244. return err
  245. }
  246. container.addLocalMountPoint(id, destination, rw)
  247. } else { // Bind mount
  248. id, source, err := parseVolumeSource(hostPath)
  249. // We should not find an error here coming
  250. // from the old configuration, but who knows.
  251. if err != nil {
  252. return err
  253. }
  254. container.addBindMountPoint(id, source, destination, rw)
  255. }
  256. }
  257. } else if len(container.MountPoints) > 0 {
  258. // Volumes created with a Docker version >= 1.7. We verify integrity in case of data created
  259. // with Docker 1.7 RC versions that put the information in
  260. // DOCKER_ROOT/volumes/VOLUME_ID rather than DOCKER_ROOT/volumes/VOLUME_ID/_container_data.
  261. l, err := getVolumeDriver(volume.DefaultDriverName)
  262. if err != nil {
  263. return err
  264. }
  265. for _, m := range container.MountPoints {
  266. if m.Driver != volume.DefaultDriverName {
  267. continue
  268. }
  269. dataPath := l.(*local.Root).DataPath(m.Name)
  270. volumePath := filepath.Dir(dataPath)
  271. d, err := ioutil.ReadDir(volumePath)
  272. if err != nil {
  273. // If the volume directory doesn't exist yet it will be recreated,
  274. // so we only return the error when there is a different issue.
  275. if !os.IsNotExist(err) {
  276. return err
  277. }
  278. // Do not check when the volume directory does not exist.
  279. continue
  280. }
  281. if validVolumeLayout(d) {
  282. continue
  283. }
  284. if err := os.Mkdir(dataPath, 0755); err != nil {
  285. return err
  286. }
  287. // Move data inside the data directory
  288. for _, f := range d {
  289. oldp := filepath.Join(volumePath, f.Name())
  290. newp := filepath.Join(dataPath, f.Name())
  291. if err := os.Rename(oldp, newp); err != nil {
  292. logrus.Errorf("Unable to move %s to %s\n", oldp, newp)
  293. }
  294. }
  295. }
  296. return container.ToDisk()
  297. }
  298. return nil
  299. }
  300. func createVolume(name, driverName string) (volume.Volume, error) {
  301. vd, err := getVolumeDriver(driverName)
  302. if err != nil {
  303. return nil, err
  304. }
  305. return vd.Create(name)
  306. }
  307. func removeVolume(v volume.Volume) error {
  308. vd, err := getVolumeDriver(v.DriverName())
  309. if err != nil {
  310. return nil
  311. }
  312. return vd.Remove(v)
  313. }