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 }