2014-04-17 21:43:01 +00:00
|
|
|
package daemon
|
2014-02-15 01:15:40 +00:00
|
|
|
|
|
|
|
import (
|
2015-05-13 01:21:26 +00:00
|
|
|
"errors"
|
2014-02-15 01:15:40 +00:00
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2014-05-05 21:45:14 +00:00
|
|
|
|
2014-10-29 19:06:51 +00:00
|
|
|
"github.com/docker/docker/pkg/chrootarchive"
|
2015-06-11 18:29:29 +00:00
|
|
|
"github.com/docker/docker/pkg/system"
|
2015-05-19 20:05:25 +00:00
|
|
|
"github.com/docker/docker/volume"
|
2014-02-15 01:15:40 +00:00
|
|
|
)
|
|
|
|
|
2015-05-13 01:21:26 +00:00
|
|
|
// ErrVolumeReadonly is used to signal an error when trying to copy data into
|
|
|
|
// a volume mount that is not writable.
|
|
|
|
var ErrVolumeReadonly = errors.New("mounted volume is marked read-only")
|
|
|
|
|
2015-07-16 21:14:58 +00:00
|
|
|
// TODO Windows. Further platform refactoring can still be done in volumes*.go
|
|
|
|
|
2015-05-19 20:05:25 +00:00
|
|
|
type mountPoint struct {
|
|
|
|
Name string
|
|
|
|
Destination string
|
|
|
|
Driver string
|
|
|
|
RW bool
|
|
|
|
Volume volume.Volume `json:"-"`
|
|
|
|
Source string
|
2015-07-22 13:24:35 +00:00
|
|
|
Mode string `json:"Relabel"` // Originally field was `Relabel`"
|
2015-05-19 20:05:25 +00:00
|
|
|
}
|
2014-12-16 15:06:45 +00:00
|
|
|
|
2015-05-19 20:05:25 +00:00
|
|
|
func (m *mountPoint) Setup() (string, error) {
|
|
|
|
if m.Volume != nil {
|
|
|
|
return m.Volume.Mount()
|
2014-08-21 21:57:46 +00:00
|
|
|
}
|
|
|
|
|
2015-05-19 20:05:25 +00:00
|
|
|
if len(m.Source) > 0 {
|
|
|
|
if _, err := os.Stat(m.Source); err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return "", err
|
|
|
|
}
|
2015-06-11 18:29:29 +00:00
|
|
|
if err := system.MkdirAll(m.Source, 0755); err != nil {
|
2015-05-19 20:05:25 +00:00
|
|
|
return "", err
|
|
|
|
}
|
2015-02-10 18:45:50 +00:00
|
|
|
}
|
2015-05-19 20:05:25 +00:00
|
|
|
return m.Source, nil
|
2014-08-28 14:18:08 +00:00
|
|
|
}
|
|
|
|
|
2015-05-19 20:05:25 +00:00
|
|
|
return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined")
|
2014-12-16 15:06:45 +00:00
|
|
|
}
|
|
|
|
|
2015-05-13 01:21:26 +00:00
|
|
|
// hasResource checks whether the given absolute path for a container is in
|
|
|
|
// this mount point. If the relative path starts with `../` then the resource
|
|
|
|
// is outside of this mount point, but we can't simply check for this prefix
|
|
|
|
// because it misses `..` which is also outside of the mount, so check both.
|
|
|
|
func (m *mountPoint) hasResource(absolutePath string) bool {
|
|
|
|
relPath, err := filepath.Rel(m.Destination, absolutePath)
|
|
|
|
|
|
|
|
return err == nil && relPath != ".." && !strings.HasPrefix(relPath, fmt.Sprintf("..%c", filepath.Separator))
|
|
|
|
}
|
|
|
|
|
2015-05-19 20:05:25 +00:00
|
|
|
func (m *mountPoint) Path() string {
|
|
|
|
if m.Volume != nil {
|
|
|
|
return m.Volume.Path()
|
2014-08-21 21:57:46 +00:00
|
|
|
}
|
|
|
|
|
2015-05-19 20:05:25 +00:00
|
|
|
return m.Source
|
2014-08-28 14:18:08 +00:00
|
|
|
}
|
2014-08-21 21:57:46 +00:00
|
|
|
|
2014-06-02 21:16:30 +00:00
|
|
|
func copyExistingContents(source, destination string) error {
|
|
|
|
volList, err := ioutil.ReadDir(source)
|
2014-05-19 22:18:37 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(volList) > 0 {
|
2014-06-02 21:16:30 +00:00
|
|
|
srcList, err := ioutil.ReadDir(destination)
|
2014-05-19 22:04:51 +00:00
|
|
|
if err != nil {
|
2014-03-18 22:28:40 +00:00
|
|
|
return err
|
2014-02-15 01:15:40 +00:00
|
|
|
}
|
2014-05-19 22:18:37 +00:00
|
|
|
if len(srcList) == 0 {
|
|
|
|
// If the source volume is empty copy files from the root into the volume
|
2014-10-29 19:06:51 +00:00
|
|
|
if err := chrootarchive.CopyWithTar(source, destination); err != nil {
|
2014-02-15 01:15:40 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2014-05-19 22:18:37 +00:00
|
|
|
}
|
2014-06-02 21:16:30 +00:00
|
|
|
return copyOwnership(source, destination)
|
|
|
|
}
|