github.com/rsampaio/docker@v0.7.2-0.20150827203920-fdc73cc3fc31/volume/local/local.go (about)

     1  // Package local provides the default implementation for volumes. It
     2  // is used to mount data volume containers and directories local to
     3  // the host server.
     4  package local
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  	"sync"
    14  
    15  	"github.com/docker/docker/volume"
    16  )
    17  
    18  // VolumeDataPathName is the name of the directory where the volume data is stored.
    19  // It uses a very distintive name to avoid collisions migrating data between
    20  // Docker versions.
    21  const (
    22  	VolumeDataPathName = "_data"
    23  	volumesPathName    = "volumes"
    24  )
    25  
    26  var (
    27  	// ErrNotFound is the typed error returned when the requested volume name can't be found
    28  	ErrNotFound = errors.New("volume not found")
    29  	oldVfsDir   = filepath.Join("vfs", "dir")
    30  )
    31  
    32  // New instantiates a new Root instance with the provided scope. Scope
    33  // is the base path that the Root instance uses to store its
    34  // volumes. The base path is created here if it does not exist.
    35  func New(scope string) (*Root, error) {
    36  	rootDirectory := filepath.Join(scope, volumesPathName)
    37  
    38  	if err := os.MkdirAll(rootDirectory, 0700); err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	r := &Root{
    43  		scope:   scope,
    44  		path:    rootDirectory,
    45  		volumes: make(map[string]*localVolume),
    46  	}
    47  
    48  	dirs, err := ioutil.ReadDir(rootDirectory)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	for _, d := range dirs {
    54  		name := filepath.Base(d.Name())
    55  		r.volumes[name] = &localVolume{
    56  			driverName: r.Name(),
    57  			name:       name,
    58  			path:       r.DataPath(name),
    59  		}
    60  	}
    61  
    62  	return r, nil
    63  }
    64  
    65  // Root implements the Driver interface for the volume package and
    66  // manages the creation/removal of volumes. It uses only standard vfs
    67  // commands to create/remove dirs within its provided scope.
    68  type Root struct {
    69  	m       sync.Mutex
    70  	scope   string
    71  	path    string
    72  	volumes map[string]*localVolume
    73  }
    74  
    75  // List lists all the volumes
    76  func (r *Root) List() []volume.Volume {
    77  	var ls []volume.Volume
    78  	for _, v := range r.volumes {
    79  		ls = append(ls, v)
    80  	}
    81  	return ls
    82  }
    83  
    84  // DataPath returns the constructed path of this volume.
    85  func (r *Root) DataPath(volumeName string) string {
    86  	return filepath.Join(r.path, volumeName, VolumeDataPathName)
    87  }
    88  
    89  // Name returns the name of Root, defined in the volume package in the DefaultDriverName constant.
    90  func (r *Root) Name() string {
    91  	return volume.DefaultDriverName
    92  }
    93  
    94  // Create creates a new volume.Volume with the provided name, creating
    95  // the underlying directory tree required for this volume in the
    96  // process.
    97  func (r *Root) Create(name string, _ map[string]string) (volume.Volume, error) {
    98  	r.m.Lock()
    99  	defer r.m.Unlock()
   100  
   101  	v, exists := r.volumes[name]
   102  	if exists {
   103  		return v, nil
   104  	}
   105  
   106  	path := r.DataPath(name)
   107  	if err := os.MkdirAll(path, 0755); err != nil {
   108  		if os.IsExist(err) {
   109  			return nil, fmt.Errorf("volume already exists under %s", filepath.Dir(path))
   110  		}
   111  		return nil, err
   112  	}
   113  	v = &localVolume{
   114  		driverName: r.Name(),
   115  		name:       name,
   116  		path:       path,
   117  	}
   118  	r.volumes[name] = v
   119  	return v, nil
   120  }
   121  
   122  // Remove removes the specified volume and all underlying data. If the
   123  // given volume does not belong to this driver and an error is
   124  // returned. The volume is reference counted, if all references are
   125  // not released then the volume is not removed.
   126  func (r *Root) Remove(v volume.Volume) error {
   127  	r.m.Lock()
   128  	defer r.m.Unlock()
   129  	lv, ok := v.(*localVolume)
   130  	if !ok {
   131  		return errors.New("unknown volume type")
   132  	}
   133  
   134  	realPath, err := filepath.EvalSymlinks(lv.path)
   135  	if err != nil {
   136  		return err
   137  	}
   138  	if !r.scopedPath(realPath) {
   139  		return fmt.Errorf("Unable to remove a directory of out the Docker root: %s", realPath)
   140  	}
   141  
   142  	if err := os.RemoveAll(realPath); err != nil {
   143  		return err
   144  	}
   145  
   146  	delete(r.volumes, lv.name)
   147  	return os.RemoveAll(filepath.Dir(lv.path))
   148  }
   149  
   150  // Get looks up the volume for the given name and returns it if found
   151  func (r *Root) Get(name string) (volume.Volume, error) {
   152  	r.m.Lock()
   153  	v, exists := r.volumes[name]
   154  	r.m.Unlock()
   155  	if !exists {
   156  		return nil, ErrNotFound
   157  	}
   158  	return v, nil
   159  }
   160  
   161  // scopedPath verifies that the path where the volume is located
   162  // is under Docker's root and the valid local paths.
   163  func (r *Root) scopedPath(realPath string) bool {
   164  	// Volumes path for Docker version >= 1.7
   165  	if strings.HasPrefix(realPath, filepath.Join(r.scope, volumesPathName)) {
   166  		return true
   167  	}
   168  
   169  	// Volumes path for Docker version < 1.7
   170  	if strings.HasPrefix(realPath, filepath.Join(r.scope, oldVfsDir)) {
   171  		return true
   172  	}
   173  
   174  	return false
   175  }
   176  
   177  // localVolume implements the Volume interface from the volume package and
   178  // represents the volumes created by Root.
   179  type localVolume struct {
   180  	m         sync.Mutex
   181  	usedCount int
   182  	// unique name of the volume
   183  	name string
   184  	// path is the path on the host where the data lives
   185  	path string
   186  	// driverName is the name of the driver that created the volume.
   187  	driverName string
   188  }
   189  
   190  // Name returns the name of the given Volume.
   191  func (v *localVolume) Name() string {
   192  	return v.name
   193  }
   194  
   195  // DriverName returns the driver that created the given Volume.
   196  func (v *localVolume) DriverName() string {
   197  	return v.driverName
   198  }
   199  
   200  // Path returns the data location.
   201  func (v *localVolume) Path() string {
   202  	return v.path
   203  }
   204  
   205  // Mount implements the localVolume interface, returning the data location.
   206  func (v *localVolume) Mount() (string, error) {
   207  	return v.path, nil
   208  }
   209  
   210  // Umount is for satisfying the localVolume interface and does not do anything in this driver.
   211  func (v *localVolume) Unmount() error {
   212  	return nil
   213  }