2015-07-21 13:50:10 -04:00
// Package local provides the default implementation for volumes. It
// is used to mount data volume containers and directories local to
// the host server.
2018-02-05 16:05:59 -05:00
package local // import "github.com/docker/docker/volume/local"
2015-05-19 16:05:25 -04:00
import (
2016-02-11 21:48:16 -05:00
"encoding/json"
2015-05-19 16:05:25 -04:00
"os"
"path/filepath"
2016-08-17 20:36:17 -04:00
"reflect"
2016-10-12 16:11:20 -04:00
"strings"
2015-05-19 16:05:25 -04:00
"sync"
2017-09-06 11:35:06 -04:00
"github.com/docker/docker/daemon/names"
2018-01-11 14:53:06 -05:00
"github.com/docker/docker/errdefs"
2015-10-08 11:51:41 -04:00
"github.com/docker/docker/pkg/idtools"
2020-08-09 15:53:09 -04:00
"github.com/docker/docker/quota"
2015-05-19 16:05:25 -04:00
"github.com/docker/docker/volume"
2017-07-19 10:20:13 -04:00
"github.com/pkg/errors"
2020-08-09 15:53:09 -04:00
"github.com/sirupsen/logrus"
2015-05-19 16:05:25 -04:00
)
2015-06-03 12:26:41 -04:00
const (
2022-05-12 16:59:16 -04:00
// volumeDataPathName is the name of the directory where the volume data is stored.
// It uses a very distinctive name to avoid collisions migrating data between
// Docker versions.
volumeDataPathName = "_data"
2015-06-03 12:26:41 -04:00
volumesPathName = "volumes"
)
2015-10-20 13:09:48 -04:00
var (
// ErrNotFound is the typed error returned when the requested volume name can't be found
2022-05-14 07:16:06 -04:00
ErrNotFound = errors . New ( "volume not found" )
2015-12-13 11:00:39 -05:00
// volumeNameRegex ensures the name assigned for the volume is valid.
2015-10-20 13:09:48 -04:00
// This name is used to create the bind directory, so we need to avoid characters that
// would make the path to escape the root directory.
2017-09-06 11:35:06 -04:00
volumeNameRegex = names . RestrictedNamePattern
2015-10-20 13:09:48 -04:00
)
2015-06-03 12:26:41 -04:00
2016-02-11 21:48:16 -05:00
type activeMount struct {
count uint64
mounted bool
}
2015-07-21 13:50:10 -04:00
// New instantiates a new Root instance with the provided scope. Scope
// is the base path that the Root instance uses to store its
// volumes. The base path is created here if it does not exist.
2017-11-16 01:20:33 -05:00
func New ( scope string , rootIdentity idtools . Identity ) ( * Root , error ) {
2015-05-19 16:05:25 -04:00
r := & Root {
2022-05-12 17:57:01 -04:00
path : filepath . Join ( scope , volumesPathName ) ,
2017-11-16 01:20:33 -05:00
volumes : make ( map [ string ] * localVolume ) ,
rootIdentity : rootIdentity ,
2015-05-19 16:05:25 -04:00
}
2015-06-03 12:26:41 -04:00
2022-05-12 17:57:01 -04:00
if err := idtools . MkdirAllAndChown ( r . path , 0701 , idtools . CurrentIdentity ( ) ) ; err != nil {
return nil , err
}
dirs , err := os . ReadDir ( r . path )
2015-05-19 16:05:25 -04:00
if err != nil {
return nil , err
}
2015-06-03 12:26:41 -04:00
2022-05-12 17:57:01 -04:00
if r . quotaCtl , err = quota . NewControl ( r . path ) ; err != nil {
logrus . Debugf ( "No quota support for local volumes in %s: %v" , r . path , err )
2020-08-09 15:53:09 -04:00
}
2015-05-19 16:05:25 -04:00
for _ , d := range dirs {
2016-03-16 17:52:34 -04:00
if ! d . IsDir ( ) {
continue
}
2020-10-07 18:36:46 -04:00
name := d . Name ( )
2016-02-11 21:48:16 -05:00
v := & localVolume {
2015-05-19 16:05:25 -04:00
driverName : r . Name ( ) ,
name : name ,
2022-05-17 09:48:03 -04:00
rootPath : filepath . Join ( r . path , name ) ,
path : filepath . Join ( r . path , name , volumeDataPathName ) ,
2020-08-09 15:53:09 -04:00
quotaCtl : r . quotaCtl ,
2015-05-19 16:05:25 -04:00
}
2022-05-17 09:48:03 -04:00
if b , err := os . ReadFile ( filepath . Join ( v . rootPath , "opts.json" ) ) ; err == nil {
2016-08-17 20:36:17 -04:00
opts := optsConfig { }
if err := json . Unmarshal ( b , & opts ) ; err != nil {
2016-09-23 10:38:19 -04:00
return nil , errors . Wrapf ( err , "error while unmarshaling volume options for volume: %s" , name )
2016-02-11 21:48:16 -05:00
}
2016-08-18 10:07:22 -04:00
// Make sure this isn't an empty optsConfig.
// This could be empty due to buggy behavior in older versions of Docker.
2016-08-17 20:36:17 -04:00
if ! reflect . DeepEqual ( opts , optsConfig { } ) {
v . opts = & opts
}
2020-09-19 12:45:41 -04:00
// unmount anything that may still be mounted (for example, from an
// unclean shutdown). This is a no-op on windows
unmount ( v . path )
2016-02-11 21:48:16 -05:00
}
2020-10-07 18:40:47 -04:00
r . volumes [ name ] = v
2015-05-19 16:05:25 -04:00
}
2015-06-12 09:25:32 -04:00
2015-05-19 16:05:25 -04:00
return r , nil
}
2015-07-21 13:50:10 -04:00
// Root implements the Driver interface for the volume package and
// manages the creation/removal of volumes. It uses only standard vfs
// commands to create/remove dirs within its provided scope.
2015-05-19 16:05:25 -04:00
type Root struct {
2017-11-16 01:20:33 -05:00
m sync . Mutex
path string
2020-08-09 15:53:09 -04:00
quotaCtl * quota . Control
2017-11-16 01:20:33 -05:00
volumes map [ string ] * localVolume
rootIdentity idtools . Identity
2015-05-19 16:05:25 -04:00
}
2015-06-12 09:25:32 -04:00
// List lists all the volumes
2015-09-23 16:29:14 -04:00
func ( r * Root ) List ( ) ( [ ] volume . Volume , error ) {
2015-06-12 09:25:32 -04:00
var ls [ ] volume . Volume
2016-03-22 14:21:38 -04:00
r . m . Lock ( )
2015-06-12 09:25:32 -04:00
for _ , v := range r . volumes {
ls = append ( ls , v )
}
2016-03-22 14:21:38 -04:00
r . m . Unlock ( )
2015-09-23 16:29:14 -04:00
return ls , nil
2015-06-12 09:25:32 -04:00
}
2015-07-21 13:50:10 -04:00
// Name returns the name of Root, defined in the volume package in the DefaultDriverName constant.
2015-05-19 16:05:25 -04:00
func ( r * Root ) Name ( ) string {
2015-07-21 13:50:10 -04:00
return volume . DefaultDriverName
2015-05-19 16:05:25 -04:00
}
2015-07-21 13:50:10 -04:00
// Create creates a new volume.Volume with the provided name, creating
// the underlying directory tree required for this volume in the
// process.
2016-02-11 21:48:16 -05:00
func ( r * Root ) Create ( name string , opts map [ string ] string ) ( volume . Volume , error ) {
2015-10-20 13:09:48 -04:00
if err := r . validateName ( name ) ; err != nil {
return nil , err
}
2022-05-17 09:54:17 -04:00
if err := r . validateOpts ( opts ) ; err != nil {
2022-05-14 07:16:06 -04:00
return nil , err
}
2015-10-20 13:09:48 -04:00
2015-05-19 16:05:25 -04:00
r . m . Lock ( )
defer r . m . Unlock ( )
2015-06-03 12:26:41 -04:00
2015-05-19 16:05:25 -04:00
v , exists := r . volumes [ name ]
2015-06-12 09:25:32 -04:00
if exists {
return v , nil
}
2022-05-17 09:48:03 -04:00
v = & localVolume {
driverName : r . Name ( ) ,
name : name ,
rootPath : filepath . Join ( r . path , name ) ,
path : filepath . Join ( r . path , name , volumeDataPathName ) ,
quotaCtl : r . quotaCtl ,
}
2020-10-06 15:43:24 -04:00
// Root dir does not need to be accessed by the remapped root
2022-05-17 09:48:03 -04:00
if err := idtools . MkdirAllAndChown ( v . rootPath , 0701 , idtools . CurrentIdentity ( ) ) ; err != nil {
return nil , errors . Wrapf ( errdefs . System ( err ) , "error while creating volume root path '%s'" , v . rootPath )
2020-10-06 15:43:24 -04:00
}
// Remapped root does need access to the data path
2022-05-17 09:48:03 -04:00
if err := idtools . MkdirAllAndChown ( v . path , 0755 , r . rootIdentity ) ; err != nil {
return nil , errors . Wrapf ( errdefs . System ( err ) , "error while creating volume data path '%s'" , v . path )
2015-05-19 16:05:25 -04:00
}
2016-02-11 21:48:16 -05:00
var err error
defer func ( ) {
if err != nil {
2022-05-17 09:48:03 -04:00
os . RemoveAll ( v . rootPath )
2016-02-11 21:48:16 -05:00
}
} ( )
2020-10-07 18:25:49 -04:00
if err = v . setOpts ( opts ) ; err != nil {
return nil , err
2016-02-11 21:48:16 -05:00
}
2015-06-12 09:25:32 -04:00
r . volumes [ name ] = v
2015-05-19 16:05:25 -04:00
return v , nil
}
2015-07-21 13:50:10 -04:00
// Remove removes the specified volume and all underlying data. If the
// given volume does not belong to this driver and an error is
// returned. The volume is reference counted, if all references are
// not released then the volume is not removed.
2015-05-19 16:05:25 -04:00
func ( r * Root ) Remove ( v volume . Volume ) error {
r . m . Lock ( )
defer r . m . Unlock ( )
2015-09-08 22:01:02 -04:00
2015-07-21 13:50:10 -04:00
lv , ok := v . ( * localVolume )
2015-05-19 16:05:25 -04:00
if ! ok {
2017-11-28 23:09:37 -05:00
return errdefs . System ( errors . Errorf ( "unknown volume type %T" , v ) )
2015-05-19 16:05:25 -04:00
}
2015-06-03 12:26:41 -04:00
2017-04-03 13:10:48 -04:00
if lv . active . count > 0 {
2022-05-14 07:16:06 -04:00
return errdefs . System ( errors . New ( "volume has active mounts" ) )
2017-04-03 13:10:48 -04:00
}
if err := lv . unmount ( ) ; err != nil {
return err
}
2022-05-17 09:48:03 -04:00
// TODO(thaJeztah) is there a reason we're evaluating the data-path here, and not the volume's rootPath?
2015-06-12 09:25:32 -04:00
realPath , err := filepath . EvalSymlinks ( lv . path )
if err != nil {
2015-09-08 22:01:02 -04:00
if ! os . IsNotExist ( err ) {
return err
}
2022-05-17 09:48:03 -04:00
// if the volume's data-directory wasn't found, fall back to using the
// volume's root path (see 8d27417bfeff316346d00c07a456b0e1b056e788)
realPath = lv . rootPath
2015-06-12 09:25:32 -04:00
}
2015-09-08 22:01:02 -04:00
2022-05-12 16:40:45 -04:00
if realPath == r . path || ! strings . HasPrefix ( realPath , r . path ) {
return errdefs . System ( errors . Errorf ( "unable to remove a directory outside of the local volume root %s: %s" , r . path , realPath ) )
2015-06-12 09:25:32 -04:00
}
2015-06-03 12:26:41 -04:00
2015-09-08 22:01:02 -04:00
if err := removePath ( realPath ) ; err != nil {
2015-06-12 09:25:32 -04:00
return err
2015-05-19 16:05:25 -04:00
}
2015-06-12 09:25:32 -04:00
delete ( r . volumes , lv . name )
2022-05-17 09:48:03 -04:00
return removePath ( lv . rootPath )
2015-09-08 22:01:02 -04:00
}
func removePath ( path string ) error {
if err := os . RemoveAll ( path ) ; err != nil {
if os . IsNotExist ( err ) {
return nil
}
2017-11-28 23:09:37 -05:00
return errdefs . System ( errors . Wrapf ( err , "error removing volume path '%s'" , path ) )
2015-09-08 22:01:02 -04:00
}
return nil
2015-06-12 09:25:32 -04:00
}
// Get looks up the volume for the given name and returns it if found
func ( r * Root ) Get ( name string ) ( volume . Volume , error ) {
r . m . Lock ( )
v , exists := r . volumes [ name ]
r . m . Unlock ( )
if ! exists {
return nil , ErrNotFound
}
return v , nil
2015-05-19 16:05:25 -04:00
}
2016-04-11 11:17:52 -04:00
// Scope returns the local volume scope
func ( r * Root ) Scope ( ) string {
return volume . LocalScope
}
2015-10-20 13:09:48 -04:00
func ( r * Root ) validateName ( name string ) error {
2016-08-17 10:40:24 -04:00
if len ( name ) == 1 {
2018-12-21 20:28:30 -05:00
return errdefs . InvalidParameter ( errors . New ( "volume name is too short, names should be at least two alphanumeric characters" ) )
2016-08-17 10:40:24 -04:00
}
2015-10-20 13:09:48 -04:00
if ! volumeNameRegex . MatchString ( name ) {
2018-12-21 20:28:30 -05:00
return errdefs . InvalidParameter ( errors . Errorf ( "%q includes invalid characters for a local volume name, only %q are allowed. If you intended to pass a host directory, use absolute path" , name , names . RestrictedNameChars ) )
2015-10-20 13:09:48 -04:00
}
return nil
}
2015-07-21 13:50:10 -04:00
// localVolume implements the Volume interface from the volume package and
// represents the volumes created by Root.
type localVolume struct {
2016-04-14 03:04:00 -04:00
m sync . Mutex
2015-05-19 16:05:25 -04:00
// unique name of the volume
name string
2022-05-17 09:48:03 -04:00
// rootPath is the volume's root path, where the volume's metadata is stored.
rootPath string
2015-05-19 16:05:25 -04:00
// path is the path on the host where the data lives
path string
// driverName is the name of the driver that created the volume.
driverName string
2016-02-11 21:48:16 -05:00
// opts is the parsed list of options used to create the volume
opts * optsConfig
// active refcounts the active mounts
active activeMount
2020-08-09 15:53:09 -04:00
// reference to Root instances quotaCtl
quotaCtl * quota . Control
2015-05-19 16:05:25 -04:00
}
2015-07-21 13:50:10 -04:00
// Name returns the name of the given Volume.
func ( v * localVolume ) Name ( ) string {
2015-05-19 16:05:25 -04:00
return v . name
}
2015-07-21 13:50:10 -04:00
// DriverName returns the driver that created the given Volume.
func ( v * localVolume ) DriverName ( ) string {
2015-05-19 16:05:25 -04:00
return v . driverName
}
2015-07-21 13:50:10 -04:00
// Path returns the data location.
func ( v * localVolume ) Path ( ) string {
2015-05-19 16:05:25 -04:00
return v . path
}
2017-08-30 13:10:15 -04:00
// CachedPath returns the data location
func ( v * localVolume ) CachedPath ( ) string {
return v . path
}
2015-07-21 13:50:10 -04:00
// Mount implements the localVolume interface, returning the data location.
2017-04-03 13:10:48 -04:00
// If there are any provided mount options, the resources will be mounted at this point
2016-03-07 21:41:44 -05:00
func ( v * localVolume ) Mount ( id string ) ( string , error ) {
2016-02-11 21:48:16 -05:00
v . m . Lock ( )
defer v . m . Unlock ( )
2020-08-09 15:53:09 -04:00
if v . needsMount ( ) {
2016-02-11 21:48:16 -05:00
if ! v . active . mounted {
if err := v . mount ( ) ; err != nil {
2017-11-28 23:09:37 -05:00
return "" , errdefs . System ( err )
2016-02-11 21:48:16 -05:00
}
v . active . mounted = true
}
v . active . count ++
}
2020-08-09 15:53:09 -04:00
if err := v . postMount ( ) ; err != nil {
return "" , err
}
2015-05-19 16:05:25 -04:00
return v . path , nil
}
2017-04-03 13:10:48 -04:00
// Unmount dereferences the id, and if it is the last reference will unmount any resources
// that were previously mounted.
2016-03-07 21:41:44 -05:00
func ( v * localVolume ) Unmount ( id string ) error {
2016-02-11 21:48:16 -05:00
v . m . Lock ( )
defer v . m . Unlock ( )
2017-04-03 13:10:48 -04:00
// Always decrement the count, even if the unmount fails
// Essentially docker doesn't care if this fails, it will send an error, but
// ultimately there's nothing that can be done. If we don't decrement the count
// this volume can never be removed until a daemon restart occurs.
2020-08-09 15:53:09 -04:00
if v . needsMount ( ) {
2016-02-11 21:48:16 -05:00
v . active . count --
2017-04-03 13:10:48 -04:00
}
if v . active . count > 0 {
return nil
}
return v . unmount ( )
}
2016-03-07 15:44:43 -05:00
func ( v * localVolume ) Status ( ) map [ string ] interface { } {
return nil
}
2016-10-12 16:11:20 -04:00
2020-10-07 18:25:49 -04:00
func ( v * localVolume ) saveOpts ( ) error {
var b [ ] byte
b , err := json . Marshal ( v . opts )
if err != nil {
return err
}
err = os . WriteFile ( filepath . Join ( v . rootPath , "opts.json" ) , b , 0600 )
if err != nil {
return errdefs . System ( errors . Wrap ( err , "error while persisting volume options" ) )
}
return nil
}
2016-10-12 16:11:20 -04:00
// getAddress finds out address/hostname from options
func getAddress ( opts string ) string {
optsList := strings . Split ( opts , "," )
for i := 0 ; i < len ( optsList ) ; i ++ {
if strings . HasPrefix ( optsList [ i ] , "addr=" ) {
2018-05-19 07:38:54 -04:00
addr := strings . SplitN ( optsList [ i ] , "=" , 2 ) [ 1 ]
2016-10-12 16:11:20 -04:00
return addr
}
}
return ""
}
2022-05-12 10:53:02 -04:00
// getPassword finds out a password from options
func getPassword ( opts string ) string {
optsList := strings . Split ( opts , "," )
for i := 0 ; i < len ( optsList ) ; i ++ {
if strings . HasPrefix ( optsList [ i ] , "password=" ) {
passwd := strings . SplitN ( optsList [ i ] , "=" , 2 ) [ 1 ]
return passwd
}
}
return ""
}