github.com/hustcat/docker@v1.3.3-0.20160314103604-901c67a8eeab/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 "encoding/json" 8 "fmt" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "sync" 13 14 "github.com/Sirupsen/logrus" 15 "github.com/docker/docker/pkg/idtools" 16 "github.com/docker/docker/pkg/mount" 17 "github.com/docker/docker/utils" 18 "github.com/docker/docker/volume" 19 ) 20 21 // VolumeDataPathName is the name of the directory where the volume data is stored. 22 // It uses a very distinctive name to avoid collisions migrating data between 23 // Docker versions. 24 const ( 25 VolumeDataPathName = "_data" 26 volumesPathName = "volumes" 27 ) 28 29 var ( 30 // ErrNotFound is the typed error returned when the requested volume name can't be found 31 ErrNotFound = fmt.Errorf("volume not found") 32 // volumeNameRegex ensures the name assigned for the volume is valid. 33 // This name is used to create the bind directory, so we need to avoid characters that 34 // would make the path to escape the root directory. 35 volumeNameRegex = utils.RestrictedVolumeNamePattern 36 ) 37 38 type validationError struct { 39 error 40 } 41 42 func (validationError) IsValidationError() bool { 43 return true 44 } 45 46 type activeMount struct { 47 count uint64 48 mounted bool 49 } 50 51 // New instantiates a new Root instance with the provided scope. Scope 52 // is the base path that the Root instance uses to store its 53 // volumes. The base path is created here if it does not exist. 54 func New(scope string, rootUID, rootGID int) (*Root, error) { 55 rootDirectory := filepath.Join(scope, volumesPathName) 56 57 if err := idtools.MkdirAllAs(rootDirectory, 0700, rootUID, rootGID); err != nil { 58 return nil, err 59 } 60 61 r := &Root{ 62 scope: scope, 63 path: rootDirectory, 64 volumes: make(map[string]*localVolume), 65 rootUID: rootUID, 66 rootGID: rootGID, 67 } 68 69 dirs, err := ioutil.ReadDir(rootDirectory) 70 if err != nil { 71 return nil, err 72 } 73 74 mountInfos, err := mount.GetMounts() 75 if err != nil { 76 logrus.Debugf("error looking up mounts for local volume cleanup: %v", err) 77 } 78 79 for _, d := range dirs { 80 name := filepath.Base(d.Name()) 81 v := &localVolume{ 82 driverName: r.Name(), 83 name: name, 84 path: r.DataPath(name), 85 } 86 r.volumes[name] = v 87 if b, err := ioutil.ReadFile(filepath.Join(name, "opts.json")); err == nil { 88 if err := json.Unmarshal(b, v.opts); err != nil { 89 return nil, err 90 } 91 92 // unmount anything that may still be mounted (for example, from an unclean shutdown) 93 for _, info := range mountInfos { 94 if info.Mountpoint == v.path { 95 mount.Unmount(v.path) 96 break 97 } 98 } 99 } 100 } 101 102 return r, nil 103 } 104 105 // Root implements the Driver interface for the volume package and 106 // manages the creation/removal of volumes. It uses only standard vfs 107 // commands to create/remove dirs within its provided scope. 108 type Root struct { 109 m sync.Mutex 110 scope string 111 path string 112 volumes map[string]*localVolume 113 rootUID int 114 rootGID int 115 } 116 117 // List lists all the volumes 118 func (r *Root) List() ([]volume.Volume, error) { 119 var ls []volume.Volume 120 for _, v := range r.volumes { 121 ls = append(ls, v) 122 } 123 return ls, nil 124 } 125 126 // DataPath returns the constructed path of this volume. 127 func (r *Root) DataPath(volumeName string) string { 128 return filepath.Join(r.path, volumeName, VolumeDataPathName) 129 } 130 131 // Name returns the name of Root, defined in the volume package in the DefaultDriverName constant. 132 func (r *Root) Name() string { 133 return volume.DefaultDriverName 134 } 135 136 // Create creates a new volume.Volume with the provided name, creating 137 // the underlying directory tree required for this volume in the 138 // process. 139 func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error) { 140 if err := r.validateName(name); err != nil { 141 return nil, err 142 } 143 144 r.m.Lock() 145 defer r.m.Unlock() 146 147 v, exists := r.volumes[name] 148 if exists { 149 return v, nil 150 } 151 152 path := r.DataPath(name) 153 if err := idtools.MkdirAllAs(path, 0755, r.rootUID, r.rootGID); err != nil { 154 if os.IsExist(err) { 155 return nil, fmt.Errorf("volume already exists under %s", filepath.Dir(path)) 156 } 157 return nil, err 158 } 159 160 var err error 161 defer func() { 162 if err != nil { 163 os.RemoveAll(filepath.Dir(path)) 164 } 165 }() 166 167 v = &localVolume{ 168 driverName: r.Name(), 169 name: name, 170 path: path, 171 } 172 173 if opts != nil { 174 if err = setOpts(v, opts); err != nil { 175 return nil, err 176 } 177 var b []byte 178 b, err = json.Marshal(v.opts) 179 if err != nil { 180 return nil, err 181 } 182 if err = ioutil.WriteFile(filepath.Join(filepath.Dir(path), "opts.json"), b, 600); err != nil { 183 return nil, err 184 } 185 } 186 187 r.volumes[name] = v 188 return v, nil 189 } 190 191 // Remove removes the specified volume and all underlying data. If the 192 // given volume does not belong to this driver and an error is 193 // returned. The volume is reference counted, if all references are 194 // not released then the volume is not removed. 195 func (r *Root) Remove(v volume.Volume) error { 196 r.m.Lock() 197 defer r.m.Unlock() 198 199 lv, ok := v.(*localVolume) 200 if !ok { 201 return fmt.Errorf("unknown volume type") 202 } 203 204 realPath, err := filepath.EvalSymlinks(lv.path) 205 if err != nil { 206 if !os.IsNotExist(err) { 207 return err 208 } 209 realPath = filepath.Dir(lv.path) 210 } 211 212 if !r.scopedPath(realPath) { 213 return fmt.Errorf("Unable to remove a directory of out the Docker root %s: %s", r.scope, realPath) 214 } 215 216 if err := removePath(realPath); err != nil { 217 return err 218 } 219 220 delete(r.volumes, lv.name) 221 return removePath(filepath.Dir(lv.path)) 222 } 223 224 func removePath(path string) error { 225 if err := os.RemoveAll(path); err != nil { 226 if os.IsNotExist(err) { 227 return nil 228 } 229 return err 230 } 231 return nil 232 } 233 234 // Get looks up the volume for the given name and returns it if found 235 func (r *Root) Get(name string) (volume.Volume, error) { 236 r.m.Lock() 237 v, exists := r.volumes[name] 238 r.m.Unlock() 239 if !exists { 240 return nil, ErrNotFound 241 } 242 return v, nil 243 } 244 245 func (r *Root) validateName(name string) error { 246 if !volumeNameRegex.MatchString(name) { 247 return validationError{fmt.Errorf("%q includes invalid characters for a local volume name, only %q are allowed", name, utils.RestrictedNameChars)} 248 } 249 return nil 250 } 251 252 // localVolume implements the Volume interface from the volume package and 253 // represents the volumes created by Root. 254 type localVolume struct { 255 m sync.Mutex 256 usedCount int 257 // unique name of the volume 258 name string 259 // path is the path on the host where the data lives 260 path string 261 // driverName is the name of the driver that created the volume. 262 driverName string 263 // opts is the parsed list of options used to create the volume 264 opts *optsConfig 265 // active refcounts the active mounts 266 active activeMount 267 } 268 269 // Name returns the name of the given Volume. 270 func (v *localVolume) Name() string { 271 return v.name 272 } 273 274 // DriverName returns the driver that created the given Volume. 275 func (v *localVolume) DriverName() string { 276 return v.driverName 277 } 278 279 // Path returns the data location. 280 func (v *localVolume) Path() string { 281 return v.path 282 } 283 284 // Mount implements the localVolume interface, returning the data location. 285 func (v *localVolume) Mount() (string, error) { 286 v.m.Lock() 287 defer v.m.Unlock() 288 if v.opts != nil { 289 if !v.active.mounted { 290 if err := v.mount(); err != nil { 291 return "", err 292 } 293 v.active.mounted = true 294 } 295 v.active.count++ 296 } 297 return v.path, nil 298 } 299 300 // Umount is for satisfying the localVolume interface and does not do anything in this driver. 301 func (v *localVolume) Unmount() error { 302 v.m.Lock() 303 defer v.m.Unlock() 304 if v.opts != nil { 305 v.active.count-- 306 if v.active.count == 0 { 307 if err := mount.Unmount(v.path); err != nil { 308 v.active.count++ 309 return err 310 } 311 v.active.mounted = false 312 } 313 } 314 return nil 315 } 316 317 func validateOpts(opts map[string]string) error { 318 for opt := range opts { 319 if !validOpts[opt] { 320 return validationError{fmt.Errorf("invalid option key: %q", opt)} 321 } 322 } 323 return nil 324 }