github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/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 if !d.IsDir() { 81 continue 82 } 83 84 name := filepath.Base(d.Name()) 85 v := &localVolume{ 86 driverName: r.Name(), 87 name: name, 88 path: r.DataPath(name), 89 } 90 r.volumes[name] = v 91 if b, err := ioutil.ReadFile(filepath.Join(name, "opts.json")); err == nil { 92 if err := json.Unmarshal(b, v.opts); err != nil { 93 return nil, err 94 } 95 96 // unmount anything that may still be mounted (for example, from an unclean shutdown) 97 for _, info := range mountInfos { 98 if info.Mountpoint == v.path { 99 mount.Unmount(v.path) 100 break 101 } 102 } 103 } 104 } 105 106 return r, nil 107 } 108 109 // Root implements the Driver interface for the volume package and 110 // manages the creation/removal of volumes. It uses only standard vfs 111 // commands to create/remove dirs within its provided scope. 112 type Root struct { 113 m sync.Mutex 114 scope string 115 path string 116 volumes map[string]*localVolume 117 rootUID int 118 rootGID int 119 } 120 121 // List lists all the volumes 122 func (r *Root) List() ([]volume.Volume, error) { 123 var ls []volume.Volume 124 r.m.Lock() 125 for _, v := range r.volumes { 126 ls = append(ls, v) 127 } 128 r.m.Unlock() 129 return ls, nil 130 } 131 132 // DataPath returns the constructed path of this volume. 133 func (r *Root) DataPath(volumeName string) string { 134 return filepath.Join(r.path, volumeName, VolumeDataPathName) 135 } 136 137 // Name returns the name of Root, defined in the volume package in the DefaultDriverName constant. 138 func (r *Root) Name() string { 139 return volume.DefaultDriverName 140 } 141 142 // Create creates a new volume.Volume with the provided name, creating 143 // the underlying directory tree required for this volume in the 144 // process. 145 func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error) { 146 if err := r.validateName(name); err != nil { 147 return nil, err 148 } 149 150 r.m.Lock() 151 defer r.m.Unlock() 152 153 v, exists := r.volumes[name] 154 if exists { 155 return v, nil 156 } 157 158 path := r.DataPath(name) 159 if err := idtools.MkdirAllAs(path, 0755, r.rootUID, r.rootGID); err != nil { 160 if os.IsExist(err) { 161 return nil, fmt.Errorf("volume already exists under %s", filepath.Dir(path)) 162 } 163 return nil, err 164 } 165 166 var err error 167 defer func() { 168 if err != nil { 169 os.RemoveAll(filepath.Dir(path)) 170 } 171 }() 172 173 v = &localVolume{ 174 driverName: r.Name(), 175 name: name, 176 path: path, 177 } 178 179 if opts != nil { 180 if err = setOpts(v, opts); err != nil { 181 return nil, err 182 } 183 var b []byte 184 b, err = json.Marshal(v.opts) 185 if err != nil { 186 return nil, err 187 } 188 if err = ioutil.WriteFile(filepath.Join(filepath.Dir(path), "opts.json"), b, 600); err != nil { 189 return nil, err 190 } 191 } 192 193 r.volumes[name] = v 194 return v, nil 195 } 196 197 // Remove removes the specified volume and all underlying data. If the 198 // given volume does not belong to this driver and an error is 199 // returned. The volume is reference counted, if all references are 200 // not released then the volume is not removed. 201 func (r *Root) Remove(v volume.Volume) error { 202 r.m.Lock() 203 defer r.m.Unlock() 204 205 lv, ok := v.(*localVolume) 206 if !ok { 207 return fmt.Errorf("unknown volume type %T", v) 208 } 209 210 realPath, err := filepath.EvalSymlinks(lv.path) 211 if err != nil { 212 if !os.IsNotExist(err) { 213 return err 214 } 215 realPath = filepath.Dir(lv.path) 216 } 217 218 if !r.scopedPath(realPath) { 219 return fmt.Errorf("Unable to remove a directory of out the Docker root %s: %s", r.scope, realPath) 220 } 221 222 if err := removePath(realPath); err != nil { 223 return err 224 } 225 226 delete(r.volumes, lv.name) 227 return removePath(filepath.Dir(lv.path)) 228 } 229 230 func removePath(path string) error { 231 if err := os.RemoveAll(path); err != nil { 232 if os.IsNotExist(err) { 233 return nil 234 } 235 return err 236 } 237 return nil 238 } 239 240 // Get looks up the volume for the given name and returns it if found 241 func (r *Root) Get(name string) (volume.Volume, error) { 242 r.m.Lock() 243 v, exists := r.volumes[name] 244 r.m.Unlock() 245 if !exists { 246 return nil, ErrNotFound 247 } 248 return v, nil 249 } 250 251 func (r *Root) validateName(name string) error { 252 if !volumeNameRegex.MatchString(name) { 253 return validationError{fmt.Errorf("%q includes invalid characters for a local volume name, only %q are allowed", name, utils.RestrictedNameChars)} 254 } 255 return nil 256 } 257 258 // localVolume implements the Volume interface from the volume package and 259 // represents the volumes created by Root. 260 type localVolume struct { 261 m sync.Mutex 262 usedCount int 263 // unique name of the volume 264 name string 265 // path is the path on the host where the data lives 266 path string 267 // driverName is the name of the driver that created the volume. 268 driverName string 269 // opts is the parsed list of options used to create the volume 270 opts *optsConfig 271 // active refcounts the active mounts 272 active activeMount 273 } 274 275 // Name returns the name of the given Volume. 276 func (v *localVolume) Name() string { 277 return v.name 278 } 279 280 // DriverName returns the driver that created the given Volume. 281 func (v *localVolume) DriverName() string { 282 return v.driverName 283 } 284 285 // Path returns the data location. 286 func (v *localVolume) Path() string { 287 return v.path 288 } 289 290 // Mount implements the localVolume interface, returning the data location. 291 func (v *localVolume) Mount() (string, error) { 292 v.m.Lock() 293 defer v.m.Unlock() 294 if v.opts != nil { 295 if !v.active.mounted { 296 if err := v.mount(); err != nil { 297 return "", err 298 } 299 v.active.mounted = true 300 } 301 v.active.count++ 302 } 303 return v.path, nil 304 } 305 306 // Umount is for satisfying the localVolume interface and does not do anything in this driver. 307 func (v *localVolume) Unmount() error { 308 v.m.Lock() 309 defer v.m.Unlock() 310 if v.opts != nil { 311 v.active.count-- 312 if v.active.count == 0 { 313 if err := mount.Unmount(v.path); err != nil { 314 v.active.count++ 315 return err 316 } 317 v.active.mounted = false 318 } 319 } 320 return nil 321 } 322 323 func validateOpts(opts map[string]string) error { 324 for opt := range opts { 325 if !validOpts[opt] { 326 return validationError{fmt.Errorf("invalid option key: %q", opt)} 327 } 328 } 329 return nil 330 }