Merge pull request #43637 from thaJeztah/remove_deprecated_storage_drivers
Remove deprecated devicemapper storage driver
This commit is contained in:
commit
50d2c94bd6
59 changed files with 33 additions and 5621 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -5,7 +5,6 @@
|
|||
|
||||
builder/** @tonistiigi
|
||||
contrib/mkimage/** @tianon
|
||||
daemon/graphdriver/devmapper/** @rhvgoyal
|
||||
daemon/graphdriver/overlay2/** @dmcgowan
|
||||
daemon/graphdriver/windows/** @johnstep
|
||||
daemon/logger/awslogs/** @samuelkarp
|
||||
|
|
2
Makefile
2
Makefile
|
@ -28,7 +28,7 @@ export VALIDATE_ORIGIN_BRANCH
|
|||
# option of "go build". For example, a built-in graphdriver priority list
|
||||
# can be changed during build time like this:
|
||||
#
|
||||
# make DOCKER_LDFLAGS="-X github.com/docker/docker/daemon/graphdriver.priority=overlay2,devicemapper" dynbinary
|
||||
# make DOCKER_LDFLAGS="-X github.com/docker/docker/daemon/graphdriver.priority=overlay2,zfs" dynbinary
|
||||
#
|
||||
DOCKER_ENVS := \
|
||||
-e BUILD_APT_MIRROR \
|
||||
|
|
|
@ -6586,7 +6586,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
ExecIDs:
|
||||
- "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca"
|
||||
- "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4"
|
||||
|
|
|
@ -374,11 +374,6 @@ check_flags BTRFS_FS_POSIX_ACL | sed 's/^/ /'
|
|||
[ "$EXITCODE" = 0 ] && STORAGE=0
|
||||
EXITCODE=0
|
||||
|
||||
echo " - \"$(wrap_color 'devicemapper' blue)\":"
|
||||
check_flags BLK_DEV_DM DM_THIN_PROVISIONING | sed 's/^/ /'
|
||||
[ "$EXITCODE" = 0 ] && STORAGE=0
|
||||
EXITCODE=0
|
||||
|
||||
echo " - \"$(wrap_color 'overlay' blue)\":"
|
||||
check_flags OVERLAY_FS | sed 's/^/ /'
|
||||
[ "$EXITCODE" = 0 ] && STORAGE=0
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
Docker device tool for devicemapper storage driver backend
|
||||
===================
|
||||
|
||||
The ./contrib/docker-device-tool contains a tool to manipulate devicemapper thin-pool.
|
||||
|
||||
Compile
|
||||
========
|
||||
|
||||
$ make shell
|
||||
## inside build container
|
||||
$ go build contrib/docker-device-tool/device_tool.go
|
||||
|
||||
# if devicemapper version is old and compilation fails, compile with `libdm_no_deferred_remove` tag
|
||||
$ go build -tags libdm_no_deferred_remove contrib/docker-device-tool/device_tool.go
|
|
@ -1,169 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/daemon/graphdriver/devmapper"
|
||||
"github.com/docker/docker/pkg/devicemapper"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s <flags> [status] | [list] | [device id] | [resize new-pool-size] | [snap new-id base-id] | [remove id] | [mount id mountpoint]\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func byteSizeFromString(arg string) (int64, error) {
|
||||
digits := ""
|
||||
rest := ""
|
||||
last := strings.LastIndexAny(arg, "0123456789")
|
||||
if last >= 0 {
|
||||
digits = arg[:last+1]
|
||||
rest = arg[last+1:]
|
||||
}
|
||||
|
||||
val, err := strconv.ParseInt(digits, 10, 64)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
rest = strings.ToLower(strings.TrimSpace(rest))
|
||||
|
||||
var multiplier int64
|
||||
switch rest {
|
||||
case "":
|
||||
multiplier = 1
|
||||
case "k", "kb":
|
||||
multiplier = 1024
|
||||
case "m", "mb":
|
||||
multiplier = 1024 * 1024
|
||||
case "g", "gb":
|
||||
multiplier = 1024 * 1024 * 1024
|
||||
case "t", "tb":
|
||||
multiplier = 1024 * 1024 * 1024 * 1024
|
||||
default:
|
||||
return 0, fmt.Errorf("Unknown size unit: %s", rest)
|
||||
}
|
||||
|
||||
return val * multiplier, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
root := flag.String("r", "/var/lib/docker", "Docker root dir")
|
||||
flDebug := flag.Bool("D", false, "Debug mode")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *flDebug {
|
||||
os.Setenv("DEBUG", "1")
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
if flag.NArg() < 1 {
|
||||
usage()
|
||||
}
|
||||
|
||||
args := flag.Args()
|
||||
|
||||
home := path.Join(*root, "devicemapper")
|
||||
devices, err := devmapper.NewDeviceSet(home, false, nil, idtools.IdentityMapping{})
|
||||
if err != nil {
|
||||
fmt.Println("Can't initialize device mapper: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
case "status":
|
||||
status := devices.Status()
|
||||
fmt.Printf("Pool name: %s\n", status.PoolName)
|
||||
fmt.Printf("Data Loopback file: %s\n", status.DataLoopback)
|
||||
fmt.Printf("Metadata Loopback file: %s\n", status.MetadataLoopback)
|
||||
fmt.Printf("Sector size: %d\n", status.SectorSize)
|
||||
fmt.Printf("Data use: %d of %d (%.1f %%)\n", status.Data.Used, status.Data.Total, 100.0*float64(status.Data.Used)/float64(status.Data.Total))
|
||||
fmt.Printf("Metadata use: %d of %d (%.1f %%)\n", status.Metadata.Used, status.Metadata.Total, 100.0*float64(status.Metadata.Used)/float64(status.Metadata.Total))
|
||||
case "list":
|
||||
ids := devices.List()
|
||||
sort.Strings(ids)
|
||||
for _, id := range ids {
|
||||
fmt.Println(id)
|
||||
}
|
||||
case "device":
|
||||
if flag.NArg() < 2 {
|
||||
usage()
|
||||
}
|
||||
status, err := devices.GetDeviceStatus(args[1])
|
||||
if err != nil {
|
||||
fmt.Println("Can't get device info: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Id: %d\n", status.DeviceID)
|
||||
fmt.Printf("Size: %d\n", status.Size)
|
||||
fmt.Printf("Transaction Id: %d\n", status.TransactionID)
|
||||
fmt.Printf("Size in Sectors: %d\n", status.SizeInSectors)
|
||||
fmt.Printf("Mapped Sectors: %d\n", status.MappedSectors)
|
||||
fmt.Printf("Highest Mapped Sector: %d\n", status.HighestMappedSector)
|
||||
case "resize":
|
||||
if flag.NArg() < 2 {
|
||||
usage()
|
||||
}
|
||||
|
||||
size, err := byteSizeFromString(args[1])
|
||||
if err != nil {
|
||||
fmt.Println("Invalid size: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = devices.ResizePool(size)
|
||||
if err != nil {
|
||||
fmt.Println("Error resizing pool: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
case "snap":
|
||||
if flag.NArg() < 3 {
|
||||
usage()
|
||||
}
|
||||
|
||||
err := devices.AddDevice(args[1], args[2], nil)
|
||||
if err != nil {
|
||||
fmt.Println("Can't create snap device: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case "remove":
|
||||
if flag.NArg() < 2 {
|
||||
usage()
|
||||
}
|
||||
|
||||
err := devicemapper.RemoveDevice(args[1])
|
||||
if err != nil {
|
||||
fmt.Println("Can't remove device: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case "mount":
|
||||
if flag.NArg() < 3 {
|
||||
usage()
|
||||
}
|
||||
|
||||
err := devices.MountDevice(args[1], args[2], "")
|
||||
if err != nil {
|
||||
fmt.Println("Can't mount device: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
default:
|
||||
fmt.Printf("Unknown command %s\n", args[0])
|
||||
usage()
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
package main
|
||||
|
||||
func main() {
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
# devicemapper - a storage backend based on Device Mapper
|
||||
|
||||
## Theory of operation
|
||||
|
||||
The device mapper graphdriver uses the device mapper thin provisioning
|
||||
module (dm-thinp) to implement CoW snapshots. The preferred model is
|
||||
to have a thin pool reserved outside of Docker and passed to the
|
||||
daemon via the `--storage-opt dm.thinpooldev` option. Alternatively,
|
||||
the device mapper graphdriver can setup a block device to handle this
|
||||
for you via the `--storage-opt dm.directlvm_device` option.
|
||||
|
||||
As a fallback if no thin pool is provided, loopback files will be
|
||||
created. Loopback is very slow, but can be used without any
|
||||
pre-configuration of storage. It is strongly recommended that you do
|
||||
not use loopback in production. Ensure your Docker daemon has a
|
||||
`--storage-opt dm.thinpooldev` argument provided.
|
||||
|
||||
In loopback, a thin pool is created at `/var/lib/docker/devicemapper`
|
||||
(devicemapper graph location) based on two block devices, one for
|
||||
data and one for metadata. By default these block devices are created
|
||||
automatically by using loopback mounts of automatically created sparse
|
||||
files.
|
||||
|
||||
The default loopback files used are
|
||||
`/var/lib/docker/devicemapper/devicemapper/data` and
|
||||
`/var/lib/docker/devicemapper/devicemapper/metadata`. Additional metadata
|
||||
required to map from docker entities to the corresponding devicemapper
|
||||
volumes is stored in the `/var/lib/docker/devicemapper/devicemapper/json`
|
||||
file (encoded as Json).
|
||||
|
||||
In order to support multiple devicemapper graphs on a system, the thin
|
||||
pool will be named something like: `docker-0:33-19478248-pool`, where
|
||||
the `0:33` part is the minor/major device nr and `19478248` is the
|
||||
inode number of the `/var/lib/docker/devicemapper` directory.
|
||||
|
||||
On the thin pool, docker automatically creates a base thin device,
|
||||
called something like `docker-0:33-19478248-base` of a fixed
|
||||
size. This is automatically formatted with an empty filesystem on
|
||||
creation. This device is the base of all docker images and
|
||||
containers. All base images are snapshots of this device and those
|
||||
images are then in turn used as snapshots for other images and
|
||||
eventually containers.
|
||||
|
||||
## Information on `docker info`
|
||||
|
||||
As of docker-1.4.1, `docker info` when using the `devicemapper` storage driver
|
||||
will display something like:
|
||||
|
||||
$ sudo docker info
|
||||
[...]
|
||||
Storage Driver: devicemapper
|
||||
Pool Name: docker-253:1-17538953-pool
|
||||
Pool Blocksize: 65.54 kB
|
||||
Base Device Size: 107.4 GB
|
||||
Data file: /dev/loop4
|
||||
Metadata file: /dev/loop4
|
||||
Data Space Used: 2.536 GB
|
||||
Data Space Total: 107.4 GB
|
||||
Data Space Available: 104.8 GB
|
||||
Metadata Space Used: 7.93 MB
|
||||
Metadata Space Total: 2.147 GB
|
||||
Metadata Space Available: 2.14 GB
|
||||
Udev Sync Supported: true
|
||||
Data loop file: /home/docker/devicemapper/devicemapper/data
|
||||
Metadata loop file: /home/docker/devicemapper/devicemapper/metadata
|
||||
Library Version: 1.02.82-git (2013-10-04)
|
||||
[...]
|
||||
|
||||
### status items
|
||||
|
||||
Each item in the indented section under `Storage Driver: devicemapper` are
|
||||
status information about the driver.
|
||||
* `Pool Name` name of the devicemapper pool for this driver.
|
||||
* `Pool Blocksize` tells the blocksize the thin pool was initialized with. This only changes on creation.
|
||||
* `Base Device Size` tells the maximum size of a container and image
|
||||
* `Data file` blockdevice file used for the devicemapper data
|
||||
* `Metadata file` blockdevice file used for the devicemapper metadata
|
||||
* `Data Space Used` tells how much of `Data file` is currently used
|
||||
* `Data Space Total` tells max size the `Data file`
|
||||
* `Data Space Available` tells how much free space there is in the `Data file`. If you are using a loop device this will report the actual space available to the loop device on the underlying filesystem.
|
||||
* `Metadata Space Used` tells how much of `Metadata file` is currently used
|
||||
* `Metadata Space Total` tells max size the `Metadata file`
|
||||
* `Metadata Space Available` tells how much free space there is in the `Metadata file`. If you are using a loop device this will report the actual space available to the loop device on the underlying filesystem.
|
||||
* `Udev Sync Supported` tells whether devicemapper is able to sync with Udev. Should be `true`.
|
||||
* `Data loop file` file attached to `Data file`, if loopback device is used
|
||||
* `Metadata loop file` file attached to `Metadata file`, if loopback device is used
|
||||
* `Library Version` from the libdevmapper used
|
||||
|
||||
## About the devicemapper options
|
||||
|
||||
The devicemapper backend supports some options that you can specify
|
||||
when starting the docker daemon using the `--storage-opt` flags.
|
||||
This uses the `dm` prefix and would be used something like `dockerd --storage-opt dm.foo=bar`.
|
||||
|
||||
These options are currently documented both in [the man
|
||||
page](../../../man/docker.1.md) and in [the online
|
||||
documentation](https://docs.docker.com/engine/reference/commandline/dockerd/#/storage-driver-options).
|
||||
If you add an options, update both the `man` page and the documentation.
|
|
@ -1,230 +0,0 @@
|
|||
package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type directLVMConfig struct {
|
||||
Device string
|
||||
ThinpPercent uint64
|
||||
ThinpMetaPercent uint64
|
||||
AutoExtendPercent uint64
|
||||
AutoExtendThreshold uint64
|
||||
}
|
||||
|
||||
var (
|
||||
errThinpPercentMissing = errors.New("must set both `dm.thinp_percent` and `dm.thinp_metapercent` if either is specified")
|
||||
errThinpPercentTooBig = errors.New("combined `dm.thinp_percent` and `dm.thinp_metapercent` must not be greater than 100")
|
||||
errMissingSetupDevice = errors.New("must provide device path in `dm.directlvm_device` in order to configure direct-lvm")
|
||||
)
|
||||
|
||||
func validateLVMConfig(cfg directLVMConfig) error {
|
||||
if reflect.DeepEqual(cfg, directLVMConfig{}) {
|
||||
return nil
|
||||
}
|
||||
if cfg.Device == "" {
|
||||
return errMissingSetupDevice
|
||||
}
|
||||
if (cfg.ThinpPercent > 0 && cfg.ThinpMetaPercent == 0) || cfg.ThinpMetaPercent > 0 && cfg.ThinpPercent == 0 {
|
||||
return errThinpPercentMissing
|
||||
}
|
||||
|
||||
if cfg.ThinpPercent+cfg.ThinpMetaPercent > 100 {
|
||||
return errThinpPercentTooBig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkDevAvailable(dev string) error {
|
||||
lvmScan, err := exec.LookPath("lvmdiskscan")
|
||||
if err != nil {
|
||||
logrus.Debug("could not find lvmdiskscan")
|
||||
return nil
|
||||
}
|
||||
|
||||
out, err := exec.Command(lvmScan).CombinedOutput()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error(string(out))
|
||||
return nil
|
||||
}
|
||||
|
||||
if !bytes.Contains(out, []byte(dev)) {
|
||||
return errors.Errorf("%s is not available for use with devicemapper", dev)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkDevInVG(dev string) error {
|
||||
pvDisplay, err := exec.LookPath("pvdisplay")
|
||||
if err != nil {
|
||||
logrus.Debug("could not find pvdisplay")
|
||||
return nil
|
||||
}
|
||||
|
||||
out, err := exec.Command(pvDisplay, dev).CombinedOutput()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error(string(out))
|
||||
return nil
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(bytes.NewReader(bytes.TrimSpace(out)))
|
||||
for scanner.Scan() {
|
||||
fields := strings.SplitAfter(strings.TrimSpace(scanner.Text()), "VG Name")
|
||||
if len(fields) > 1 {
|
||||
// got "VG Name" line"
|
||||
vg := strings.TrimSpace(fields[1])
|
||||
if len(vg) > 0 {
|
||||
return errors.Errorf("%s is already part of a volume group %q: must remove this device from any volume group or provide a different device", dev, vg)
|
||||
}
|
||||
logrus.Error(fields)
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkDevHasFS(dev string) error {
|
||||
blkid, err := exec.LookPath("blkid")
|
||||
if err != nil {
|
||||
logrus.Debug("could not find blkid")
|
||||
return nil
|
||||
}
|
||||
|
||||
out, err := exec.Command(blkid, dev).CombinedOutput()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error(string(out))
|
||||
return nil
|
||||
}
|
||||
|
||||
fields := bytes.Fields(out)
|
||||
for _, f := range fields {
|
||||
kv := bytes.Split(f, []byte{'='})
|
||||
if bytes.Equal(kv[0], []byte("TYPE")) {
|
||||
v := bytes.Trim(kv[1], "\"")
|
||||
if len(v) > 0 {
|
||||
return errors.Errorf("%s has a filesystem already, use dm.directlvm_device_force=true if you want to wipe the device", dev)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyBlockDevice(dev string, force bool) error {
|
||||
if err := checkDevAvailable(dev); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkDevInVG(dev); err != nil {
|
||||
return err
|
||||
}
|
||||
if force {
|
||||
return nil
|
||||
}
|
||||
return checkDevHasFS(dev)
|
||||
}
|
||||
|
||||
func readLVMConfig(root string) (directLVMConfig, error) {
|
||||
var cfg directLVMConfig
|
||||
|
||||
p := filepath.Join(root, "setup-config.json")
|
||||
b, err := os.ReadFile(p)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return cfg, nil
|
||||
}
|
||||
return cfg, errors.Wrap(err, "error reading existing setup config")
|
||||
}
|
||||
|
||||
// check if this is just an empty file, no need to produce a json error later if so
|
||||
if len(b) == 0 {
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &cfg)
|
||||
return cfg, errors.Wrap(err, "error unmarshaling previous device setup config")
|
||||
}
|
||||
|
||||
func writeLVMConfig(root string, cfg directLVMConfig) error {
|
||||
p := filepath.Join(root, "setup-config.json")
|
||||
b, err := json.Marshal(cfg)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error marshalling direct lvm config")
|
||||
}
|
||||
err = os.WriteFile(p, b, 0600)
|
||||
return errors.Wrap(err, "error writing direct lvm config to file")
|
||||
}
|
||||
|
||||
func setupDirectLVM(cfg directLVMConfig) error {
|
||||
lvmProfileDir := "/etc/lvm/profile"
|
||||
binaries := []string{"pvcreate", "vgcreate", "lvcreate", "lvconvert", "lvchange", "thin_check"}
|
||||
|
||||
for _, bin := range binaries {
|
||||
if _, err := exec.LookPath(bin); err != nil {
|
||||
return errors.Wrap(err, "error looking up command `"+bin+"` while setting up direct lvm")
|
||||
}
|
||||
}
|
||||
|
||||
err := os.MkdirAll(lvmProfileDir, 0755)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error creating lvm profile directory")
|
||||
}
|
||||
|
||||
if cfg.AutoExtendPercent == 0 {
|
||||
cfg.AutoExtendPercent = 20
|
||||
}
|
||||
|
||||
if cfg.AutoExtendThreshold == 0 {
|
||||
cfg.AutoExtendThreshold = 80
|
||||
}
|
||||
|
||||
if cfg.ThinpPercent == 0 {
|
||||
cfg.ThinpPercent = 95
|
||||
}
|
||||
if cfg.ThinpMetaPercent == 0 {
|
||||
cfg.ThinpMetaPercent = 1
|
||||
}
|
||||
|
||||
out, err := exec.Command("pvcreate", "-f", cfg.Device).CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, string(out))
|
||||
}
|
||||
|
||||
out, err = exec.Command("vgcreate", "docker", cfg.Device).CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, string(out))
|
||||
}
|
||||
|
||||
out, err = exec.Command("lvcreate", "--wipesignatures", "y", "-n", "thinpool", "docker", "--extents", fmt.Sprintf("%d%%VG", cfg.ThinpPercent)).CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, string(out))
|
||||
}
|
||||
out, err = exec.Command("lvcreate", "--wipesignatures", "y", "-n", "thinpoolmeta", "docker", "--extents", fmt.Sprintf("%d%%VG", cfg.ThinpMetaPercent)).CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, string(out))
|
||||
}
|
||||
|
||||
out, err = exec.Command("lvconvert", "-y", "--zero", "n", "-c", "512K", "--thinpool", "docker/thinpool", "--poolmetadata", "docker/thinpoolmeta").CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, string(out))
|
||||
}
|
||||
|
||||
profile := fmt.Sprintf("activation{\nthin_pool_autoextend_threshold=%d\nthin_pool_autoextend_percent=%d\n}", cfg.AutoExtendThreshold, cfg.AutoExtendPercent)
|
||||
err = os.WriteFile(lvmProfileDir+"/docker-thinpool.profile", []byte(profile), 0600)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error writing docker thinp autoextend profile")
|
||||
}
|
||||
|
||||
out, err = exec.Command("lvchange", "--metadataprofile", "docker-thinpool", "docker/thinpool").CombinedOutput()
|
||||
return errors.Wrap(err, string(out))
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,106 +0,0 @@
|
|||
package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper"
|
||||
|
||||
// Definition of struct dm_task and sub structures (from lvm2)
|
||||
//
|
||||
// struct dm_ioctl {
|
||||
// /*
|
||||
// * The version number is made up of three parts:
|
||||
// * major - no backward or forward compatibility,
|
||||
// * minor - only backwards compatible,
|
||||
// * patch - both backwards and forwards compatible.
|
||||
// *
|
||||
// * All clients of the ioctl interface should fill in the
|
||||
// * version number of the interface that they were
|
||||
// * compiled with.
|
||||
// *
|
||||
// * All recognized ioctl commands (ie. those that don't
|
||||
// * return -ENOTTY) fill out this field, even if the
|
||||
// * command failed.
|
||||
// */
|
||||
// uint32_t version[3]; /* in/out */
|
||||
// uint32_t data_size; /* total size of data passed in
|
||||
// * including this struct */
|
||||
|
||||
// uint32_t data_start; /* offset to start of data
|
||||
// * relative to start of this struct */
|
||||
|
||||
// uint32_t target_count; /* in/out */
|
||||
// int32_t open_count; /* out */
|
||||
// uint32_t flags; /* in/out */
|
||||
|
||||
// /*
|
||||
// * event_nr holds either the event number (input and output) or the
|
||||
// * udev cookie value (input only).
|
||||
// * The DM_DEV_WAIT ioctl takes an event number as input.
|
||||
// * The DM_SUSPEND, DM_DEV_REMOVE and DM_DEV_RENAME ioctls
|
||||
// * use the field as a cookie to return in the DM_COOKIE
|
||||
// * variable with the uevents they issue.
|
||||
// * For output, the ioctls return the event number, not the cookie.
|
||||
// */
|
||||
// uint32_t event_nr; /* in/out */
|
||||
// uint32_t padding;
|
||||
|
||||
// uint64_t dev; /* in/out */
|
||||
|
||||
// char name[DM_NAME_LEN]; /* device name */
|
||||
// char uuid[DM_UUID_LEN]; /* unique identifier for
|
||||
// * the block device */
|
||||
// char data[7]; /* padding or data */
|
||||
// };
|
||||
|
||||
// struct target {
|
||||
// uint64_t start;
|
||||
// uint64_t length;
|
||||
// char *type;
|
||||
// char *params;
|
||||
|
||||
// struct target *next;
|
||||
// };
|
||||
|
||||
// typedef enum {
|
||||
// DM_ADD_NODE_ON_RESUME, /* add /dev/mapper node with dmsetup resume */
|
||||
// DM_ADD_NODE_ON_CREATE /* add /dev/mapper node with dmsetup create */
|
||||
// } dm_add_node_t;
|
||||
|
||||
// struct dm_task {
|
||||
// int type;
|
||||
// char *dev_name;
|
||||
// char *mangled_dev_name;
|
||||
|
||||
// struct target *head, *tail;
|
||||
|
||||
// int read_only;
|
||||
// uint32_t event_nr;
|
||||
// int major;
|
||||
// int minor;
|
||||
// int allow_default_major_fallback;
|
||||
// uid_t uid;
|
||||
// gid_t gid;
|
||||
// mode_t mode;
|
||||
// uint32_t read_ahead;
|
||||
// uint32_t read_ahead_flags;
|
||||
// union {
|
||||
// struct dm_ioctl *v4;
|
||||
// } dmi;
|
||||
// char *newname;
|
||||
// char *message;
|
||||
// char *geometry;
|
||||
// uint64_t sector;
|
||||
// int no_flush;
|
||||
// int no_open_count;
|
||||
// int skip_lockfs;
|
||||
// int query_inactive_table;
|
||||
// int suppress_identical_reload;
|
||||
// dm_add_node_t add_node;
|
||||
// uint64_t existing_table_size;
|
||||
// int cookie_set;
|
||||
// int new_uuid;
|
||||
// int secure_data;
|
||||
// int retry_remove;
|
||||
// int enable_checks;
|
||||
// int expected_errno;
|
||||
|
||||
// char *uuid;
|
||||
// char *mangled_uuid;
|
||||
// };
|
||||
//
|
|
@ -1,208 +0,0 @@
|
|||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/daemon/graphdriver"
|
||||
"github.com/docker/docker/daemon/graphdriver/graphtest"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/parsers/kernel"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Reduce the size of the base fs and loopback for the tests
|
||||
defaultDataLoopbackSize = 300 * 1024 * 1024
|
||||
defaultMetaDataLoopbackSize = 200 * 1024 * 1024
|
||||
defaultBaseFsSize = 300 * 1024 * 1024
|
||||
defaultUdevSyncOverride = true
|
||||
if err := initLoopbacks(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// initLoopbacks ensures that the loopback devices are properly created within
|
||||
// the system running the device mapper tests.
|
||||
func initLoopbacks() error {
|
||||
statT, err := getBaseLoopStats()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// create at least 128 loopback files, since a few first ones
|
||||
// might be already in use by the host OS
|
||||
for i := 0; i < 128; i++ {
|
||||
loopPath := fmt.Sprintf("/dev/loop%d", i)
|
||||
// only create new loopback files if they don't exist
|
||||
if _, err := os.Stat(loopPath); err != nil {
|
||||
if mkerr := syscall.Mknod(loopPath,
|
||||
uint32(statT.Mode|syscall.S_IFBLK), int((7<<8)|(i&0xff)|((i&0xfff00)<<12))); mkerr != nil { //nolint: unconvert
|
||||
return mkerr
|
||||
}
|
||||
os.Chown(loopPath, int(statT.Uid), int(statT.Gid))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getBaseLoopStats inspects /dev/loop0 to collect uid,gid, and mode for the
|
||||
// loop0 device on the system. If it does not exist we assume 0,0,0660 for the
|
||||
// stat data
|
||||
func getBaseLoopStats() (*syscall.Stat_t, error) {
|
||||
loop0, err := os.Stat("/dev/loop0")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return &syscall.Stat_t{
|
||||
Uid: 0,
|
||||
Gid: 0,
|
||||
Mode: 0660,
|
||||
}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return loop0.Sys().(*syscall.Stat_t), nil
|
||||
}
|
||||
|
||||
// This avoids creating a new driver for each test if all tests are run
|
||||
// Make sure to put new tests between TestDevmapperSetup and TestDevmapperTeardown
|
||||
func TestDevmapperSetup(t *testing.T) {
|
||||
graphtest.GetDriver(t, "devicemapper")
|
||||
}
|
||||
|
||||
func TestDevmapperCreateEmpty(t *testing.T) {
|
||||
graphtest.DriverTestCreateEmpty(t, "devicemapper")
|
||||
}
|
||||
|
||||
func TestDevmapperCreateBase(t *testing.T) {
|
||||
graphtest.DriverTestCreateBase(t, "devicemapper")
|
||||
}
|
||||
|
||||
func TestDevmapperCreateSnap(t *testing.T) {
|
||||
graphtest.DriverTestCreateSnap(t, "devicemapper")
|
||||
}
|
||||
|
||||
func TestDevmapperTeardown(t *testing.T) {
|
||||
graphtest.PutDriver(t)
|
||||
}
|
||||
|
||||
func TestDevmapperReduceLoopBackSize(t *testing.T) {
|
||||
tenMB := int64(10 * 1024 * 1024)
|
||||
testChangeLoopBackSize(t, -tenMB, defaultDataLoopbackSize, defaultMetaDataLoopbackSize)
|
||||
}
|
||||
|
||||
func TestDevmapperIncreaseLoopBackSize(t *testing.T) {
|
||||
tenMB := int64(10 * 1024 * 1024)
|
||||
testChangeLoopBackSize(t, tenMB, defaultDataLoopbackSize+tenMB, defaultMetaDataLoopbackSize+tenMB)
|
||||
}
|
||||
|
||||
func testChangeLoopBackSize(t *testing.T, delta, expectDataSize, expectMetaDataSize int64) {
|
||||
driver := graphtest.GetDriver(t, "devicemapper").(*graphtest.Driver).Driver.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver)
|
||||
defer graphtest.PutDriver(t)
|
||||
// make sure data or metadata loopback size are the default size
|
||||
if s := driver.DeviceSet.Status(); s.Data.Total != uint64(defaultDataLoopbackSize) || s.Metadata.Total != uint64(defaultMetaDataLoopbackSize) {
|
||||
t.Fatal("data or metadata loop back size is incorrect")
|
||||
}
|
||||
if err := driver.Cleanup(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Reload
|
||||
d, err := Init(driver.home, []string{
|
||||
fmt.Sprintf("dm.loopdatasize=%d", defaultDataLoopbackSize+delta),
|
||||
fmt.Sprintf("dm.loopmetadatasize=%d", defaultMetaDataLoopbackSize+delta),
|
||||
}, idtools.IdentityMapping{})
|
||||
if err != nil {
|
||||
t.Fatalf("error creating devicemapper driver: %v", err)
|
||||
}
|
||||
driver = d.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver)
|
||||
if s := driver.DeviceSet.Status(); s.Data.Total != uint64(expectDataSize) || s.Metadata.Total != uint64(expectMetaDataSize) {
|
||||
t.Fatal("data or metadata loop back size is incorrect")
|
||||
}
|
||||
if err := driver.Cleanup(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure devices.Lock() has been release upon return from cleanupDeletedDevices() function
|
||||
func TestDevmapperLockReleasedDeviceDeletion(t *testing.T) {
|
||||
driver := graphtest.GetDriver(t, "devicemapper").(*graphtest.Driver).Driver.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver)
|
||||
defer graphtest.PutDriver(t)
|
||||
|
||||
// Call cleanupDeletedDevices() and after the call take and release
|
||||
// DeviceSet Lock. If lock has not been released, this will hang.
|
||||
driver.DeviceSet.cleanupDeletedDevices()
|
||||
|
||||
doneChan := make(chan bool, 1)
|
||||
|
||||
go func() {
|
||||
driver.DeviceSet.Lock()
|
||||
defer driver.DeviceSet.Unlock()
|
||||
doneChan <- true
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(time.Second * 5):
|
||||
// Timer expired. That means lock was not released upon
|
||||
// function return and we are deadlocked. Release lock
|
||||
// here so that cleanup could succeed and fail the test.
|
||||
driver.DeviceSet.Unlock()
|
||||
t.Fatal("Could not acquire devices lock after call to cleanupDeletedDevices()")
|
||||
case <-doneChan:
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that mounts aren't leakedriver. It's non-trivial for us to test the full
|
||||
// reproducer of #34573 in a unit test, but we can at least make sure that a
|
||||
// simple command run in a new namespace doesn't break things horribly.
|
||||
func TestDevmapperMountLeaks(t *testing.T) {
|
||||
if !kernel.CheckKernelVersion(3, 18, 0) {
|
||||
t.Skipf("kernel version <3.18.0 and so is missing torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe.")
|
||||
}
|
||||
|
||||
driver := graphtest.GetDriver(t, "devicemapper", "dm.use_deferred_removal=false", "dm.use_deferred_deletion=false").(*graphtest.Driver).Driver.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver)
|
||||
defer graphtest.PutDriver(t)
|
||||
|
||||
// We need to create a new (dummy) device.
|
||||
if err := driver.Create("some-layer", "", nil); err != nil {
|
||||
t.Fatalf("setting up some-layer: %v", err)
|
||||
}
|
||||
|
||||
// Mount the device.
|
||||
_, err := driver.Get("some-layer", "")
|
||||
if err != nil {
|
||||
t.Fatalf("mounting some-layer: %v", err)
|
||||
}
|
||||
|
||||
// Create a new subprocess which will inherit our mountpoint, then
|
||||
// intentionally leak it and stick around. We can't do this entirely within
|
||||
// Go because forking and namespaces in Go are really not handled well at
|
||||
// all.
|
||||
cmd := exec.Cmd{
|
||||
Path: "/bin/sh",
|
||||
Args: []string{
|
||||
"/bin/sh", "-c",
|
||||
"mount --make-rprivate / && sleep 1000s",
|
||||
},
|
||||
SysProcAttr: &syscall.SysProcAttr{
|
||||
Unshareflags: syscall.CLONE_NEWNS,
|
||||
},
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatalf("starting sub-command: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
unix.Kill(cmd.Process.Pid, unix.SIGKILL)
|
||||
cmd.Wait()
|
||||
}()
|
||||
|
||||
// Now try to "drop" the device.
|
||||
if err := driver.Put("some-layer"); err != nil {
|
||||
t.Fatalf("unmounting some-layer: %v", err)
|
||||
}
|
||||
}
|
|
@ -1,244 +0,0 @@
|
|||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/docker/daemon/graphdriver"
|
||||
"github.com/docker/docker/pkg/devicemapper"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/moby/locker"
|
||||
"github.com/moby/sys/mount"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func init() {
|
||||
graphdriver.Register("devicemapper", Init)
|
||||
}
|
||||
|
||||
// Driver contains the device set mounted and the home directory
|
||||
type Driver struct {
|
||||
*DeviceSet
|
||||
home string
|
||||
ctr *graphdriver.RefCounter
|
||||
locker *locker.Locker
|
||||
}
|
||||
|
||||
// Init creates a driver with the given home and the set of options.
|
||||
func Init(home string, options []string, idMap idtools.IdentityMapping) (graphdriver.Driver, error) {
|
||||
deviceSet, err := NewDeviceSet(home, true, options, idMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Driver{
|
||||
DeviceSet: deviceSet,
|
||||
home: home,
|
||||
ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()),
|
||||
locker: locker.New(),
|
||||
}
|
||||
|
||||
return graphdriver.NewNaiveDiffDriver(d, d.idMap), nil
|
||||
}
|
||||
|
||||
func (d *Driver) String() string {
|
||||
return "devicemapper"
|
||||
}
|
||||
|
||||
// Status returns the status about the driver in a printable format.
|
||||
// Information returned contains Pool Name, Data File, Metadata file, disk usage by
|
||||
// the data and metadata, etc.
|
||||
func (d *Driver) Status() [][2]string {
|
||||
s := d.DeviceSet.Status()
|
||||
|
||||
status := [][2]string{
|
||||
{"Pool Name", s.PoolName},
|
||||
{"Pool Blocksize", units.HumanSize(float64(s.SectorSize))},
|
||||
{"Base Device Size", units.HumanSize(float64(s.BaseDeviceSize))},
|
||||
{"Backing Filesystem", s.BaseDeviceFS},
|
||||
{"Udev Sync Supported", fmt.Sprintf("%v", s.UdevSyncSupported)},
|
||||
}
|
||||
|
||||
if len(s.DataFile) > 0 {
|
||||
status = append(status, [2]string{"Data file", s.DataFile})
|
||||
}
|
||||
if len(s.MetadataFile) > 0 {
|
||||
status = append(status, [2]string{"Metadata file", s.MetadataFile})
|
||||
}
|
||||
if len(s.DataLoopback) > 0 {
|
||||
status = append(status, [2]string{"Data loop file", s.DataLoopback})
|
||||
}
|
||||
if len(s.MetadataLoopback) > 0 {
|
||||
status = append(status, [2]string{"Metadata loop file", s.MetadataLoopback})
|
||||
}
|
||||
|
||||
status = append(status, [][2]string{
|
||||
{"Data Space Used", units.HumanSize(float64(s.Data.Used))},
|
||||
{"Data Space Total", units.HumanSize(float64(s.Data.Total))},
|
||||
{"Data Space Available", units.HumanSize(float64(s.Data.Available))},
|
||||
{"Metadata Space Used", units.HumanSize(float64(s.Metadata.Used))},
|
||||
{"Metadata Space Total", units.HumanSize(float64(s.Metadata.Total))},
|
||||
{"Metadata Space Available", units.HumanSize(float64(s.Metadata.Available))},
|
||||
{"Thin Pool Minimum Free Space", units.HumanSize(float64(s.MinFreeSpace))},
|
||||
{"Deferred Removal Enabled", fmt.Sprintf("%v", s.DeferredRemoveEnabled)},
|
||||
{"Deferred Deletion Enabled", fmt.Sprintf("%v", s.DeferredDeleteEnabled)},
|
||||
{"Deferred Deleted Device Count", fmt.Sprintf("%v", s.DeferredDeletedDeviceCount)},
|
||||
}...)
|
||||
|
||||
if vStr, err := devicemapper.GetLibraryVersion(); err == nil {
|
||||
status = append(status, [2]string{"Library Version", vStr})
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
// GetMetadata returns a map of information about the device.
|
||||
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
|
||||
m, err := d.DeviceSet.exportDeviceMetadata(id)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metadata := make(map[string]string)
|
||||
metadata["DeviceId"] = strconv.Itoa(m.deviceID)
|
||||
metadata["DeviceSize"] = strconv.FormatUint(m.deviceSize, 10)
|
||||
metadata["DeviceName"] = m.deviceName
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
// Cleanup unmounts a device.
|
||||
func (d *Driver) Cleanup() error {
|
||||
err := d.DeviceSet.Shutdown(d.home)
|
||||
umountErr := mount.RecursiveUnmount(d.home)
|
||||
|
||||
// in case we have two errors, prefer the one from Shutdown()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return umountErr
|
||||
}
|
||||
|
||||
// CreateReadWrite creates a layer that is writable for use as a container
|
||||
// file system.
|
||||
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
|
||||
return d.Create(id, parent, opts)
|
||||
}
|
||||
|
||||
// Create adds a device with a given id and the parent.
|
||||
func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
|
||||
var storageOpt map[string]string
|
||||
if opts != nil {
|
||||
storageOpt = opts.StorageOpt
|
||||
}
|
||||
return d.DeviceSet.AddDevice(id, parent, storageOpt)
|
||||
}
|
||||
|
||||
// Remove removes a device with a given id, unmounts the filesystem, and removes the mount point.
|
||||
func (d *Driver) Remove(id string) error {
|
||||
d.locker.Lock(id)
|
||||
defer d.locker.Unlock(id)
|
||||
if !d.DeviceSet.HasDevice(id) {
|
||||
// Consider removing a non-existing device a no-op
|
||||
// This is useful to be able to progress on container removal
|
||||
// if the underlying device has gone away due to earlier errors
|
||||
return nil
|
||||
}
|
||||
|
||||
// This assumes the device has been properly Get/Put:ed and thus is unmounted
|
||||
if err := d.DeviceSet.DeleteDevice(id, false); err != nil {
|
||||
return fmt.Errorf("failed to remove device %s: %v", id, err)
|
||||
}
|
||||
|
||||
// Most probably the mount point is already removed on Put()
|
||||
// (see DeviceSet.UnmountDevice()), but just in case it was not
|
||||
// let's try to remove it here as well, ignoring errors as
|
||||
// an older kernel can return EBUSY if e.g. the mount was leaked
|
||||
// to other mount namespaces. A failure to remove the container's
|
||||
// mount point is not important and should not be treated
|
||||
// as a failure to remove the container.
|
||||
mp := path.Join(d.home, "mnt", id)
|
||||
err := unix.Rmdir(mp)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
logrus.WithField("storage-driver", "devicemapper").Warnf("unable to remove mount point %q: %s", mp, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get mounts a device with given id into the root filesystem
|
||||
func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
d.locker.Lock(id)
|
||||
defer d.locker.Unlock(id)
|
||||
mp := path.Join(d.home, "mnt", id)
|
||||
rootFs := path.Join(mp, "rootfs")
|
||||
if count := d.ctr.Increment(mp); count > 1 {
|
||||
return rootFs, nil
|
||||
}
|
||||
|
||||
root := d.idMap.RootPair()
|
||||
|
||||
// Create the target directories if they don't exist
|
||||
if err := idtools.MkdirAllAndChown(path.Join(d.home, "mnt"), 0755, root); err != nil {
|
||||
d.ctr.Decrement(mp)
|
||||
return "", err
|
||||
}
|
||||
if err := idtools.MkdirAndChown(mp, 0755, root); err != nil && !os.IsExist(err) {
|
||||
d.ctr.Decrement(mp)
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Mount the device
|
||||
if err := d.DeviceSet.MountDevice(id, mp, mountLabel); err != nil {
|
||||
d.ctr.Decrement(mp)
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := idtools.MkdirAllAndChown(rootFs, 0755, root); err != nil {
|
||||
d.ctr.Decrement(mp)
|
||||
d.DeviceSet.UnmountDevice(id, mp)
|
||||
return "", err
|
||||
}
|
||||
|
||||
idFile := path.Join(mp, "id")
|
||||
if _, err := os.Stat(idFile); err != nil && os.IsNotExist(err) {
|
||||
// Create an "id" file with the container/image id in it to help reconstruct this in case
|
||||
// of later problems
|
||||
if err := os.WriteFile(idFile, []byte(id), 0600); err != nil {
|
||||
d.ctr.Decrement(mp)
|
||||
d.DeviceSet.UnmountDevice(id, mp)
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return rootFs, nil
|
||||
}
|
||||
|
||||
// Put unmounts a device and removes it.
|
||||
func (d *Driver) Put(id string) error {
|
||||
d.locker.Lock(id)
|
||||
defer d.locker.Unlock(id)
|
||||
mp := path.Join(d.home, "mnt", id)
|
||||
if count := d.ctr.Decrement(mp); count > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := d.DeviceSet.UnmountDevice(id, mp)
|
||||
if err != nil {
|
||||
logrus.WithField("storage-driver", "devicemapper").Errorf("Error unmounting device %s: %v", id, err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Exists checks to see if the device exists.
|
||||
func (d *Driver) Exists(id string) bool {
|
||||
return d.DeviceSet.HasDevice(id)
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type probeData struct {
|
||||
fsName string
|
||||
magic string
|
||||
offset uint64
|
||||
}
|
||||
|
||||
// ProbeFsType returns the filesystem name for the given device id.
|
||||
func ProbeFsType(device string) (string, error) {
|
||||
probes := []probeData{
|
||||
{"btrfs", "_BHRfS_M", 0x10040},
|
||||
{"ext4", "\123\357", 0x438},
|
||||
{"xfs", "XFSB", 0},
|
||||
}
|
||||
|
||||
maxLen := uint64(0)
|
||||
for _, p := range probes {
|
||||
l := p.offset + uint64(len(p.magic))
|
||||
if l > maxLen {
|
||||
maxLen = l
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Open(device)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
buffer := make([]byte, maxLen)
|
||||
l, err := file.Read(buffer)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if uint64(l) != maxLen {
|
||||
return "", fmt.Errorf("devmapper: unable to detect filesystem type of %s, short read", device)
|
||||
}
|
||||
|
||||
for _, p := range probes {
|
||||
if bytes.Equal([]byte(p.magic), buffer[p.offset:p.offset+uint64(len(p.magic))]) {
|
||||
return p.fsName, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("devmapper: Unknown filesystem type on %s", device)
|
||||
}
|
||||
|
||||
func joinMountOptions(a, b string) string {
|
||||
if a == "" {
|
||||
return b
|
||||
}
|
||||
if b == "" {
|
||||
return a
|
||||
}
|
||||
return a + "," + b
|
||||
}
|
|
@ -196,9 +196,6 @@ func New(name string, pg plugingetter.PluginGetter, config Options) (Driver, err
|
|||
if err := checkRemoved(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isDeprecated(name) {
|
||||
logrus.Warnf("[graphdriver] WARNING: the %s storage-driver is deprecated and will be removed in a future release; visit https://docs.docker.com/go/storage-driver/ for more information", name)
|
||||
}
|
||||
return GetDriver(name, pg, config)
|
||||
}
|
||||
|
||||
|
@ -219,11 +216,6 @@ func New(name string, pg plugingetter.PluginGetter, config Options) (Driver, err
|
|||
logrus.Errorf("[graphdriver] prior storage driver %s failed: %s", name, err)
|
||||
return nil, err
|
||||
}
|
||||
if isDeprecated(name) {
|
||||
err = errors.Errorf("prior storage driver %s is deprecated and will be removed in a future release; update the the daemon configuration and explicitly choose this storage driver to continue using it; visit https://docs.docker.com/go/storage-driver/ for more information", name)
|
||||
logrus.Errorf("[graphdriver] %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// abort starting when there are other prior configured drivers
|
||||
// to ensure the user explicitly selects the driver to load
|
||||
|
@ -246,11 +238,6 @@ func New(name string, pg plugingetter.PluginGetter, config Options) (Driver, err
|
|||
// If no prior state was found, continue with automatic selection, and pick
|
||||
// the first supported, non-deprecated, storage driver (in order of priorityList).
|
||||
for _, name := range priorityList {
|
||||
if isDeprecated(name) {
|
||||
// Deprecated storage-drivers are skipped in automatic selection, but
|
||||
// can be selected through configuration.
|
||||
continue
|
||||
}
|
||||
driver, err := getBuiltinDriver(name, config.Root, config.DriverOptions, config.IDMap)
|
||||
if err != nil {
|
||||
if IsDriverNotSupported(err) {
|
||||
|
@ -263,11 +250,6 @@ func New(name string, pg plugingetter.PluginGetter, config Options) (Driver, err
|
|||
|
||||
// Check all registered drivers if no priority driver is found
|
||||
for name, initFunc := range drivers {
|
||||
if isDeprecated(name) {
|
||||
// Deprecated storage-drivers are skipped in automatic selection, but
|
||||
// can be selected through configuration.
|
||||
continue
|
||||
}
|
||||
driver, err := initFunc(filepath.Join(config.Root, name), config.DriverOptions, config.IDMap)
|
||||
if err != nil {
|
||||
if IsDriverNotSupported(err) {
|
||||
|
@ -314,20 +296,10 @@ func isEmptyDir(name string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// isDeprecated checks if a storage-driver is marked "deprecated"
|
||||
func isDeprecated(name string) bool {
|
||||
switch name {
|
||||
// NOTE: when deprecating a driver, update daemon.fillDriverInfo() accordingly
|
||||
case "devicemapper":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// checkRemoved checks if a storage-driver has been deprecated (and removed)
|
||||
func checkRemoved(name string) error {
|
||||
switch name {
|
||||
case "aufs", "overlay":
|
||||
case "aufs", "devicemapper", "overlay":
|
||||
return NotSupportedError(fmt.Sprintf("[graphdriver] ERROR: the %s storage-driver has been deprecated and removed; visit https://docs.docker.com/go/storage-driver/ for more information", name))
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -50,7 +50,7 @@ const (
|
|||
|
||||
var (
|
||||
// List of drivers that should be used in an order
|
||||
priority = "overlay2,fuse-overlayfs,btrfs,zfs,devicemapper,vfs"
|
||||
priority = "overlay2,fuse-overlayfs,btrfs,zfs,vfs"
|
||||
|
||||
// FsNames maps filesystem id to name of the filesystem.
|
||||
FsNames = map[FsMagic]string{
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
//go:build !exclude_graphdriver_devicemapper && !static_build && linux
|
||||
// +build !exclude_graphdriver_devicemapper,!static_build,linux
|
||||
|
||||
package register // import "github.com/docker/docker/daemon/graphdriver/register"
|
||||
|
||||
import (
|
||||
// register the devmapper graphdriver
|
||||
_ "github.com/docker/docker/daemon/graphdriver/devmapper"
|
||||
)
|
|
@ -128,7 +128,7 @@ WARNING: The %s storage-driver is deprecated, and will be removed in a future re
|
|||
Refer to the documentation for more information: https://docs.docker.com/go/storage-driver/`
|
||||
|
||||
switch v.Driver {
|
||||
case "devicemapper", "overlay":
|
||||
case "overlay":
|
||||
v.Warnings = append(v.Warnings, fmt.Sprintf(warnMsg, v.Driver))
|
||||
}
|
||||
|
||||
|
|
|
@ -377,7 +377,7 @@ Return low-level information on the container `id`
|
|||
"WorkingDir": ""
|
||||
},
|
||||
"Created": "2015-01-06T15:47:31.485331387Z",
|
||||
"Driver": "devicemapper",
|
||||
"Driver": "overlay2",
|
||||
"ExecDriver": "native-0.2",
|
||||
"ExecIDs": null,
|
||||
"HostConfig": {
|
||||
|
|
|
@ -387,7 +387,7 @@ Return low-level information on the container `id`
|
|||
"WorkingDir": ""
|
||||
},
|
||||
"Created": "2015-01-06T15:47:31.485331387Z",
|
||||
"Driver": "devicemapper",
|
||||
"Driver": "overlay2",
|
||||
"ExecDriver": "native-0.2",
|
||||
"ExecIDs": null,
|
||||
"HostConfig": {
|
||||
|
|
|
@ -390,7 +390,7 @@ Return low-level information on the container `id`
|
|||
"WorkingDir": ""
|
||||
},
|
||||
"Created": "2015-01-06T15:47:31.485331387Z",
|
||||
"Driver": "devicemapper",
|
||||
"Driver": "overlay2",
|
||||
"ExecDriver": "native-0.2",
|
||||
"ExecIDs": null,
|
||||
"HostConfig": {
|
||||
|
|
|
@ -412,7 +412,7 @@ Return low-level information on the container `id`
|
|||
"StopSignal": "SIGTERM"
|
||||
},
|
||||
"Created": "2015-01-06T15:47:31.485331387Z",
|
||||
"Driver": "devicemapper",
|
||||
"Driver": "overlay2",
|
||||
"ExecDriver": "native-0.2",
|
||||
"ExecIDs": null,
|
||||
"HostConfig": {
|
||||
|
|
|
@ -529,7 +529,7 @@ Return low-level information on the container `id`
|
|||
"StopSignal": "SIGTERM"
|
||||
},
|
||||
"Created": "2015-01-06T15:47:31.485331387Z",
|
||||
"Driver": "devicemapper",
|
||||
"Driver": "overlay2",
|
||||
"ExecIDs": null,
|
||||
"HostConfig": {
|
||||
"Binds": null,
|
||||
|
|
|
@ -555,7 +555,7 @@ Return low-level information on the container `id`
|
|||
"StopSignal": "SIGTERM"
|
||||
},
|
||||
"Created": "2015-01-06T15:47:31.485331387Z",
|
||||
"Driver": "devicemapper",
|
||||
"Driver": "overlay2",
|
||||
"ExecIDs": null,
|
||||
"HostConfig": {
|
||||
"Binds": null,
|
||||
|
|
|
@ -597,7 +597,7 @@ Return low-level information on the container `id`
|
|||
"StopSignal": "SIGTERM"
|
||||
},
|
||||
"Created": "2015-01-06T15:47:31.485331387Z",
|
||||
"Driver": "devicemapper",
|
||||
"Driver": "overlay2",
|
||||
"ExecIDs": null,
|
||||
"HostConfig": {
|
||||
"Binds": null,
|
||||
|
|
|
@ -2995,7 +2995,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
HostConfig:
|
||||
MaximumIOps: 0
|
||||
MaximumIOBps: 0
|
||||
|
|
|
@ -3000,7 +3000,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
HostConfig:
|
||||
MaximumIOps: 0
|
||||
MaximumIOBps: 0
|
||||
|
|
|
@ -3060,7 +3060,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
HostConfig:
|
||||
MaximumIOps: 0
|
||||
MaximumIOBps: 0
|
||||
|
|
|
@ -3150,7 +3150,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
HostConfig:
|
||||
MaximumIOps: 0
|
||||
MaximumIOBps: 0
|
||||
|
|
|
@ -3184,7 +3184,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
HostConfig:
|
||||
MaximumIOps: 0
|
||||
MaximumIOBps: 0
|
||||
|
|
|
@ -3410,7 +3410,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
HostConfig:
|
||||
MaximumIOps: 0
|
||||
MaximumIOBps: 0
|
||||
|
|
|
@ -3480,7 +3480,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
HostConfig:
|
||||
MaximumIOps: 0
|
||||
MaximumIOBps: 0
|
||||
|
|
|
@ -4722,7 +4722,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
HostConfig:
|
||||
MaximumIOps: 0
|
||||
MaximumIOBps: 0
|
||||
|
|
|
@ -4727,7 +4727,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
HostConfig:
|
||||
MaximumIOps: 0
|
||||
MaximumIOBps: 0
|
||||
|
|
|
@ -4756,7 +4756,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
HostConfig:
|
||||
MaximumIOps: 0
|
||||
MaximumIOBps: 0
|
||||
|
|
|
@ -4738,7 +4738,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
HostConfig:
|
||||
MaximumIOps: 0
|
||||
MaximumIOBps: 0
|
||||
|
|
|
@ -4754,7 +4754,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
HostConfig:
|
||||
MaximumIOps: 0
|
||||
MaximumIOBps: 0
|
||||
|
|
|
@ -4774,7 +4774,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
HostConfig:
|
||||
MaximumIOps: 0
|
||||
MaximumIOBps: 0
|
||||
|
|
|
@ -4832,7 +4832,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
ExecIDs:
|
||||
- "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca"
|
||||
- "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4"
|
||||
|
|
|
@ -5824,7 +5824,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
ExecIDs:
|
||||
- "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca"
|
||||
- "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4"
|
||||
|
|
|
@ -6125,7 +6125,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
ExecIDs:
|
||||
- "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca"
|
||||
- "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4"
|
||||
|
|
|
@ -6317,7 +6317,7 @@ paths:
|
|||
StopSignal: "SIGTERM"
|
||||
StopTimeout: 10
|
||||
Created: "2015-01-06T15:47:31.485331387Z"
|
||||
Driver: "devicemapper"
|
||||
Driver: "overlay2"
|
||||
ExecIDs:
|
||||
- "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca"
|
||||
- "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4"
|
||||
|
|
|
@ -130,7 +130,7 @@ can take over 15 minutes to complete.
|
|||
```none
|
||||
Successfully built 3d872560918e
|
||||
Successfully tagged docker-dev:dry-run-test
|
||||
docker run --rm -i --privileged -e BUILDFLAGS -e KEEPBUNDLE -e DOCKER_BUILD_GOGC -e DOCKER_BUILD_PKGS -e DOCKER_CLIENTONLY -e DOCKER_DEBUG -e DOCKER_EXPERIMENTAL -e DOCKER_GITCOMMIT -e DOCKER_GRAPHDRIVER=devicemapper -e DOCKER_REMAP_ROOT -e DOCKER_STORAGE_OPTS -e DOCKER_USERLANDPROXY -e TESTDIRS -e TESTFLAGS -e TIMEOUT -v "home/ubuntu/repos/docker/bundles:/go/src/github.com/docker/docker/bundles" -t "docker-dev:dry-run-test" bash
|
||||
docker run --rm -i --privileged -e BUILDFLAGS -e KEEPBUNDLE -e DOCKER_BUILD_GOGC -e DOCKER_BUILD_PKGS -e DOCKER_CLIENTONLY -e DOCKER_DEBUG -e DOCKER_EXPERIMENTAL -e DOCKER_GITCOMMIT -e DOCKER_GRAPHDRIVER=vfs -e DOCKER_REMAP_ROOT -e DOCKER_STORAGE_OPTS -e DOCKER_USERLANDPROXY -e TESTDIRS -e TESTFLAGS -e TIMEOUT -v "home/ubuntu/repos/docker/bundles:/go/src/github.com/docker/docker/bundles" -t "docker-dev:dry-run-test" bash
|
||||
#
|
||||
```
|
||||
|
||||
|
|
|
@ -63,30 +63,6 @@ func (s *DockerCLICreateSuite) TestCreateArgs(c *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Make sure we can grow the container's rootfs at creation time.
|
||||
func (s *DockerCLICreateSuite) TestCreateGrowRootfs(c *testing.T) {
|
||||
// Windows and Devicemapper support growing the rootfs
|
||||
if testEnv.OSType != "windows" {
|
||||
testRequires(c, Devicemapper)
|
||||
}
|
||||
out, _ := dockerCmd(c, "create", "--storage-opt", "size=120G", "busybox")
|
||||
|
||||
cleanedContainerID := strings.TrimSpace(out)
|
||||
|
||||
inspectOut := inspectField(c, cleanedContainerID, "HostConfig.StorageOpt")
|
||||
assert.Equal(c, inspectOut, "map[size:120G]")
|
||||
}
|
||||
|
||||
// Make sure we cannot shrink the container's rootfs at creation time.
|
||||
func (s *DockerCLICreateSuite) TestCreateShrinkRootfs(c *testing.T) {
|
||||
testRequires(c, Devicemapper)
|
||||
|
||||
// Ensure this fails because of the defaultBaseFsSize is 10G
|
||||
out, _, err := dockerCmdWithError("create", "--storage-opt", "size=5G", "busybox")
|
||||
assert.ErrorContains(c, err, "", out)
|
||||
assert.Assert(c, strings.Contains(out, "Container size cannot be smaller than"))
|
||||
}
|
||||
|
||||
// Make sure we can set hostconfig options too
|
||||
func (s *DockerCLICreateSuite) TestCreateHostConfig(c *testing.T) {
|
||||
out, _ := dockerCmd(c, "create", "-P", "busybox", "echo")
|
||||
|
|
|
@ -34,7 +34,6 @@ import (
|
|||
"github.com/docker/docker/libnetwork/iptables"
|
||||
"github.com/docker/docker/opts"
|
||||
testdaemon "github.com/docker/docker/testutil/daemon"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/moby/sys/mount"
|
||||
"golang.org/x/sys/unix"
|
||||
"gotest.tools/v3/assert"
|
||||
|
@ -203,77 +202,6 @@ func (s *DockerDaemonSuite) TestDaemonStartIptablesFalse(c *testing.T) {
|
|||
s.d.Start(c, "--iptables=false")
|
||||
}
|
||||
|
||||
// Make sure we cannot shrink base device at daemon restart.
|
||||
func (s *DockerDaemonSuite) TestDaemonRestartWithInvalidBasesize(c *testing.T) {
|
||||
testRequires(c, Devicemapper)
|
||||
s.d.Start(c)
|
||||
|
||||
oldBasesizeBytes := getBaseDeviceSize(c, s.d)
|
||||
var newBasesizeBytes int64 = 1073741824 // 1GB in bytes
|
||||
|
||||
if newBasesizeBytes < oldBasesizeBytes {
|
||||
err := s.d.RestartWithError("--storage-opt", fmt.Sprintf("dm.basesize=%d", newBasesizeBytes))
|
||||
assert.Assert(c, err != nil, "daemon should not have started as new base device size is less than existing base device size: %v", err)
|
||||
// 'err != nil' is expected behaviour, no new daemon started,
|
||||
// so no need to stop daemon.
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
s.d.Stop(c)
|
||||
}
|
||||
|
||||
// Make sure we can grow base device at daemon restart.
|
||||
func (s *DockerDaemonSuite) TestDaemonRestartWithIncreasedBasesize(c *testing.T) {
|
||||
testRequires(c, Devicemapper)
|
||||
s.d.Start(c)
|
||||
|
||||
oldBasesizeBytes := getBaseDeviceSize(c, s.d)
|
||||
|
||||
var newBasesizeBytes int64 = 53687091200 // 50GB in bytes
|
||||
|
||||
if newBasesizeBytes < oldBasesizeBytes {
|
||||
c.Skipf("New base device size (%v) must be greater than (%s)", units.HumanSize(float64(newBasesizeBytes)), units.HumanSize(float64(oldBasesizeBytes)))
|
||||
}
|
||||
|
||||
err := s.d.RestartWithError("--storage-opt", fmt.Sprintf("dm.basesize=%d", newBasesizeBytes))
|
||||
assert.Assert(c, err == nil, "we should have been able to start the daemon with increased base device size: %v", err)
|
||||
|
||||
basesizeAfterRestart := getBaseDeviceSize(c, s.d)
|
||||
newBasesize, err := convertBasesize(newBasesizeBytes)
|
||||
assert.Assert(c, err == nil, "Error in converting base device size: %v", err)
|
||||
assert.Equal(c, newBasesize, basesizeAfterRestart, "Basesize passed is not equal to Basesize set")
|
||||
s.d.Stop(c)
|
||||
}
|
||||
|
||||
func getBaseDeviceSize(c *testing.T, d *daemon.Daemon) int64 {
|
||||
info := d.Info(c)
|
||||
for _, statusLine := range info.DriverStatus {
|
||||
key, value := statusLine[0], statusLine[1]
|
||||
if key == "Base Device Size" {
|
||||
return parseDeviceSize(c, value)
|
||||
}
|
||||
}
|
||||
c.Fatal("failed to parse Base Device Size from info")
|
||||
return int64(0)
|
||||
}
|
||||
|
||||
func parseDeviceSize(c *testing.T, raw string) int64 {
|
||||
size, err := units.RAMInBytes(strings.TrimSpace(raw))
|
||||
assert.NilError(c, err)
|
||||
return size
|
||||
}
|
||||
|
||||
func convertBasesize(basesizeBytes int64) (int64, error) {
|
||||
basesize := units.HumanSize(float64(basesizeBytes))
|
||||
basesize = strings.Trim(basesize, " ")[:len(basesize)-3]
|
||||
basesizeFloat, err := strconv.ParseFloat(strings.Trim(basesize, " "), 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int64(basesizeFloat) * 1024 * 1024 * 1024, nil
|
||||
}
|
||||
|
||||
// Issue #8444: If docker0 bridge is modified (intentionally or unintentionally) and
|
||||
// no longer has an IP associated, we should gracefully handle that case and associate
|
||||
// an IP with it rather than fail daemon start
|
||||
|
|
|
@ -27,12 +27,6 @@ func (s *DockerCLIInspectSuite) OnTimeout(c *testing.T) {
|
|||
s.ds.OnTimeout(c)
|
||||
}
|
||||
|
||||
func checkValidGraphDriver(c *testing.T, name string) {
|
||||
if name != "devicemapper" && name != "overlay" && name != "vfs" && name != "zfs" && name != "btrfs" && name != "aufs" {
|
||||
c.Fatalf("%v is not a valid graph driver name", name)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerCLIInspectSuite) TestInspectImage(c *testing.T) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
imageTest := "emptyfs"
|
||||
|
@ -177,49 +171,6 @@ func (s *DockerCLIInspectSuite) TestInspectContainerFilterInt(c *testing.T) {
|
|||
assert.Equal(c, inspectResult, true)
|
||||
}
|
||||
|
||||
func (s *DockerCLIInspectSuite) TestInspectImageGraphDriver(c *testing.T) {
|
||||
testRequires(c, DaemonIsLinux, Devicemapper)
|
||||
imageTest := "emptyfs"
|
||||
name := inspectField(c, imageTest, "GraphDriver.Name")
|
||||
|
||||
checkValidGraphDriver(c, name)
|
||||
|
||||
deviceID := inspectField(c, imageTest, "GraphDriver.Data.DeviceId")
|
||||
|
||||
_, err := strconv.Atoi(deviceID)
|
||||
assert.Assert(c, err == nil, "failed to inspect DeviceId of the image: %s, %v", deviceID, err)
|
||||
|
||||
deviceSize := inspectField(c, imageTest, "GraphDriver.Data.DeviceSize")
|
||||
|
||||
_, err = strconv.ParseUint(deviceSize, 10, 64)
|
||||
assert.Assert(c, err == nil, "failed to inspect DeviceSize of the image: %s, %v", deviceSize, err)
|
||||
}
|
||||
|
||||
func (s *DockerCLIInspectSuite) TestInspectContainerGraphDriver(c *testing.T) {
|
||||
testRequires(c, DaemonIsLinux, Devicemapper)
|
||||
|
||||
out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
|
||||
out = strings.TrimSpace(out)
|
||||
|
||||
name := inspectField(c, out, "GraphDriver.Name")
|
||||
|
||||
checkValidGraphDriver(c, name)
|
||||
|
||||
imageDeviceID := inspectField(c, "busybox", "GraphDriver.Data.DeviceId")
|
||||
|
||||
deviceID := inspectField(c, out, "GraphDriver.Data.DeviceId")
|
||||
|
||||
assert.Assert(c, imageDeviceID != deviceID)
|
||||
|
||||
_, err := strconv.Atoi(deviceID)
|
||||
assert.Assert(c, err == nil, "failed to inspect DeviceId of the image: %s, %v", deviceID, err)
|
||||
|
||||
deviceSize := inspectField(c, out, "GraphDriver.Data.DeviceSize")
|
||||
|
||||
_, err = strconv.ParseUint(deviceSize, 10, 64)
|
||||
assert.Assert(c, err == nil, "failed to inspect DeviceSize of the image: %s, %v", deviceSize, err)
|
||||
}
|
||||
|
||||
func (s *DockerCLIInspectSuite) TestInspectBindMountPoint(c *testing.T) {
|
||||
modifier := ",z"
|
||||
prefix, slash := getPrefixAndSlashFromDaemonPlatform()
|
||||
|
|
|
@ -100,10 +100,6 @@ func Apparmor() bool {
|
|||
return err == nil && len(buf) > 1 && buf[0] == 'Y'
|
||||
}
|
||||
|
||||
func Devicemapper() bool {
|
||||
return strings.HasPrefix(testEnv.DaemonInfo.Driver, "devicemapper")
|
||||
}
|
||||
|
||||
// containerdSnapshotterEnabled checks if the daemon in the test-environment is
|
||||
// configured with containerd-snapshotters enabled.
|
||||
func containerdSnapshotterEnabled() bool {
|
||||
|
|
|
@ -2,19 +2,14 @@ package container // import "github.com/docker/docker/integration/container"
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/integration/internal/container"
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/icmd"
|
||||
"gotest.tools/v3/poll"
|
||||
"gotest.tools/v3/skip"
|
||||
)
|
||||
|
||||
// TestStopContainerWithTimeout checks that ContainerStop with
|
||||
|
@ -69,32 +64,3 @@ func TestStopContainerWithTimeout(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteDevicemapper(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.Driver != "devicemapper")
|
||||
skip.If(t, testEnv.IsRemoteDaemon)
|
||||
|
||||
defer setupTest(t)()
|
||||
client := testEnv.APIClient()
|
||||
ctx := context.Background()
|
||||
|
||||
id := container.Run(ctx, t, client, container.WithName("foo-"+t.Name()), container.WithCmd("echo"))
|
||||
|
||||
poll.WaitOn(t, container.IsStopped(ctx, client, id), poll.WithDelay(100*time.Millisecond))
|
||||
|
||||
inspect, err := client.ContainerInspect(ctx, id)
|
||||
assert.NilError(t, err)
|
||||
|
||||
deviceID := inspect.GraphDriver.Data["DeviceId"]
|
||||
|
||||
// Find pool name from device name
|
||||
deviceName := inspect.GraphDriver.Data["DeviceName"]
|
||||
devicePrefix := deviceName[:strings.LastIndex(deviceName, "-")]
|
||||
devicePool := fmt.Sprintf("/dev/mapper/%s-pool", devicePrefix)
|
||||
|
||||
result := icmd.RunCommand("dmsetup", "message", devicePool, "0", fmt.Sprintf("delete %s", deviceID))
|
||||
result.Assert(t, icmd.Success)
|
||||
|
||||
err = client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
|
|
@ -1,811 +0,0 @@
|
|||
//go:build linux && cgo
|
||||
// +build linux,cgo
|
||||
|
||||
package devicemapper // import "github.com/docker/docker/pkg/devicemapper"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Same as DM_DEVICE_* enum values from libdevmapper.h
|
||||
//
|
||||
//nolint:unused
|
||||
const (
|
||||
deviceCreate TaskType = iota
|
||||
deviceReload
|
||||
deviceRemove
|
||||
deviceRemoveAll
|
||||
deviceSuspend
|
||||
deviceResume
|
||||
deviceInfo
|
||||
deviceDeps
|
||||
deviceRename
|
||||
deviceVersion
|
||||
deviceStatus
|
||||
deviceTable
|
||||
deviceWaitevent
|
||||
deviceList
|
||||
deviceClear
|
||||
deviceMknodes
|
||||
deviceListVersions
|
||||
deviceTargetMsg
|
||||
deviceSetGeometry
|
||||
)
|
||||
|
||||
const (
|
||||
addNodeOnResume AddNodeType = iota
|
||||
addNodeOnCreate
|
||||
)
|
||||
|
||||
// List of errors returned when using devicemapper.
|
||||
var (
|
||||
ErrTaskRun = errors.New("dm_task_run failed")
|
||||
ErrTaskSetName = errors.New("dm_task_set_name failed")
|
||||
ErrTaskSetMessage = errors.New("dm_task_set_message failed")
|
||||
ErrTaskSetAddNode = errors.New("dm_task_set_add_node failed")
|
||||
ErrTaskAddTarget = errors.New("dm_task_add_target failed")
|
||||
ErrTaskSetSector = errors.New("dm_task_set_sector failed")
|
||||
ErrTaskGetDeps = errors.New("dm_task_get_deps failed")
|
||||
ErrTaskGetInfo = errors.New("dm_task_get_info failed")
|
||||
ErrTaskGetDriverVersion = errors.New("dm_task_get_driver_version failed")
|
||||
ErrTaskDeferredRemove = errors.New("dm_task_deferred_remove failed")
|
||||
ErrTaskSetCookie = errors.New("dm_task_set_cookie failed")
|
||||
ErrNilCookie = errors.New("cookie ptr can't be nil")
|
||||
ErrGetBlockSize = errors.New("Can't get block size")
|
||||
ErrUdevWait = errors.New("wait on udev cookie failed")
|
||||
ErrSetDevDir = errors.New("dm_set_dev_dir failed")
|
||||
ErrGetLibraryVersion = errors.New("dm_get_library_version failed")
|
||||
ErrInvalidAddNode = errors.New("Invalid AddNode type")
|
||||
ErrBusy = errors.New("Device is Busy")
|
||||
ErrDeviceIDExists = errors.New("Device Id Exists")
|
||||
ErrEnxio = errors.New("No such device or address")
|
||||
)
|
||||
|
||||
var (
|
||||
dmSawBusy bool
|
||||
dmSawExist bool
|
||||
dmSawEnxio bool // No Such Device or Address
|
||||
dmSawEnoData bool // No data available
|
||||
)
|
||||
|
||||
type (
|
||||
// Task represents a devicemapper task (like lvcreate, etc.) ; a task is needed for each ioctl
|
||||
// command to execute.
|
||||
Task struct {
|
||||
unmanaged *cdmTask
|
||||
}
|
||||
// Deps represents dependents (layer) of a device.
|
||||
Deps struct {
|
||||
Count uint32
|
||||
Filler uint32
|
||||
Device []uint64
|
||||
}
|
||||
// Info represents information about a device.
|
||||
Info struct {
|
||||
Exists int
|
||||
Suspended int
|
||||
LiveTable int
|
||||
InactiveTable int
|
||||
OpenCount int32
|
||||
EventNr uint32
|
||||
Major uint32
|
||||
Minor uint32
|
||||
ReadOnly int
|
||||
TargetCount int32
|
||||
DeferredRemove int
|
||||
}
|
||||
// TaskType represents a type of task
|
||||
TaskType int
|
||||
// AddNodeType represents a type of node to be added
|
||||
AddNodeType int
|
||||
)
|
||||
|
||||
// DeviceIDExists returns whether error conveys the information about device Id already
|
||||
// exist or not. This will be true if device creation or snap creation
|
||||
// operation fails if device or snap device already exists in pool.
|
||||
// Current implementation is little crude as it scans the error string
|
||||
// for exact pattern match. Replacing it with more robust implementation
|
||||
// is desirable.
|
||||
func DeviceIDExists(err error) bool {
|
||||
return fmt.Sprint(err) == fmt.Sprint(ErrDeviceIDExists)
|
||||
}
|
||||
|
||||
func (t *Task) destroy() {
|
||||
if t != nil {
|
||||
DmTaskDestroy(t.unmanaged)
|
||||
runtime.SetFinalizer(t, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// TaskCreateNamed is a convenience function for TaskCreate when a name
|
||||
// will be set on the task as well
|
||||
func TaskCreateNamed(t TaskType, name string) (*Task, error) {
|
||||
task := TaskCreate(t)
|
||||
if task == nil {
|
||||
return nil, fmt.Errorf("devicemapper: Can't create task of type %d", int(t))
|
||||
}
|
||||
if err := task.setName(name); err != nil {
|
||||
return nil, fmt.Errorf("devicemapper: Can't set task name %s", name)
|
||||
}
|
||||
return task, nil
|
||||
}
|
||||
|
||||
// TaskCreate initializes a devicemapper task of tasktype
|
||||
func TaskCreate(tasktype TaskType) *Task {
|
||||
Ctask := DmTaskCreate(int(tasktype))
|
||||
if Ctask == nil {
|
||||
return nil
|
||||
}
|
||||
task := &Task{unmanaged: Ctask}
|
||||
runtime.SetFinalizer(task, (*Task).destroy)
|
||||
return task
|
||||
}
|
||||
|
||||
func (t *Task) run() error {
|
||||
if res := DmTaskRun(t.unmanaged); res != 1 {
|
||||
return ErrTaskRun
|
||||
}
|
||||
runtime.KeepAlive(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Task) setName(name string) error {
|
||||
if res := DmTaskSetName(t.unmanaged, name); res != 1 {
|
||||
return ErrTaskSetName
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Task) setMessage(message string) error {
|
||||
if res := DmTaskSetMessage(t.unmanaged, message); res != 1 {
|
||||
return ErrTaskSetMessage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Task) setSector(sector uint64) error {
|
||||
if res := DmTaskSetSector(t.unmanaged, sector); res != 1 {
|
||||
return ErrTaskSetSector
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Task) setCookie(cookie *uint, flags uint16) error {
|
||||
if cookie == nil {
|
||||
return ErrNilCookie
|
||||
}
|
||||
if res := DmTaskSetCookie(t.unmanaged, cookie, flags); res != 1 {
|
||||
return ErrTaskSetCookie
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Task) setAddNode(addNode AddNodeType) error {
|
||||
if addNode != addNodeOnResume && addNode != addNodeOnCreate {
|
||||
return ErrInvalidAddNode
|
||||
}
|
||||
if res := DmTaskSetAddNode(t.unmanaged, addNode); res != 1 {
|
||||
return ErrTaskSetAddNode
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Task) addTarget(start, size uint64, ttype, params string) error {
|
||||
if res := DmTaskAddTarget(t.unmanaged, start, size,
|
||||
ttype, params); res != 1 {
|
||||
return ErrTaskAddTarget
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Task) getDeps() (*Deps, error) {
|
||||
var deps *Deps
|
||||
if deps = DmTaskGetDeps(t.unmanaged); deps == nil {
|
||||
return nil, ErrTaskGetDeps
|
||||
}
|
||||
return deps, nil
|
||||
}
|
||||
|
||||
func (t *Task) getInfo() (*Info, error) {
|
||||
info := &Info{}
|
||||
if res := DmTaskGetInfo(t.unmanaged, info); res != 1 {
|
||||
return nil, ErrTaskGetInfo
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (t *Task) getInfoWithDeferred() (*Info, error) {
|
||||
info := &Info{}
|
||||
if res := DmTaskGetInfoWithDeferred(t.unmanaged, info); res != 1 {
|
||||
return nil, ErrTaskGetInfo
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (t *Task) getDriverVersion() (string, error) {
|
||||
res := DmTaskGetDriverVersion(t.unmanaged)
|
||||
if res == "" {
|
||||
return "", ErrTaskGetDriverVersion
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (t *Task) getNextTarget(next unsafe.Pointer) (nextPtr unsafe.Pointer, start uint64, length uint64, targetType string, params string) {
|
||||
return DmGetNextTarget(t.unmanaged, next, &start, &length, &targetType, ¶ms), start, length, targetType, params
|
||||
}
|
||||
|
||||
// UdevWait waits for any processes that are waiting for udev to complete the specified cookie.
|
||||
func UdevWait(cookie *uint) error {
|
||||
if res := DmUdevWait(*cookie); res != 1 {
|
||||
logrus.Debugf("devicemapper: Failed to wait on udev cookie %d, %d", *cookie, res)
|
||||
return ErrUdevWait
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDevDir sets the dev folder for the device mapper library (usually /dev).
|
||||
func SetDevDir(dir string) error {
|
||||
if res := DmSetDevDir(dir); res != 1 {
|
||||
logrus.Debug("devicemapper: Error dm_set_dev_dir")
|
||||
return ErrSetDevDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLibraryVersion returns the device mapper library version.
|
||||
func GetLibraryVersion() (string, error) {
|
||||
var version string
|
||||
if res := DmGetLibraryVersion(&version); res != 1 {
|
||||
return "", ErrGetLibraryVersion
|
||||
}
|
||||
return version, nil
|
||||
}
|
||||
|
||||
// UdevSyncSupported returns whether device-mapper is able to sync with udev
|
||||
//
|
||||
// This is essential otherwise race conditions can arise where both udev and
|
||||
// device-mapper attempt to create and destroy devices.
|
||||
func UdevSyncSupported() bool {
|
||||
return DmUdevGetSyncSupport() != 0
|
||||
}
|
||||
|
||||
// UdevSetSyncSupport allows setting whether the udev sync should be enabled.
|
||||
// The return bool indicates the state of whether the sync is enabled.
|
||||
func UdevSetSyncSupport(enable bool) bool {
|
||||
if enable {
|
||||
DmUdevSetSyncSupport(1)
|
||||
} else {
|
||||
DmUdevSetSyncSupport(0)
|
||||
}
|
||||
|
||||
return UdevSyncSupported()
|
||||
}
|
||||
|
||||
// CookieSupported returns whether the version of device-mapper supports the
|
||||
// use of cookie's in the tasks.
|
||||
// This is largely a lower level call that other functions use.
|
||||
func CookieSupported() bool {
|
||||
return DmCookieSupported() != 0
|
||||
}
|
||||
|
||||
// RemoveDevice is a useful helper for cleaning up a device.
|
||||
func RemoveDevice(name string) error {
|
||||
task, err := TaskCreateNamed(deviceRemove, name)
|
||||
if task == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cookie := new(uint)
|
||||
if err := task.setCookie(cookie, 0); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can not set cookie: %s", err)
|
||||
}
|
||||
defer UdevWait(cookie)
|
||||
|
||||
dmSawBusy = false // reset before the task is run
|
||||
dmSawEnxio = false
|
||||
if err = task.run(); err != nil {
|
||||
if dmSawBusy {
|
||||
return ErrBusy
|
||||
}
|
||||
if dmSawEnxio {
|
||||
return ErrEnxio
|
||||
}
|
||||
return fmt.Errorf("devicemapper: Error running RemoveDevice %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveDeviceDeferred is a useful helper for cleaning up a device, but deferred.
|
||||
func RemoveDeviceDeferred(name string) error {
|
||||
logrus.Debugf("devicemapper: RemoveDeviceDeferred START(%s)", name)
|
||||
defer logrus.Debugf("devicemapper: RemoveDeviceDeferred END(%s)", name)
|
||||
task, err := TaskCreateNamed(deviceRemove, name)
|
||||
if task == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := DmTaskDeferredRemove(task.unmanaged); err != 1 {
|
||||
return ErrTaskDeferredRemove
|
||||
}
|
||||
|
||||
// set a task cookie and disable library fallback, or else libdevmapper will
|
||||
// disable udev dm rules and delete the symlink under /dev/mapper by itself,
|
||||
// even if the removal is deferred by the kernel.
|
||||
cookie := new(uint)
|
||||
flags := uint16(DmUdevDisableLibraryFallback)
|
||||
if err := task.setCookie(cookie, flags); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can not set cookie: %s", err)
|
||||
}
|
||||
|
||||
// libdevmapper and udev relies on System V semaphore for synchronization,
|
||||
// semaphores created in `task.setCookie` will be cleaned up in `UdevWait`.
|
||||
// So these two function call must come in pairs, otherwise semaphores will
|
||||
// be leaked, and the limit of number of semaphores defined in `/proc/sys/kernel/sem`
|
||||
// will be reached, which will eventually make all following calls to 'task.SetCookie'
|
||||
// fail.
|
||||
// this call will not wait for the deferred removal's final executing, since no
|
||||
// udev event will be generated, and the semaphore's value will not be incremented
|
||||
// by udev, what UdevWait is just cleaning up the semaphore.
|
||||
defer UdevWait(cookie)
|
||||
|
||||
dmSawEnxio = false
|
||||
if err = task.run(); err != nil {
|
||||
if dmSawEnxio {
|
||||
return ErrEnxio
|
||||
}
|
||||
return fmt.Errorf("devicemapper: Error running RemoveDeviceDeferred %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CancelDeferredRemove cancels a deferred remove for a device.
|
||||
func CancelDeferredRemove(deviceName string) error {
|
||||
task, err := TaskCreateNamed(deviceTargetMsg, deviceName)
|
||||
if task == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := task.setSector(0); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
||||
}
|
||||
|
||||
if err := task.setMessage("@cancel_deferred_remove"); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't set message %s", err)
|
||||
}
|
||||
|
||||
dmSawBusy = false
|
||||
dmSawEnxio = false
|
||||
if err := task.run(); err != nil {
|
||||
// A device might be being deleted already
|
||||
if dmSawBusy {
|
||||
return ErrBusy
|
||||
} else if dmSawEnxio {
|
||||
return ErrEnxio
|
||||
}
|
||||
return fmt.Errorf("devicemapper: Error running CancelDeferredRemove %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBlockDeviceSize returns the size of a block device identified by the specified file.
|
||||
func GetBlockDeviceSize(file *os.File) (uint64, error) {
|
||||
size, err := ioctlBlkGetSize64(file.Fd())
|
||||
if err != nil {
|
||||
logrus.Errorf("devicemapper: Error getblockdevicesize: %s", err)
|
||||
return 0, ErrGetBlockSize
|
||||
}
|
||||
return uint64(size), nil
|
||||
}
|
||||
|
||||
// BlockDeviceDiscard runs discard for the given path.
|
||||
// This is used as a workaround for the kernel not discarding block so
|
||||
// on the thin pool when we remove a thinp device, so we do it
|
||||
// manually
|
||||
func BlockDeviceDiscard(path string) error {
|
||||
file, err := os.OpenFile(path, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
size, err := GetBlockDeviceSize(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ioctlBlkDiscard(file.Fd(), 0, size); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Without this sometimes the remove of the device that happens after
|
||||
// discard fails with EBUSY.
|
||||
unix.Sync()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreatePool is the programmatic example of "dmsetup create".
|
||||
// It creates a device with the specified poolName, data and metadata file and block size.
|
||||
func CreatePool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
|
||||
task, err := TaskCreateNamed(deviceCreate, poolName)
|
||||
if task == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
size, err := GetBlockDeviceSize(dataFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't get data size %s", err)
|
||||
}
|
||||
|
||||
params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
|
||||
if err := task.addTarget(0, size/512, "thin-pool", params); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't add target %s", err)
|
||||
}
|
||||
|
||||
cookie := new(uint)
|
||||
flags := uint16(DmUdevDisableSubsystemRulesFlag | DmUdevDisableDiskRulesFlag | DmUdevDisableOtherRulesFlag)
|
||||
if err := task.setCookie(cookie, flags); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't set cookie %s", err)
|
||||
}
|
||||
defer UdevWait(cookie)
|
||||
|
||||
if err := task.run(); err != nil {
|
||||
return fmt.Errorf("devicemapper: Error running deviceCreate (CreatePool) %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReloadPool is the programmatic example of "dmsetup reload".
|
||||
// It reloads the table with the specified poolName, data and metadata file and block size.
|
||||
func ReloadPool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
|
||||
task, err := TaskCreateNamed(deviceReload, poolName)
|
||||
if task == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
size, err := GetBlockDeviceSize(dataFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't get data size %s", err)
|
||||
}
|
||||
|
||||
params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
|
||||
if err := task.addTarget(0, size/512, "thin-pool", params); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't add target %s", err)
|
||||
}
|
||||
|
||||
if err := task.run(); err != nil {
|
||||
return fmt.Errorf("devicemapper: Error running ReloadPool %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDeps is the programmatic example of "dmsetup deps".
|
||||
// It outputs a list of devices referenced by the live table for the specified device.
|
||||
func GetDeps(name string) (*Deps, error) {
|
||||
task, err := TaskCreateNamed(deviceDeps, name)
|
||||
if task == nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := task.run(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return task.getDeps()
|
||||
}
|
||||
|
||||
// GetInfo is the programmatic example of "dmsetup info".
|
||||
// It outputs some brief information about the device.
|
||||
func GetInfo(name string) (*Info, error) {
|
||||
task, err := TaskCreateNamed(deviceInfo, name)
|
||||
if task == nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := task.run(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return task.getInfo()
|
||||
}
|
||||
|
||||
// GetInfoWithDeferred is the programmatic example of "dmsetup info", but deferred.
|
||||
// It outputs some brief information about the device.
|
||||
func GetInfoWithDeferred(name string) (*Info, error) {
|
||||
task, err := TaskCreateNamed(deviceInfo, name)
|
||||
if task == nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := task.run(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return task.getInfoWithDeferred()
|
||||
}
|
||||
|
||||
// GetDriverVersion is the programmatic example of "dmsetup version".
|
||||
// It outputs version information of the driver.
|
||||
func GetDriverVersion() (string, error) {
|
||||
task := TaskCreate(deviceVersion)
|
||||
if task == nil {
|
||||
return "", fmt.Errorf("devicemapper: Can't create deviceVersion task")
|
||||
}
|
||||
if err := task.run(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return task.getDriverVersion()
|
||||
}
|
||||
|
||||
// GetStatus is the programmatic example of "dmsetup status".
|
||||
// It outputs status information for the specified device name.
|
||||
func GetStatus(name string) (uint64, uint64, string, string, error) {
|
||||
task, err := TaskCreateNamed(deviceStatus, name)
|
||||
if task == nil {
|
||||
logrus.Debugf("devicemapper: GetStatus() Error TaskCreateNamed: %s", err)
|
||||
return 0, 0, "", "", err
|
||||
}
|
||||
if err := task.run(); err != nil {
|
||||
logrus.Debugf("devicemapper: GetStatus() Error Run: %s", err)
|
||||
return 0, 0, "", "", err
|
||||
}
|
||||
|
||||
devinfo, err := task.getInfo()
|
||||
if err != nil {
|
||||
logrus.Debugf("devicemapper: GetStatus() Error GetInfo: %s", err)
|
||||
return 0, 0, "", "", err
|
||||
}
|
||||
if devinfo.Exists == 0 {
|
||||
logrus.Debugf("devicemapper: GetStatus() Non existing device %s", name)
|
||||
return 0, 0, "", "", fmt.Errorf("devicemapper: Non existing device %s", name)
|
||||
}
|
||||
|
||||
_, start, length, targetType, params := task.getNextTarget(unsafe.Pointer(nil))
|
||||
return start, length, targetType, params, nil
|
||||
}
|
||||
|
||||
// GetTable is the programmatic example for "dmsetup table".
|
||||
// It outputs the current table for the specified device name.
|
||||
func GetTable(name string) (uint64, uint64, string, string, error) {
|
||||
task, err := TaskCreateNamed(deviceTable, name)
|
||||
if task == nil {
|
||||
logrus.Debugf("devicemapper: GetTable() Error TaskCreateNamed: %s", err)
|
||||
return 0, 0, "", "", err
|
||||
}
|
||||
if err := task.run(); err != nil {
|
||||
logrus.Debugf("devicemapper: GetTable() Error Run: %s", err)
|
||||
return 0, 0, "", "", err
|
||||
}
|
||||
|
||||
devinfo, err := task.getInfo()
|
||||
if err != nil {
|
||||
logrus.Debugf("devicemapper: GetTable() Error GetInfo: %s", err)
|
||||
return 0, 0, "", "", err
|
||||
}
|
||||
if devinfo.Exists == 0 {
|
||||
logrus.Debugf("devicemapper: GetTable() Non existing device %s", name)
|
||||
return 0, 0, "", "", fmt.Errorf("devicemapper: Non existing device %s", name)
|
||||
}
|
||||
|
||||
_, start, length, targetType, params := task.getNextTarget(unsafe.Pointer(nil))
|
||||
return start, length, targetType, params, nil
|
||||
}
|
||||
|
||||
// SetTransactionID sets a transaction id for the specified device name.
|
||||
func SetTransactionID(poolName string, oldID uint64, newID uint64) error {
|
||||
task, err := TaskCreateNamed(deviceTargetMsg, poolName)
|
||||
if task == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := task.setSector(0); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
||||
}
|
||||
|
||||
if err := task.setMessage(fmt.Sprintf("set_transaction_id %d %d", oldID, newID)); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't set message %s", err)
|
||||
}
|
||||
|
||||
if err := task.run(); err != nil {
|
||||
return fmt.Errorf("devicemapper: Error running SetTransactionID %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SuspendDevice is the programmatic example of "dmsetup suspend".
|
||||
// It suspends the specified device.
|
||||
func SuspendDevice(name string) error {
|
||||
task, err := TaskCreateNamed(deviceSuspend, name)
|
||||
if task == nil {
|
||||
return err
|
||||
}
|
||||
if err := task.run(); err != nil {
|
||||
return fmt.Errorf("devicemapper: Error running deviceSuspend %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResumeDevice is the programmatic example of "dmsetup resume".
|
||||
// It un-suspends the specified device.
|
||||
func ResumeDevice(name string) error {
|
||||
task, err := TaskCreateNamed(deviceResume, name)
|
||||
if task == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cookie := new(uint)
|
||||
if err := task.setCookie(cookie, 0); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't set cookie %s", err)
|
||||
}
|
||||
defer UdevWait(cookie)
|
||||
|
||||
if err := task.run(); err != nil {
|
||||
return fmt.Errorf("devicemapper: Error running deviceResume %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateDevice creates a device with the specified poolName with the specified device id.
|
||||
func CreateDevice(poolName string, deviceID int) error {
|
||||
logrus.Debugf("devicemapper: CreateDevice(poolName=%v, deviceID=%v)", poolName, deviceID)
|
||||
task, err := TaskCreateNamed(deviceTargetMsg, poolName)
|
||||
if task == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := task.setSector(0); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
||||
}
|
||||
|
||||
if err := task.setMessage(fmt.Sprintf("create_thin %d", deviceID)); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't set message %s", err)
|
||||
}
|
||||
|
||||
dmSawExist = false // reset before the task is run
|
||||
if err := task.run(); err != nil {
|
||||
// Caller wants to know about ErrDeviceIDExists so that it can try with a different device id.
|
||||
if dmSawExist {
|
||||
return ErrDeviceIDExists
|
||||
}
|
||||
|
||||
return fmt.Errorf("devicemapper: Error running CreateDevice %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteDevice deletes a device with the specified poolName with the specified device id.
|
||||
func DeleteDevice(poolName string, deviceID int) error {
|
||||
task, err := TaskCreateNamed(deviceTargetMsg, poolName)
|
||||
if task == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := task.setSector(0); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
||||
}
|
||||
|
||||
if err := task.setMessage(fmt.Sprintf("delete %d", deviceID)); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't set message %s", err)
|
||||
}
|
||||
|
||||
dmSawBusy = false
|
||||
dmSawEnoData = false
|
||||
if err := task.run(); err != nil {
|
||||
if dmSawBusy {
|
||||
return ErrBusy
|
||||
}
|
||||
if dmSawEnoData {
|
||||
logrus.Debugf("devicemapper: Device(id: %d) from pool(%s) does not exist", deviceID, poolName)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("devicemapper: Error running DeleteDevice %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActivateDevice activates the device identified by the specified
|
||||
// poolName, name and deviceID with the specified size.
|
||||
func ActivateDevice(poolName string, name string, deviceID int, size uint64) error {
|
||||
return activateDevice(poolName, name, deviceID, size, "")
|
||||
}
|
||||
|
||||
// ActivateDeviceWithExternal activates the device identified by the specified
|
||||
// poolName, name and deviceID with the specified size.
|
||||
func ActivateDeviceWithExternal(poolName string, name string, deviceID int, size uint64, external string) error {
|
||||
return activateDevice(poolName, name, deviceID, size, external)
|
||||
}
|
||||
|
||||
func activateDevice(poolName string, name string, deviceID int, size uint64, external string) error {
|
||||
task, err := TaskCreateNamed(deviceCreate, name)
|
||||
if task == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var params string
|
||||
if len(external) > 0 {
|
||||
params = fmt.Sprintf("%s %d %s", poolName, deviceID, external)
|
||||
} else {
|
||||
params = fmt.Sprintf("%s %d", poolName, deviceID)
|
||||
}
|
||||
if err := task.addTarget(0, size/512, "thin", params); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't add target %s", err)
|
||||
}
|
||||
if err := task.setAddNode(addNodeOnCreate); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't add node %s", err)
|
||||
}
|
||||
|
||||
cookie := new(uint)
|
||||
if err := task.setCookie(cookie, 0); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't set cookie %s", err)
|
||||
}
|
||||
|
||||
defer UdevWait(cookie)
|
||||
|
||||
if err := task.run(); err != nil {
|
||||
return fmt.Errorf("devicemapper: Error running deviceCreate (ActivateDevice) %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSnapDeviceRaw creates a snapshot device. Caller needs to suspend and resume the origin device if it is active.
|
||||
func CreateSnapDeviceRaw(poolName string, deviceID int, baseDeviceID int) error {
|
||||
task, err := TaskCreateNamed(deviceTargetMsg, poolName)
|
||||
if task == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := task.setSector(0); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
||||
}
|
||||
|
||||
if err := task.setMessage(fmt.Sprintf("create_snap %d %d", deviceID, baseDeviceID)); err != nil {
|
||||
return fmt.Errorf("devicemapper: Can't set message %s", err)
|
||||
}
|
||||
|
||||
dmSawExist = false // reset before the task is run
|
||||
if err := task.run(); err != nil {
|
||||
// Caller wants to know about ErrDeviceIDExists so that it can try with a different device id.
|
||||
if dmSawExist {
|
||||
return ErrDeviceIDExists
|
||||
}
|
||||
return fmt.Errorf("devicemapper: Error running deviceCreate (CreateSnapDeviceRaw) %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSnapDevice creates a snapshot based on the device identified by the baseName and baseDeviceId,
|
||||
func CreateSnapDevice(poolName string, deviceID int, baseName string, baseDeviceID int) error {
|
||||
devinfo, _ := GetInfo(baseName)
|
||||
doSuspend := devinfo != nil && devinfo.Exists != 0
|
||||
|
||||
if doSuspend {
|
||||
if err := SuspendDevice(baseName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := CreateSnapDeviceRaw(poolName, deviceID, baseDeviceID); err != nil {
|
||||
if doSuspend {
|
||||
if err2 := ResumeDevice(baseName); err2 != nil {
|
||||
return fmt.Errorf("CreateSnapDeviceRaw Error: (%v): ResumeDevice Error: (%v)", err, err2)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if doSuspend {
|
||||
if err := ResumeDevice(baseName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
//go:build linux && cgo
|
||||
// +build linux,cgo
|
||||
|
||||
package devicemapper // import "github.com/docker/docker/pkg/devicemapper"
|
||||
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// DevmapperLogger defines methods required to register as a callback for
|
||||
// logging events received from devicemapper. Note that devicemapper will send
|
||||
// *all* logs regardless to callbacks (including debug logs) so it's
|
||||
// recommended to not spam the console with the outputs.
|
||||
type DevmapperLogger interface {
|
||||
// DMLog is the logging callback containing all of the information from
|
||||
// devicemapper. The interface is identical to the C libdm counterpart.
|
||||
DMLog(level int, file string, line int, dmError int, message string)
|
||||
}
|
||||
|
||||
// dmLogger is the current logger in use that is being forwarded our messages.
|
||||
var dmLogger DevmapperLogger
|
||||
|
||||
// LogInit changes the logging callback called after processing libdm logs for
|
||||
// error message information. The default logger simply forwards all logs to
|
||||
// logrus. Calling LogInit(nil) disables the calling of callbacks.
|
||||
func LogInit(logger DevmapperLogger) {
|
||||
dmLogger = logger
|
||||
}
|
||||
|
||||
// Due to the way cgo works this has to be in a separate file, as devmapper.go has
|
||||
// definitions in the cgo block, which is incompatible with using "//export"
|
||||
|
||||
// DevmapperLogCallback exports the devmapper log callback for cgo. Note that
|
||||
// because we are using callbacks, this function will be called for *every* log
|
||||
// in libdm (even debug ones because there's no way of setting the verbosity
|
||||
// level for an external logging callback).
|
||||
//
|
||||
//export DevmapperLogCallback
|
||||
func DevmapperLogCallback(level C.int, file *C.char, line, dmErrnoOrClass C.int, message *C.char) {
|
||||
msg := C.GoString(message)
|
||||
|
||||
// Track what errno libdm saw, because the library only gives us 0 or 1.
|
||||
if level < LogLevelDebug {
|
||||
if strings.Contains(msg, "busy") {
|
||||
dmSawBusy = true
|
||||
}
|
||||
|
||||
if strings.Contains(msg, "File exists") {
|
||||
dmSawExist = true
|
||||
}
|
||||
|
||||
if strings.Contains(msg, "No such device or address") {
|
||||
dmSawEnxio = true
|
||||
}
|
||||
if strings.Contains(msg, "No data available") {
|
||||
dmSawEnoData = true
|
||||
}
|
||||
}
|
||||
|
||||
if dmLogger != nil {
|
||||
dmLogger.DMLog(int(level), C.GoString(file), int(line), int(dmErrnoOrClass), msg)
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultLogger is the default logger used by pkg/devicemapper. It forwards
|
||||
// all logs that are of higher or equal priority to the given level to the
|
||||
// corresponding logrus level.
|
||||
type DefaultLogger struct {
|
||||
// Level corresponds to the highest libdm level that will be forwarded to
|
||||
// logrus. In order to change this, register a new DefaultLogger.
|
||||
Level int
|
||||
}
|
||||
|
||||
// DMLog is the logging callback containing all of the information from
|
||||
// devicemapper. The interface is identical to the C libdm counterpart.
|
||||
func (l DefaultLogger) DMLog(level int, file string, line, dmError int, message string) {
|
||||
if level <= l.Level {
|
||||
// Forward the log to the correct logrus level, if allowed by dmLogLevel.
|
||||
logMsg := fmt.Sprintf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
|
||||
switch level {
|
||||
case LogLevelFatal, LogLevelErr:
|
||||
logrus.Error(logMsg)
|
||||
case LogLevelWarn:
|
||||
logrus.Warn(logMsg)
|
||||
case LogLevelNotice, LogLevelInfo:
|
||||
logrus.Info(logMsg)
|
||||
case LogLevelDebug:
|
||||
logrus.Debug(logMsg)
|
||||
default:
|
||||
// Don't drop any "unknown" levels.
|
||||
logrus.Info(logMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// registerLogCallback registers our own logging callback function for libdm
|
||||
// (which is DevmapperLogCallback).
|
||||
//
|
||||
// Because libdm only gives us {0,1} error codes we need to parse the logs
|
||||
// produced by libdm (to set dmSawBusy and so on). Note that by registering a
|
||||
// callback using DevmapperLogCallback, libdm will no longer output logs to
|
||||
// stderr so we have to log everything ourselves. None of this handling is
|
||||
// optional because we depend on log callbacks to parse the logs, and if we
|
||||
// don't forward the log information we'll be in a lot of trouble when
|
||||
// debugging things.
|
||||
func registerLogCallback() {
|
||||
LogWithErrnoInit()
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Use the default logger by default. We only allow LogLevelFatal by
|
||||
// default, because internally we mask a lot of libdm errors by retrying
|
||||
// and similar tricks. Also, libdm is very chatty and we don't want to
|
||||
// worry users for no reason.
|
||||
dmLogger = DefaultLogger{
|
||||
Level: LogLevelFatal,
|
||||
}
|
||||
|
||||
// Register as early as possible so we don't miss anything.
|
||||
registerLogCallback()
|
||||
}
|
|
@ -1,246 +0,0 @@
|
|||
//go:build linux && cgo
|
||||
// +build linux,cgo
|
||||
|
||||
package devicemapper // import "github.com/docker/docker/pkg/devicemapper"
|
||||
|
||||
/*
|
||||
#define _GNU_SOURCE
|
||||
#include <libdevmapper.h>
|
||||
#include <linux/fs.h> // FIXME: present only for BLKGETSIZE64, maybe we can remove it?
|
||||
|
||||
// FIXME: Can't we find a way to do the logging in pure Go?
|
||||
extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str);
|
||||
|
||||
static void log_cb(int level, const char *file, int line, int dm_errno_or_class, const char *f, ...)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, f);
|
||||
ret = vasprintf(&buffer, f, ap);
|
||||
va_end(ap);
|
||||
if (ret < 0) {
|
||||
// memory allocation failed -- should never happen?
|
||||
return;
|
||||
}
|
||||
|
||||
DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void log_with_errno_init()
|
||||
{
|
||||
dm_log_with_errno_init(log_cb);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type (
|
||||
cdmTask C.struct_dm_task
|
||||
)
|
||||
|
||||
// IOCTL consts
|
||||
const (
|
||||
BlkGetSize64 = C.BLKGETSIZE64
|
||||
BlkDiscard = C.BLKDISCARD
|
||||
)
|
||||
|
||||
// Devicemapper cookie flags.
|
||||
const (
|
||||
DmUdevDisableSubsystemRulesFlag = C.DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG
|
||||
DmUdevDisableDiskRulesFlag = C.DM_UDEV_DISABLE_DISK_RULES_FLAG
|
||||
DmUdevDisableOtherRulesFlag = C.DM_UDEV_DISABLE_OTHER_RULES_FLAG
|
||||
DmUdevDisableLibraryFallback = C.DM_UDEV_DISABLE_LIBRARY_FALLBACK
|
||||
)
|
||||
|
||||
// DeviceMapper mapped functions.
|
||||
var (
|
||||
DmGetLibraryVersion = dmGetLibraryVersionFct
|
||||
DmGetNextTarget = dmGetNextTargetFct
|
||||
DmSetDevDir = dmSetDevDirFct
|
||||
DmTaskAddTarget = dmTaskAddTargetFct
|
||||
DmTaskCreate = dmTaskCreateFct
|
||||
DmTaskDestroy = dmTaskDestroyFct
|
||||
DmTaskGetDeps = dmTaskGetDepsFct
|
||||
DmTaskGetInfo = dmTaskGetInfoFct
|
||||
DmTaskGetDriverVersion = dmTaskGetDriverVersionFct
|
||||
DmTaskRun = dmTaskRunFct
|
||||
DmTaskSetAddNode = dmTaskSetAddNodeFct
|
||||
DmTaskSetCookie = dmTaskSetCookieFct
|
||||
DmTaskSetMessage = dmTaskSetMessageFct
|
||||
DmTaskSetName = dmTaskSetNameFct
|
||||
DmTaskSetSector = dmTaskSetSectorFct
|
||||
DmUdevWait = dmUdevWaitFct
|
||||
DmUdevSetSyncSupport = dmUdevSetSyncSupportFct
|
||||
DmUdevGetSyncSupport = dmUdevGetSyncSupportFct
|
||||
DmCookieSupported = dmCookieSupportedFct
|
||||
LogWithErrnoInit = logWithErrnoInitFct
|
||||
DmTaskDeferredRemove = dmTaskDeferredRemoveFct
|
||||
DmTaskGetInfoWithDeferred = dmTaskGetInfoWithDeferredFct
|
||||
)
|
||||
|
||||
func free(p *C.char) {
|
||||
C.free(unsafe.Pointer(p))
|
||||
}
|
||||
|
||||
func dmTaskDestroyFct(task *cdmTask) {
|
||||
C.dm_task_destroy((*C.struct_dm_task)(task))
|
||||
}
|
||||
|
||||
func dmTaskCreateFct(taskType int) *cdmTask {
|
||||
return (*cdmTask)(C.dm_task_create(C.int(taskType)))
|
||||
}
|
||||
|
||||
func dmTaskRunFct(task *cdmTask) int {
|
||||
ret, _ := C.dm_task_run((*C.struct_dm_task)(task))
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func dmTaskSetNameFct(task *cdmTask, name string) int {
|
||||
Cname := C.CString(name)
|
||||
defer free(Cname)
|
||||
|
||||
return int(C.dm_task_set_name((*C.struct_dm_task)(task), Cname))
|
||||
}
|
||||
|
||||
func dmTaskSetMessageFct(task *cdmTask, message string) int {
|
||||
Cmessage := C.CString(message)
|
||||
defer free(Cmessage)
|
||||
|
||||
return int(C.dm_task_set_message((*C.struct_dm_task)(task), Cmessage))
|
||||
}
|
||||
|
||||
func dmTaskSetSectorFct(task *cdmTask, sector uint64) int {
|
||||
return int(C.dm_task_set_sector((*C.struct_dm_task)(task), C.uint64_t(sector)))
|
||||
}
|
||||
|
||||
func dmTaskSetCookieFct(task *cdmTask, cookie *uint, flags uint16) int {
|
||||
cCookie := C.uint32_t(*cookie)
|
||||
defer func() {
|
||||
*cookie = uint(cCookie)
|
||||
}()
|
||||
return int(C.dm_task_set_cookie((*C.struct_dm_task)(task), &cCookie, C.uint16_t(flags)))
|
||||
}
|
||||
|
||||
func dmTaskSetAddNodeFct(task *cdmTask, addNode AddNodeType) int {
|
||||
return int(C.dm_task_set_add_node((*C.struct_dm_task)(task), C.dm_add_node_t(addNode)))
|
||||
}
|
||||
|
||||
func dmTaskAddTargetFct(task *cdmTask, start, size uint64, ttype, params string) int {
|
||||
Cttype := C.CString(ttype)
|
||||
defer free(Cttype)
|
||||
|
||||
Cparams := C.CString(params)
|
||||
defer free(Cparams)
|
||||
|
||||
return int(C.dm_task_add_target((*C.struct_dm_task)(task), C.uint64_t(start), C.uint64_t(size), Cttype, Cparams))
|
||||
}
|
||||
|
||||
func dmTaskGetDepsFct(task *cdmTask) *Deps {
|
||||
Cdeps := C.dm_task_get_deps((*C.struct_dm_task)(task))
|
||||
if Cdeps == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// golang issue: https://github.com/golang/go/issues/11925
|
||||
var devices []C.uint64_t
|
||||
devicesHdr := (*reflect.SliceHeader)(unsafe.Pointer(&devices))
|
||||
devicesHdr.Data = uintptr(unsafe.Pointer(uintptr(unsafe.Pointer(Cdeps)) + unsafe.Sizeof(*Cdeps)))
|
||||
devicesHdr.Len = int(Cdeps.count)
|
||||
devicesHdr.Cap = int(Cdeps.count)
|
||||
|
||||
deps := &Deps{
|
||||
Count: uint32(Cdeps.count),
|
||||
Filler: uint32(Cdeps.filler),
|
||||
}
|
||||
for _, device := range devices {
|
||||
deps.Device = append(deps.Device, uint64(device))
|
||||
}
|
||||
return deps
|
||||
}
|
||||
|
||||
func dmTaskGetInfoFct(task *cdmTask, info *Info) int {
|
||||
Cinfo := C.struct_dm_info{}
|
||||
defer func() {
|
||||
info.Exists = int(Cinfo.exists)
|
||||
info.Suspended = int(Cinfo.suspended)
|
||||
info.LiveTable = int(Cinfo.live_table)
|
||||
info.InactiveTable = int(Cinfo.inactive_table)
|
||||
info.OpenCount = int32(Cinfo.open_count)
|
||||
info.EventNr = uint32(Cinfo.event_nr)
|
||||
info.Major = uint32(Cinfo.major)
|
||||
info.Minor = uint32(Cinfo.minor)
|
||||
info.ReadOnly = int(Cinfo.read_only)
|
||||
info.TargetCount = int32(Cinfo.target_count)
|
||||
}()
|
||||
return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo))
|
||||
}
|
||||
|
||||
func dmTaskGetDriverVersionFct(task *cdmTask) string {
|
||||
buffer := C.malloc(128)
|
||||
defer C.free(buffer)
|
||||
res := C.dm_task_get_driver_version((*C.struct_dm_task)(task), (*C.char)(buffer), 128)
|
||||
if res == 0 {
|
||||
return ""
|
||||
}
|
||||
return C.GoString((*C.char)(buffer))
|
||||
}
|
||||
|
||||
func dmGetNextTargetFct(task *cdmTask, next unsafe.Pointer, start, length *uint64, target, params *string) unsafe.Pointer {
|
||||
var (
|
||||
Cstart, Clength C.uint64_t
|
||||
CtargetType, Cparams *C.char
|
||||
)
|
||||
defer func() {
|
||||
*start = uint64(Cstart)
|
||||
*length = uint64(Clength)
|
||||
*target = C.GoString(CtargetType)
|
||||
*params = C.GoString(Cparams)
|
||||
}()
|
||||
|
||||
//lint:ignore SA4000 false positive on (identical expressions on the left and right side of the '==' operator) (staticcheck)
|
||||
nextp := C.dm_get_next_target((*C.struct_dm_task)(task), next, &Cstart, &Clength, &CtargetType, &Cparams)
|
||||
return nextp
|
||||
}
|
||||
|
||||
func dmUdevSetSyncSupportFct(syncWithUdev int) {
|
||||
C.dm_udev_set_sync_support(C.int(syncWithUdev))
|
||||
}
|
||||
|
||||
func dmUdevGetSyncSupportFct() int {
|
||||
return int(C.dm_udev_get_sync_support())
|
||||
}
|
||||
|
||||
func dmUdevWaitFct(cookie uint) int {
|
||||
return int(C.dm_udev_wait(C.uint32_t(cookie)))
|
||||
}
|
||||
|
||||
func dmCookieSupportedFct() int {
|
||||
return int(C.dm_cookie_supported())
|
||||
}
|
||||
|
||||
func logWithErrnoInitFct() {
|
||||
C.log_with_errno_init()
|
||||
}
|
||||
|
||||
func dmSetDevDirFct(dir string) int {
|
||||
Cdir := C.CString(dir)
|
||||
defer free(Cdir)
|
||||
|
||||
return int(C.dm_set_dev_dir(Cdir))
|
||||
}
|
||||
|
||||
func dmGetLibraryVersionFct(version *string) int {
|
||||
buffer := C.CString(string(make([]byte, 128)))
|
||||
defer free(buffer)
|
||||
defer func() {
|
||||
*version = C.GoString(buffer)
|
||||
}()
|
||||
return int(C.dm_get_library_version(buffer, 128))
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
//go:build linux && cgo && !static_build
|
||||
// +build linux,cgo,!static_build
|
||||
|
||||
package devicemapper // import "github.com/docker/docker/pkg/devicemapper"
|
||||
|
||||
// #cgo pkg-config: devmapper
|
||||
import "C"
|
|
@ -1,35 +0,0 @@
|
|||
//go:build linux && cgo && !static_build && !libdm_dlsym_deferred_remove && !libdm_no_deferred_remove
|
||||
// +build linux,cgo,!static_build,!libdm_dlsym_deferred_remove,!libdm_no_deferred_remove
|
||||
|
||||
package devicemapper // import "github.com/docker/docker/pkg/devicemapper"
|
||||
|
||||
/*
|
||||
#include <libdevmapper.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// LibraryDeferredRemovalSupport tells if the feature is supported by the
|
||||
// current Docker invocation.
|
||||
const LibraryDeferredRemovalSupport = true
|
||||
|
||||
func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
||||
return int(C.dm_task_deferred_remove((*C.struct_dm_task)(task)))
|
||||
}
|
||||
|
||||
func dmTaskGetInfoWithDeferredFct(task *cdmTask, info *Info) int {
|
||||
Cinfo := C.struct_dm_info{}
|
||||
defer func() {
|
||||
info.Exists = int(Cinfo.exists)
|
||||
info.Suspended = int(Cinfo.suspended)
|
||||
info.LiveTable = int(Cinfo.live_table)
|
||||
info.InactiveTable = int(Cinfo.inactive_table)
|
||||
info.OpenCount = int32(Cinfo.open_count)
|
||||
info.EventNr = uint32(Cinfo.event_nr)
|
||||
info.Major = uint32(Cinfo.major)
|
||||
info.Minor = uint32(Cinfo.minor)
|
||||
info.ReadOnly = int(Cinfo.read_only)
|
||||
info.TargetCount = int32(Cinfo.target_count)
|
||||
info.DeferredRemove = int(Cinfo.deferred_remove)
|
||||
}()
|
||||
return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo))
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
//go:build linux && cgo && !static_build && libdm_dlsym_deferred_remove && !libdm_no_deferred_remove
|
||||
// +build linux,cgo,!static_build,libdm_dlsym_deferred_remove,!libdm_no_deferred_remove
|
||||
|
||||
package devicemapper
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -ldl
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
#include <libdevmapper.h>
|
||||
|
||||
// Yes, I know this looks scary. In order to be able to fill our own internal
|
||||
// dm_info with deferred_remove we need to have a struct definition that is
|
||||
// correct (regardless of the version of libdm that was used to compile it). To
|
||||
// this end, we define struct_backport_dm_info. This code comes from lvm2, and
|
||||
// I have verified that the structure has only ever had elements *appended* to
|
||||
// it (since 2001).
|
||||
//
|
||||
// It is also important that this structure be _larger_ than the dm_info that
|
||||
// libdevmapper expected. Otherwise libdm might try to write to memory it
|
||||
// shouldn't (they don't have a "known size" API).
|
||||
struct backport_dm_info {
|
||||
int exists;
|
||||
int suspended;
|
||||
int live_table;
|
||||
int inactive_table;
|
||||
int32_t open_count;
|
||||
uint32_t event_nr;
|
||||
uint32_t major;
|
||||
uint32_t minor;
|
||||
int read_only;
|
||||
|
||||
int32_t target_count;
|
||||
|
||||
int deferred_remove;
|
||||
int internal_suspend;
|
||||
|
||||
// Padding, purely for our own safety. This is to avoid cases where libdm
|
||||
// was updated underneath us and we call into dm_task_get_info() with too
|
||||
// small of a buffer.
|
||||
char _[512];
|
||||
};
|
||||
|
||||
// We have to wrap this in CGo, because Go really doesn't like function pointers.
|
||||
int call_dm_task_deferred_remove(void *fn, struct dm_task *task)
|
||||
{
|
||||
int (*_dm_task_deferred_remove)(struct dm_task *task) = fn;
|
||||
return _dm_task_deferred_remove(task);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// dm_task_deferred_remove is not supported by all distributions, due to
|
||||
// out-dated versions of devicemapper. However, in the case where the
|
||||
// devicemapper library was updated without rebuilding Docker (which can happen
|
||||
// in some distributions) then we should attempt to dynamically load the
|
||||
// relevant object rather than try to link to it.
|
||||
|
||||
// dmTaskDeferredRemoveFct is a "bound" version of dm_task_deferred_remove.
|
||||
// It is nil if dm_task_deferred_remove was not found in the libdevmapper that
|
||||
// is currently loaded.
|
||||
var dmTaskDeferredRemovePtr unsafe.Pointer
|
||||
|
||||
// LibraryDeferredRemovalSupport tells if the feature is supported by the
|
||||
// current Docker invocation. This value is fixed during init.
|
||||
var LibraryDeferredRemovalSupport bool
|
||||
|
||||
func init() {
|
||||
// Clear any errors.
|
||||
var err *C.char
|
||||
C.dlerror()
|
||||
|
||||
// The symbol we want to fetch.
|
||||
symName := C.CString("dm_task_deferred_remove")
|
||||
defer C.free(unsafe.Pointer(symName))
|
||||
|
||||
// See if we can find dm_task_deferred_remove. Since we already are linked
|
||||
// to libdevmapper, we can search our own address space (rather than trying
|
||||
// to guess what libdevmapper is called). We use NULL here, as RTLD_DEFAULT
|
||||
// is not available in CGO (even if you set _GNU_SOURCE for some reason).
|
||||
// The semantics are identical on glibc.
|
||||
sym := C.dlsym(nil, symName)
|
||||
err = C.dlerror()
|
||||
if err != nil {
|
||||
logrus.Debugf("devmapper: could not load dm_task_deferred_remove: %s", C.GoString(err))
|
||||
return
|
||||
}
|
||||
|
||||
logrus.Debugf("devmapper: found dm_task_deferred_remove at %x", uintptr(sym))
|
||||
dmTaskDeferredRemovePtr = sym
|
||||
LibraryDeferredRemovalSupport = true
|
||||
}
|
||||
|
||||
func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
||||
sym := dmTaskDeferredRemovePtr
|
||||
if sym == nil || !LibraryDeferredRemovalSupport {
|
||||
return -1
|
||||
}
|
||||
return int(C.call_dm_task_deferred_remove(sym, (*C.struct_dm_task)(task)))
|
||||
}
|
||||
|
||||
func dmTaskGetInfoWithDeferredFct(task *cdmTask, info *Info) int {
|
||||
if !LibraryDeferredRemovalSupport {
|
||||
return -1
|
||||
}
|
||||
|
||||
Cinfo := C.struct_backport_dm_info{}
|
||||
defer func() {
|
||||
info.Exists = int(Cinfo.exists)
|
||||
info.Suspended = int(Cinfo.suspended)
|
||||
info.LiveTable = int(Cinfo.live_table)
|
||||
info.InactiveTable = int(Cinfo.inactive_table)
|
||||
info.OpenCount = int32(Cinfo.open_count)
|
||||
info.EventNr = uint32(Cinfo.event_nr)
|
||||
info.Major = uint32(Cinfo.major)
|
||||
info.Minor = uint32(Cinfo.minor)
|
||||
info.ReadOnly = int(Cinfo.read_only)
|
||||
info.TargetCount = int32(Cinfo.target_count)
|
||||
info.DeferredRemove = int(Cinfo.deferred_remove)
|
||||
}()
|
||||
return int(C.dm_task_get_info((*C.struct_dm_task)(task), (*C.struct_dm_info)(unsafe.Pointer(&Cinfo))))
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
//go:build linux && cgo && !libdm_dlsym_deferred_remove && libdm_no_deferred_remove
|
||||
// +build linux,cgo,!libdm_dlsym_deferred_remove,libdm_no_deferred_remove
|
||||
|
||||
package devicemapper // import "github.com/docker/docker/pkg/devicemapper"
|
||||
|
||||
// LibraryDeferredRemovalSupport tells if the feature is supported by the
|
||||
// current Docker invocation.
|
||||
const LibraryDeferredRemovalSupport = false
|
||||
|
||||
func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
||||
// Error. Nobody should be calling it.
|
||||
return -1
|
||||
}
|
||||
|
||||
func dmTaskGetInfoWithDeferredFct(task *cdmTask, info *Info) int {
|
||||
return -1
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
//go:build linux && cgo
|
||||
// +build linux,cgo
|
||||
|
||||
package devicemapper // import "github.com/docker/docker/pkg/devicemapper"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func ioctlBlkGetSize64(fd uintptr) (int64, error) {
|
||||
var size int64
|
||||
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, BlkGetSize64, uintptr(unsafe.Pointer(&size))); err != 0 {
|
||||
return 0, err
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func ioctlBlkDiscard(fd uintptr, offset, length uint64) error {
|
||||
var r [2]uint64
|
||||
r[0] = offset
|
||||
r[1] = length
|
||||
|
||||
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, BlkDiscard, uintptr(unsafe.Pointer(&r[0]))); err != 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package devicemapper // import "github.com/docker/docker/pkg/devicemapper"
|
||||
|
||||
// definitions from lvm2 lib/log/log.h
|
||||
const (
|
||||
LogLevelFatal = 2 + iota // _LOG_FATAL
|
||||
LogLevelErr // _LOG_ERR
|
||||
LogLevelWarn // _LOG_WARN
|
||||
LogLevelNotice // _LOG_NOTICE
|
||||
LogLevelInfo // _LOG_INFO
|
||||
LogLevelDebug // _LOG_DEBUG
|
||||
)
|
|
@ -71,7 +71,6 @@ have:
|
|||
| area/security/trust |
|
||||
| area/storage |
|
||||
| area/storage/btrfs |
|
||||
| area/storage/devicemapper |
|
||||
| area/storage/overlay |
|
||||
| area/storage/zfs |
|
||||
| area/swarm |
|
||||
|
|
|
@ -89,14 +89,14 @@ To disable btrfs:
|
|||
export DOCKER_BUILDTAGS='exclude_graphdriver_btrfs'
|
||||
```
|
||||
|
||||
To disable devicemapper:
|
||||
To disable zfs:
|
||||
```bash
|
||||
export DOCKER_BUILDTAGS='exclude_graphdriver_devicemapper'
|
||||
export DOCKER_BUILDTAGS='exclude_graphdriver_zfs'
|
||||
```
|
||||
|
||||
NOTE: if you need to set more than one build tag, space separate them:
|
||||
```bash
|
||||
export DOCKER_BUILDTAGS='exclude_graphdriver_devicemapper exclude_graphdriver_btrfs'
|
||||
export DOCKER_BUILDTAGS='exclude_graphdriver_btrfs exclude_graphdriver_zfs'
|
||||
```
|
||||
|
||||
## System Dependencies
|
||||
|
|
Loading…
Reference in a new issue