github.com/toophy/docker@v1.8.2/volume/local/local.go (about)

     1  package local
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  	"sync"
    11  
    12  	"github.com/docker/docker/volume"
    13  )
    14  
    15  // VolumeDataPathName is the name of the directory where the volume data is stored.
    16  // It uses a very distintive name to avoid colissions migrating data between
    17  // Docker versions.
    18  const (
    19  	VolumeDataPathName = "_data"
    20  	volumesPathName    = "volumes"
    21  )
    22  
    23  var oldVfsDir = filepath.Join("vfs", "dir")
    24  
    25  func New(scope string) (*Root, error) {
    26  	rootDirectory := filepath.Join(scope, volumesPathName)
    27  
    28  	if err := os.MkdirAll(rootDirectory, 0700); err != nil {
    29  		return nil, err
    30  	}
    31  
    32  	r := &Root{
    33  		scope:   scope,
    34  		path:    rootDirectory,
    35  		volumes: make(map[string]*Volume),
    36  	}
    37  
    38  	dirs, err := ioutil.ReadDir(rootDirectory)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	for _, d := range dirs {
    44  		name := filepath.Base(d.Name())
    45  		r.volumes[name] = &Volume{
    46  			driverName: r.Name(),
    47  			name:       name,
    48  			path:       r.DataPath(name),
    49  		}
    50  	}
    51  	return r, nil
    52  }
    53  
    54  type Root struct {
    55  	m       sync.Mutex
    56  	scope   string
    57  	path    string
    58  	volumes map[string]*Volume
    59  }
    60  
    61  func (r *Root) DataPath(volumeName string) string {
    62  	return filepath.Join(r.path, volumeName, VolumeDataPathName)
    63  }
    64  
    65  func (r *Root) Name() string {
    66  	return "local"
    67  }
    68  
    69  func (r *Root) Create(name string) (volume.Volume, error) {
    70  	r.m.Lock()
    71  	defer r.m.Unlock()
    72  
    73  	v, exists := r.volumes[name]
    74  	if !exists {
    75  		path := r.DataPath(name)
    76  		if err := os.MkdirAll(path, 0755); err != nil {
    77  			if os.IsExist(err) {
    78  				return nil, fmt.Errorf("volume already exists under %s", filepath.Dir(path))
    79  			}
    80  			return nil, err
    81  		}
    82  		v = &Volume{
    83  			driverName: r.Name(),
    84  			name:       name,
    85  			path:       path,
    86  		}
    87  		r.volumes[name] = v
    88  	}
    89  	v.use()
    90  	return v, nil
    91  }
    92  
    93  func (r *Root) Remove(v volume.Volume) error {
    94  	r.m.Lock()
    95  	defer r.m.Unlock()
    96  	lv, ok := v.(*Volume)
    97  	if !ok {
    98  		return errors.New("unknown volume type")
    99  	}
   100  	lv.release()
   101  	if lv.usedCount == 0 {
   102  		realPath, err := filepath.EvalSymlinks(lv.path)
   103  		if err != nil {
   104  			return err
   105  		}
   106  		if !r.scopedPath(realPath) {
   107  			return fmt.Errorf("Unable to remove a directory of out the Docker root: %s", realPath)
   108  		}
   109  
   110  		if err := os.RemoveAll(realPath); err != nil {
   111  			return err
   112  		}
   113  
   114  		delete(r.volumes, lv.name)
   115  		return os.RemoveAll(filepath.Dir(lv.path))
   116  	}
   117  	return nil
   118  }
   119  
   120  // scopedPath verifies that the path where the volume is located
   121  // is under Docker's root and the valid local paths.
   122  func (r *Root) scopedPath(realPath string) bool {
   123  	// Volumes path for Docker version >= 1.7
   124  	if strings.HasPrefix(realPath, filepath.Join(r.scope, volumesPathName)) {
   125  		return true
   126  	}
   127  
   128  	// Volumes path for Docker version < 1.7
   129  	if strings.HasPrefix(realPath, filepath.Join(r.scope, oldVfsDir)) {
   130  		return true
   131  	}
   132  
   133  	return false
   134  }
   135  
   136  type Volume struct {
   137  	m         sync.Mutex
   138  	usedCount int
   139  	// unique name of the volume
   140  	name string
   141  	// path is the path on the host where the data lives
   142  	path string
   143  	// driverName is the name of the driver that created the volume.
   144  	driverName string
   145  }
   146  
   147  func (v *Volume) Name() string {
   148  	return v.name
   149  }
   150  
   151  func (v *Volume) DriverName() string {
   152  	return v.driverName
   153  }
   154  
   155  func (v *Volume) Path() string {
   156  	return v.path
   157  }
   158  
   159  func (v *Volume) Mount() (string, error) {
   160  	return v.path, nil
   161  }
   162  
   163  func (v *Volume) Unmount() error {
   164  	return nil
   165  }
   166  
   167  func (v *Volume) use() {
   168  	v.m.Lock()
   169  	v.usedCount++
   170  	v.m.Unlock()
   171  }
   172  
   173  func (v *Volume) release() {
   174  	v.m.Lock()
   175  	v.usedCount--
   176  	v.m.Unlock()
   177  }