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