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