github.com/webwurst/docker@v1.7.0/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 }