Initial commit

This commit is contained in:
Ria Bhatia
2017-12-04 13:32:57 -06:00
committed by Erik St. Martin
commit 0075e5b0f3
9056 changed files with 2523100 additions and 0 deletions

View File

@@ -0,0 +1,232 @@
// Package local provides the default implementation for volumes. It
// is used to mount data volume containers and directories local to
// the host server.
package local
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sync"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/utils"
"github.com/docker/docker/volume"
)
// 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.
const (
VolumeDataPathName = "_data"
volumesPathName = "volumes"
)
var (
// ErrNotFound is the typed error returned when the requested volume name can't be found
ErrNotFound = errors.New("volume not found")
// volumeNameRegex ensures the name assigned for the volume is valid.
// 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.
volumeNameRegex = utils.RestrictedVolumeNamePattern
)
// 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.
func New(scope string, rootUID, rootGID int) (*Root, error) {
rootDirectory := filepath.Join(scope, volumesPathName)
if err := idtools.MkdirAllAs(rootDirectory, 0700, rootUID, rootGID); err != nil {
return nil, err
}
r := &Root{
scope: scope,
path: rootDirectory,
volumes: make(map[string]*localVolume),
rootUID: rootUID,
rootGID: rootGID,
}
dirs, err := ioutil.ReadDir(rootDirectory)
if err != nil {
return nil, err
}
for _, d := range dirs {
name := filepath.Base(d.Name())
r.volumes[name] = &localVolume{
driverName: r.Name(),
name: name,
path: r.DataPath(name),
}
}
return r, nil
}
// 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.
type Root struct {
m sync.Mutex
scope string
path string
volumes map[string]*localVolume
rootUID int
rootGID int
}
// List lists all the volumes
func (r *Root) List() ([]volume.Volume, error) {
var ls []volume.Volume
for _, v := range r.volumes {
ls = append(ls, v)
}
return ls, nil
}
// DataPath returns the constructed path of this volume.
func (r *Root) DataPath(volumeName string) string {
return filepath.Join(r.path, volumeName, VolumeDataPathName)
}
// Name returns the name of Root, defined in the volume package in the DefaultDriverName constant.
func (r *Root) Name() string {
return volume.DefaultDriverName
}
// Create creates a new volume.Volume with the provided name, creating
// the underlying directory tree required for this volume in the
// process.
func (r *Root) Create(name string, _ map[string]string) (volume.Volume, error) {
if err := r.validateName(name); err != nil {
return nil, err
}
r.m.Lock()
defer r.m.Unlock()
v, exists := r.volumes[name]
if exists {
return v, nil
}
path := r.DataPath(name)
if err := idtools.MkdirAllAs(path, 0755, r.rootUID, r.rootGID); err != nil {
if os.IsExist(err) {
return nil, fmt.Errorf("volume already exists under %s", filepath.Dir(path))
}
return nil, err
}
v = &localVolume{
driverName: r.Name(),
name: name,
path: path,
}
r.volumes[name] = v
return v, nil
}
// 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.
func (r *Root) Remove(v volume.Volume) error {
r.m.Lock()
defer r.m.Unlock()
lv, ok := v.(*localVolume)
if !ok {
return errors.New("unknown volume type")
}
realPath, err := filepath.EvalSymlinks(lv.path)
if err != nil {
if !os.IsNotExist(err) {
return err
}
realPath = filepath.Dir(lv.path)
}
if !r.scopedPath(realPath) {
return fmt.Errorf("Unable to remove a directory of out the Docker root %s: %s", r.scope, realPath)
}
if err := removePath(realPath); err != nil {
return err
}
delete(r.volumes, lv.name)
return removePath(filepath.Dir(lv.path))
}
func removePath(path string) error {
if err := os.RemoveAll(path); err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
return nil
}
// 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
}
func (r *Root) validateName(name string) error {
if !volumeNameRegex.MatchString(name) {
return derr.ErrorCodeVolumeName.WithArgs(name, utils.RestrictedNameChars)
}
return nil
}
// localVolume implements the Volume interface from the volume package and
// represents the volumes created by Root.
type localVolume struct {
m sync.Mutex
usedCount int
// unique name of the volume
name string
// 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
}
// Name returns the name of the given Volume.
func (v *localVolume) Name() string {
return v.name
}
// DriverName returns the driver that created the given Volume.
func (v *localVolume) DriverName() string {
return v.driverName
}
// Path returns the data location.
func (v *localVolume) Path() string {
return v.path
}
// Mount implements the localVolume interface, returning the data location.
func (v *localVolume) Mount() (string, error) {
return v.path, nil
}
// Umount is for satisfying the localVolume interface and does not do anything in this driver.
func (v *localVolume) Unmount() error {
return nil
}

View File

@@ -0,0 +1,147 @@
package local
import (
"io/ioutil"
"os"
"testing"
)
func TestRemove(t *testing.T) {
rootDir, err := ioutil.TempDir("", "local-volume-test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(rootDir)
r, err := New(rootDir, 0, 0)
if err != nil {
t.Fatal(err)
}
vol, err := r.Create("testing", nil)
if err != nil {
t.Fatal(err)
}
if err := r.Remove(vol); err != nil {
t.Fatal(err)
}
vol, err = r.Create("testing2", nil)
if err != nil {
t.Fatal(err)
}
if err := os.RemoveAll(vol.Path()); err != nil {
t.Fatal(err)
}
if err := r.Remove(vol); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(vol.Path()); err != nil && !os.IsNotExist(err) {
t.Fatal("volume dir not removed")
}
if l, _ := r.List(); len(l) != 0 {
t.Fatal("expected there to be no volumes")
}
}
func TestInitializeWithVolumes(t *testing.T) {
rootDir, err := ioutil.TempDir("", "local-volume-test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(rootDir)
r, err := New(rootDir, 0, 0)
if err != nil {
t.Fatal(err)
}
vol, err := r.Create("testing", nil)
if err != nil {
t.Fatal(err)
}
r, err = New(rootDir, 0, 0)
if err != nil {
t.Fatal(err)
}
v, err := r.Get(vol.Name())
if err != nil {
t.Fatal(err)
}
if v.Path() != vol.Path() {
t.Fatal("expected to re-initialize root with existing volumes")
}
}
func TestCreate(t *testing.T) {
rootDir, err := ioutil.TempDir("", "local-volume-test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(rootDir)
r, err := New(rootDir, 0, 0)
if err != nil {
t.Fatal(err)
}
cases := map[string]bool{
"name": true,
"name-with-dash": true,
"name_with_underscore": true,
"name/with/slash": false,
"name/with/../../slash": false,
"./name": false,
"../name": false,
"./": false,
"../": false,
"~": false,
".": false,
"..": false,
"...": false,
}
for name, success := range cases {
v, err := r.Create(name, nil)
if success {
if err != nil {
t.Fatal(err)
}
if v.Name() != name {
t.Fatalf("Expected volume with name %s, got %s", name, v.Name())
}
} else {
if err == nil {
t.Fatalf("Expected error creating volume with name %s, got nil", name)
}
}
}
}
func TestValidateName(t *testing.T) {
r := &Root{}
names := map[string]bool{
"/testvol": false,
"thing.d": true,
"hello-world": true,
"./hello": false,
".hello": false,
}
for vol, expected := range names {
err := r.validateName(vol)
if expected && err != nil {
t.Fatalf("expected %s to be valid got %v", vol, err)
}
if !expected && err == nil {
t.Fatalf("expected %s to be invalid", vol)
}
}
}

View File

@@ -0,0 +1,29 @@
// +build linux freebsd
// Package local provides the default implementation for volumes. It
// is used to mount data volume containers and directories local to
// the host server.
package local
import (
"path/filepath"
"strings"
)
var oldVfsDir = filepath.Join("vfs", "dir")
// scopedPath verifies that the path where the volume is located
// is under Docker's root and the valid local paths.
func (r *Root) scopedPath(realPath string) bool {
// Volumes path for Docker version >= 1.7
if strings.HasPrefix(realPath, filepath.Join(r.scope, volumesPathName)) && realPath != filepath.Join(r.scope, volumesPathName) {
return true
}
// Volumes path for Docker version < 1.7
if strings.HasPrefix(realPath, filepath.Join(r.scope, oldVfsDir)) {
return true
}
return false
}

View File

@@ -0,0 +1,18 @@
// Package local provides the default implementation for volumes. It
// is used to mount data volume containers and directories local to
// the host server.
package local
import (
"path/filepath"
"strings"
)
// scopedPath verifies that the path where the volume is located
// is under Docker's root and the valid local paths.
func (r *Root) scopedPath(realPath string) bool {
if strings.HasPrefix(realPath, filepath.Join(r.scope, volumesPathName)) && realPath != filepath.Join(r.scope, volumesPathName) {
return true
}
return false
}