github.com/psychoss/docker@v1.9.0/daemon/volumes.go (about)

     1  package daemon
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"github.com/Sirupsen/logrus"
    12  	"github.com/docker/docker/api/types"
    13  	derr "github.com/docker/docker/errors"
    14  	"github.com/docker/docker/pkg/chrootarchive"
    15  	"github.com/docker/docker/pkg/system"
    16  	"github.com/docker/docker/volume"
    17  )
    18  
    19  var (
    20  	// ErrVolumeReadonly is used to signal an error when trying to copy data into
    21  	// a volume mount that is not writable.
    22  	ErrVolumeReadonly = errors.New("mounted volume is marked read-only")
    23  )
    24  
    25  // mountPoint is the intersection point between a volume and a container. It
    26  // specifies which volume is to be used and where inside a container it should
    27  // be mounted.
    28  type mountPoint struct {
    29  	Name        string
    30  	Destination string
    31  	Driver      string
    32  	RW          bool
    33  	Volume      volume.Volume `json:"-"`
    34  	Source      string
    35  	Mode        string `json:"Relabel"` // Originally field was `Relabel`"
    36  }
    37  
    38  // Setup sets up a mount point by either mounting the volume if it is
    39  // configured, or creating the source directory if supplied.
    40  func (m *mountPoint) Setup() (string, error) {
    41  	if m.Volume != nil {
    42  		return m.Volume.Mount()
    43  	}
    44  
    45  	if len(m.Source) > 0 {
    46  		if _, err := os.Stat(m.Source); err != nil {
    47  			if !os.IsNotExist(err) {
    48  				return "", err
    49  			}
    50  			logrus.Warnf("Auto-creating non-existant volume host path %s, this is deprecated and will be removed soon", m.Source)
    51  			if err := system.MkdirAll(m.Source, 0755); err != nil {
    52  				return "", err
    53  			}
    54  		}
    55  		return m.Source, nil
    56  	}
    57  
    58  	return "", derr.ErrorCodeMountSetup
    59  }
    60  
    61  // hasResource checks whether the given absolute path for a container is in
    62  // this mount point. If the relative path starts with `../` then the resource
    63  // is outside of this mount point, but we can't simply check for this prefix
    64  // because it misses `..` which is also outside of the mount, so check both.
    65  func (m *mountPoint) hasResource(absolutePath string) bool {
    66  	relPath, err := filepath.Rel(m.Destination, absolutePath)
    67  
    68  	return err == nil && relPath != ".." && !strings.HasPrefix(relPath, fmt.Sprintf("..%c", filepath.Separator))
    69  }
    70  
    71  // Path returns the path of a volume in a mount point.
    72  func (m *mountPoint) Path() string {
    73  	if m.Volume != nil {
    74  		return m.Volume.Path()
    75  	}
    76  
    77  	return m.Source
    78  }
    79  
    80  // copyExistingContents copies from the source to the destination and
    81  // ensures the ownership is appropriately set.
    82  func copyExistingContents(source, destination string) error {
    83  	volList, err := ioutil.ReadDir(source)
    84  	if err != nil {
    85  		return err
    86  	}
    87  	if len(volList) > 0 {
    88  		srcList, err := ioutil.ReadDir(destination)
    89  		if err != nil {
    90  			return err
    91  		}
    92  		if len(srcList) == 0 {
    93  			// If the source volume is empty copy files from the root into the volume
    94  			if err := chrootarchive.CopyWithTar(source, destination); err != nil {
    95  				return err
    96  			}
    97  		}
    98  	}
    99  	return copyOwnership(source, destination)
   100  }
   101  
   102  // volumeToAPIType converts a volume.Volume to the type used by the remote API
   103  func volumeToAPIType(v volume.Volume) *types.Volume {
   104  	return &types.Volume{
   105  		Name:       v.Name(),
   106  		Driver:     v.DriverName(),
   107  		Mountpoint: v.Path(),
   108  	}
   109  }