package daemon import ( "errors" "fmt" "io/ioutil" "os" "path/filepath" "strings" "github.com/Sirupsen/logrus" "github.com/docker/docker/api/types" derr "github.com/docker/docker/errors" "github.com/docker/docker/pkg/chrootarchive" "github.com/docker/docker/pkg/system" "github.com/docker/docker/volume" ) var ( // ErrVolumeReadonly is used to signal an error when trying to copy data into // a volume mount that is not writable. ErrVolumeReadonly = errors.New("mounted volume is marked read-only") ) // mountPoint is the intersection point between a volume and a container. It // specifies which volume is to be used and where inside a container it should // be mounted. type mountPoint struct { Name string Destination string Driver string RW bool Volume volume.Volume `json:"-"` Source string Mode string `json:"Relabel"` // Originally field was `Relabel`" } // Setup sets up a mount point by either mounting the volume if it is // configured, or creating the source directory if supplied. func (m *mountPoint) Setup() (string, error) { if m.Volume != nil { return m.Volume.Mount() } if len(m.Source) > 0 { if _, err := os.Stat(m.Source); err != nil { if !os.IsNotExist(err) { return "", err } logrus.Warnf("Auto-creating non-existant volume host path %s, this is deprecated and will be removed soon", m.Source) if err := system.MkdirAll(m.Source, 0755); err != nil { return "", err } } return m.Source, nil } return "", derr.ErrorCodeMountSetup } // 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)) } // Path returns the path of a volume in a mount point. func (m *mountPoint) Path() string { if m.Volume != nil { return m.Volume.Path() } return m.Source } // copyExistingContents copies from the source to the destination and // ensures the ownership is appropriately set. func copyExistingContents(source, destination string) error { volList, err := ioutil.ReadDir(source) if err != nil { return err } if len(volList) > 0 { srcList, err := ioutil.ReadDir(destination) if err != nil { return err } if len(srcList) == 0 { // If the source volume is empty copy files from the root into the volume if err := chrootarchive.CopyWithTar(source, destination); err != nil { return err } } } return copyOwnership(source, destination) } // volumeToAPIType converts a volume.Volume to the type used by the remote API func volumeToAPIType(v volume.Volume) *types.Volume { return &types.Volume{ Name: v.Name(), Driver: v.DriverName(), Mountpoint: v.Path(), } }