123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- // +build linux freebsd darwin solaris
- package volume
- import (
- "fmt"
- "path/filepath"
- "strings"
- )
- // read-write modes
- var rwModes = map[string]bool{
- "rw": true,
- "ro": true,
- }
- // label modes
- var labelModes = map[string]bool{
- "Z": true,
- "z": true,
- }
- // BackwardsCompatible decides whether this mount point can be
- // used in old versions of Docker or not.
- // Only bind mounts and local volumes can be used in old versions of Docker.
- func (m *MountPoint) BackwardsCompatible() bool {
- return len(m.Source) > 0 || m.Driver == DefaultDriverName
- }
- // 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))
- }
- // ParseMountSpec validates the configuration of mount information is valid.
- func ParseMountSpec(spec, volumeDriver string) (*MountPoint, error) {
- spec = filepath.ToSlash(spec)
- mp := &MountPoint{
- RW: true,
- Propagation: DefaultPropagationMode,
- }
- if strings.Count(spec, ":") > 2 {
- return nil, errInvalidSpec(spec)
- }
- arr := strings.SplitN(spec, ":", 3)
- if arr[0] == "" {
- return nil, errInvalidSpec(spec)
- }
- switch len(arr) {
- case 1:
- // Just a destination path in the container
- mp.Destination = filepath.Clean(arr[0])
- case 2:
- if isValid := ValidMountMode(arr[1]); isValid {
- // Destination + Mode is not a valid volume - volumes
- // cannot include a mode. eg /foo:rw
- return nil, errInvalidSpec(spec)
- }
- // Host Source Path or Name + Destination
- mp.Source = arr[0]
- mp.Destination = arr[1]
- case 3:
- // HostSourcePath+DestinationPath+Mode
- mp.Source = arr[0]
- mp.Destination = arr[1]
- mp.Mode = arr[2] // Mode field is used by SELinux to decide whether to apply label
- if !ValidMountMode(mp.Mode) {
- return nil, errInvalidMode(mp.Mode)
- }
- mp.RW = ReadWrite(mp.Mode)
- mp.Propagation = GetPropagation(mp.Mode)
- default:
- return nil, errInvalidSpec(spec)
- }
- //validate the volumes destination path
- mp.Destination = filepath.Clean(mp.Destination)
- if !filepath.IsAbs(mp.Destination) {
- return nil, fmt.Errorf("Invalid volume destination path: '%s' mount path must be absolute.", mp.Destination)
- }
- // Destination cannot be "/"
- if mp.Destination == "/" {
- return nil, fmt.Errorf("Invalid specification: destination can't be '/' in '%s'", spec)
- }
- name, source := ParseVolumeSource(mp.Source)
- if len(source) == 0 {
- mp.Source = "" // Clear it out as we previously assumed it was not a name
- mp.Driver = volumeDriver
- // Named volumes can't have propagation properties specified.
- // Their defaults will be decided by docker. This is just a
- // safeguard. Don't want to get into situations where named
- // volumes were mounted as '[r]shared' inside container and
- // container does further mounts under that volume and these
- // mounts become visible on host and later original volume
- // cleanup becomes an issue if container does not unmount
- // submounts explicitly.
- if HasPropagation(mp.Mode) {
- return nil, errInvalidSpec(spec)
- }
- } else {
- mp.Source = filepath.Clean(source)
- }
- copyData, isSet := getCopyMode(mp.Mode)
- // do not allow copy modes on binds
- if len(name) == 0 && isSet {
- return nil, errInvalidMode(mp.Mode)
- }
- mp.CopyData = copyData
- mp.Name = name
- return mp, nil
- }
- // ParseVolumeSource parses the origin sources that's mounted into the container.
- // It returns a name and a source. It looks to see if the spec passed in
- // is an absolute file. If it is, it assumes the spec is a source. If not,
- // it assumes the spec is a name.
- func ParseVolumeSource(spec string) (string, string) {
- if !filepath.IsAbs(spec) {
- return spec, ""
- }
- return "", spec
- }
- // IsVolumeNameValid checks a volume name in a platform specific manner.
- func IsVolumeNameValid(name string) (bool, error) {
- return true, nil
- }
- // ValidMountMode will make sure the mount mode is valid.
- // returns if it's a valid mount mode or not.
- func ValidMountMode(mode string) bool {
- rwModeCount := 0
- labelModeCount := 0
- propagationModeCount := 0
- copyModeCount := 0
- for _, o := range strings.Split(mode, ",") {
- switch {
- case rwModes[o]:
- rwModeCount++
- case labelModes[o]:
- labelModeCount++
- case propagationModes[o]:
- propagationModeCount++
- case copyModeExists(o):
- copyModeCount++
- default:
- return false
- }
- }
- // Only one string for each mode is allowed.
- if rwModeCount > 1 || labelModeCount > 1 || propagationModeCount > 1 || copyModeCount > 1 {
- return false
- }
- return true
- }
- // ReadWrite tells you if a mode string is a valid read-write mode or not.
- // If there are no specifications w.r.t read write mode, then by default
- // it returns true.
- func ReadWrite(mode string) bool {
- if !ValidMountMode(mode) {
- return false
- }
- for _, o := range strings.Split(mode, ",") {
- if o == "ro" {
- return false
- }
- }
- return true
- }
|