Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| ... | ... |
@@ -28,7 +28,7 @@ export VALIDATE_ORIGIN_BRANCH |
| 28 | 28 |
# option of "go build". For example, a built-in graphdriver priority list |
| 29 | 29 |
# can be changed during build time like this: |
| 30 | 30 |
# |
| 31 |
-# make DOCKER_LDFLAGS="-X github.com/docker/docker/daemon/graphdriver.priority=overlay2,devicemapper" dynbinary |
|
| 31 |
+# make DOCKER_LDFLAGS="-X github.com/docker/docker/daemon/graphdriver.priority=overlay2,zfs" dynbinary |
|
| 32 | 32 |
# |
| 33 | 33 |
DOCKER_ENVS := \ |
| 34 | 34 |
-e BUILD_APT_MIRROR \ |
| ... | ... |
@@ -6561,7 +6561,7 @@ paths: |
| 6561 | 6561 |
StopSignal: "SIGTERM" |
| 6562 | 6562 |
StopTimeout: 10 |
| 6563 | 6563 |
Created: "2015-01-06T15:47:31.485331387Z" |
| 6564 |
- Driver: "devicemapper" |
|
| 6564 |
+ Driver: "overlay2" |
|
| 6565 | 6565 |
ExecIDs: |
| 6566 | 6566 |
- "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca" |
| 6567 | 6567 |
- "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4" |
| ... | ... |
@@ -374,11 +374,6 @@ check_flags BTRFS_FS_POSIX_ACL | sed 's/^/ /' |
| 374 | 374 |
[ "$EXITCODE" = 0 ] && STORAGE=0 |
| 375 | 375 |
EXITCODE=0 |
| 376 | 376 |
|
| 377 |
-echo " - \"$(wrap_color 'devicemapper' blue)\":" |
|
| 378 |
-check_flags BLK_DEV_DM DM_THIN_PROVISIONING | sed 's/^/ /' |
|
| 379 |
-[ "$EXITCODE" = 0 ] && STORAGE=0 |
|
| 380 |
-EXITCODE=0 |
|
| 381 |
- |
|
| 382 | 377 |
echo " - \"$(wrap_color 'overlay' blue)\":" |
| 383 | 378 |
check_flags OVERLAY_FS | sed 's/^/ /' |
| 384 | 379 |
[ "$EXITCODE" = 0 ] && STORAGE=0 |
| 385 | 380 |
deleted file mode 100644 |
| ... | ... |
@@ -1,14 +0,0 @@ |
| 1 |
-Docker device tool for devicemapper storage driver backend |
|
| 2 |
-=================== |
|
| 3 |
- |
|
| 4 |
-The ./contrib/docker-device-tool contains a tool to manipulate devicemapper thin-pool. |
|
| 5 |
- |
|
| 6 |
-Compile |
|
| 7 |
-======== |
|
| 8 |
- |
|
| 9 |
- $ make shell |
|
| 10 |
- ## inside build container |
|
| 11 |
- $ go build contrib/docker-device-tool/device_tool.go |
|
| 12 |
- |
|
| 13 |
- # if devicemapper version is old and compilation fails, compile with `libdm_no_deferred_remove` tag |
|
| 14 |
- $ go build -tags libdm_no_deferred_remove contrib/docker-device-tool/device_tool.go |
| 15 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,169 +0,0 @@ |
| 1 |
-//go:build !windows |
|
| 2 |
-// +build !windows |
|
| 3 |
- |
|
| 4 |
-package main |
|
| 5 |
- |
|
| 6 |
-import ( |
|
| 7 |
- "flag" |
|
| 8 |
- "fmt" |
|
| 9 |
- "os" |
|
| 10 |
- "path" |
|
| 11 |
- "sort" |
|
| 12 |
- "strconv" |
|
| 13 |
- "strings" |
|
| 14 |
- |
|
| 15 |
- "github.com/docker/docker/daemon/graphdriver/devmapper" |
|
| 16 |
- "github.com/docker/docker/pkg/devicemapper" |
|
| 17 |
- "github.com/docker/docker/pkg/idtools" |
|
| 18 |
- "github.com/sirupsen/logrus" |
|
| 19 |
-) |
|
| 20 |
- |
|
| 21 |
-func usage() {
|
|
| 22 |
- 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]) |
|
| 23 |
- flag.PrintDefaults() |
|
| 24 |
- os.Exit(1) |
|
| 25 |
-} |
|
| 26 |
- |
|
| 27 |
-func byteSizeFromString(arg string) (int64, error) {
|
|
| 28 |
- digits := "" |
|
| 29 |
- rest := "" |
|
| 30 |
- last := strings.LastIndexAny(arg, "0123456789") |
|
| 31 |
- if last >= 0 {
|
|
| 32 |
- digits = arg[:last+1] |
|
| 33 |
- rest = arg[last+1:] |
|
| 34 |
- } |
|
| 35 |
- |
|
| 36 |
- val, err := strconv.ParseInt(digits, 10, 64) |
|
| 37 |
- if err != nil {
|
|
| 38 |
- return val, err |
|
| 39 |
- } |
|
| 40 |
- |
|
| 41 |
- rest = strings.ToLower(strings.TrimSpace(rest)) |
|
| 42 |
- |
|
| 43 |
- var multiplier int64 |
|
| 44 |
- switch rest {
|
|
| 45 |
- case "": |
|
| 46 |
- multiplier = 1 |
|
| 47 |
- case "k", "kb": |
|
| 48 |
- multiplier = 1024 |
|
| 49 |
- case "m", "mb": |
|
| 50 |
- multiplier = 1024 * 1024 |
|
| 51 |
- case "g", "gb": |
|
| 52 |
- multiplier = 1024 * 1024 * 1024 |
|
| 53 |
- case "t", "tb": |
|
| 54 |
- multiplier = 1024 * 1024 * 1024 * 1024 |
|
| 55 |
- default: |
|
| 56 |
- return 0, fmt.Errorf("Unknown size unit: %s", rest)
|
|
| 57 |
- } |
|
| 58 |
- |
|
| 59 |
- return val * multiplier, nil |
|
| 60 |
-} |
|
| 61 |
- |
|
| 62 |
-func main() {
|
|
| 63 |
- root := flag.String("r", "/var/lib/docker", "Docker root dir")
|
|
| 64 |
- flDebug := flag.Bool("D", false, "Debug mode")
|
|
| 65 |
- |
|
| 66 |
- flag.Parse() |
|
| 67 |
- |
|
| 68 |
- if *flDebug {
|
|
| 69 |
- os.Setenv("DEBUG", "1")
|
|
| 70 |
- logrus.SetLevel(logrus.DebugLevel) |
|
| 71 |
- } |
|
| 72 |
- |
|
| 73 |
- if flag.NArg() < 1 {
|
|
| 74 |
- usage() |
|
| 75 |
- } |
|
| 76 |
- |
|
| 77 |
- args := flag.Args() |
|
| 78 |
- |
|
| 79 |
- home := path.Join(*root, "devicemapper") |
|
| 80 |
- devices, err := devmapper.NewDeviceSet(home, false, nil, idtools.IdentityMapping{})
|
|
| 81 |
- if err != nil {
|
|
| 82 |
- fmt.Println("Can't initialize device mapper: ", err)
|
|
| 83 |
- os.Exit(1) |
|
| 84 |
- } |
|
| 85 |
- |
|
| 86 |
- switch args[0] {
|
|
| 87 |
- case "status": |
|
| 88 |
- status := devices.Status() |
|
| 89 |
- fmt.Printf("Pool name: %s\n", status.PoolName)
|
|
| 90 |
- fmt.Printf("Data Loopback file: %s\n", status.DataLoopback)
|
|
| 91 |
- fmt.Printf("Metadata Loopback file: %s\n", status.MetadataLoopback)
|
|
| 92 |
- fmt.Printf("Sector size: %d\n", status.SectorSize)
|
|
| 93 |
- 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))
|
|
| 94 |
- 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))
|
|
| 95 |
- case "list": |
|
| 96 |
- ids := devices.List() |
|
| 97 |
- sort.Strings(ids) |
|
| 98 |
- for _, id := range ids {
|
|
| 99 |
- fmt.Println(id) |
|
| 100 |
- } |
|
| 101 |
- case "device": |
|
| 102 |
- if flag.NArg() < 2 {
|
|
| 103 |
- usage() |
|
| 104 |
- } |
|
| 105 |
- status, err := devices.GetDeviceStatus(args[1]) |
|
| 106 |
- if err != nil {
|
|
| 107 |
- fmt.Println("Can't get device info: ", err)
|
|
| 108 |
- os.Exit(1) |
|
| 109 |
- } |
|
| 110 |
- fmt.Printf("Id: %d\n", status.DeviceID)
|
|
| 111 |
- fmt.Printf("Size: %d\n", status.Size)
|
|
| 112 |
- fmt.Printf("Transaction Id: %d\n", status.TransactionID)
|
|
| 113 |
- fmt.Printf("Size in Sectors: %d\n", status.SizeInSectors)
|
|
| 114 |
- fmt.Printf("Mapped Sectors: %d\n", status.MappedSectors)
|
|
| 115 |
- fmt.Printf("Highest Mapped Sector: %d\n", status.HighestMappedSector)
|
|
| 116 |
- case "resize": |
|
| 117 |
- if flag.NArg() < 2 {
|
|
| 118 |
- usage() |
|
| 119 |
- } |
|
| 120 |
- |
|
| 121 |
- size, err := byteSizeFromString(args[1]) |
|
| 122 |
- if err != nil {
|
|
| 123 |
- fmt.Println("Invalid size: ", err)
|
|
| 124 |
- os.Exit(1) |
|
| 125 |
- } |
|
| 126 |
- |
|
| 127 |
- err = devices.ResizePool(size) |
|
| 128 |
- if err != nil {
|
|
| 129 |
- fmt.Println("Error resizing pool: ", err)
|
|
| 130 |
- os.Exit(1) |
|
| 131 |
- } |
|
| 132 |
- |
|
| 133 |
- case "snap": |
|
| 134 |
- if flag.NArg() < 3 {
|
|
| 135 |
- usage() |
|
| 136 |
- } |
|
| 137 |
- |
|
| 138 |
- err := devices.AddDevice(args[1], args[2], nil) |
|
| 139 |
- if err != nil {
|
|
| 140 |
- fmt.Println("Can't create snap device: ", err)
|
|
| 141 |
- os.Exit(1) |
|
| 142 |
- } |
|
| 143 |
- case "remove": |
|
| 144 |
- if flag.NArg() < 2 {
|
|
| 145 |
- usage() |
|
| 146 |
- } |
|
| 147 |
- |
|
| 148 |
- err := devicemapper.RemoveDevice(args[1]) |
|
| 149 |
- if err != nil {
|
|
| 150 |
- fmt.Println("Can't remove device: ", err)
|
|
| 151 |
- os.Exit(1) |
|
| 152 |
- } |
|
| 153 |
- case "mount": |
|
| 154 |
- if flag.NArg() < 3 {
|
|
| 155 |
- usage() |
|
| 156 |
- } |
|
| 157 |
- |
|
| 158 |
- err := devices.MountDevice(args[1], args[2], "") |
|
| 159 |
- if err != nil {
|
|
| 160 |
- fmt.Println("Can't mount device: ", err)
|
|
| 161 |
- os.Exit(1) |
|
| 162 |
- } |
|
| 163 |
- default: |
|
| 164 |
- fmt.Printf("Unknown command %s\n", args[0])
|
|
| 165 |
- usage() |
|
| 166 |
- |
|
| 167 |
- os.Exit(1) |
|
| 168 |
- } |
|
| 169 |
-} |
| 5 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,98 +0,0 @@ |
| 1 |
-# devicemapper - a storage backend based on Device Mapper |
|
| 2 |
- |
|
| 3 |
-## Theory of operation |
|
| 4 |
- |
|
| 5 |
-The device mapper graphdriver uses the device mapper thin provisioning |
|
| 6 |
-module (dm-thinp) to implement CoW snapshots. The preferred model is |
|
| 7 |
-to have a thin pool reserved outside of Docker and passed to the |
|
| 8 |
-daemon via the `--storage-opt dm.thinpooldev` option. Alternatively, |
|
| 9 |
-the device mapper graphdriver can setup a block device to handle this |
|
| 10 |
-for you via the `--storage-opt dm.directlvm_device` option. |
|
| 11 |
- |
|
| 12 |
-As a fallback if no thin pool is provided, loopback files will be |
|
| 13 |
-created. Loopback is very slow, but can be used without any |
|
| 14 |
-pre-configuration of storage. It is strongly recommended that you do |
|
| 15 |
-not use loopback in production. Ensure your Docker daemon has a |
|
| 16 |
-`--storage-opt dm.thinpooldev` argument provided. |
|
| 17 |
- |
|
| 18 |
-In loopback, a thin pool is created at `/var/lib/docker/devicemapper` |
|
| 19 |
-(devicemapper graph location) based on two block devices, one for |
|
| 20 |
-data and one for metadata. By default these block devices are created |
|
| 21 |
-automatically by using loopback mounts of automatically created sparse |
|
| 22 |
-files. |
|
| 23 |
- |
|
| 24 |
-The default loopback files used are |
|
| 25 |
-`/var/lib/docker/devicemapper/devicemapper/data` and |
|
| 26 |
-`/var/lib/docker/devicemapper/devicemapper/metadata`. Additional metadata |
|
| 27 |
-required to map from docker entities to the corresponding devicemapper |
|
| 28 |
-volumes is stored in the `/var/lib/docker/devicemapper/devicemapper/json` |
|
| 29 |
-file (encoded as Json). |
|
| 30 |
- |
|
| 31 |
-In order to support multiple devicemapper graphs on a system, the thin |
|
| 32 |
-pool will be named something like: `docker-0:33-19478248-pool`, where |
|
| 33 |
-the `0:33` part is the minor/major device nr and `19478248` is the |
|
| 34 |
-inode number of the `/var/lib/docker/devicemapper` directory. |
|
| 35 |
- |
|
| 36 |
-On the thin pool, docker automatically creates a base thin device, |
|
| 37 |
-called something like `docker-0:33-19478248-base` of a fixed |
|
| 38 |
-size. This is automatically formatted with an empty filesystem on |
|
| 39 |
-creation. This device is the base of all docker images and |
|
| 40 |
-containers. All base images are snapshots of this device and those |
|
| 41 |
-images are then in turn used as snapshots for other images and |
|
| 42 |
-eventually containers. |
|
| 43 |
- |
|
| 44 |
-## Information on `docker info` |
|
| 45 |
- |
|
| 46 |
-As of docker-1.4.1, `docker info` when using the `devicemapper` storage driver |
|
| 47 |
-will display something like: |
|
| 48 |
- |
|
| 49 |
- $ sudo docker info |
|
| 50 |
- [...] |
|
| 51 |
- Storage Driver: devicemapper |
|
| 52 |
- Pool Name: docker-253:1-17538953-pool |
|
| 53 |
- Pool Blocksize: 65.54 kB |
|
| 54 |
- Base Device Size: 107.4 GB |
|
| 55 |
- Data file: /dev/loop4 |
|
| 56 |
- Metadata file: /dev/loop4 |
|
| 57 |
- Data Space Used: 2.536 GB |
|
| 58 |
- Data Space Total: 107.4 GB |
|
| 59 |
- Data Space Available: 104.8 GB |
|
| 60 |
- Metadata Space Used: 7.93 MB |
|
| 61 |
- Metadata Space Total: 2.147 GB |
|
| 62 |
- Metadata Space Available: 2.14 GB |
|
| 63 |
- Udev Sync Supported: true |
|
| 64 |
- Data loop file: /home/docker/devicemapper/devicemapper/data |
|
| 65 |
- Metadata loop file: /home/docker/devicemapper/devicemapper/metadata |
|
| 66 |
- Library Version: 1.02.82-git (2013-10-04) |
|
| 67 |
- [...] |
|
| 68 |
- |
|
| 69 |
-### status items |
|
| 70 |
- |
|
| 71 |
-Each item in the indented section under `Storage Driver: devicemapper` are |
|
| 72 |
-status information about the driver. |
|
| 73 |
- * `Pool Name` name of the devicemapper pool for this driver. |
|
| 74 |
- * `Pool Blocksize` tells the blocksize the thin pool was initialized with. This only changes on creation. |
|
| 75 |
- * `Base Device Size` tells the maximum size of a container and image |
|
| 76 |
- * `Data file` blockdevice file used for the devicemapper data |
|
| 77 |
- * `Metadata file` blockdevice file used for the devicemapper metadata |
|
| 78 |
- * `Data Space Used` tells how much of `Data file` is currently used |
|
| 79 |
- * `Data Space Total` tells max size the `Data file` |
|
| 80 |
- * `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. |
|
| 81 |
- * `Metadata Space Used` tells how much of `Metadata file` is currently used |
|
| 82 |
- * `Metadata Space Total` tells max size the `Metadata file` |
|
| 83 |
- * `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. |
|
| 84 |
- * `Udev Sync Supported` tells whether devicemapper is able to sync with Udev. Should be `true`. |
|
| 85 |
- * `Data loop file` file attached to `Data file`, if loopback device is used |
|
| 86 |
- * `Metadata loop file` file attached to `Metadata file`, if loopback device is used |
|
| 87 |
- * `Library Version` from the libdevmapper used |
|
| 88 |
- |
|
| 89 |
-## About the devicemapper options |
|
| 90 |
- |
|
| 91 |
-The devicemapper backend supports some options that you can specify |
|
| 92 |
-when starting the docker daemon using the `--storage-opt` flags. |
|
| 93 |
-This uses the `dm` prefix and would be used something like `dockerd --storage-opt dm.foo=bar`. |
|
| 94 |
- |
|
| 95 |
-These options are currently documented both in [the man |
|
| 96 |
-page](../../../man/docker.1.md) and in [the online |
|
| 97 |
-documentation](https://docs.docker.com/engine/reference/commandline/dockerd/#/storage-driver-options). |
|
| 98 |
-If you add an options, update both the `man` page and the documentation. |
| 99 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,230 +0,0 @@ |
| 1 |
-package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper" |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bufio" |
|
| 5 |
- "bytes" |
|
| 6 |
- "encoding/json" |
|
| 7 |
- "fmt" |
|
| 8 |
- "os" |
|
| 9 |
- "os/exec" |
|
| 10 |
- "path/filepath" |
|
| 11 |
- "reflect" |
|
| 12 |
- "strings" |
|
| 13 |
- |
|
| 14 |
- "github.com/pkg/errors" |
|
| 15 |
- "github.com/sirupsen/logrus" |
|
| 16 |
-) |
|
| 17 |
- |
|
| 18 |
-type directLVMConfig struct {
|
|
| 19 |
- Device string |
|
| 20 |
- ThinpPercent uint64 |
|
| 21 |
- ThinpMetaPercent uint64 |
|
| 22 |
- AutoExtendPercent uint64 |
|
| 23 |
- AutoExtendThreshold uint64 |
|
| 24 |
-} |
|
| 25 |
- |
|
| 26 |
-var ( |
|
| 27 |
- errThinpPercentMissing = errors.New("must set both `dm.thinp_percent` and `dm.thinp_metapercent` if either is specified")
|
|
| 28 |
- errThinpPercentTooBig = errors.New("combined `dm.thinp_percent` and `dm.thinp_metapercent` must not be greater than 100")
|
|
| 29 |
- errMissingSetupDevice = errors.New("must provide device path in `dm.directlvm_device` in order to configure direct-lvm")
|
|
| 30 |
-) |
|
| 31 |
- |
|
| 32 |
-func validateLVMConfig(cfg directLVMConfig) error {
|
|
| 33 |
- if reflect.DeepEqual(cfg, directLVMConfig{}) {
|
|
| 34 |
- return nil |
|
| 35 |
- } |
|
| 36 |
- if cfg.Device == "" {
|
|
| 37 |
- return errMissingSetupDevice |
|
| 38 |
- } |
|
| 39 |
- if (cfg.ThinpPercent > 0 && cfg.ThinpMetaPercent == 0) || cfg.ThinpMetaPercent > 0 && cfg.ThinpPercent == 0 {
|
|
| 40 |
- return errThinpPercentMissing |
|
| 41 |
- } |
|
| 42 |
- |
|
| 43 |
- if cfg.ThinpPercent+cfg.ThinpMetaPercent > 100 {
|
|
| 44 |
- return errThinpPercentTooBig |
|
| 45 |
- } |
|
| 46 |
- return nil |
|
| 47 |
-} |
|
| 48 |
- |
|
| 49 |
-func checkDevAvailable(dev string) error {
|
|
| 50 |
- lvmScan, err := exec.LookPath("lvmdiskscan")
|
|
| 51 |
- if err != nil {
|
|
| 52 |
- logrus.Debug("could not find lvmdiskscan")
|
|
| 53 |
- return nil |
|
| 54 |
- } |
|
| 55 |
- |
|
| 56 |
- out, err := exec.Command(lvmScan).CombinedOutput() |
|
| 57 |
- if err != nil {
|
|
| 58 |
- logrus.WithError(err).Error(string(out)) |
|
| 59 |
- return nil |
|
| 60 |
- } |
|
| 61 |
- |
|
| 62 |
- if !bytes.Contains(out, []byte(dev)) {
|
|
| 63 |
- return errors.Errorf("%s is not available for use with devicemapper", dev)
|
|
| 64 |
- } |
|
| 65 |
- return nil |
|
| 66 |
-} |
|
| 67 |
- |
|
| 68 |
-func checkDevInVG(dev string) error {
|
|
| 69 |
- pvDisplay, err := exec.LookPath("pvdisplay")
|
|
| 70 |
- if err != nil {
|
|
| 71 |
- logrus.Debug("could not find pvdisplay")
|
|
| 72 |
- return nil |
|
| 73 |
- } |
|
| 74 |
- |
|
| 75 |
- out, err := exec.Command(pvDisplay, dev).CombinedOutput() |
|
| 76 |
- if err != nil {
|
|
| 77 |
- logrus.WithError(err).Error(string(out)) |
|
| 78 |
- return nil |
|
| 79 |
- } |
|
| 80 |
- |
|
| 81 |
- scanner := bufio.NewScanner(bytes.NewReader(bytes.TrimSpace(out))) |
|
| 82 |
- for scanner.Scan() {
|
|
| 83 |
- fields := strings.SplitAfter(strings.TrimSpace(scanner.Text()), "VG Name") |
|
| 84 |
- if len(fields) > 1 {
|
|
| 85 |
- // got "VG Name" line" |
|
| 86 |
- vg := strings.TrimSpace(fields[1]) |
|
| 87 |
- if len(vg) > 0 {
|
|
| 88 |
- 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)
|
|
| 89 |
- } |
|
| 90 |
- logrus.Error(fields) |
|
| 91 |
- break |
|
| 92 |
- } |
|
| 93 |
- } |
|
| 94 |
- return nil |
|
| 95 |
-} |
|
| 96 |
- |
|
| 97 |
-func checkDevHasFS(dev string) error {
|
|
| 98 |
- blkid, err := exec.LookPath("blkid")
|
|
| 99 |
- if err != nil {
|
|
| 100 |
- logrus.Debug("could not find blkid")
|
|
| 101 |
- return nil |
|
| 102 |
- } |
|
| 103 |
- |
|
| 104 |
- out, err := exec.Command(blkid, dev).CombinedOutput() |
|
| 105 |
- if err != nil {
|
|
| 106 |
- logrus.WithError(err).Error(string(out)) |
|
| 107 |
- return nil |
|
| 108 |
- } |
|
| 109 |
- |
|
| 110 |
- fields := bytes.Fields(out) |
|
| 111 |
- for _, f := range fields {
|
|
| 112 |
- kv := bytes.Split(f, []byte{'='})
|
|
| 113 |
- if bytes.Equal(kv[0], []byte("TYPE")) {
|
|
| 114 |
- v := bytes.Trim(kv[1], "\"") |
|
| 115 |
- if len(v) > 0 {
|
|
| 116 |
- return errors.Errorf("%s has a filesystem already, use dm.directlvm_device_force=true if you want to wipe the device", dev)
|
|
| 117 |
- } |
|
| 118 |
- return nil |
|
| 119 |
- } |
|
| 120 |
- } |
|
| 121 |
- return nil |
|
| 122 |
-} |
|
| 123 |
- |
|
| 124 |
-func verifyBlockDevice(dev string, force bool) error {
|
|
| 125 |
- if err := checkDevAvailable(dev); err != nil {
|
|
| 126 |
- return err |
|
| 127 |
- } |
|
| 128 |
- if err := checkDevInVG(dev); err != nil {
|
|
| 129 |
- return err |
|
| 130 |
- } |
|
| 131 |
- if force {
|
|
| 132 |
- return nil |
|
| 133 |
- } |
|
| 134 |
- return checkDevHasFS(dev) |
|
| 135 |
-} |
|
| 136 |
- |
|
| 137 |
-func readLVMConfig(root string) (directLVMConfig, error) {
|
|
| 138 |
- var cfg directLVMConfig |
|
| 139 |
- |
|
| 140 |
- p := filepath.Join(root, "setup-config.json") |
|
| 141 |
- b, err := os.ReadFile(p) |
|
| 142 |
- if err != nil {
|
|
| 143 |
- if os.IsNotExist(err) {
|
|
| 144 |
- return cfg, nil |
|
| 145 |
- } |
|
| 146 |
- return cfg, errors.Wrap(err, "error reading existing setup config") |
|
| 147 |
- } |
|
| 148 |
- |
|
| 149 |
- // check if this is just an empty file, no need to produce a json error later if so |
|
| 150 |
- if len(b) == 0 {
|
|
| 151 |
- return cfg, nil |
|
| 152 |
- } |
|
| 153 |
- |
|
| 154 |
- err = json.Unmarshal(b, &cfg) |
|
| 155 |
- return cfg, errors.Wrap(err, "error unmarshaling previous device setup config") |
|
| 156 |
-} |
|
| 157 |
- |
|
| 158 |
-func writeLVMConfig(root string, cfg directLVMConfig) error {
|
|
| 159 |
- p := filepath.Join(root, "setup-config.json") |
|
| 160 |
- b, err := json.Marshal(cfg) |
|
| 161 |
- if err != nil {
|
|
| 162 |
- return errors.Wrap(err, "error marshalling direct lvm config") |
|
| 163 |
- } |
|
| 164 |
- err = os.WriteFile(p, b, 0600) |
|
| 165 |
- return errors.Wrap(err, "error writing direct lvm config to file") |
|
| 166 |
-} |
|
| 167 |
- |
|
| 168 |
-func setupDirectLVM(cfg directLVMConfig) error {
|
|
| 169 |
- lvmProfileDir := "/etc/lvm/profile" |
|
| 170 |
- binaries := []string{"pvcreate", "vgcreate", "lvcreate", "lvconvert", "lvchange", "thin_check"}
|
|
| 171 |
- |
|
| 172 |
- for _, bin := range binaries {
|
|
| 173 |
- if _, err := exec.LookPath(bin); err != nil {
|
|
| 174 |
- return errors.Wrap(err, "error looking up command `"+bin+"` while setting up direct lvm") |
|
| 175 |
- } |
|
| 176 |
- } |
|
| 177 |
- |
|
| 178 |
- err := os.MkdirAll(lvmProfileDir, 0755) |
|
| 179 |
- if err != nil {
|
|
| 180 |
- return errors.Wrap(err, "error creating lvm profile directory") |
|
| 181 |
- } |
|
| 182 |
- |
|
| 183 |
- if cfg.AutoExtendPercent == 0 {
|
|
| 184 |
- cfg.AutoExtendPercent = 20 |
|
| 185 |
- } |
|
| 186 |
- |
|
| 187 |
- if cfg.AutoExtendThreshold == 0 {
|
|
| 188 |
- cfg.AutoExtendThreshold = 80 |
|
| 189 |
- } |
|
| 190 |
- |
|
| 191 |
- if cfg.ThinpPercent == 0 {
|
|
| 192 |
- cfg.ThinpPercent = 95 |
|
| 193 |
- } |
|
| 194 |
- if cfg.ThinpMetaPercent == 0 {
|
|
| 195 |
- cfg.ThinpMetaPercent = 1 |
|
| 196 |
- } |
|
| 197 |
- |
|
| 198 |
- out, err := exec.Command("pvcreate", "-f", cfg.Device).CombinedOutput()
|
|
| 199 |
- if err != nil {
|
|
| 200 |
- return errors.Wrap(err, string(out)) |
|
| 201 |
- } |
|
| 202 |
- |
|
| 203 |
- out, err = exec.Command("vgcreate", "docker", cfg.Device).CombinedOutput()
|
|
| 204 |
- if err != nil {
|
|
| 205 |
- return errors.Wrap(err, string(out)) |
|
| 206 |
- } |
|
| 207 |
- |
|
| 208 |
- out, err = exec.Command("lvcreate", "--wipesignatures", "y", "-n", "thinpool", "docker", "--extents", fmt.Sprintf("%d%%VG", cfg.ThinpPercent)).CombinedOutput()
|
|
| 209 |
- if err != nil {
|
|
| 210 |
- return errors.Wrap(err, string(out)) |
|
| 211 |
- } |
|
| 212 |
- out, err = exec.Command("lvcreate", "--wipesignatures", "y", "-n", "thinpoolmeta", "docker", "--extents", fmt.Sprintf("%d%%VG", cfg.ThinpMetaPercent)).CombinedOutput()
|
|
| 213 |
- if err != nil {
|
|
| 214 |
- return errors.Wrap(err, string(out)) |
|
| 215 |
- } |
|
| 216 |
- |
|
| 217 |
- out, err = exec.Command("lvconvert", "-y", "--zero", "n", "-c", "512K", "--thinpool", "docker/thinpool", "--poolmetadata", "docker/thinpoolmeta").CombinedOutput()
|
|
| 218 |
- if err != nil {
|
|
| 219 |
- return errors.Wrap(err, string(out)) |
|
| 220 |
- } |
|
| 221 |
- |
|
| 222 |
- profile := fmt.Sprintf("activation{\nthin_pool_autoextend_threshold=%d\nthin_pool_autoextend_percent=%d\n}", cfg.AutoExtendThreshold, cfg.AutoExtendPercent)
|
|
| 223 |
- err = os.WriteFile(lvmProfileDir+"/docker-thinpool.profile", []byte(profile), 0600) |
|
| 224 |
- if err != nil {
|
|
| 225 |
- return errors.Wrap(err, "error writing docker thinp autoextend profile") |
|
| 226 |
- } |
|
| 227 |
- |
|
| 228 |
- out, err = exec.Command("lvchange", "--metadataprofile", "docker-thinpool", "docker/thinpool").CombinedOutput()
|
|
| 229 |
- return errors.Wrap(err, string(out)) |
|
| 230 |
-} |
| 231 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,2811 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
-// +build linux |
|
| 3 |
- |
|
| 4 |
-package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper" |
|
| 5 |
- |
|
| 6 |
-import ( |
|
| 7 |
- "bufio" |
|
| 8 |
- "encoding/json" |
|
| 9 |
- "fmt" |
|
| 10 |
- "io" |
|
| 11 |
- "os" |
|
| 12 |
- "os/exec" |
|
| 13 |
- "path" |
|
| 14 |
- "path/filepath" |
|
| 15 |
- "reflect" |
|
| 16 |
- "strconv" |
|
| 17 |
- "strings" |
|
| 18 |
- "sync" |
|
| 19 |
- "time" |
|
| 20 |
- |
|
| 21 |
- "github.com/docker/docker/daemon/graphdriver" |
|
| 22 |
- "github.com/docker/docker/pkg/devicemapper" |
|
| 23 |
- "github.com/docker/docker/pkg/dmesg" |
|
| 24 |
- "github.com/docker/docker/pkg/idtools" |
|
| 25 |
- "github.com/docker/docker/pkg/loopback" |
|
| 26 |
- "github.com/docker/docker/pkg/parsers" |
|
| 27 |
- "github.com/docker/docker/pkg/parsers/kernel" |
|
| 28 |
- units "github.com/docker/go-units" |
|
| 29 |
- "github.com/moby/sys/mount" |
|
| 30 |
- "github.com/opencontainers/selinux/go-selinux/label" |
|
| 31 |
- "github.com/pkg/errors" |
|
| 32 |
- "github.com/sirupsen/logrus" |
|
| 33 |
- "golang.org/x/sys/unix" |
|
| 34 |
-) |
|
| 35 |
- |
|
| 36 |
-var ( |
|
| 37 |
- defaultDataLoopbackSize int64 = 100 * 1024 * 1024 * 1024 |
|
| 38 |
- defaultMetaDataLoopbackSize int64 = 2 * 1024 * 1024 * 1024 |
|
| 39 |
- defaultBaseFsSize uint64 = 10 * 1024 * 1024 * 1024 |
|
| 40 |
- defaultThinpBlockSize uint32 = 128 // 64K = 128 512b sectors |
|
| 41 |
- defaultUdevSyncOverride = false |
|
| 42 |
- maxDeviceID = 0xffffff // 24 bit, pool limit |
|
| 43 |
- deviceIDMapSz = (maxDeviceID + 1) / 8 |
|
| 44 |
- driverDeferredRemovalSupport = false |
|
| 45 |
- enableDeferredRemoval = false |
|
| 46 |
- enableDeferredDeletion = false |
|
| 47 |
- userBaseSize = false |
|
| 48 |
- defaultMinFreeSpacePercent uint32 = 10 |
|
| 49 |
- lvmSetupConfigForce bool |
|
| 50 |
-) |
|
| 51 |
- |
|
| 52 |
-const deviceSetMetaFile = "deviceset-metadata" |
|
| 53 |
-const transactionMetaFile = "transaction-metadata" |
|
| 54 |
- |
|
| 55 |
-type transaction struct {
|
|
| 56 |
- OpenTransactionID uint64 `json:"open_transaction_id"` |
|
| 57 |
- DeviceIDHash string `json:"device_hash"` |
|
| 58 |
- DeviceID int `json:"device_id"` |
|
| 59 |
-} |
|
| 60 |
- |
|
| 61 |
-type devInfo struct {
|
|
| 62 |
- Hash string `json:"-"` |
|
| 63 |
- DeviceID int `json:"device_id"` |
|
| 64 |
- Size uint64 `json:"size"` |
|
| 65 |
- TransactionID uint64 `json:"transaction_id"` |
|
| 66 |
- Initialized bool `json:"initialized"` |
|
| 67 |
- Deleted bool `json:"deleted"` |
|
| 68 |
- devices *DeviceSet |
|
| 69 |
- |
|
| 70 |
- // The global DeviceSet lock guarantees that we serialize all |
|
| 71 |
- // the calls to libdevmapper (which is not threadsafe), but we |
|
| 72 |
- // sometimes release that lock while sleeping. In that case |
|
| 73 |
- // this per-device lock is still held, protecting against |
|
| 74 |
- // other accesses to the device that we're doing the wait on. |
|
| 75 |
- // |
|
| 76 |
- // WARNING: In order to avoid AB-BA deadlocks when releasing |
|
| 77 |
- // the global lock while holding the per-device locks all |
|
| 78 |
- // device locks must be acquired *before* the device lock, and |
|
| 79 |
- // multiple device locks should be acquired parent before child. |
|
| 80 |
- lock sync.Mutex |
|
| 81 |
-} |
|
| 82 |
- |
|
| 83 |
-type metaData struct {
|
|
| 84 |
- Devices map[string]*devInfo `json:"Devices"` |
|
| 85 |
-} |
|
| 86 |
- |
|
| 87 |
-// DeviceSet holds information about list of devices |
|
| 88 |
-type DeviceSet struct {
|
|
| 89 |
- metaData `json:"-"` |
|
| 90 |
- sync.Mutex `json:"-"` // Protects all fields of DeviceSet and serializes calls into libdevmapper |
|
| 91 |
- root string |
|
| 92 |
- devicePrefix string |
|
| 93 |
- TransactionID uint64 `json:"-"` |
|
| 94 |
- NextDeviceID int `json:"next_device_id"` |
|
| 95 |
- deviceIDMap []byte |
|
| 96 |
- |
|
| 97 |
- // Options |
|
| 98 |
- dataLoopbackSize int64 |
|
| 99 |
- metaDataLoopbackSize int64 |
|
| 100 |
- baseFsSize uint64 |
|
| 101 |
- filesystem string |
|
| 102 |
- mountOptions string |
|
| 103 |
- mkfsArgs []string |
|
| 104 |
- dataDevice string // block or loop dev |
|
| 105 |
- dataLoopFile string // loopback file, if used |
|
| 106 |
- metadataDevice string // block or loop dev |
|
| 107 |
- metadataLoopFile string // loopback file, if used |
|
| 108 |
- doBlkDiscard bool |
|
| 109 |
- thinpBlockSize uint32 |
|
| 110 |
- thinPoolDevice string |
|
| 111 |
- transaction `json:"-"` |
|
| 112 |
- overrideUdevSyncCheck bool |
|
| 113 |
- deferredRemove bool // use deferred removal |
|
| 114 |
- deferredDelete bool // use deferred deletion |
|
| 115 |
- BaseDeviceUUID string // save UUID of base device |
|
| 116 |
- BaseDeviceFilesystem string // save filesystem of base device |
|
| 117 |
- nrDeletedDevices uint // number of deleted devices |
|
| 118 |
- deletionWorkerTicker *time.Ticker |
|
| 119 |
- idMap idtools.IdentityMapping |
|
| 120 |
- minFreeSpacePercent uint32 // min free space percentage in thinpool |
|
| 121 |
- xfsNospaceRetries string // max retries when xfs receives ENOSPC |
|
| 122 |
- lvmSetupConfig directLVMConfig |
|
| 123 |
-} |
|
| 124 |
- |
|
| 125 |
-// DiskUsage contains information about disk usage and is used when reporting Status of a device. |
|
| 126 |
-type DiskUsage struct {
|
|
| 127 |
- // Used bytes on the disk. |
|
| 128 |
- Used uint64 |
|
| 129 |
- // Total bytes on the disk. |
|
| 130 |
- Total uint64 |
|
| 131 |
- // Available bytes on the disk. |
|
| 132 |
- Available uint64 |
|
| 133 |
-} |
|
| 134 |
- |
|
| 135 |
-// Status returns the information about the device. |
|
| 136 |
-type Status struct {
|
|
| 137 |
- // PoolName is the name of the data pool. |
|
| 138 |
- PoolName string |
|
| 139 |
- // DataFile is the actual block device for data. |
|
| 140 |
- DataFile string |
|
| 141 |
- // DataLoopback loopback file, if used. |
|
| 142 |
- DataLoopback string |
|
| 143 |
- // MetadataFile is the actual block device for metadata. |
|
| 144 |
- MetadataFile string |
|
| 145 |
- // MetadataLoopback is the loopback file, if used. |
|
| 146 |
- MetadataLoopback string |
|
| 147 |
- // Data is the disk used for data. |
|
| 148 |
- Data DiskUsage |
|
| 149 |
- // Metadata is the disk used for meta data. |
|
| 150 |
- Metadata DiskUsage |
|
| 151 |
- // BaseDeviceSize is base size of container and image |
|
| 152 |
- BaseDeviceSize uint64 |
|
| 153 |
- // BaseDeviceFS is backing filesystem. |
|
| 154 |
- BaseDeviceFS string |
|
| 155 |
- // SectorSize size of the vector. |
|
| 156 |
- SectorSize uint64 |
|
| 157 |
- // UdevSyncSupported is true if sync is supported. |
|
| 158 |
- UdevSyncSupported bool |
|
| 159 |
- // DeferredRemoveEnabled is true then the device is not unmounted. |
|
| 160 |
- DeferredRemoveEnabled bool |
|
| 161 |
- // True if deferred deletion is enabled. This is different from |
|
| 162 |
- // deferred removal. "removal" means that device mapper device is |
|
| 163 |
- // deactivated. Thin device is still in thin pool and can be activated |
|
| 164 |
- // again. But "deletion" means that thin device will be deleted from |
|
| 165 |
- // thin pool and it can't be activated again. |
|
| 166 |
- DeferredDeleteEnabled bool |
|
| 167 |
- DeferredDeletedDeviceCount uint |
|
| 168 |
- MinFreeSpace uint64 |
|
| 169 |
-} |
|
| 170 |
- |
|
| 171 |
-// Structure used to export image/container metadata in docker inspect. |
|
| 172 |
-type deviceMetadata struct {
|
|
| 173 |
- deviceID int |
|
| 174 |
- deviceSize uint64 // size in bytes |
|
| 175 |
- deviceName string // Device name as used during activation |
|
| 176 |
-} |
|
| 177 |
- |
|
| 178 |
-// DevStatus returns information about device mounted containing its id, size and sector information. |
|
| 179 |
-type DevStatus struct {
|
|
| 180 |
- // DeviceID is the id of the device. |
|
| 181 |
- DeviceID int |
|
| 182 |
- // Size is the size of the filesystem. |
|
| 183 |
- Size uint64 |
|
| 184 |
- // TransactionID is a unique integer per device set used to identify an operation on the file system, this number is incremental. |
|
| 185 |
- TransactionID uint64 |
|
| 186 |
- // SizeInSectors indicates the size of the sectors allocated. |
|
| 187 |
- SizeInSectors uint64 |
|
| 188 |
- // MappedSectors indicates number of mapped sectors. |
|
| 189 |
- MappedSectors uint64 |
|
| 190 |
- // HighestMappedSector is the pointer to the highest mapped sector. |
|
| 191 |
- HighestMappedSector uint64 |
|
| 192 |
-} |
|
| 193 |
- |
|
| 194 |
-func getDevName(name string) string {
|
|
| 195 |
- return "/dev/mapper/" + name |
|
| 196 |
-} |
|
| 197 |
- |
|
| 198 |
-func (info *devInfo) Name() string {
|
|
| 199 |
- hash := info.Hash |
|
| 200 |
- if hash == "" {
|
|
| 201 |
- hash = "base" |
|
| 202 |
- } |
|
| 203 |
- return fmt.Sprintf("%s-%s", info.devices.devicePrefix, hash)
|
|
| 204 |
-} |
|
| 205 |
- |
|
| 206 |
-func (info *devInfo) DevName() string {
|
|
| 207 |
- return getDevName(info.Name()) |
|
| 208 |
-} |
|
| 209 |
- |
|
| 210 |
-func (devices *DeviceSet) loopbackDir() string {
|
|
| 211 |
- return path.Join(devices.root, "devicemapper") |
|
| 212 |
-} |
|
| 213 |
- |
|
| 214 |
-func (devices *DeviceSet) metadataDir() string {
|
|
| 215 |
- return path.Join(devices.root, "metadata") |
|
| 216 |
-} |
|
| 217 |
- |
|
| 218 |
-func (devices *DeviceSet) metadataFile(info *devInfo) string {
|
|
| 219 |
- file := info.Hash |
|
| 220 |
- if file == "" {
|
|
| 221 |
- file = "base" |
|
| 222 |
- } |
|
| 223 |
- return path.Join(devices.metadataDir(), file) |
|
| 224 |
-} |
|
| 225 |
- |
|
| 226 |
-func (devices *DeviceSet) transactionMetaFile() string {
|
|
| 227 |
- return path.Join(devices.metadataDir(), transactionMetaFile) |
|
| 228 |
-} |
|
| 229 |
- |
|
| 230 |
-func (devices *DeviceSet) deviceSetMetaFile() string {
|
|
| 231 |
- return path.Join(devices.metadataDir(), deviceSetMetaFile) |
|
| 232 |
-} |
|
| 233 |
- |
|
| 234 |
-func (devices *DeviceSet) oldMetadataFile() string {
|
|
| 235 |
- return path.Join(devices.loopbackDir(), "json") |
|
| 236 |
-} |
|
| 237 |
- |
|
| 238 |
-func (devices *DeviceSet) getPoolName() string {
|
|
| 239 |
- if devices.thinPoolDevice == "" {
|
|
| 240 |
- return devices.devicePrefix + "-pool" |
|
| 241 |
- } |
|
| 242 |
- return devices.thinPoolDevice |
|
| 243 |
-} |
|
| 244 |
- |
|
| 245 |
-func (devices *DeviceSet) getPoolDevName() string {
|
|
| 246 |
- return getDevName(devices.getPoolName()) |
|
| 247 |
-} |
|
| 248 |
- |
|
| 249 |
-func (devices *DeviceSet) hasImage(name string) bool {
|
|
| 250 |
- dirname := devices.loopbackDir() |
|
| 251 |
- filename := path.Join(dirname, name) |
|
| 252 |
- |
|
| 253 |
- _, err := os.Stat(filename) |
|
| 254 |
- return err == nil |
|
| 255 |
-} |
|
| 256 |
- |
|
| 257 |
-// ensureImage creates a sparse file of <size> bytes at the path |
|
| 258 |
-// <root>/devicemapper/<name>. |
|
| 259 |
-// If the file already exists and new size is larger than its current size, it grows to the new size. |
|
| 260 |
-// Either way it returns the full path. |
|
| 261 |
-func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) {
|
|
| 262 |
- dirname := devices.loopbackDir() |
|
| 263 |
- filename := path.Join(dirname, name) |
|
| 264 |
- |
|
| 265 |
- if err := idtools.MkdirAllAndChown(dirname, 0700, devices.idMap.RootPair()); err != nil {
|
|
| 266 |
- return "", err |
|
| 267 |
- } |
|
| 268 |
- |
|
| 269 |
- if fi, err := os.Stat(filename); err != nil {
|
|
| 270 |
- if !os.IsNotExist(err) {
|
|
| 271 |
- return "", err |
|
| 272 |
- } |
|
| 273 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("Creating loopback file %s for device-manage use", filename)
|
|
| 274 |
- file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0600) |
|
| 275 |
- if err != nil {
|
|
| 276 |
- return "", err |
|
| 277 |
- } |
|
| 278 |
- defer file.Close() |
|
| 279 |
- |
|
| 280 |
- if err := file.Truncate(size); err != nil {
|
|
| 281 |
- return "", err |
|
| 282 |
- } |
|
| 283 |
- } else {
|
|
| 284 |
- if fi.Size() < size {
|
|
| 285 |
- file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0600) |
|
| 286 |
- if err != nil {
|
|
| 287 |
- return "", err |
|
| 288 |
- } |
|
| 289 |
- defer file.Close() |
|
| 290 |
- if err := file.Truncate(size); err != nil {
|
|
| 291 |
- return "", fmt.Errorf("devmapper: Unable to grow loopback file %s: %v", filename, err)
|
|
| 292 |
- } |
|
| 293 |
- } else if fi.Size() > size {
|
|
| 294 |
- logrus.WithField("storage-driver", "devicemapper").Warnf("Can't shrink loopback file %s", filename)
|
|
| 295 |
- } |
|
| 296 |
- } |
|
| 297 |
- return filename, nil |
|
| 298 |
-} |
|
| 299 |
- |
|
| 300 |
-func (devices *DeviceSet) allocateTransactionID() uint64 {
|
|
| 301 |
- devices.OpenTransactionID = devices.TransactionID + 1 |
|
| 302 |
- return devices.OpenTransactionID |
|
| 303 |
-} |
|
| 304 |
- |
|
| 305 |
-func (devices *DeviceSet) updatePoolTransactionID() error {
|
|
| 306 |
- if err := devicemapper.SetTransactionID(devices.getPoolDevName(), devices.TransactionID, devices.OpenTransactionID); err != nil {
|
|
| 307 |
- return fmt.Errorf("devmapper: Error setting devmapper transaction ID: %s", err)
|
|
| 308 |
- } |
|
| 309 |
- devices.TransactionID = devices.OpenTransactionID |
|
| 310 |
- return nil |
|
| 311 |
-} |
|
| 312 |
- |
|
| 313 |
-func (devices *DeviceSet) removeMetadata(info *devInfo) error {
|
|
| 314 |
- if err := os.RemoveAll(devices.metadataFile(info)); err != nil {
|
|
| 315 |
- return fmt.Errorf("devmapper: Error removing metadata file %s: %s", devices.metadataFile(info), err)
|
|
| 316 |
- } |
|
| 317 |
- return nil |
|
| 318 |
-} |
|
| 319 |
- |
|
| 320 |
-// Given json data and file path, write it to disk |
|
| 321 |
-func (devices *DeviceSet) writeMetaFile(jsonData []byte, filePath string) error {
|
|
| 322 |
- tmpFile, err := os.CreateTemp(devices.metadataDir(), ".tmp") |
|
| 323 |
- if err != nil {
|
|
| 324 |
- return fmt.Errorf("devmapper: Error creating metadata file: %s", err)
|
|
| 325 |
- } |
|
| 326 |
- |
|
| 327 |
- n, err := tmpFile.Write(jsonData) |
|
| 328 |
- if err != nil {
|
|
| 329 |
- return fmt.Errorf("devmapper: Error writing metadata to %s: %s", tmpFile.Name(), err)
|
|
| 330 |
- } |
|
| 331 |
- if n < len(jsonData) {
|
|
| 332 |
- return io.ErrShortWrite |
|
| 333 |
- } |
|
| 334 |
- if err := tmpFile.Sync(); err != nil {
|
|
| 335 |
- return fmt.Errorf("devmapper: Error syncing metadata file %s: %s", tmpFile.Name(), err)
|
|
| 336 |
- } |
|
| 337 |
- if err := tmpFile.Close(); err != nil {
|
|
| 338 |
- return fmt.Errorf("devmapper: Error closing metadata file %s: %s", tmpFile.Name(), err)
|
|
| 339 |
- } |
|
| 340 |
- if err := os.Rename(tmpFile.Name(), filePath); err != nil {
|
|
| 341 |
- return fmt.Errorf("devmapper: Error committing metadata file %s: %s", tmpFile.Name(), err)
|
|
| 342 |
- } |
|
| 343 |
- |
|
| 344 |
- return nil |
|
| 345 |
-} |
|
| 346 |
- |
|
| 347 |
-func (devices *DeviceSet) saveMetadata(info *devInfo) error {
|
|
| 348 |
- jsonData, err := json.Marshal(info) |
|
| 349 |
- if err != nil {
|
|
| 350 |
- return fmt.Errorf("devmapper: Error encoding metadata to json: %s", err)
|
|
| 351 |
- } |
|
| 352 |
- return devices.writeMetaFile(jsonData, devices.metadataFile(info)) |
|
| 353 |
-} |
|
| 354 |
- |
|
| 355 |
-func (devices *DeviceSet) markDeviceIDUsed(deviceID int) {
|
|
| 356 |
- var mask byte |
|
| 357 |
- i := deviceID % 8 |
|
| 358 |
- mask = 1 << uint(i) |
|
| 359 |
- devices.deviceIDMap[deviceID/8] = devices.deviceIDMap[deviceID/8] | mask |
|
| 360 |
-} |
|
| 361 |
- |
|
| 362 |
-func (devices *DeviceSet) markDeviceIDFree(deviceID int) {
|
|
| 363 |
- var mask byte |
|
| 364 |
- i := deviceID % 8 |
|
| 365 |
- mask = ^(1 << uint(i)) |
|
| 366 |
- devices.deviceIDMap[deviceID/8] = devices.deviceIDMap[deviceID/8] & mask |
|
| 367 |
-} |
|
| 368 |
- |
|
| 369 |
-func (devices *DeviceSet) isDeviceIDFree(deviceID int) bool {
|
|
| 370 |
- var mask byte |
|
| 371 |
- i := deviceID % 8 |
|
| 372 |
- mask = (1 << uint(i)) |
|
| 373 |
- return (devices.deviceIDMap[deviceID/8] & mask) == 0 |
|
| 374 |
-} |
|
| 375 |
- |
|
| 376 |
-// Should be called with devices.Lock() held. |
|
| 377 |
-func (devices *DeviceSet) lookupDevice(hash string) (*devInfo, error) {
|
|
| 378 |
- info := devices.Devices[hash] |
|
| 379 |
- if info == nil {
|
|
| 380 |
- info = devices.loadMetadata(hash) |
|
| 381 |
- if info == nil {
|
|
| 382 |
- return nil, fmt.Errorf("devmapper: Unknown device %s", hash)
|
|
| 383 |
- } |
|
| 384 |
- |
|
| 385 |
- devices.Devices[hash] = info |
|
| 386 |
- } |
|
| 387 |
- return info, nil |
|
| 388 |
-} |
|
| 389 |
- |
|
| 390 |
-func (devices *DeviceSet) lookupDeviceWithLock(hash string) (*devInfo, error) {
|
|
| 391 |
- devices.Lock() |
|
| 392 |
- defer devices.Unlock() |
|
| 393 |
- info, err := devices.lookupDevice(hash) |
|
| 394 |
- return info, err |
|
| 395 |
-} |
|
| 396 |
- |
|
| 397 |
-// This function relies on that device hash map has been loaded in advance. |
|
| 398 |
-// Should be called with devices.Lock() held. |
|
| 399 |
-func (devices *DeviceSet) constructDeviceIDMap() {
|
|
| 400 |
- logrus.WithField("storage-driver", "devicemapper").Debug("constructDeviceIDMap()")
|
|
| 401 |
- defer logrus.WithField("storage-driver", "devicemapper").Debug("constructDeviceIDMap() END")
|
|
| 402 |
- |
|
| 403 |
- for _, info := range devices.Devices {
|
|
| 404 |
- devices.markDeviceIDUsed(info.DeviceID) |
|
| 405 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("Added deviceId=%d to DeviceIdMap", info.DeviceID)
|
|
| 406 |
- } |
|
| 407 |
-} |
|
| 408 |
- |
|
| 409 |
-func (devices *DeviceSet) deviceFileWalkFunction(path string, name string) error {
|
|
| 410 |
- logger := logrus.WithField("storage-driver", "devicemapper")
|
|
| 411 |
- |
|
| 412 |
- // Skip some of the meta files which are not device files. |
|
| 413 |
- if strings.HasSuffix(name, ".migrated") {
|
|
| 414 |
- logger.Debugf("Skipping file %s", path)
|
|
| 415 |
- return nil |
|
| 416 |
- } |
|
| 417 |
- |
|
| 418 |
- if strings.HasPrefix(name, ".") {
|
|
| 419 |
- logger.Debugf("Skipping file %s", path)
|
|
| 420 |
- return nil |
|
| 421 |
- } |
|
| 422 |
- |
|
| 423 |
- if name == deviceSetMetaFile {
|
|
| 424 |
- logger.Debugf("Skipping file %s", path)
|
|
| 425 |
- return nil |
|
| 426 |
- } |
|
| 427 |
- |
|
| 428 |
- if name == transactionMetaFile {
|
|
| 429 |
- logger.Debugf("Skipping file %s", path)
|
|
| 430 |
- return nil |
|
| 431 |
- } |
|
| 432 |
- |
|
| 433 |
- logger.Debugf("Loading data for file %s", path)
|
|
| 434 |
- |
|
| 435 |
- hash := name |
|
| 436 |
- if hash == "base" {
|
|
| 437 |
- hash = "" |
|
| 438 |
- } |
|
| 439 |
- |
|
| 440 |
- // Include deleted devices also as cleanup delete device logic |
|
| 441 |
- // will go through it and see if there are any deleted devices. |
|
| 442 |
- if _, err := devices.lookupDevice(hash); err != nil {
|
|
| 443 |
- return fmt.Errorf("devmapper: Error looking up device %s:%v", hash, err)
|
|
| 444 |
- } |
|
| 445 |
- |
|
| 446 |
- return nil |
|
| 447 |
-} |
|
| 448 |
- |
|
| 449 |
-func (devices *DeviceSet) loadDeviceFilesOnStart() error {
|
|
| 450 |
- logrus.WithField("storage-driver", "devicemapper").Debug("loadDeviceFilesOnStart()")
|
|
| 451 |
- defer logrus.WithField("storage-driver", "devicemapper").Debug("loadDeviceFilesOnStart() END")
|
|
| 452 |
- |
|
| 453 |
- var scan = func(path string, info os.DirEntry, err error) error {
|
|
| 454 |
- if err != nil {
|
|
| 455 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("Can't walk the file %s", path)
|
|
| 456 |
- return nil |
|
| 457 |
- } |
|
| 458 |
- |
|
| 459 |
- // Skip any directories |
|
| 460 |
- if info.IsDir() {
|
|
| 461 |
- return nil |
|
| 462 |
- } |
|
| 463 |
- |
|
| 464 |
- return devices.deviceFileWalkFunction(path, info.Name()) |
|
| 465 |
- } |
|
| 466 |
- |
|
| 467 |
- return filepath.WalkDir(devices.metadataDir(), scan) |
|
| 468 |
-} |
|
| 469 |
- |
|
| 470 |
-// Should be called with devices.Lock() held. |
|
| 471 |
-func (devices *DeviceSet) unregisterDevice(hash string) error {
|
|
| 472 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("unregisterDevice(%v)", hash)
|
|
| 473 |
- info := &devInfo{
|
|
| 474 |
- Hash: hash, |
|
| 475 |
- } |
|
| 476 |
- |
|
| 477 |
- delete(devices.Devices, hash) |
|
| 478 |
- |
|
| 479 |
- if err := devices.removeMetadata(info); err != nil {
|
|
| 480 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("Error removing metadata: %s", err)
|
|
| 481 |
- return err |
|
| 482 |
- } |
|
| 483 |
- |
|
| 484 |
- return nil |
|
| 485 |
-} |
|
| 486 |
- |
|
| 487 |
-// Should be called with devices.Lock() held. |
|
| 488 |
-func (devices *DeviceSet) registerDevice(id int, hash string, size uint64, transactionID uint64) (*devInfo, error) {
|
|
| 489 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("registerDevice(%v, %v)", id, hash)
|
|
| 490 |
- info := &devInfo{
|
|
| 491 |
- Hash: hash, |
|
| 492 |
- DeviceID: id, |
|
| 493 |
- Size: size, |
|
| 494 |
- TransactionID: transactionID, |
|
| 495 |
- Initialized: false, |
|
| 496 |
- devices: devices, |
|
| 497 |
- } |
|
| 498 |
- |
|
| 499 |
- devices.Devices[hash] = info |
|
| 500 |
- |
|
| 501 |
- if err := devices.saveMetadata(info); err != nil {
|
|
| 502 |
- // Try to remove unused device |
|
| 503 |
- delete(devices.Devices, hash) |
|
| 504 |
- return nil, err |
|
| 505 |
- } |
|
| 506 |
- |
|
| 507 |
- return info, nil |
|
| 508 |
-} |
|
| 509 |
- |
|
| 510 |
-func (devices *DeviceSet) activateDeviceIfNeeded(info *devInfo, ignoreDeleted bool) error {
|
|
| 511 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("activateDeviceIfNeeded(%v)", info.Hash)
|
|
| 512 |
- |
|
| 513 |
- if info.Deleted && !ignoreDeleted {
|
|
| 514 |
- return fmt.Errorf("devmapper: Can't activate device %v as it is marked for deletion", info.Hash)
|
|
| 515 |
- } |
|
| 516 |
- |
|
| 517 |
- // Make sure deferred removal on device is canceled, if one was |
|
| 518 |
- // scheduled. |
|
| 519 |
- if err := devices.cancelDeferredRemovalIfNeeded(info); err != nil {
|
|
| 520 |
- return fmt.Errorf("devmapper: Device Deferred Removal Cancellation Failed: %s", err)
|
|
| 521 |
- } |
|
| 522 |
- |
|
| 523 |
- if devinfo, _ := devicemapper.GetInfo(info.Name()); devinfo != nil && devinfo.Exists != 0 {
|
|
| 524 |
- return nil |
|
| 525 |
- } |
|
| 526 |
- |
|
| 527 |
- return devicemapper.ActivateDevice(devices.getPoolDevName(), info.Name(), info.DeviceID, info.Size) |
|
| 528 |
-} |
|
| 529 |
- |
|
| 530 |
-// xfsSupported checks if xfs is supported, returns nil if it is, otherwise an error |
|
| 531 |
-func xfsSupported() error {
|
|
| 532 |
- // Make sure mkfs.xfs is available |
|
| 533 |
- if _, err := exec.LookPath("mkfs.xfs"); err != nil {
|
|
| 534 |
- return err // error text is descriptive enough |
|
| 535 |
- } |
|
| 536 |
- |
|
| 537 |
- mountTarget, err := os.MkdirTemp("", "supportsXFS")
|
|
| 538 |
- if err != nil {
|
|
| 539 |
- return errors.Wrapf(err, "error checking for xfs support") |
|
| 540 |
- } |
|
| 541 |
- |
|
| 542 |
- /* The mounting will fail--after the module has been loaded.*/ |
|
| 543 |
- defer os.RemoveAll(mountTarget) |
|
| 544 |
- unix.Mount("none", mountTarget, "xfs", 0, "")
|
|
| 545 |
- |
|
| 546 |
- f, err := os.Open("/proc/filesystems")
|
|
| 547 |
- if err != nil {
|
|
| 548 |
- return errors.Wrapf(err, "error checking for xfs support") |
|
| 549 |
- } |
|
| 550 |
- defer f.Close() |
|
| 551 |
- |
|
| 552 |
- s := bufio.NewScanner(f) |
|
| 553 |
- for s.Scan() {
|
|
| 554 |
- if strings.HasSuffix(s.Text(), "\txfs") {
|
|
| 555 |
- return nil |
|
| 556 |
- } |
|
| 557 |
- } |
|
| 558 |
- |
|
| 559 |
- if err := s.Err(); err != nil {
|
|
| 560 |
- return errors.Wrapf(err, "error checking for xfs support") |
|
| 561 |
- } |
|
| 562 |
- |
|
| 563 |
- return errors.New(`kernel does not support xfs, or "modprobe xfs" failed`) |
|
| 564 |
-} |
|
| 565 |
- |
|
| 566 |
-func determineDefaultFS() string {
|
|
| 567 |
- err := xfsSupported() |
|
| 568 |
- if err == nil {
|
|
| 569 |
- return "xfs" |
|
| 570 |
- } |
|
| 571 |
- |
|
| 572 |
- logrus.WithField("storage-driver", "devicemapper").Warnf("XFS is not supported in your system (%v). Defaulting to ext4 filesystem", err)
|
|
| 573 |
- return "ext4" |
|
| 574 |
-} |
|
| 575 |
- |
|
| 576 |
-// mkfsOptions tries to figure out whether some additional mkfs options are required |
|
| 577 |
-func mkfsOptions(fs string) []string {
|
|
| 578 |
- if fs == "xfs" && !kernel.CheckKernelVersion(3, 16, 0) {
|
|
| 579 |
- // For kernels earlier than 3.16 (and newer xfsutils), |
|
| 580 |
- // some xfs features need to be explicitly disabled. |
|
| 581 |
- return []string{"-m", "crc=0,finobt=0"}
|
|
| 582 |
- } |
|
| 583 |
- |
|
| 584 |
- return []string{}
|
|
| 585 |
-} |
|
| 586 |
- |
|
| 587 |
-func (devices *DeviceSet) createFilesystem(info *devInfo) (err error) {
|
|
| 588 |
- devname := info.DevName() |
|
| 589 |
- |
|
| 590 |
- if devices.filesystem == "" {
|
|
| 591 |
- devices.filesystem = determineDefaultFS() |
|
| 592 |
- } |
|
| 593 |
- if err := devices.saveBaseDeviceFilesystem(devices.filesystem); err != nil {
|
|
| 594 |
- return err |
|
| 595 |
- } |
|
| 596 |
- |
|
| 597 |
- args := mkfsOptions(devices.filesystem) |
|
| 598 |
- args = append(args, devices.mkfsArgs...) |
|
| 599 |
- args = append(args, devname) |
|
| 600 |
- |
|
| 601 |
- logrus.WithField("storage-driver", "devicemapper").Infof("Creating filesystem %s on device %s, mkfs args: %v", devices.filesystem, info.Name(), args)
|
|
| 602 |
- defer func() {
|
|
| 603 |
- if err != nil {
|
|
| 604 |
- logrus.WithField("storage-driver", "devicemapper").Infof("Error while creating filesystem %s on device %s: %v", devices.filesystem, info.Name(), err)
|
|
| 605 |
- } else {
|
|
| 606 |
- logrus.WithField("storage-driver", "devicemapper").Infof("Successfully created filesystem %s on device %s", devices.filesystem, info.Name())
|
|
| 607 |
- } |
|
| 608 |
- }() |
|
| 609 |
- |
|
| 610 |
- switch devices.filesystem {
|
|
| 611 |
- case "xfs": |
|
| 612 |
- err = exec.Command("mkfs.xfs", args...).Run()
|
|
| 613 |
- case "ext4": |
|
| 614 |
- err = exec.Command("mkfs.ext4", append([]string{"-E", "nodiscard,lazy_itable_init=0,lazy_journal_init=0"}, args...)...).Run()
|
|
| 615 |
- if err != nil {
|
|
| 616 |
- err = exec.Command("mkfs.ext4", append([]string{"-E", "nodiscard,lazy_itable_init=0"}, args...)...).Run()
|
|
| 617 |
- } |
|
| 618 |
- if err != nil {
|
|
| 619 |
- return err |
|
| 620 |
- } |
|
| 621 |
- err = exec.Command("tune2fs", append([]string{"-c", "-1", "-i", "0"}, devname)...).Run()
|
|
| 622 |
- default: |
|
| 623 |
- err = fmt.Errorf("devmapper: Unsupported filesystem type %s", devices.filesystem)
|
|
| 624 |
- } |
|
| 625 |
- return |
|
| 626 |
-} |
|
| 627 |
- |
|
| 628 |
-func (devices *DeviceSet) migrateOldMetaData() error {
|
|
| 629 |
- // Migrate old metadata file |
|
| 630 |
- jsonData, err := os.ReadFile(devices.oldMetadataFile()) |
|
| 631 |
- if err != nil && !os.IsNotExist(err) {
|
|
| 632 |
- return err |
|
| 633 |
- } |
|
| 634 |
- |
|
| 635 |
- if jsonData != nil {
|
|
| 636 |
- m := metaData{Devices: make(map[string]*devInfo)}
|
|
| 637 |
- |
|
| 638 |
- if err := json.Unmarshal(jsonData, &m); err != nil {
|
|
| 639 |
- return err |
|
| 640 |
- } |
|
| 641 |
- |
|
| 642 |
- for hash, info := range m.Devices {
|
|
| 643 |
- info.Hash = hash |
|
| 644 |
- devices.saveMetadata(info) |
|
| 645 |
- } |
|
| 646 |
- if err := os.Rename(devices.oldMetadataFile(), devices.oldMetadataFile()+".migrated"); err != nil {
|
|
| 647 |
- return err |
|
| 648 |
- } |
|
| 649 |
- } |
|
| 650 |
- |
|
| 651 |
- return nil |
|
| 652 |
-} |
|
| 653 |
- |
|
| 654 |
-// Cleanup deleted devices. It assumes that all the devices have been |
|
| 655 |
-// loaded in the hash table. |
|
| 656 |
-func (devices *DeviceSet) cleanupDeletedDevices() error {
|
|
| 657 |
- devices.Lock() |
|
| 658 |
- |
|
| 659 |
- // If there are no deleted devices, there is nothing to do. |
|
| 660 |
- if devices.nrDeletedDevices == 0 {
|
|
| 661 |
- devices.Unlock() |
|
| 662 |
- return nil |
|
| 663 |
- } |
|
| 664 |
- |
|
| 665 |
- var deletedDevices []*devInfo |
|
| 666 |
- |
|
| 667 |
- for _, info := range devices.Devices {
|
|
| 668 |
- if !info.Deleted {
|
|
| 669 |
- continue |
|
| 670 |
- } |
|
| 671 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("Found deleted device %s.", info.Hash)
|
|
| 672 |
- deletedDevices = append(deletedDevices, info) |
|
| 673 |
- } |
|
| 674 |
- |
|
| 675 |
- // Delete the deleted devices. DeleteDevice() first takes the info lock |
|
| 676 |
- // and then devices.Lock(). So drop it to avoid deadlock. |
|
| 677 |
- devices.Unlock() |
|
| 678 |
- |
|
| 679 |
- for _, info := range deletedDevices {
|
|
| 680 |
- // This will again try deferred deletion. |
|
| 681 |
- if err := devices.DeleteDevice(info.Hash, false); err != nil {
|
|
| 682 |
- logrus.WithField("storage-driver", "devicemapper").Warnf("Deletion of device %s, device_id=%v failed:%v", info.Hash, info.DeviceID, err)
|
|
| 683 |
- } |
|
| 684 |
- } |
|
| 685 |
- |
|
| 686 |
- return nil |
|
| 687 |
-} |
|
| 688 |
- |
|
| 689 |
-func (devices *DeviceSet) countDeletedDevices() {
|
|
| 690 |
- for _, info := range devices.Devices {
|
|
| 691 |
- if !info.Deleted {
|
|
| 692 |
- continue |
|
| 693 |
- } |
|
| 694 |
- devices.nrDeletedDevices++ |
|
| 695 |
- } |
|
| 696 |
-} |
|
| 697 |
- |
|
| 698 |
-func (devices *DeviceSet) startDeviceDeletionWorker() {
|
|
| 699 |
- // Deferred deletion is not enabled. Don't do anything. |
|
| 700 |
- if !devices.deferredDelete {
|
|
| 701 |
- return |
|
| 702 |
- } |
|
| 703 |
- |
|
| 704 |
- logrus.WithField("storage-driver", "devicemapper").Debug("Worker to cleanup deleted devices started")
|
|
| 705 |
- for range devices.deletionWorkerTicker.C {
|
|
| 706 |
- devices.cleanupDeletedDevices() |
|
| 707 |
- } |
|
| 708 |
-} |
|
| 709 |
- |
|
| 710 |
-func (devices *DeviceSet) initMetaData() error {
|
|
| 711 |
- devices.Lock() |
|
| 712 |
- defer devices.Unlock() |
|
| 713 |
- |
|
| 714 |
- if err := devices.migrateOldMetaData(); err != nil {
|
|
| 715 |
- return err |
|
| 716 |
- } |
|
| 717 |
- |
|
| 718 |
- _, transactionID, _, _, _, _, err := devices.poolStatus() |
|
| 719 |
- if err != nil {
|
|
| 720 |
- return err |
|
| 721 |
- } |
|
| 722 |
- |
|
| 723 |
- devices.TransactionID = transactionID |
|
| 724 |
- |
|
| 725 |
- if err := devices.loadDeviceFilesOnStart(); err != nil {
|
|
| 726 |
- return fmt.Errorf("devmapper: Failed to load device files:%v", err)
|
|
| 727 |
- } |
|
| 728 |
- |
|
| 729 |
- devices.constructDeviceIDMap() |
|
| 730 |
- devices.countDeletedDevices() |
|
| 731 |
- |
|
| 732 |
- if err := devices.processPendingTransaction(); err != nil {
|
|
| 733 |
- return err |
|
| 734 |
- } |
|
| 735 |
- |
|
| 736 |
- // Start a goroutine to cleanup Deleted Devices |
|
| 737 |
- go devices.startDeviceDeletionWorker() |
|
| 738 |
- return nil |
|
| 739 |
-} |
|
| 740 |
- |
|
| 741 |
-func (devices *DeviceSet) incNextDeviceID() {
|
|
| 742 |
- // IDs are 24bit, so wrap around |
|
| 743 |
- devices.NextDeviceID = (devices.NextDeviceID + 1) & maxDeviceID |
|
| 744 |
-} |
|
| 745 |
- |
|
| 746 |
-func (devices *DeviceSet) getNextFreeDeviceID() (int, error) {
|
|
| 747 |
- devices.incNextDeviceID() |
|
| 748 |
- for i := 0; i <= maxDeviceID; i++ {
|
|
| 749 |
- if devices.isDeviceIDFree(devices.NextDeviceID) {
|
|
| 750 |
- devices.markDeviceIDUsed(devices.NextDeviceID) |
|
| 751 |
- return devices.NextDeviceID, nil |
|
| 752 |
- } |
|
| 753 |
- devices.incNextDeviceID() |
|
| 754 |
- } |
|
| 755 |
- |
|
| 756 |
- return 0, fmt.Errorf("devmapper: Unable to find a free device ID")
|
|
| 757 |
-} |
|
| 758 |
- |
|
| 759 |
-func (devices *DeviceSet) poolHasFreeSpace() error {
|
|
| 760 |
- if devices.minFreeSpacePercent == 0 {
|
|
| 761 |
- return nil |
|
| 762 |
- } |
|
| 763 |
- |
|
| 764 |
- _, _, dataUsed, dataTotal, metadataUsed, metadataTotal, err := devices.poolStatus() |
|
| 765 |
- if err != nil {
|
|
| 766 |
- return err |
|
| 767 |
- } |
|
| 768 |
- |
|
| 769 |
- minFreeData := (dataTotal * uint64(devices.minFreeSpacePercent)) / 100 |
|
| 770 |
- if minFreeData < 1 {
|
|
| 771 |
- minFreeData = 1 |
|
| 772 |
- } |
|
| 773 |
- dataFree := dataTotal - dataUsed |
|
| 774 |
- if dataFree < minFreeData {
|
|
| 775 |
- return fmt.Errorf("devmapper: Thin Pool has %v free data blocks which is less than minimum required %v free data blocks. Create more free space in thin pool or use dm.min_free_space option to change behavior", (dataTotal - dataUsed), minFreeData)
|
|
| 776 |
- } |
|
| 777 |
- |
|
| 778 |
- minFreeMetadata := (metadataTotal * uint64(devices.minFreeSpacePercent)) / 100 |
|
| 779 |
- if minFreeMetadata < 1 {
|
|
| 780 |
- minFreeMetadata = 1 |
|
| 781 |
- } |
|
| 782 |
- |
|
| 783 |
- metadataFree := metadataTotal - metadataUsed |
|
| 784 |
- if metadataFree < minFreeMetadata {
|
|
| 785 |
- return fmt.Errorf("devmapper: Thin Pool has %v free metadata blocks which is less than minimum required %v free metadata blocks. Create more free metadata space in thin pool or use dm.min_free_space option to change behavior", (metadataTotal - metadataUsed), minFreeMetadata)
|
|
| 786 |
- } |
|
| 787 |
- |
|
| 788 |
- return nil |
|
| 789 |
-} |
|
| 790 |
- |
|
| 791 |
-func (devices *DeviceSet) createRegisterDevice(hash string) (*devInfo, error) {
|
|
| 792 |
- devices.Lock() |
|
| 793 |
- defer devices.Unlock() |
|
| 794 |
- |
|
| 795 |
- deviceID, err := devices.getNextFreeDeviceID() |
|
| 796 |
- if err != nil {
|
|
| 797 |
- return nil, err |
|
| 798 |
- } |
|
| 799 |
- |
|
| 800 |
- logger := logrus.WithField("storage-driver", "devicemapper")
|
|
| 801 |
- |
|
| 802 |
- if err := devices.openTransaction(hash, deviceID); err != nil {
|
|
| 803 |
- logger.Debugf("Error opening transaction hash = %s deviceID = %d", hash, deviceID)
|
|
| 804 |
- devices.markDeviceIDFree(deviceID) |
|
| 805 |
- return nil, err |
|
| 806 |
- } |
|
| 807 |
- |
|
| 808 |
- for {
|
|
| 809 |
- if err := devicemapper.CreateDevice(devices.getPoolDevName(), deviceID); err != nil {
|
|
| 810 |
- if devicemapper.DeviceIDExists(err) {
|
|
| 811 |
- // Device ID already exists. This should not |
|
| 812 |
- // happen. Now we have a mechanism to find |
|
| 813 |
- // a free device ID. So something is not right. |
|
| 814 |
- // Give a warning and continue. |
|
| 815 |
- logger.Errorf("Device ID %d exists in pool but it is supposed to be unused", deviceID)
|
|
| 816 |
- deviceID, err = devices.getNextFreeDeviceID() |
|
| 817 |
- if err != nil {
|
|
| 818 |
- return nil, err |
|
| 819 |
- } |
|
| 820 |
- // Save new device id into transaction |
|
| 821 |
- devices.refreshTransaction(deviceID) |
|
| 822 |
- continue |
|
| 823 |
- } |
|
| 824 |
- logger.Debugf("Error creating device: %s", err)
|
|
| 825 |
- devices.markDeviceIDFree(deviceID) |
|
| 826 |
- return nil, err |
|
| 827 |
- } |
|
| 828 |
- break |
|
| 829 |
- } |
|
| 830 |
- |
|
| 831 |
- logger.Debugf("Registering device (id %v) with FS size %v", deviceID, devices.baseFsSize)
|
|
| 832 |
- info, err := devices.registerDevice(deviceID, hash, devices.baseFsSize, devices.OpenTransactionID) |
|
| 833 |
- if err != nil {
|
|
| 834 |
- _ = devicemapper.DeleteDevice(devices.getPoolDevName(), deviceID) |
|
| 835 |
- devices.markDeviceIDFree(deviceID) |
|
| 836 |
- return nil, err |
|
| 837 |
- } |
|
| 838 |
- |
|
| 839 |
- if err := devices.closeTransaction(); err != nil {
|
|
| 840 |
- devices.unregisterDevice(hash) |
|
| 841 |
- devicemapper.DeleteDevice(devices.getPoolDevName(), deviceID) |
|
| 842 |
- devices.markDeviceIDFree(deviceID) |
|
| 843 |
- return nil, err |
|
| 844 |
- } |
|
| 845 |
- return info, nil |
|
| 846 |
-} |
|
| 847 |
- |
|
| 848 |
-func (devices *DeviceSet) takeSnapshot(hash string, baseInfo *devInfo, size uint64) error {
|
|
| 849 |
- var ( |
|
| 850 |
- devinfo *devicemapper.Info |
|
| 851 |
- err error |
|
| 852 |
- ) |
|
| 853 |
- |
|
| 854 |
- if err = devices.poolHasFreeSpace(); err != nil {
|
|
| 855 |
- return err |
|
| 856 |
- } |
|
| 857 |
- |
|
| 858 |
- if devices.deferredRemove {
|
|
| 859 |
- devinfo, err = devicemapper.GetInfoWithDeferred(baseInfo.Name()) |
|
| 860 |
- if err != nil {
|
|
| 861 |
- return err |
|
| 862 |
- } |
|
| 863 |
- if devinfo != nil && devinfo.DeferredRemove != 0 {
|
|
| 864 |
- err = devices.cancelDeferredRemoval(baseInfo) |
|
| 865 |
- if err != nil {
|
|
| 866 |
- // If Error is ErrEnxio. Device is probably already gone. Continue. |
|
| 867 |
- if err != devicemapper.ErrEnxio {
|
|
| 868 |
- return err |
|
| 869 |
- } |
|
| 870 |
- devinfo = nil |
|
| 871 |
- } else {
|
|
| 872 |
- defer devices.deactivateDevice(baseInfo) |
|
| 873 |
- } |
|
| 874 |
- } |
|
| 875 |
- } else {
|
|
| 876 |
- devinfo, err = devicemapper.GetInfo(baseInfo.Name()) |
|
| 877 |
- if err != nil {
|
|
| 878 |
- return err |
|
| 879 |
- } |
|
| 880 |
- } |
|
| 881 |
- |
|
| 882 |
- doSuspend := devinfo != nil && devinfo.Exists != 0 |
|
| 883 |
- |
|
| 884 |
- if doSuspend {
|
|
| 885 |
- if err = devicemapper.SuspendDevice(baseInfo.Name()); err != nil {
|
|
| 886 |
- return err |
|
| 887 |
- } |
|
| 888 |
- defer devicemapper.ResumeDevice(baseInfo.Name()) |
|
| 889 |
- } |
|
| 890 |
- |
|
| 891 |
- return devices.createRegisterSnapDevice(hash, baseInfo, size) |
|
| 892 |
-} |
|
| 893 |
- |
|
| 894 |
-func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInfo, size uint64) error {
|
|
| 895 |
- deviceID, err := devices.getNextFreeDeviceID() |
|
| 896 |
- if err != nil {
|
|
| 897 |
- return err |
|
| 898 |
- } |
|
| 899 |
- |
|
| 900 |
- logger := logrus.WithField("storage-driver", "devicemapper")
|
|
| 901 |
- |
|
| 902 |
- if err := devices.openTransaction(hash, deviceID); err != nil {
|
|
| 903 |
- logger.Debugf("Error opening transaction hash = %s deviceID = %d", hash, deviceID)
|
|
| 904 |
- devices.markDeviceIDFree(deviceID) |
|
| 905 |
- return err |
|
| 906 |
- } |
|
| 907 |
- |
|
| 908 |
- for {
|
|
| 909 |
- if err := devicemapper.CreateSnapDeviceRaw(devices.getPoolDevName(), deviceID, baseInfo.DeviceID); err != nil {
|
|
| 910 |
- if devicemapper.DeviceIDExists(err) {
|
|
| 911 |
- // Device ID already exists. This should not |
|
| 912 |
- // happen. Now we have a mechanism to find |
|
| 913 |
- // a free device ID. So something is not right. |
|
| 914 |
- // Give a warning and continue. |
|
| 915 |
- logger.Errorf("Device ID %d exists in pool but it is supposed to be unused", deviceID)
|
|
| 916 |
- deviceID, err = devices.getNextFreeDeviceID() |
|
| 917 |
- if err != nil {
|
|
| 918 |
- return err |
|
| 919 |
- } |
|
| 920 |
- // Save new device id into transaction |
|
| 921 |
- devices.refreshTransaction(deviceID) |
|
| 922 |
- continue |
|
| 923 |
- } |
|
| 924 |
- logger.Debugf("Error creating snap device: %s", err)
|
|
| 925 |
- devices.markDeviceIDFree(deviceID) |
|
| 926 |
- return err |
|
| 927 |
- } |
|
| 928 |
- break |
|
| 929 |
- } |
|
| 930 |
- |
|
| 931 |
- if _, err := devices.registerDevice(deviceID, hash, size, devices.OpenTransactionID); err != nil {
|
|
| 932 |
- devicemapper.DeleteDevice(devices.getPoolDevName(), deviceID) |
|
| 933 |
- devices.markDeviceIDFree(deviceID) |
|
| 934 |
- logger.Debugf("Error registering device: %s", err)
|
|
| 935 |
- return err |
|
| 936 |
- } |
|
| 937 |
- |
|
| 938 |
- if err := devices.closeTransaction(); err != nil {
|
|
| 939 |
- devices.unregisterDevice(hash) |
|
| 940 |
- devicemapper.DeleteDevice(devices.getPoolDevName(), deviceID) |
|
| 941 |
- devices.markDeviceIDFree(deviceID) |
|
| 942 |
- return err |
|
| 943 |
- } |
|
| 944 |
- return nil |
|
| 945 |
-} |
|
| 946 |
- |
|
| 947 |
-func (devices *DeviceSet) loadMetadata(hash string) *devInfo {
|
|
| 948 |
- info := &devInfo{Hash: hash, devices: devices}
|
|
| 949 |
- logger := logrus.WithField("storage-driver", "devicemapper")
|
|
| 950 |
- |
|
| 951 |
- jsonData, err := os.ReadFile(devices.metadataFile(info)) |
|
| 952 |
- if err != nil {
|
|
| 953 |
- logger.Debugf("Failed to read %s with err: %v", devices.metadataFile(info), err)
|
|
| 954 |
- return nil |
|
| 955 |
- } |
|
| 956 |
- |
|
| 957 |
- if err := json.Unmarshal(jsonData, &info); err != nil {
|
|
| 958 |
- logger.Debugf("Failed to unmarshal devInfo from %s with err: %v", devices.metadataFile(info), err)
|
|
| 959 |
- return nil |
|
| 960 |
- } |
|
| 961 |
- |
|
| 962 |
- if info.DeviceID > maxDeviceID {
|
|
| 963 |
- logger.Errorf("Ignoring Invalid DeviceId=%d", info.DeviceID)
|
|
| 964 |
- return nil |
|
| 965 |
- } |
|
| 966 |
- |
|
| 967 |
- return info |
|
| 968 |
-} |
|
| 969 |
- |
|
| 970 |
-func getDeviceUUID(device string) (string, error) {
|
|
| 971 |
- out, err := exec.Command("blkid", "-s", "UUID", "-o", "value", device).Output()
|
|
| 972 |
- if err != nil {
|
|
| 973 |
- return "", fmt.Errorf("devmapper: Failed to find uuid for device %s:%v", device, err)
|
|
| 974 |
- } |
|
| 975 |
- |
|
| 976 |
- uuid := strings.TrimSuffix(string(out), "\n") |
|
| 977 |
- uuid = strings.TrimSpace(uuid) |
|
| 978 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("UUID for device: %s is:%s", device, uuid)
|
|
| 979 |
- return uuid, nil |
|
| 980 |
-} |
|
| 981 |
- |
|
| 982 |
-func (devices *DeviceSet) getBaseDeviceSize() uint64 {
|
|
| 983 |
- info, _ := devices.lookupDevice("")
|
|
| 984 |
- if info == nil {
|
|
| 985 |
- return 0 |
|
| 986 |
- } |
|
| 987 |
- return info.Size |
|
| 988 |
-} |
|
| 989 |
- |
|
| 990 |
-func (devices *DeviceSet) getBaseDeviceFS() string {
|
|
| 991 |
- return devices.BaseDeviceFilesystem |
|
| 992 |
-} |
|
| 993 |
- |
|
| 994 |
-func (devices *DeviceSet) verifyBaseDeviceUUIDFS(baseInfo *devInfo) error {
|
|
| 995 |
- devices.Lock() |
|
| 996 |
- defer devices.Unlock() |
|
| 997 |
- |
|
| 998 |
- if err := devices.activateDeviceIfNeeded(baseInfo, false); err != nil {
|
|
| 999 |
- return err |
|
| 1000 |
- } |
|
| 1001 |
- defer devices.deactivateDevice(baseInfo) |
|
| 1002 |
- |
|
| 1003 |
- uuid, err := getDeviceUUID(baseInfo.DevName()) |
|
| 1004 |
- if err != nil {
|
|
| 1005 |
- return err |
|
| 1006 |
- } |
|
| 1007 |
- |
|
| 1008 |
- if devices.BaseDeviceUUID != uuid {
|
|
| 1009 |
- return fmt.Errorf("devmapper: Current Base Device UUID:%s does not match with stored UUID:%s. Possibly using a different thin pool than last invocation", uuid, devices.BaseDeviceUUID)
|
|
| 1010 |
- } |
|
| 1011 |
- |
|
| 1012 |
- if devices.BaseDeviceFilesystem == "" {
|
|
| 1013 |
- fsType, err := ProbeFsType(baseInfo.DevName()) |
|
| 1014 |
- if err != nil {
|
|
| 1015 |
- return err |
|
| 1016 |
- } |
|
| 1017 |
- if err := devices.saveBaseDeviceFilesystem(fsType); err != nil {
|
|
| 1018 |
- return err |
|
| 1019 |
- } |
|
| 1020 |
- } |
|
| 1021 |
- |
|
| 1022 |
- // If user specified a filesystem using dm.fs option and current |
|
| 1023 |
- // file system of base image is not same, warn user that dm.fs |
|
| 1024 |
- // will be ignored. |
|
| 1025 |
- if devices.BaseDeviceFilesystem != devices.filesystem {
|
|
| 1026 |
- logrus.WithField("storage-driver", "devicemapper").Warnf("Base device already exists and has filesystem %s on it. User specified filesystem %s will be ignored.", devices.BaseDeviceFilesystem, devices.filesystem)
|
|
| 1027 |
- devices.filesystem = devices.BaseDeviceFilesystem |
|
| 1028 |
- } |
|
| 1029 |
- return nil |
|
| 1030 |
-} |
|
| 1031 |
- |
|
| 1032 |
-func (devices *DeviceSet) saveBaseDeviceFilesystem(fs string) error {
|
|
| 1033 |
- devices.BaseDeviceFilesystem = fs |
|
| 1034 |
- return devices.saveDeviceSetMetaData() |
|
| 1035 |
-} |
|
| 1036 |
- |
|
| 1037 |
-func (devices *DeviceSet) saveBaseDeviceUUID(baseInfo *devInfo) error {
|
|
| 1038 |
- devices.Lock() |
|
| 1039 |
- defer devices.Unlock() |
|
| 1040 |
- |
|
| 1041 |
- if err := devices.activateDeviceIfNeeded(baseInfo, false); err != nil {
|
|
| 1042 |
- return err |
|
| 1043 |
- } |
|
| 1044 |
- defer devices.deactivateDevice(baseInfo) |
|
| 1045 |
- |
|
| 1046 |
- uuid, err := getDeviceUUID(baseInfo.DevName()) |
|
| 1047 |
- if err != nil {
|
|
| 1048 |
- return err |
|
| 1049 |
- } |
|
| 1050 |
- |
|
| 1051 |
- devices.BaseDeviceUUID = uuid |
|
| 1052 |
- return devices.saveDeviceSetMetaData() |
|
| 1053 |
-} |
|
| 1054 |
- |
|
| 1055 |
-func (devices *DeviceSet) createBaseImage() error {
|
|
| 1056 |
- logrus.WithField("storage-driver", "devicemapper").Debug("Initializing base device-mapper thin volume")
|
|
| 1057 |
- |
|
| 1058 |
- // Create initial device |
|
| 1059 |
- info, err := devices.createRegisterDevice("")
|
|
| 1060 |
- if err != nil {
|
|
| 1061 |
- return err |
|
| 1062 |
- } |
|
| 1063 |
- |
|
| 1064 |
- logrus.WithField("storage-driver", "devicemapper").Debug("Creating filesystem on base device-mapper thin volume")
|
|
| 1065 |
- |
|
| 1066 |
- if err := devices.activateDeviceIfNeeded(info, false); err != nil {
|
|
| 1067 |
- return err |
|
| 1068 |
- } |
|
| 1069 |
- |
|
| 1070 |
- if err := devices.createFilesystem(info); err != nil {
|
|
| 1071 |
- return err |
|
| 1072 |
- } |
|
| 1073 |
- |
|
| 1074 |
- info.Initialized = true |
|
| 1075 |
- if err := devices.saveMetadata(info); err != nil {
|
|
| 1076 |
- info.Initialized = false |
|
| 1077 |
- return err |
|
| 1078 |
- } |
|
| 1079 |
- |
|
| 1080 |
- if err := devices.saveBaseDeviceUUID(info); err != nil {
|
|
| 1081 |
- return fmt.Errorf("devmapper: Could not query and save base device UUID:%v", err)
|
|
| 1082 |
- } |
|
| 1083 |
- |
|
| 1084 |
- return nil |
|
| 1085 |
-} |
|
| 1086 |
- |
|
| 1087 |
-// Returns if thin pool device exists or not. If device exists, also makes |
|
| 1088 |
-// sure it is a thin pool device and not some other type of device. |
|
| 1089 |
-func (devices *DeviceSet) thinPoolExists(thinPoolDevice string) (bool, error) {
|
|
| 1090 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("Checking for existence of the pool %s", thinPoolDevice)
|
|
| 1091 |
- |
|
| 1092 |
- info, err := devicemapper.GetInfo(thinPoolDevice) |
|
| 1093 |
- if err != nil {
|
|
| 1094 |
- return false, fmt.Errorf("devmapper: GetInfo() on device %s failed: %v", thinPoolDevice, err)
|
|
| 1095 |
- } |
|
| 1096 |
- |
|
| 1097 |
- // Device does not exist. |
|
| 1098 |
- if info.Exists == 0 {
|
|
| 1099 |
- return false, nil |
|
| 1100 |
- } |
|
| 1101 |
- |
|
| 1102 |
- _, _, deviceType, _, err := devicemapper.GetStatus(thinPoolDevice) |
|
| 1103 |
- if err != nil {
|
|
| 1104 |
- return false, fmt.Errorf("devmapper: GetStatus() on device %s failed: %v", thinPoolDevice, err)
|
|
| 1105 |
- } |
|
| 1106 |
- |
|
| 1107 |
- if deviceType != "thin-pool" {
|
|
| 1108 |
- return false, fmt.Errorf("devmapper: Device %s is not a thin pool", thinPoolDevice)
|
|
| 1109 |
- } |
|
| 1110 |
- |
|
| 1111 |
- return true, nil |
|
| 1112 |
-} |
|
| 1113 |
- |
|
| 1114 |
-func (devices *DeviceSet) checkThinPool() error {
|
|
| 1115 |
- _, transactionID, dataUsed, _, _, _, err := devices.poolStatus() |
|
| 1116 |
- if err != nil {
|
|
| 1117 |
- return err |
|
| 1118 |
- } |
|
| 1119 |
- if dataUsed != 0 {
|
|
| 1120 |
- return fmt.Errorf("devmapper: Unable to take ownership of thin-pool (%s) that already has used data blocks",
|
|
| 1121 |
- devices.thinPoolDevice) |
|
| 1122 |
- } |
|
| 1123 |
- if transactionID != 0 {
|
|
| 1124 |
- return fmt.Errorf("devmapper: Unable to take ownership of thin-pool (%s) with non-zero transaction ID",
|
|
| 1125 |
- devices.thinPoolDevice) |
|
| 1126 |
- } |
|
| 1127 |
- return nil |
|
| 1128 |
-} |
|
| 1129 |
- |
|
| 1130 |
-// Base image is initialized properly. Either save UUID for first time (for |
|
| 1131 |
-// upgrade case or verify UUID. |
|
| 1132 |
-func (devices *DeviceSet) setupVerifyBaseImageUUIDFS(baseInfo *devInfo) error {
|
|
| 1133 |
- // If BaseDeviceUUID is nil (upgrade case), save it and return success. |
|
| 1134 |
- if devices.BaseDeviceUUID == "" {
|
|
| 1135 |
- if err := devices.saveBaseDeviceUUID(baseInfo); err != nil {
|
|
| 1136 |
- return fmt.Errorf("devmapper: Could not query and save base device UUID:%v", err)
|
|
| 1137 |
- } |
|
| 1138 |
- return nil |
|
| 1139 |
- } |
|
| 1140 |
- |
|
| 1141 |
- if err := devices.verifyBaseDeviceUUIDFS(baseInfo); err != nil {
|
|
| 1142 |
- return fmt.Errorf("devmapper: Base Device UUID and Filesystem verification failed: %v", err)
|
|
| 1143 |
- } |
|
| 1144 |
- |
|
| 1145 |
- return nil |
|
| 1146 |
-} |
|
| 1147 |
- |
|
| 1148 |
-func (devices *DeviceSet) checkGrowBaseDeviceFS(info *devInfo) error {
|
|
| 1149 |
- if !userBaseSize {
|
|
| 1150 |
- return nil |
|
| 1151 |
- } |
|
| 1152 |
- |
|
| 1153 |
- if devices.baseFsSize < devices.getBaseDeviceSize() {
|
|
| 1154 |
- return fmt.Errorf("devmapper: Base device size cannot be smaller than %s", units.HumanSize(float64(devices.getBaseDeviceSize())))
|
|
| 1155 |
- } |
|
| 1156 |
- |
|
| 1157 |
- if devices.baseFsSize == devices.getBaseDeviceSize() {
|
|
| 1158 |
- return nil |
|
| 1159 |
- } |
|
| 1160 |
- |
|
| 1161 |
- info.lock.Lock() |
|
| 1162 |
- defer info.lock.Unlock() |
|
| 1163 |
- |
|
| 1164 |
- devices.Lock() |
|
| 1165 |
- defer devices.Unlock() |
|
| 1166 |
- |
|
| 1167 |
- info.Size = devices.baseFsSize |
|
| 1168 |
- |
|
| 1169 |
- if err := devices.saveMetadata(info); err != nil {
|
|
| 1170 |
- // Try to remove unused device |
|
| 1171 |
- delete(devices.Devices, info.Hash) |
|
| 1172 |
- return err |
|
| 1173 |
- } |
|
| 1174 |
- |
|
| 1175 |
- return devices.growFS(info) |
|
| 1176 |
-} |
|
| 1177 |
- |
|
| 1178 |
-func (devices *DeviceSet) growFS(info *devInfo) error {
|
|
| 1179 |
- if err := devices.activateDeviceIfNeeded(info, false); err != nil {
|
|
| 1180 |
- return fmt.Errorf("Error activating devmapper device: %s", err)
|
|
| 1181 |
- } |
|
| 1182 |
- |
|
| 1183 |
- defer devices.deactivateDevice(info) |
|
| 1184 |
- |
|
| 1185 |
- fsMountPoint := "/run/docker/mnt" |
|
| 1186 |
- if _, err := os.Stat(fsMountPoint); os.IsNotExist(err) {
|
|
| 1187 |
- if err := os.MkdirAll(fsMountPoint, 0700); err != nil {
|
|
| 1188 |
- return err |
|
| 1189 |
- } |
|
| 1190 |
- defer os.RemoveAll(fsMountPoint) |
|
| 1191 |
- } |
|
| 1192 |
- |
|
| 1193 |
- options := "" |
|
| 1194 |
- if devices.BaseDeviceFilesystem == "xfs" {
|
|
| 1195 |
- // XFS needs nouuid or it can't mount filesystems with the same fs |
|
| 1196 |
- options = joinMountOptions(options, "nouuid") |
|
| 1197 |
- } |
|
| 1198 |
- options = joinMountOptions(options, devices.mountOptions) |
|
| 1199 |
- |
|
| 1200 |
- if err := mount.Mount(info.DevName(), fsMountPoint, devices.BaseDeviceFilesystem, options); err != nil {
|
|
| 1201 |
- return errors.Wrapf(err, "Failed to mount; dmesg: %s", string(dmesg.Dmesg(256))) |
|
| 1202 |
- } |
|
| 1203 |
- |
|
| 1204 |
- defer unix.Unmount(fsMountPoint, unix.MNT_DETACH) |
|
| 1205 |
- |
|
| 1206 |
- switch devices.BaseDeviceFilesystem {
|
|
| 1207 |
- case "ext4": |
|
| 1208 |
- if out, err := exec.Command("resize2fs", info.DevName()).CombinedOutput(); err != nil {
|
|
| 1209 |
- return fmt.Errorf("Failed to grow rootfs:%v:%s", err, string(out))
|
|
| 1210 |
- } |
|
| 1211 |
- case "xfs": |
|
| 1212 |
- if out, err := exec.Command("xfs_growfs", info.DevName()).CombinedOutput(); err != nil {
|
|
| 1213 |
- return fmt.Errorf("Failed to grow rootfs:%v:%s", err, string(out))
|
|
| 1214 |
- } |
|
| 1215 |
- default: |
|
| 1216 |
- return fmt.Errorf("Unsupported filesystem type %s", devices.BaseDeviceFilesystem)
|
|
| 1217 |
- } |
|
| 1218 |
- return nil |
|
| 1219 |
-} |
|
| 1220 |
- |
|
| 1221 |
-func (devices *DeviceSet) setupBaseImage() error {
|
|
| 1222 |
- oldInfo, _ := devices.lookupDeviceWithLock("")
|
|
| 1223 |
- |
|
| 1224 |
- // base image already exists. If it is initialized properly, do UUID |
|
| 1225 |
- // verification and return. Otherwise remove image and set it up |
|
| 1226 |
- // fresh. |
|
| 1227 |
- |
|
| 1228 |
- if oldInfo != nil {
|
|
| 1229 |
- if oldInfo.Initialized && !oldInfo.Deleted {
|
|
| 1230 |
- if err := devices.setupVerifyBaseImageUUIDFS(oldInfo); err != nil {
|
|
| 1231 |
- return err |
|
| 1232 |
- } |
|
| 1233 |
- return devices.checkGrowBaseDeviceFS(oldInfo) |
|
| 1234 |
- } |
|
| 1235 |
- |
|
| 1236 |
- logrus.WithField("storage-driver", "devicemapper").Debug("Removing uninitialized base image")
|
|
| 1237 |
- // If previous base device is in deferred delete state, |
|
| 1238 |
- // that needs to be cleaned up first. So don't try |
|
| 1239 |
- // deferred deletion. |
|
| 1240 |
- if err := devices.DeleteDevice("", true); err != nil {
|
|
| 1241 |
- return err |
|
| 1242 |
- } |
|
| 1243 |
- } |
|
| 1244 |
- |
|
| 1245 |
- // If we are setting up base image for the first time, make sure |
|
| 1246 |
- // thin pool is empty. |
|
| 1247 |
- if devices.thinPoolDevice != "" && oldInfo == nil {
|
|
| 1248 |
- if err := devices.checkThinPool(); err != nil {
|
|
| 1249 |
- return err |
|
| 1250 |
- } |
|
| 1251 |
- } |
|
| 1252 |
- |
|
| 1253 |
- // Create new base image device |
|
| 1254 |
- return devices.createBaseImage() |
|
| 1255 |
-} |
|
| 1256 |
- |
|
| 1257 |
-func setCloseOnExec(name string) {
|
|
| 1258 |
- fileInfos, _ := os.ReadDir("/proc/self/fd")
|
|
| 1259 |
- for _, i := range fileInfos {
|
|
| 1260 |
- link, _ := os.Readlink(filepath.Join("/proc/self/fd", i.Name()))
|
|
| 1261 |
- if link == name {
|
|
| 1262 |
- fd, err := strconv.Atoi(i.Name()) |
|
| 1263 |
- if err == nil {
|
|
| 1264 |
- unix.CloseOnExec(fd) |
|
| 1265 |
- } |
|
| 1266 |
- } |
|
| 1267 |
- } |
|
| 1268 |
-} |
|
| 1269 |
- |
|
| 1270 |
-func major(device uint64) uint64 {
|
|
| 1271 |
- return (device >> 8) & 0xfff |
|
| 1272 |
-} |
|
| 1273 |
- |
|
| 1274 |
-func minor(device uint64) uint64 {
|
|
| 1275 |
- return (device & 0xff) | ((device >> 12) & 0xfff00) |
|
| 1276 |
-} |
|
| 1277 |
- |
|
| 1278 |
-// ResizePool increases the size of the pool. |
|
| 1279 |
-func (devices *DeviceSet) ResizePool(size int64) error {
|
|
| 1280 |
- dirname := devices.loopbackDir() |
|
| 1281 |
- datafilename := path.Join(dirname, "data") |
|
| 1282 |
- if len(devices.dataDevice) > 0 {
|
|
| 1283 |
- datafilename = devices.dataDevice |
|
| 1284 |
- } |
|
| 1285 |
- metadatafilename := path.Join(dirname, "metadata") |
|
| 1286 |
- if len(devices.metadataDevice) > 0 {
|
|
| 1287 |
- metadatafilename = devices.metadataDevice |
|
| 1288 |
- } |
|
| 1289 |
- |
|
| 1290 |
- datafile, err := os.OpenFile(datafilename, os.O_RDWR, 0) |
|
| 1291 |
- if datafile == nil {
|
|
| 1292 |
- return err |
|
| 1293 |
- } |
|
| 1294 |
- defer datafile.Close() |
|
| 1295 |
- |
|
| 1296 |
- fi, err := datafile.Stat() |
|
| 1297 |
- if fi == nil {
|
|
| 1298 |
- return err |
|
| 1299 |
- } |
|
| 1300 |
- |
|
| 1301 |
- if fi.Size() > size {
|
|
| 1302 |
- return fmt.Errorf("devmapper: Can't shrink file")
|
|
| 1303 |
- } |
|
| 1304 |
- |
|
| 1305 |
- dataloopback := loopback.FindLoopDeviceFor(datafile) |
|
| 1306 |
- if dataloopback == nil {
|
|
| 1307 |
- return fmt.Errorf("devmapper: Unable to find loopback mount for: %s", datafilename)
|
|
| 1308 |
- } |
|
| 1309 |
- defer dataloopback.Close() |
|
| 1310 |
- |
|
| 1311 |
- metadatafile, err := os.OpenFile(metadatafilename, os.O_RDWR, 0) |
|
| 1312 |
- if metadatafile == nil {
|
|
| 1313 |
- return err |
|
| 1314 |
- } |
|
| 1315 |
- defer metadatafile.Close() |
|
| 1316 |
- |
|
| 1317 |
- metadataloopback := loopback.FindLoopDeviceFor(metadatafile) |
|
| 1318 |
- if metadataloopback == nil {
|
|
| 1319 |
- return fmt.Errorf("devmapper: Unable to find loopback mount for: %s", metadatafilename)
|
|
| 1320 |
- } |
|
| 1321 |
- defer metadataloopback.Close() |
|
| 1322 |
- |
|
| 1323 |
- // Grow loopback file |
|
| 1324 |
- if err := datafile.Truncate(size); err != nil {
|
|
| 1325 |
- return fmt.Errorf("devmapper: Unable to grow loopback file: %s", err)
|
|
| 1326 |
- } |
|
| 1327 |
- |
|
| 1328 |
- // Reload size for loopback device |
|
| 1329 |
- if err := loopback.SetCapacity(dataloopback); err != nil {
|
|
| 1330 |
- return fmt.Errorf("Unable to update loopback capacity: %s", err)
|
|
| 1331 |
- } |
|
| 1332 |
- |
|
| 1333 |
- // Suspend the pool |
|
| 1334 |
- if err := devicemapper.SuspendDevice(devices.getPoolName()); err != nil {
|
|
| 1335 |
- return fmt.Errorf("devmapper: Unable to suspend pool: %s", err)
|
|
| 1336 |
- } |
|
| 1337 |
- |
|
| 1338 |
- // Reload with the new block sizes |
|
| 1339 |
- if err := devicemapper.ReloadPool(devices.getPoolName(), dataloopback, metadataloopback, devices.thinpBlockSize); err != nil {
|
|
| 1340 |
- return fmt.Errorf("devmapper: Unable to reload pool: %s", err)
|
|
| 1341 |
- } |
|
| 1342 |
- |
|
| 1343 |
- // Resume the pool |
|
| 1344 |
- if err := devicemapper.ResumeDevice(devices.getPoolName()); err != nil {
|
|
| 1345 |
- return fmt.Errorf("devmapper: Unable to resume pool: %s", err)
|
|
| 1346 |
- } |
|
| 1347 |
- |
|
| 1348 |
- return nil |
|
| 1349 |
-} |
|
| 1350 |
- |
|
| 1351 |
-func (devices *DeviceSet) loadTransactionMetaData() error {
|
|
| 1352 |
- jsonData, err := os.ReadFile(devices.transactionMetaFile()) |
|
| 1353 |
- if err != nil {
|
|
| 1354 |
- // There is no active transaction. This will be the case |
|
| 1355 |
- // during upgrade. |
|
| 1356 |
- if os.IsNotExist(err) {
|
|
| 1357 |
- devices.OpenTransactionID = devices.TransactionID |
|
| 1358 |
- return nil |
|
| 1359 |
- } |
|
| 1360 |
- return err |
|
| 1361 |
- } |
|
| 1362 |
- |
|
| 1363 |
- json.Unmarshal(jsonData, &devices.transaction) |
|
| 1364 |
- return nil |
|
| 1365 |
-} |
|
| 1366 |
- |
|
| 1367 |
-func (devices *DeviceSet) saveTransactionMetaData() error {
|
|
| 1368 |
- jsonData, err := json.Marshal(&devices.transaction) |
|
| 1369 |
- if err != nil {
|
|
| 1370 |
- return fmt.Errorf("devmapper: Error encoding metadata to json: %s", err)
|
|
| 1371 |
- } |
|
| 1372 |
- |
|
| 1373 |
- return devices.writeMetaFile(jsonData, devices.transactionMetaFile()) |
|
| 1374 |
-} |
|
| 1375 |
- |
|
| 1376 |
-func (devices *DeviceSet) removeTransactionMetaData() error {
|
|
| 1377 |
- return os.RemoveAll(devices.transactionMetaFile()) |
|
| 1378 |
-} |
|
| 1379 |
- |
|
| 1380 |
-func (devices *DeviceSet) rollbackTransaction() error {
|
|
| 1381 |
- logger := logrus.WithField("storage-driver", "devicemapper")
|
|
| 1382 |
- |
|
| 1383 |
- logger.Debugf("Rolling back open transaction: TransactionID=%d hash=%s device_id=%d", devices.OpenTransactionID, devices.DeviceIDHash, devices.DeviceID)
|
|
| 1384 |
- |
|
| 1385 |
- // A device id might have already been deleted before transaction |
|
| 1386 |
- // closed. In that case this call will fail. Just leave a message |
|
| 1387 |
- // in case of failure. |
|
| 1388 |
- if err := devicemapper.DeleteDevice(devices.getPoolDevName(), devices.DeviceID); err != nil {
|
|
| 1389 |
- logger.Errorf("Unable to delete device: %s", err)
|
|
| 1390 |
- } |
|
| 1391 |
- |
|
| 1392 |
- dinfo := &devInfo{Hash: devices.DeviceIDHash}
|
|
| 1393 |
- if err := devices.removeMetadata(dinfo); err != nil {
|
|
| 1394 |
- logger.Errorf("Unable to remove metadata: %s", err)
|
|
| 1395 |
- } else {
|
|
| 1396 |
- devices.markDeviceIDFree(devices.DeviceID) |
|
| 1397 |
- } |
|
| 1398 |
- |
|
| 1399 |
- if err := devices.removeTransactionMetaData(); err != nil {
|
|
| 1400 |
- logger.Errorf("Unable to remove transaction meta file %s: %s", devices.transactionMetaFile(), err)
|
|
| 1401 |
- } |
|
| 1402 |
- |
|
| 1403 |
- return nil |
|
| 1404 |
-} |
|
| 1405 |
- |
|
| 1406 |
-func (devices *DeviceSet) processPendingTransaction() error {
|
|
| 1407 |
- if err := devices.loadTransactionMetaData(); err != nil {
|
|
| 1408 |
- return err |
|
| 1409 |
- } |
|
| 1410 |
- |
|
| 1411 |
- // If there was open transaction but pool transaction ID is same |
|
| 1412 |
- // as open transaction ID, nothing to roll back. |
|
| 1413 |
- if devices.TransactionID == devices.OpenTransactionID {
|
|
| 1414 |
- return nil |
|
| 1415 |
- } |
|
| 1416 |
- |
|
| 1417 |
- // If open transaction ID is less than pool transaction ID, something |
|
| 1418 |
- // is wrong. Bail out. |
|
| 1419 |
- if devices.OpenTransactionID < devices.TransactionID {
|
|
| 1420 |
- logrus.WithField("storage-driver", "devicemapper").Errorf("Open Transaction id %d is less than pool transaction id %d", devices.OpenTransactionID, devices.TransactionID)
|
|
| 1421 |
- return nil |
|
| 1422 |
- } |
|
| 1423 |
- |
|
| 1424 |
- // Pool transaction ID is not same as open transaction. There is |
|
| 1425 |
- // a transaction which was not completed. |
|
| 1426 |
- if err := devices.rollbackTransaction(); err != nil {
|
|
| 1427 |
- return fmt.Errorf("devmapper: Rolling back open transaction failed: %s", err)
|
|
| 1428 |
- } |
|
| 1429 |
- |
|
| 1430 |
- devices.OpenTransactionID = devices.TransactionID |
|
| 1431 |
- return nil |
|
| 1432 |
-} |
|
| 1433 |
- |
|
| 1434 |
-func (devices *DeviceSet) loadDeviceSetMetaData() error {
|
|
| 1435 |
- jsonData, err := os.ReadFile(devices.deviceSetMetaFile()) |
|
| 1436 |
- if err != nil {
|
|
| 1437 |
- // For backward compatibility return success if file does |
|
| 1438 |
- // not exist. |
|
| 1439 |
- if os.IsNotExist(err) {
|
|
| 1440 |
- return nil |
|
| 1441 |
- } |
|
| 1442 |
- return err |
|
| 1443 |
- } |
|
| 1444 |
- |
|
| 1445 |
- return json.Unmarshal(jsonData, devices) |
|
| 1446 |
-} |
|
| 1447 |
- |
|
| 1448 |
-func (devices *DeviceSet) saveDeviceSetMetaData() error {
|
|
| 1449 |
- jsonData, err := json.Marshal(devices) |
|
| 1450 |
- if err != nil {
|
|
| 1451 |
- return fmt.Errorf("devmapper: Error encoding metadata to json: %s", err)
|
|
| 1452 |
- } |
|
| 1453 |
- |
|
| 1454 |
- return devices.writeMetaFile(jsonData, devices.deviceSetMetaFile()) |
|
| 1455 |
-} |
|
| 1456 |
- |
|
| 1457 |
-func (devices *DeviceSet) openTransaction(hash string, DeviceID int) error {
|
|
| 1458 |
- devices.allocateTransactionID() |
|
| 1459 |
- devices.DeviceIDHash = hash |
|
| 1460 |
- devices.DeviceID = DeviceID |
|
| 1461 |
- if err := devices.saveTransactionMetaData(); err != nil {
|
|
| 1462 |
- return fmt.Errorf("devmapper: Error saving transaction metadata: %s", err)
|
|
| 1463 |
- } |
|
| 1464 |
- return nil |
|
| 1465 |
-} |
|
| 1466 |
- |
|
| 1467 |
-func (devices *DeviceSet) refreshTransaction(DeviceID int) error {
|
|
| 1468 |
- devices.DeviceID = DeviceID |
|
| 1469 |
- if err := devices.saveTransactionMetaData(); err != nil {
|
|
| 1470 |
- return fmt.Errorf("devmapper: Error saving transaction metadata: %s", err)
|
|
| 1471 |
- } |
|
| 1472 |
- return nil |
|
| 1473 |
-} |
|
| 1474 |
- |
|
| 1475 |
-func (devices *DeviceSet) closeTransaction() error {
|
|
| 1476 |
- if err := devices.updatePoolTransactionID(); err != nil {
|
|
| 1477 |
- logrus.WithField("storage-driver", "devicemapper").Debug("Failed to close Transaction")
|
|
| 1478 |
- return err |
|
| 1479 |
- } |
|
| 1480 |
- return nil |
|
| 1481 |
-} |
|
| 1482 |
- |
|
| 1483 |
-func determineDriverCapabilities(version string) error {
|
|
| 1484 |
- // Kernel driver version >= 4.27.0 support deferred removal |
|
| 1485 |
- |
|
| 1486 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("kernel dm driver version is %s", version)
|
|
| 1487 |
- |
|
| 1488 |
- versionSplit := strings.Split(version, ".") |
|
| 1489 |
- major, err := strconv.Atoi(versionSplit[0]) |
|
| 1490 |
- if err != nil {
|
|
| 1491 |
- return graphdriver.ErrNotSupported |
|
| 1492 |
- } |
|
| 1493 |
- |
|
| 1494 |
- if major > 4 {
|
|
| 1495 |
- driverDeferredRemovalSupport = true |
|
| 1496 |
- return nil |
|
| 1497 |
- } |
|
| 1498 |
- |
|
| 1499 |
- if major < 4 {
|
|
| 1500 |
- return nil |
|
| 1501 |
- } |
|
| 1502 |
- |
|
| 1503 |
- minor, err := strconv.Atoi(versionSplit[1]) |
|
| 1504 |
- if err != nil {
|
|
| 1505 |
- return graphdriver.ErrNotSupported |
|
| 1506 |
- } |
|
| 1507 |
- |
|
| 1508 |
- /* |
|
| 1509 |
- * If major is 4 and minor is 27, then there is no need to |
|
| 1510 |
- * check for patch level as it can not be less than 0. |
|
| 1511 |
- */ |
|
| 1512 |
- if minor >= 27 {
|
|
| 1513 |
- driverDeferredRemovalSupport = true |
|
| 1514 |
- return nil |
|
| 1515 |
- } |
|
| 1516 |
- |
|
| 1517 |
- return nil |
|
| 1518 |
-} |
|
| 1519 |
- |
|
| 1520 |
-// Determine the major and minor number of loopback device |
|
| 1521 |
-func getDeviceMajorMinor(file *os.File) (uint64, uint64, error) {
|
|
| 1522 |
- var stat unix.Stat_t |
|
| 1523 |
- err := unix.Stat(file.Name(), &stat) |
|
| 1524 |
- if err != nil {
|
|
| 1525 |
- return 0, 0, err |
|
| 1526 |
- } |
|
| 1527 |
- |
|
| 1528 |
- // the type is 32bit on mips |
|
| 1529 |
- dev := uint64(stat.Rdev) //nolint: unconvert |
|
| 1530 |
- majorNum := major(dev) |
|
| 1531 |
- minorNum := minor(dev) |
|
| 1532 |
- |
|
| 1533 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("Major:Minor for device: %s is:%v:%v", file.Name(), majorNum, minorNum)
|
|
| 1534 |
- return majorNum, minorNum, nil |
|
| 1535 |
-} |
|
| 1536 |
- |
|
| 1537 |
-// Given a file which is backing file of a loop back device, find the |
|
| 1538 |
-// loopback device name and its major/minor number. |
|
| 1539 |
-func getLoopFileDeviceMajMin(filename string) (string, uint64, uint64, error) {
|
|
| 1540 |
- file, err := os.Open(filename) |
|
| 1541 |
- if err != nil {
|
|
| 1542 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("Failed to open file %s", filename)
|
|
| 1543 |
- return "", 0, 0, err |
|
| 1544 |
- } |
|
| 1545 |
- |
|
| 1546 |
- defer file.Close() |
|
| 1547 |
- loopbackDevice := loopback.FindLoopDeviceFor(file) |
|
| 1548 |
- if loopbackDevice == nil {
|
|
| 1549 |
- return "", 0, 0, fmt.Errorf("devmapper: Unable to find loopback mount for: %s", filename)
|
|
| 1550 |
- } |
|
| 1551 |
- defer loopbackDevice.Close() |
|
| 1552 |
- |
|
| 1553 |
- Major, Minor, err := getDeviceMajorMinor(loopbackDevice) |
|
| 1554 |
- if err != nil {
|
|
| 1555 |
- return "", 0, 0, err |
|
| 1556 |
- } |
|
| 1557 |
- return loopbackDevice.Name(), Major, Minor, nil |
|
| 1558 |
-} |
|
| 1559 |
- |
|
| 1560 |
-// Get the major/minor numbers of thin pool data and metadata devices |
|
| 1561 |
-func (devices *DeviceSet) getThinPoolDataMetaMajMin() (uint64, uint64, uint64, uint64, error) {
|
|
| 1562 |
- var params, poolDataMajMin, poolMetadataMajMin string |
|
| 1563 |
- |
|
| 1564 |
- _, _, _, params, err := devicemapper.GetTable(devices.getPoolName()) |
|
| 1565 |
- if err != nil {
|
|
| 1566 |
- return 0, 0, 0, 0, err |
|
| 1567 |
- } |
|
| 1568 |
- |
|
| 1569 |
- if _, err = fmt.Sscanf(params, "%s %s", &poolMetadataMajMin, &poolDataMajMin); err != nil {
|
|
| 1570 |
- return 0, 0, 0, 0, err |
|
| 1571 |
- } |
|
| 1572 |
- |
|
| 1573 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("poolDataMajMin=%s poolMetaMajMin=%s\n", poolDataMajMin, poolMetadataMajMin)
|
|
| 1574 |
- |
|
| 1575 |
- poolDataMajMinorSplit := strings.Split(poolDataMajMin, ":") |
|
| 1576 |
- poolDataMajor, err := strconv.ParseUint(poolDataMajMinorSplit[0], 10, 32) |
|
| 1577 |
- if err != nil {
|
|
| 1578 |
- return 0, 0, 0, 0, err |
|
| 1579 |
- } |
|
| 1580 |
- |
|
| 1581 |
- poolDataMinor, err := strconv.ParseUint(poolDataMajMinorSplit[1], 10, 32) |
|
| 1582 |
- if err != nil {
|
|
| 1583 |
- return 0, 0, 0, 0, err |
|
| 1584 |
- } |
|
| 1585 |
- |
|
| 1586 |
- poolMetadataMajMinorSplit := strings.Split(poolMetadataMajMin, ":") |
|
| 1587 |
- poolMetadataMajor, err := strconv.ParseUint(poolMetadataMajMinorSplit[0], 10, 32) |
|
| 1588 |
- if err != nil {
|
|
| 1589 |
- return 0, 0, 0, 0, err |
|
| 1590 |
- } |
|
| 1591 |
- |
|
| 1592 |
- poolMetadataMinor, err := strconv.ParseUint(poolMetadataMajMinorSplit[1], 10, 32) |
|
| 1593 |
- if err != nil {
|
|
| 1594 |
- return 0, 0, 0, 0, err |
|
| 1595 |
- } |
|
| 1596 |
- |
|
| 1597 |
- return poolDataMajor, poolDataMinor, poolMetadataMajor, poolMetadataMinor, nil |
|
| 1598 |
-} |
|
| 1599 |
- |
|
| 1600 |
-func (devices *DeviceSet) loadThinPoolLoopBackInfo() error {
|
|
| 1601 |
- poolDataMajor, poolDataMinor, poolMetadataMajor, poolMetadataMinor, err := devices.getThinPoolDataMetaMajMin() |
|
| 1602 |
- if err != nil {
|
|
| 1603 |
- return err |
|
| 1604 |
- } |
|
| 1605 |
- |
|
| 1606 |
- dirname := devices.loopbackDir() |
|
| 1607 |
- |
|
| 1608 |
- // data device has not been passed in. So there should be a data file |
|
| 1609 |
- // which is being mounted as loop device. |
|
| 1610 |
- if devices.dataDevice == "" {
|
|
| 1611 |
- datafilename := path.Join(dirname, "data") |
|
| 1612 |
- dataLoopDevice, dataMajor, dataMinor, err := getLoopFileDeviceMajMin(datafilename) |
|
| 1613 |
- if err != nil {
|
|
| 1614 |
- return err |
|
| 1615 |
- } |
|
| 1616 |
- |
|
| 1617 |
- // Compare the two |
|
| 1618 |
- if poolDataMajor == dataMajor && poolDataMinor == dataMinor {
|
|
| 1619 |
- devices.dataDevice = dataLoopDevice |
|
| 1620 |
- devices.dataLoopFile = datafilename |
|
| 1621 |
- } |
|
| 1622 |
- } |
|
| 1623 |
- |
|
| 1624 |
- // metadata device has not been passed in. So there should be a |
|
| 1625 |
- // metadata file which is being mounted as loop device. |
|
| 1626 |
- if devices.metadataDevice == "" {
|
|
| 1627 |
- metadatafilename := path.Join(dirname, "metadata") |
|
| 1628 |
- metadataLoopDevice, metadataMajor, metadataMinor, err := getLoopFileDeviceMajMin(metadatafilename) |
|
| 1629 |
- if err != nil {
|
|
| 1630 |
- return err |
|
| 1631 |
- } |
|
| 1632 |
- if poolMetadataMajor == metadataMajor && poolMetadataMinor == metadataMinor {
|
|
| 1633 |
- devices.metadataDevice = metadataLoopDevice |
|
| 1634 |
- devices.metadataLoopFile = metadatafilename |
|
| 1635 |
- } |
|
| 1636 |
- } |
|
| 1637 |
- |
|
| 1638 |
- return nil |
|
| 1639 |
-} |
|
| 1640 |
- |
|
| 1641 |
-func (devices *DeviceSet) enableDeferredRemovalDeletion() error {
|
|
| 1642 |
- // If user asked for deferred removal then check both libdm library |
|
| 1643 |
- // and kernel driver support deferred removal otherwise error out. |
|
| 1644 |
- if enableDeferredRemoval {
|
|
| 1645 |
- if !driverDeferredRemovalSupport {
|
|
| 1646 |
- return fmt.Errorf("devmapper: Deferred removal can not be enabled as kernel does not support it")
|
|
| 1647 |
- } |
|
| 1648 |
- if !devicemapper.LibraryDeferredRemovalSupport {
|
|
| 1649 |
- return fmt.Errorf("devmapper: Deferred removal can not be enabled as libdm does not support it")
|
|
| 1650 |
- } |
|
| 1651 |
- logrus.WithField("storage-driver", "devicemapper").Debug("Deferred removal support enabled.")
|
|
| 1652 |
- devices.deferredRemove = true |
|
| 1653 |
- } |
|
| 1654 |
- |
|
| 1655 |
- if enableDeferredDeletion {
|
|
| 1656 |
- if !devices.deferredRemove {
|
|
| 1657 |
- return fmt.Errorf("devmapper: Deferred deletion can not be enabled as deferred removal is not enabled. Enable deferred removal using --storage-opt dm.use_deferred_removal=true parameter")
|
|
| 1658 |
- } |
|
| 1659 |
- logrus.WithField("storage-driver", "devicemapper").Debug("Deferred deletion support enabled.")
|
|
| 1660 |
- devices.deferredDelete = true |
|
| 1661 |
- } |
|
| 1662 |
- return nil |
|
| 1663 |
-} |
|
| 1664 |
- |
|
| 1665 |
-func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) {
|
|
| 1666 |
- if err := devices.enableDeferredRemovalDeletion(); err != nil {
|
|
| 1667 |
- return err |
|
| 1668 |
- } |
|
| 1669 |
- |
|
| 1670 |
- logger := logrus.WithField("storage-driver", "devicemapper")
|
|
| 1671 |
- |
|
| 1672 |
- // https://github.com/docker/docker/issues/4036 |
|
| 1673 |
- if supported := devicemapper.UdevSetSyncSupport(true); !supported {
|
|
| 1674 |
- logger.Error("Udev sync is not supported, which will lead to data loss and unexpected behavior. Make sure you have a recent version of libdevmapper installed and are running a dynamic binary, or select a different storage driver. For more information, see https://docs.docker.com/go/storage-driver/")
|
|
| 1675 |
- if !devices.overrideUdevSyncCheck {
|
|
| 1676 |
- return graphdriver.ErrNotSupported |
|
| 1677 |
- } |
|
| 1678 |
- } |
|
| 1679 |
- |
|
| 1680 |
- // create the root dir of the devmapper driver ownership to match this |
|
| 1681 |
- // daemon's remapped root uid/gid so containers can start properly |
|
| 1682 |
- if err := idtools.MkdirAndChown(devices.root, 0700, devices.idMap.RootPair()); err != nil {
|
|
| 1683 |
- return err |
|
| 1684 |
- } |
|
| 1685 |
- if err := os.MkdirAll(devices.metadataDir(), 0700); err != nil {
|
|
| 1686 |
- return err |
|
| 1687 |
- } |
|
| 1688 |
- |
|
| 1689 |
- prevSetupConfig, err := readLVMConfig(devices.root) |
|
| 1690 |
- if err != nil {
|
|
| 1691 |
- return err |
|
| 1692 |
- } |
|
| 1693 |
- |
|
| 1694 |
- if !reflect.DeepEqual(devices.lvmSetupConfig, directLVMConfig{}) {
|
|
| 1695 |
- if devices.thinPoolDevice != "" {
|
|
| 1696 |
- return errors.New("cannot setup direct-lvm when `dm.thinpooldev` is also specified")
|
|
| 1697 |
- } |
|
| 1698 |
- |
|
| 1699 |
- if !reflect.DeepEqual(prevSetupConfig, devices.lvmSetupConfig) {
|
|
| 1700 |
- if !reflect.DeepEqual(prevSetupConfig, directLVMConfig{}) {
|
|
| 1701 |
- return errors.New("changing direct-lvm config is not supported")
|
|
| 1702 |
- } |
|
| 1703 |
- logger.WithField("direct-lvm-config", devices.lvmSetupConfig).Debugf("Setting up direct lvm mode")
|
|
| 1704 |
- if err := verifyBlockDevice(devices.lvmSetupConfig.Device, lvmSetupConfigForce); err != nil {
|
|
| 1705 |
- return err |
|
| 1706 |
- } |
|
| 1707 |
- if err := setupDirectLVM(devices.lvmSetupConfig); err != nil {
|
|
| 1708 |
- return err |
|
| 1709 |
- } |
|
| 1710 |
- if err := writeLVMConfig(devices.root, devices.lvmSetupConfig); err != nil {
|
|
| 1711 |
- return err |
|
| 1712 |
- } |
|
| 1713 |
- } |
|
| 1714 |
- devices.thinPoolDevice = "docker-thinpool" |
|
| 1715 |
- logger.Debugf("Setting dm.thinpooldev to %q", devices.thinPoolDevice)
|
|
| 1716 |
- } |
|
| 1717 |
- |
|
| 1718 |
- // Set the device prefix from the device id and inode of the docker root dir |
|
| 1719 |
- var st unix.Stat_t |
|
| 1720 |
- if err := unix.Stat(devices.root, &st); err != nil {
|
|
| 1721 |
- return fmt.Errorf("devmapper: Error looking up dir %s: %s", devices.root, err)
|
|
| 1722 |
- } |
|
| 1723 |
- // "reg-" stands for "regular file". |
|
| 1724 |
- // In the future we might use "dev-" for "device file", etc. |
|
| 1725 |
- // docker-maj,min[-inode] stands for: |
|
| 1726 |
- // - Managed by docker |
|
| 1727 |
- // - The target of this device is at major <maj> and minor <min> |
|
| 1728 |
- // - If <inode> is defined, use that file inside the device as a loopback image. Otherwise use the device itself. |
|
| 1729 |
- // The type Dev in Stat_t is 32bit on mips. |
|
| 1730 |
- devices.devicePrefix = fmt.Sprintf("docker-%d:%d-%d", major(uint64(st.Dev)), minor(uint64(st.Dev)), st.Ino) //nolint: unconvert
|
|
| 1731 |
- logger.Debugf("Generated prefix: %s", devices.devicePrefix)
|
|
| 1732 |
- |
|
| 1733 |
- // Check for the existence of the thin-pool device |
|
| 1734 |
- poolExists, err := devices.thinPoolExists(devices.getPoolName()) |
|
| 1735 |
- if err != nil {
|
|
| 1736 |
- return err |
|
| 1737 |
- } |
|
| 1738 |
- |
|
| 1739 |
- // It seems libdevmapper opens this without O_CLOEXEC, and go exec will not close files |
|
| 1740 |
- // that are not Close-on-exec, |
|
| 1741 |
- // so we add this badhack to make sure it closes itself |
|
| 1742 |
- setCloseOnExec("/dev/mapper/control")
|
|
| 1743 |
- |
|
| 1744 |
- // Make sure the sparse images exist in <root>/devicemapper/data and |
|
| 1745 |
- // <root>/devicemapper/metadata |
|
| 1746 |
- |
|
| 1747 |
- createdLoopback := false |
|
| 1748 |
- |
|
| 1749 |
- // If the pool doesn't exist, create it |
|
| 1750 |
- if !poolExists && devices.thinPoolDevice == "" {
|
|
| 1751 |
- logger.Debug("Pool doesn't exist. Creating it.")
|
|
| 1752 |
- |
|
| 1753 |
- var ( |
|
| 1754 |
- dataFile *os.File |
|
| 1755 |
- metadataFile *os.File |
|
| 1756 |
- ) |
|
| 1757 |
- |
|
| 1758 |
- if devices.dataDevice == "" {
|
|
| 1759 |
- // Make sure the sparse images exist in <root>/devicemapper/data |
|
| 1760 |
- |
|
| 1761 |
- hasData := devices.hasImage("data")
|
|
| 1762 |
- |
|
| 1763 |
- if !doInit && !hasData {
|
|
| 1764 |
- return errors.New("loopback data file not found")
|
|
| 1765 |
- } |
|
| 1766 |
- |
|
| 1767 |
- if !hasData {
|
|
| 1768 |
- createdLoopback = true |
|
| 1769 |
- } |
|
| 1770 |
- |
|
| 1771 |
- data, err := devices.ensureImage("data", devices.dataLoopbackSize)
|
|
| 1772 |
- if err != nil {
|
|
| 1773 |
- logger.Debugf("Error device ensureImage (data): %s", err)
|
|
| 1774 |
- return err |
|
| 1775 |
- } |
|
| 1776 |
- |
|
| 1777 |
- dataFile, err = loopback.AttachLoopDevice(data) |
|
| 1778 |
- if err != nil {
|
|
| 1779 |
- return err |
|
| 1780 |
- } |
|
| 1781 |
- devices.dataLoopFile = data |
|
| 1782 |
- devices.dataDevice = dataFile.Name() |
|
| 1783 |
- } else {
|
|
| 1784 |
- dataFile, err = os.OpenFile(devices.dataDevice, os.O_RDWR, 0600) |
|
| 1785 |
- if err != nil {
|
|
| 1786 |
- return err |
|
| 1787 |
- } |
|
| 1788 |
- } |
|
| 1789 |
- defer dataFile.Close() |
|
| 1790 |
- |
|
| 1791 |
- if devices.metadataDevice == "" {
|
|
| 1792 |
- // Make sure the sparse images exist in <root>/devicemapper/metadata |
|
| 1793 |
- |
|
| 1794 |
- hasMetadata := devices.hasImage("metadata")
|
|
| 1795 |
- |
|
| 1796 |
- if !doInit && !hasMetadata {
|
|
| 1797 |
- return errors.New("loopback metadata file not found")
|
|
| 1798 |
- } |
|
| 1799 |
- |
|
| 1800 |
- if !hasMetadata {
|
|
| 1801 |
- createdLoopback = true |
|
| 1802 |
- } |
|
| 1803 |
- |
|
| 1804 |
- metadata, err := devices.ensureImage("metadata", devices.metaDataLoopbackSize)
|
|
| 1805 |
- if err != nil {
|
|
| 1806 |
- logger.Debugf("Error device ensureImage (metadata): %s", err)
|
|
| 1807 |
- return err |
|
| 1808 |
- } |
|
| 1809 |
- |
|
| 1810 |
- metadataFile, err = loopback.AttachLoopDevice(metadata) |
|
| 1811 |
- if err != nil {
|
|
| 1812 |
- return err |
|
| 1813 |
- } |
|
| 1814 |
- devices.metadataLoopFile = metadata |
|
| 1815 |
- devices.metadataDevice = metadataFile.Name() |
|
| 1816 |
- } else {
|
|
| 1817 |
- metadataFile, err = os.OpenFile(devices.metadataDevice, os.O_RDWR, 0600) |
|
| 1818 |
- if err != nil {
|
|
| 1819 |
- return err |
|
| 1820 |
- } |
|
| 1821 |
- } |
|
| 1822 |
- defer metadataFile.Close() |
|
| 1823 |
- |
|
| 1824 |
- if err := devicemapper.CreatePool(devices.getPoolName(), dataFile, metadataFile, devices.thinpBlockSize); err != nil {
|
|
| 1825 |
- return err |
|
| 1826 |
- } |
|
| 1827 |
- defer func() {
|
|
| 1828 |
- if retErr != nil {
|
|
| 1829 |
- err = devices.deactivatePool() |
|
| 1830 |
- if err != nil {
|
|
| 1831 |
- logger.Warnf("Failed to deactivatePool: %v", err)
|
|
| 1832 |
- } |
|
| 1833 |
- } |
|
| 1834 |
- }() |
|
| 1835 |
- } |
|
| 1836 |
- |
|
| 1837 |
- // Pool already exists and caller did not pass us a pool. That means |
|
| 1838 |
- // we probably created pool earlier and could not remove it as some |
|
| 1839 |
- // containers were still using it. Detect some of the properties of |
|
| 1840 |
- // pool, like is it using loop devices. |
|
| 1841 |
- if poolExists && devices.thinPoolDevice == "" {
|
|
| 1842 |
- if err := devices.loadThinPoolLoopBackInfo(); err != nil {
|
|
| 1843 |
- logger.Debugf("Failed to load thin pool loopback device information:%v", err)
|
|
| 1844 |
- return err |
|
| 1845 |
- } |
|
| 1846 |
- } |
|
| 1847 |
- |
|
| 1848 |
- // If we didn't just create the data or metadata image, we need to |
|
| 1849 |
- // load the transaction id and migrate old metadata |
|
| 1850 |
- if !createdLoopback {
|
|
| 1851 |
- if err := devices.initMetaData(); err != nil {
|
|
| 1852 |
- return err |
|
| 1853 |
- } |
|
| 1854 |
- } |
|
| 1855 |
- |
|
| 1856 |
- if devices.thinPoolDevice == "" {
|
|
| 1857 |
- if devices.metadataLoopFile != "" || devices.dataLoopFile != "" {
|
|
| 1858 |
- logger.Warn("Usage of loopback devices is strongly discouraged for production use. Please use `--storage-opt dm.thinpooldev` or use `man dockerd` to refer to dm.thinpooldev section.")
|
|
| 1859 |
- } |
|
| 1860 |
- } |
|
| 1861 |
- |
|
| 1862 |
- // Right now this loads only NextDeviceID. If there is more metadata |
|
| 1863 |
- // down the line, we might have to move it earlier. |
|
| 1864 |
- if err := devices.loadDeviceSetMetaData(); err != nil {
|
|
| 1865 |
- return err |
|
| 1866 |
- } |
|
| 1867 |
- |
|
| 1868 |
- // Setup the base image |
|
| 1869 |
- if doInit {
|
|
| 1870 |
- if err := devices.setupBaseImage(); err != nil {
|
|
| 1871 |
- logger.Debugf("Error device setupBaseImage: %s", err)
|
|
| 1872 |
- return err |
|
| 1873 |
- } |
|
| 1874 |
- } |
|
| 1875 |
- |
|
| 1876 |
- return nil |
|
| 1877 |
-} |
|
| 1878 |
- |
|
| 1879 |
-// AddDevice adds a device and registers in the hash. |
|
| 1880 |
-func (devices *DeviceSet) AddDevice(hash, baseHash string, storageOpt map[string]string) error {
|
|
| 1881 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("AddDevice START(hash=%s basehash=%s)", hash, baseHash)
|
|
| 1882 |
- defer logrus.WithField("storage-driver", "devicemapper").Debugf("AddDevice END(hash=%s basehash=%s)", hash, baseHash)
|
|
| 1883 |
- |
|
| 1884 |
- // If a deleted device exists, return error. |
|
| 1885 |
- baseInfo, err := devices.lookupDeviceWithLock(baseHash) |
|
| 1886 |
- if err != nil {
|
|
| 1887 |
- return err |
|
| 1888 |
- } |
|
| 1889 |
- |
|
| 1890 |
- if baseInfo.Deleted {
|
|
| 1891 |
- return fmt.Errorf("devmapper: Base device %v has been marked for deferred deletion", baseInfo.Hash)
|
|
| 1892 |
- } |
|
| 1893 |
- |
|
| 1894 |
- baseInfo.lock.Lock() |
|
| 1895 |
- defer baseInfo.lock.Unlock() |
|
| 1896 |
- |
|
| 1897 |
- devices.Lock() |
|
| 1898 |
- defer devices.Unlock() |
|
| 1899 |
- |
|
| 1900 |
- // Also include deleted devices in case hash of new device is |
|
| 1901 |
- // same as one of the deleted devices. |
|
| 1902 |
- if info, _ := devices.lookupDevice(hash); info != nil {
|
|
| 1903 |
- return fmt.Errorf("devmapper: device %s already exists. Deleted=%v", hash, info.Deleted)
|
|
| 1904 |
- } |
|
| 1905 |
- |
|
| 1906 |
- size, err := devices.parseStorageOpt(storageOpt) |
|
| 1907 |
- if err != nil {
|
|
| 1908 |
- return err |
|
| 1909 |
- } |
|
| 1910 |
- |
|
| 1911 |
- if size == 0 {
|
|
| 1912 |
- size = baseInfo.Size |
|
| 1913 |
- } |
|
| 1914 |
- |
|
| 1915 |
- if size < baseInfo.Size {
|
|
| 1916 |
- return fmt.Errorf("devmapper: Container size cannot be smaller than %s", units.HumanSize(float64(baseInfo.Size)))
|
|
| 1917 |
- } |
|
| 1918 |
- |
|
| 1919 |
- if err := devices.takeSnapshot(hash, baseInfo, size); err != nil {
|
|
| 1920 |
- return err |
|
| 1921 |
- } |
|
| 1922 |
- |
|
| 1923 |
- // Grow the container rootfs. |
|
| 1924 |
- if size > baseInfo.Size {
|
|
| 1925 |
- info, err := devices.lookupDevice(hash) |
|
| 1926 |
- if err != nil {
|
|
| 1927 |
- return err |
|
| 1928 |
- } |
|
| 1929 |
- |
|
| 1930 |
- if err := devices.growFS(info); err != nil {
|
|
| 1931 |
- return err |
|
| 1932 |
- } |
|
| 1933 |
- } |
|
| 1934 |
- |
|
| 1935 |
- return nil |
|
| 1936 |
-} |
|
| 1937 |
- |
|
| 1938 |
-func (devices *DeviceSet) parseStorageOpt(storageOpt map[string]string) (uint64, error) {
|
|
| 1939 |
- // Read size to change the block device size per container. |
|
| 1940 |
- for key, val := range storageOpt {
|
|
| 1941 |
- key := strings.ToLower(key) |
|
| 1942 |
- switch key {
|
|
| 1943 |
- case "size": |
|
| 1944 |
- size, err := units.RAMInBytes(val) |
|
| 1945 |
- if err != nil {
|
|
| 1946 |
- return 0, err |
|
| 1947 |
- } |
|
| 1948 |
- return uint64(size), nil |
|
| 1949 |
- default: |
|
| 1950 |
- return 0, fmt.Errorf("Unknown option %s", key)
|
|
| 1951 |
- } |
|
| 1952 |
- } |
|
| 1953 |
- |
|
| 1954 |
- return 0, nil |
|
| 1955 |
-} |
|
| 1956 |
- |
|
| 1957 |
-func (devices *DeviceSet) markForDeferredDeletion(info *devInfo) error {
|
|
| 1958 |
- // If device is already in deleted state, there is nothing to be done. |
|
| 1959 |
- if info.Deleted {
|
|
| 1960 |
- return nil |
|
| 1961 |
- } |
|
| 1962 |
- |
|
| 1963 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("Marking device %s for deferred deletion.", info.Hash)
|
|
| 1964 |
- |
|
| 1965 |
- info.Deleted = true |
|
| 1966 |
- |
|
| 1967 |
- // save device metadata to reflect deleted state. |
|
| 1968 |
- if err := devices.saveMetadata(info); err != nil {
|
|
| 1969 |
- info.Deleted = false |
|
| 1970 |
- return err |
|
| 1971 |
- } |
|
| 1972 |
- |
|
| 1973 |
- devices.nrDeletedDevices++ |
|
| 1974 |
- return nil |
|
| 1975 |
-} |
|
| 1976 |
- |
|
| 1977 |
-// Should be called with devices.Lock() held. |
|
| 1978 |
-func (devices *DeviceSet) deleteTransaction(info *devInfo, syncDelete bool) error {
|
|
| 1979 |
- if err := devices.openTransaction(info.Hash, info.DeviceID); err != nil {
|
|
| 1980 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("Error opening transaction hash = %s deviceId = %d", "", info.DeviceID)
|
|
| 1981 |
- return err |
|
| 1982 |
- } |
|
| 1983 |
- |
|
| 1984 |
- defer devices.closeTransaction() |
|
| 1985 |
- |
|
| 1986 |
- err := devicemapper.DeleteDevice(devices.getPoolDevName(), info.DeviceID) |
|
| 1987 |
- if err != nil {
|
|
| 1988 |
- // If syncDelete is true, we want to return error. If deferred |
|
| 1989 |
- // deletion is not enabled, we return an error. If error is |
|
| 1990 |
- // something other then EBUSY, return an error. |
|
| 1991 |
- if syncDelete || !devices.deferredDelete || err != devicemapper.ErrBusy {
|
|
| 1992 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("Error deleting device: %s", err)
|
|
| 1993 |
- return err |
|
| 1994 |
- } |
|
| 1995 |
- } |
|
| 1996 |
- |
|
| 1997 |
- if err == nil {
|
|
| 1998 |
- if err := devices.unregisterDevice(info.Hash); err != nil {
|
|
| 1999 |
- return err |
|
| 2000 |
- } |
|
| 2001 |
- // If device was already in deferred delete state that means |
|
| 2002 |
- // deletion was being tried again later. Reduce the deleted |
|
| 2003 |
- // device count. |
|
| 2004 |
- if info.Deleted {
|
|
| 2005 |
- devices.nrDeletedDevices-- |
|
| 2006 |
- } |
|
| 2007 |
- devices.markDeviceIDFree(info.DeviceID) |
|
| 2008 |
- } else {
|
|
| 2009 |
- if err := devices.markForDeferredDeletion(info); err != nil {
|
|
| 2010 |
- return err |
|
| 2011 |
- } |
|
| 2012 |
- } |
|
| 2013 |
- |
|
| 2014 |
- return nil |
|
| 2015 |
-} |
|
| 2016 |
- |
|
| 2017 |
-// Issue discard only if device open count is zero. |
|
| 2018 |
-func (devices *DeviceSet) issueDiscard(info *devInfo) error {
|
|
| 2019 |
- logger := logrus.WithField("storage-driver", "devicemapper")
|
|
| 2020 |
- logger.Debugf("issueDiscard START(device: %s).", info.Hash)
|
|
| 2021 |
- defer logger.Debugf("issueDiscard END(device: %s).", info.Hash)
|
|
| 2022 |
- // This is a workaround for the kernel not discarding block so |
|
| 2023 |
- // on the thin pool when we remove a thinp device, so we do it |
|
| 2024 |
- // manually. |
|
| 2025 |
- // Even if device is deferred deleted, activate it and issue |
|
| 2026 |
- // discards. |
|
| 2027 |
- if err := devices.activateDeviceIfNeeded(info, true); err != nil {
|
|
| 2028 |
- return err |
|
| 2029 |
- } |
|
| 2030 |
- |
|
| 2031 |
- devinfo, err := devicemapper.GetInfo(info.Name()) |
|
| 2032 |
- if err != nil {
|
|
| 2033 |
- return err |
|
| 2034 |
- } |
|
| 2035 |
- |
|
| 2036 |
- if devinfo.OpenCount != 0 {
|
|
| 2037 |
- logger.Debugf("Device: %s is in use. OpenCount=%d. Not issuing discards.", info.Hash, devinfo.OpenCount)
|
|
| 2038 |
- return nil |
|
| 2039 |
- } |
|
| 2040 |
- |
|
| 2041 |
- if err := devicemapper.BlockDeviceDiscard(info.DevName()); err != nil {
|
|
| 2042 |
- logger.Debugf("Error discarding block on device: %s (ignoring)", err)
|
|
| 2043 |
- } |
|
| 2044 |
- return nil |
|
| 2045 |
-} |
|
| 2046 |
- |
|
| 2047 |
-// Should be called with devices.Lock() held. |
|
| 2048 |
-func (devices *DeviceSet) deleteDevice(info *devInfo, syncDelete bool) error {
|
|
| 2049 |
- if devices.doBlkDiscard {
|
|
| 2050 |
- devices.issueDiscard(info) |
|
| 2051 |
- } |
|
| 2052 |
- |
|
| 2053 |
- // Try to deactivate device in case it is active. |
|
| 2054 |
- // If deferred removal is enabled and deferred deletion is disabled |
|
| 2055 |
- // then make sure device is removed synchronously. There have been |
|
| 2056 |
- // some cases of device being busy for short duration and we would |
|
| 2057 |
- // rather busy wait for device removal to take care of these cases. |
|
| 2058 |
- deferredRemove := devices.deferredRemove |
|
| 2059 |
- if !devices.deferredDelete {
|
|
| 2060 |
- deferredRemove = false |
|
| 2061 |
- } |
|
| 2062 |
- |
|
| 2063 |
- if err := devices.deactivateDeviceMode(info, deferredRemove); err != nil {
|
|
| 2064 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("Error deactivating device: %s", err)
|
|
| 2065 |
- return err |
|
| 2066 |
- } |
|
| 2067 |
- |
|
| 2068 |
- return devices.deleteTransaction(info, syncDelete) |
|
| 2069 |
-} |
|
| 2070 |
- |
|
| 2071 |
-// DeleteDevice will return success if device has been marked for deferred |
|
| 2072 |
-// removal. If one wants to override that and want DeleteDevice() to fail if |
|
| 2073 |
-// device was busy and could not be deleted, set syncDelete=true. |
|
| 2074 |
-func (devices *DeviceSet) DeleteDevice(hash string, syncDelete bool) error {
|
|
| 2075 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("DeleteDevice START(hash=%v syncDelete=%v)", hash, syncDelete)
|
|
| 2076 |
- defer logrus.WithField("storage-driver", "devicemapper").Debugf("DeleteDevice END(hash=%v syncDelete=%v)", hash, syncDelete)
|
|
| 2077 |
- info, err := devices.lookupDeviceWithLock(hash) |
|
| 2078 |
- if err != nil {
|
|
| 2079 |
- return err |
|
| 2080 |
- } |
|
| 2081 |
- |
|
| 2082 |
- info.lock.Lock() |
|
| 2083 |
- defer info.lock.Unlock() |
|
| 2084 |
- |
|
| 2085 |
- devices.Lock() |
|
| 2086 |
- defer devices.Unlock() |
|
| 2087 |
- |
|
| 2088 |
- return devices.deleteDevice(info, syncDelete) |
|
| 2089 |
-} |
|
| 2090 |
- |
|
| 2091 |
-func (devices *DeviceSet) deactivatePool() error {
|
|
| 2092 |
- logrus.WithField("storage-driver", "devicemapper").Debug("deactivatePool() START")
|
|
| 2093 |
- defer logrus.WithField("storage-driver", "devicemapper").Debug("deactivatePool() END")
|
|
| 2094 |
- devname := devices.getPoolDevName() |
|
| 2095 |
- |
|
| 2096 |
- devinfo, err := devicemapper.GetInfo(devname) |
|
| 2097 |
- if err != nil {
|
|
| 2098 |
- return err |
|
| 2099 |
- } |
|
| 2100 |
- |
|
| 2101 |
- if devinfo.Exists == 0 {
|
|
| 2102 |
- return nil |
|
| 2103 |
- } |
|
| 2104 |
- if err := devicemapper.RemoveDevice(devname); err != nil {
|
|
| 2105 |
- return err |
|
| 2106 |
- } |
|
| 2107 |
- |
|
| 2108 |
- if d, err := devicemapper.GetDeps(devname); err == nil {
|
|
| 2109 |
- logrus.WithField("storage-driver", "devicemapper").Warnf("device %s still has %d active dependents", devname, d.Count)
|
|
| 2110 |
- } |
|
| 2111 |
- |
|
| 2112 |
- return nil |
|
| 2113 |
-} |
|
| 2114 |
- |
|
| 2115 |
-func (devices *DeviceSet) deactivateDevice(info *devInfo) error {
|
|
| 2116 |
- return devices.deactivateDeviceMode(info, devices.deferredRemove) |
|
| 2117 |
-} |
|
| 2118 |
- |
|
| 2119 |
-func (devices *DeviceSet) deactivateDeviceMode(info *devInfo, deferredRemove bool) error {
|
|
| 2120 |
- var err error |
|
| 2121 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("deactivateDevice START(%s)", info.Hash)
|
|
| 2122 |
- defer logrus.WithField("storage-driver", "devicemapper").Debugf("deactivateDevice END(%s)", info.Hash)
|
|
| 2123 |
- |
|
| 2124 |
- devinfo, err := devicemapper.GetInfo(info.Name()) |
|
| 2125 |
- if err != nil {
|
|
| 2126 |
- return err |
|
| 2127 |
- } |
|
| 2128 |
- |
|
| 2129 |
- if devinfo.Exists == 0 {
|
|
| 2130 |
- return nil |
|
| 2131 |
- } |
|
| 2132 |
- |
|
| 2133 |
- if deferredRemove {
|
|
| 2134 |
- err = devicemapper.RemoveDeviceDeferred(info.Name()) |
|
| 2135 |
- } else {
|
|
| 2136 |
- err = devices.removeDevice(info.Name()) |
|
| 2137 |
- } |
|
| 2138 |
- |
|
| 2139 |
- // This function's semantics is such that it does not return an |
|
| 2140 |
- // error if device does not exist. So if device went away by |
|
| 2141 |
- // the time we actually tried to remove it, do not return error. |
|
| 2142 |
- if err != devicemapper.ErrEnxio {
|
|
| 2143 |
- return err |
|
| 2144 |
- } |
|
| 2145 |
- return nil |
|
| 2146 |
-} |
|
| 2147 |
- |
|
| 2148 |
-// Issues the underlying dm remove operation. |
|
| 2149 |
-func (devices *DeviceSet) removeDevice(devname string) error {
|
|
| 2150 |
- var err error |
|
| 2151 |
- |
|
| 2152 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("removeDevice START(%s)", devname)
|
|
| 2153 |
- defer logrus.WithField("storage-driver", "devicemapper").Debugf("removeDevice END(%s)", devname)
|
|
| 2154 |
- |
|
| 2155 |
- for i := 0; i < 200; i++ {
|
|
| 2156 |
- err = devicemapper.RemoveDevice(devname) |
|
| 2157 |
- if err == nil {
|
|
| 2158 |
- break |
|
| 2159 |
- } |
|
| 2160 |
- if err != devicemapper.ErrBusy {
|
|
| 2161 |
- return err |
|
| 2162 |
- } |
|
| 2163 |
- |
|
| 2164 |
- // If we see EBUSY it may be a transient error, |
|
| 2165 |
- // sleep a bit a retry a few times. |
|
| 2166 |
- devices.Unlock() |
|
| 2167 |
- time.Sleep(100 * time.Millisecond) |
|
| 2168 |
- devices.Lock() |
|
| 2169 |
- } |
|
| 2170 |
- |
|
| 2171 |
- return err |
|
| 2172 |
-} |
|
| 2173 |
- |
|
| 2174 |
-func (devices *DeviceSet) cancelDeferredRemovalIfNeeded(info *devInfo) error {
|
|
| 2175 |
- if !devices.deferredRemove {
|
|
| 2176 |
- return nil |
|
| 2177 |
- } |
|
| 2178 |
- |
|
| 2179 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("cancelDeferredRemovalIfNeeded START(%s)", info.Name())
|
|
| 2180 |
- defer logrus.WithField("storage-driver", "devicemapper").Debugf("cancelDeferredRemovalIfNeeded END(%s)", info.Name())
|
|
| 2181 |
- |
|
| 2182 |
- devinfo, err := devicemapper.GetInfoWithDeferred(info.Name()) |
|
| 2183 |
- if err != nil {
|
|
| 2184 |
- return err |
|
| 2185 |
- } |
|
| 2186 |
- |
|
| 2187 |
- if devinfo != nil && devinfo.DeferredRemove == 0 {
|
|
| 2188 |
- return nil |
|
| 2189 |
- } |
|
| 2190 |
- |
|
| 2191 |
- // Cancel deferred remove |
|
| 2192 |
- if err := devices.cancelDeferredRemoval(info); err != nil {
|
|
| 2193 |
- // If Error is ErrEnxio. Device is probably already gone. Continue. |
|
| 2194 |
- if err != devicemapper.ErrEnxio {
|
|
| 2195 |
- return err |
|
| 2196 |
- } |
|
| 2197 |
- } |
|
| 2198 |
- return nil |
|
| 2199 |
-} |
|
| 2200 |
- |
|
| 2201 |
-func (devices *DeviceSet) cancelDeferredRemoval(info *devInfo) error {
|
|
| 2202 |
- logrus.WithField("storage-driver", "devicemapper").Debugf("cancelDeferredRemoval START(%s)", info.Name())
|
|
| 2203 |
- defer logrus.WithField("storage-driver", "devicemapper").Debugf("cancelDeferredRemoval END(%s)", info.Name())
|
|
| 2204 |
- |
|
| 2205 |
- var err error |
|
| 2206 |
- |
|
| 2207 |
- // Cancel deferred remove |
|
| 2208 |
- for i := 0; i < 100; i++ {
|
|
| 2209 |
- err = devicemapper.CancelDeferredRemove(info.Name()) |
|
| 2210 |
- if err != nil {
|
|
| 2211 |
- if err == devicemapper.ErrBusy {
|
|
| 2212 |
- // If we see EBUSY it may be a transient error, |
|
| 2213 |
- // sleep a bit a retry a few times. |
|
| 2214 |
- devices.Unlock() |
|
| 2215 |
- time.Sleep(100 * time.Millisecond) |
|
| 2216 |
- devices.Lock() |
|
| 2217 |
- continue |
|
| 2218 |
- } |
|
| 2219 |
- } |
|
| 2220 |
- break |
|
| 2221 |
- } |
|
| 2222 |
- return err |
|
| 2223 |
-} |
|
| 2224 |
- |
|
| 2225 |
-func (devices *DeviceSet) unmountAndDeactivateAll(dir string) {
|
|
| 2226 |
- logger := logrus.WithField("storage-driver", "devicemapper")
|
|
| 2227 |
- |
|
| 2228 |
- files, err := os.ReadDir(dir) |
|
| 2229 |
- if err != nil {
|
|
| 2230 |
- logger.Warnf("unmountAndDeactivate: %s", err)
|
|
| 2231 |
- return |
|
| 2232 |
- } |
|
| 2233 |
- |
|
| 2234 |
- for _, d := range files {
|
|
| 2235 |
- if !d.IsDir() {
|
|
| 2236 |
- continue |
|
| 2237 |
- } |
|
| 2238 |
- |
|
| 2239 |
- name := d.Name() |
|
| 2240 |
- fullname := path.Join(dir, name) |
|
| 2241 |
- |
|
| 2242 |
- // We use MNT_DETACH here in case it is still busy in some running |
|
| 2243 |
- // container. This means it'll go away from the global scope directly, |
|
| 2244 |
- // and the device will be released when that container dies. |
|
| 2245 |
- if err := unix.Unmount(fullname, unix.MNT_DETACH); err != nil && err != unix.EINVAL {
|
|
| 2246 |
- logger.Warnf("Shutdown unmounting %s, error: %s", fullname, err)
|
|
| 2247 |
- } |
|
| 2248 |
- |
|
| 2249 |
- if devInfo, err := devices.lookupDevice(name); err != nil {
|
|
| 2250 |
- logger.Debugf("Shutdown lookup device %s, error: %s", name, err)
|
|
| 2251 |
- } else {
|
|
| 2252 |
- if err := devices.deactivateDevice(devInfo); err != nil {
|
|
| 2253 |
- logger.Debugf("Shutdown deactivate %s, error: %s", devInfo.Hash, err)
|
|
| 2254 |
- } |
|
| 2255 |
- } |
|
| 2256 |
- } |
|
| 2257 |
-} |
|
| 2258 |
- |
|
| 2259 |
-// Shutdown shuts down the device by unmounting the root. |
|
| 2260 |
-func (devices *DeviceSet) Shutdown(home string) error {
|
|
| 2261 |
- logger := logrus.WithField("storage-driver", "devicemapper")
|
|
| 2262 |
- |
|
| 2263 |
- logger.Debugf("[deviceset %s] Shutdown()", devices.devicePrefix)
|
|
| 2264 |
- logger.Debugf("Shutting down DeviceSet: %s", devices.root)
|
|
| 2265 |
- defer logger.Debugf("[deviceset %s] Shutdown() END", devices.devicePrefix)
|
|
| 2266 |
- |
|
| 2267 |
- // Stop deletion worker. This should start delivering new events to |
|
| 2268 |
- // ticker channel. That means no new instance of cleanupDeletedDevice() |
|
| 2269 |
- // will run after this call. If one instance is already running at |
|
| 2270 |
- // the time of the call, it must be holding devices.Lock() and |
|
| 2271 |
- // we will block on this lock till cleanup function exits. |
|
| 2272 |
- devices.deletionWorkerTicker.Stop() |
|
| 2273 |
- |
|
| 2274 |
- devices.Lock() |
|
| 2275 |
- // Save DeviceSet Metadata first. Docker kills all threads if they |
|
| 2276 |
- // don't finish in certain time. It is possible that Shutdown() |
|
| 2277 |
- // routine does not finish in time as we loop trying to deactivate |
|
| 2278 |
- // some devices while these are busy. In that case shutdown() routine |
|
| 2279 |
- // will be killed and we will not get a chance to save deviceset |
|
| 2280 |
- // metadata. Hence save this early before trying to deactivate devices. |
|
| 2281 |
- devices.saveDeviceSetMetaData() |
|
| 2282 |
- devices.unmountAndDeactivateAll(path.Join(home, "mnt")) |
|
| 2283 |
- devices.Unlock() |
|
| 2284 |
- |
|
| 2285 |
- info, _ := devices.lookupDeviceWithLock("")
|
|
| 2286 |
- if info != nil {
|
|
| 2287 |
- info.lock.Lock() |
|
| 2288 |
- devices.Lock() |
|
| 2289 |
- if err := devices.deactivateDevice(info); err != nil {
|
|
| 2290 |
- logger.Debugf("Shutdown deactivate base , error: %s", err)
|
|
| 2291 |
- } |
|
| 2292 |
- devices.Unlock() |
|
| 2293 |
- info.lock.Unlock() |
|
| 2294 |
- } |
|
| 2295 |
- |
|
| 2296 |
- devices.Lock() |
|
| 2297 |
- if devices.thinPoolDevice == "" {
|
|
| 2298 |
- if err := devices.deactivatePool(); err != nil {
|
|
| 2299 |
- logger.Debugf("Shutdown deactivate pool , error: %s", err)
|
|
| 2300 |
- } |
|
| 2301 |
- } |
|
| 2302 |
- devices.Unlock() |
|
| 2303 |
- |
|
| 2304 |
- return nil |
|
| 2305 |
-} |
|
| 2306 |
- |
|
| 2307 |
-// Recent XFS changes allow changing behavior of filesystem in case of errors. |
|
| 2308 |
-// When thin pool gets full and XFS gets ENOSPC error, currently it tries |
|
| 2309 |
-// IO infinitely and sometimes it can block the container process |
|
| 2310 |
-// and process can't be killWith 0 value, XFS will not retry upon error |
|
| 2311 |
-// and instead will shutdown filesystem. |
|
| 2312 |
- |
|
| 2313 |
-func (devices *DeviceSet) xfsSetNospaceRetries(info *devInfo) error {
|
|
| 2314 |
- dmDevicePath, err := os.Readlink(info.DevName()) |
|
| 2315 |
- if err != nil {
|
|
| 2316 |
- return fmt.Errorf("devmapper: readlink failed for device %v:%v", info.DevName(), err)
|
|
| 2317 |
- } |
|
| 2318 |
- |
|
| 2319 |
- dmDeviceName := path.Base(dmDevicePath) |
|
| 2320 |
- filePath := "/sys/fs/xfs/" + dmDeviceName + "/error/metadata/ENOSPC/max_retries" |
|
| 2321 |
- maxRetriesFile, err := os.OpenFile(filePath, os.O_WRONLY, 0) |
|
| 2322 |
- if err != nil {
|
|
| 2323 |
- return fmt.Errorf("devmapper: user specified daemon option dm.xfs_nospace_max_retries but it does not seem to be supported on this system :%v", err)
|
|
| 2324 |
- } |
|
| 2325 |
- defer maxRetriesFile.Close() |
|
| 2326 |
- |
|
| 2327 |
- // Set max retries to 0 |
|
| 2328 |
- _, err = maxRetriesFile.WriteString(devices.xfsNospaceRetries) |
|
| 2329 |
- if err != nil {
|
|
| 2330 |
- return fmt.Errorf("devmapper: Failed to write string %v to file %v:%v", devices.xfsNospaceRetries, filePath, err)
|
|
| 2331 |
- } |
|
| 2332 |
- return nil |
|
| 2333 |
-} |
|
| 2334 |
- |
|
| 2335 |
-// MountDevice mounts the device if not already mounted. |
|
| 2336 |
-func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error {
|
|
| 2337 |
- info, err := devices.lookupDeviceWithLock(hash) |
|
| 2338 |
- if err != nil {
|
|
| 2339 |
- return err |
|
| 2340 |
- } |
|
| 2341 |
- |
|
| 2342 |
- if info.Deleted {
|
|
| 2343 |
- return fmt.Errorf("devmapper: Can't mount device %v as it has been marked for deferred deletion", info.Hash)
|
|
| 2344 |
- } |
|
| 2345 |
- |
|
| 2346 |
- info.lock.Lock() |
|
| 2347 |
- defer info.lock.Unlock() |
|
| 2348 |
- |
|
| 2349 |
- devices.Lock() |
|
| 2350 |
- defer devices.Unlock() |
|
| 2351 |
- |
|
| 2352 |
- if err := devices.activateDeviceIfNeeded(info, false); err != nil {
|
|
| 2353 |
- return fmt.Errorf("devmapper: Error activating devmapper device for '%s': %s", hash, err)
|
|
| 2354 |
- } |
|
| 2355 |
- |
|
| 2356 |
- fstype, err := ProbeFsType(info.DevName()) |
|
| 2357 |
- if err != nil {
|
|
| 2358 |
- return err |
|
| 2359 |
- } |
|
| 2360 |
- |
|
| 2361 |
- options := "" |
|
| 2362 |
- |
|
| 2363 |
- if fstype == "xfs" {
|
|
| 2364 |
- // XFS needs nouuid or it can't mount filesystems with the same fs |
|
| 2365 |
- options = joinMountOptions(options, "nouuid") |
|
| 2366 |
- } |
|
| 2367 |
- |
|
| 2368 |
- options = joinMountOptions(options, devices.mountOptions) |
|
| 2369 |
- options = joinMountOptions(options, label.FormatMountLabel("", mountLabel))
|
|
| 2370 |
- |
|
| 2371 |
- if err := mount.Mount(info.DevName(), path, fstype, options); err != nil {
|
|
| 2372 |
- return errors.Wrapf(err, "Failed to mount; dmesg: %s", string(dmesg.Dmesg(256))) |
|
| 2373 |
- } |
|
| 2374 |
- |
|
| 2375 |
- if fstype == "xfs" && devices.xfsNospaceRetries != "" {
|
|
| 2376 |
- if err := devices.xfsSetNospaceRetries(info); err != nil {
|
|
| 2377 |
- unix.Unmount(path, unix.MNT_DETACH) |
|
| 2378 |
- devices.deactivateDevice(info) |
|
| 2379 |
- return err |
|
| 2380 |
- } |
|
| 2381 |
- } |
|
| 2382 |
- |
|
| 2383 |
- return nil |
|
| 2384 |
-} |
|
| 2385 |
- |
|
| 2386 |
-// UnmountDevice unmounts the device and removes it from hash. |
|
| 2387 |
-func (devices *DeviceSet) UnmountDevice(hash, mountPath string) error {
|
|
| 2388 |
- logger := logrus.WithField("storage-driver", "devicemapper")
|
|
| 2389 |
- |
|
| 2390 |
- logger.Debugf("UnmountDevice START(hash=%s)", hash)
|
|
| 2391 |
- defer logger.Debugf("UnmountDevice END(hash=%s)", hash)
|
|
| 2392 |
- |
|
| 2393 |
- info, err := devices.lookupDeviceWithLock(hash) |
|
| 2394 |
- if err != nil {
|
|
| 2395 |
- return err |
|
| 2396 |
- } |
|
| 2397 |
- |
|
| 2398 |
- info.lock.Lock() |
|
| 2399 |
- defer info.lock.Unlock() |
|
| 2400 |
- |
|
| 2401 |
- devices.Lock() |
|
| 2402 |
- defer devices.Unlock() |
|
| 2403 |
- |
|
| 2404 |
- logger.Debugf("Unmount(%s)", mountPath)
|
|
| 2405 |
- if err := unix.Unmount(mountPath, unix.MNT_DETACH); err != nil {
|
|
| 2406 |
- return err |
|
| 2407 |
- } |
|
| 2408 |
- logger.Debug("Unmount done")
|
|
| 2409 |
- |
|
| 2410 |
- // Remove the mountpoint here. Removing the mountpoint (in newer kernels) |
|
| 2411 |
- // will cause all other instances of this mount in other mount namespaces |
|
| 2412 |
- // to be killed (this is an anti-DoS measure that is necessary for things |
|
| 2413 |
- // like devicemapper). This is necessary to avoid cases where a libdm mount |
|
| 2414 |
- // that is present in another namespace will cause subsequent RemoveDevice |
|
| 2415 |
- // operations to fail. We ignore any errors here because this may fail on |
|
| 2416 |
- // older kernels which don't have |
|
| 2417 |
- // torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied. |
|
| 2418 |
- if err := os.Remove(mountPath); err != nil {
|
|
| 2419 |
- logger.Debugf("error doing a remove on unmounted device %s: %v", mountPath, err)
|
|
| 2420 |
- } |
|
| 2421 |
- |
|
| 2422 |
- return devices.deactivateDevice(info) |
|
| 2423 |
-} |
|
| 2424 |
- |
|
| 2425 |
-// HasDevice returns true if the device metadata exists. |
|
| 2426 |
-func (devices *DeviceSet) HasDevice(hash string) bool {
|
|
| 2427 |
- info, _ := devices.lookupDeviceWithLock(hash) |
|
| 2428 |
- return info != nil |
|
| 2429 |
-} |
|
| 2430 |
- |
|
| 2431 |
-// List returns a list of device ids. |
|
| 2432 |
-func (devices *DeviceSet) List() []string {
|
|
| 2433 |
- devices.Lock() |
|
| 2434 |
- defer devices.Unlock() |
|
| 2435 |
- |
|
| 2436 |
- ids := make([]string, len(devices.Devices)) |
|
| 2437 |
- i := 0 |
|
| 2438 |
- for k := range devices.Devices {
|
|
| 2439 |
- ids[i] = k |
|
| 2440 |
- i++ |
|
| 2441 |
- } |
|
| 2442 |
- return ids |
|
| 2443 |
-} |
|
| 2444 |
- |
|
| 2445 |
-func (devices *DeviceSet) deviceStatus(devName string) (sizeInSectors, mappedSectors, highestMappedSector uint64, err error) {
|
|
| 2446 |
- var params string |
|
| 2447 |
- _, sizeInSectors, _, params, err = devicemapper.GetStatus(devName) |
|
| 2448 |
- if err != nil {
|
|
| 2449 |
- return |
|
| 2450 |
- } |
|
| 2451 |
- if _, err = fmt.Sscanf(params, "%d %d", &mappedSectors, &highestMappedSector); err == nil {
|
|
| 2452 |
- return |
|
| 2453 |
- } |
|
| 2454 |
- return |
|
| 2455 |
-} |
|
| 2456 |
- |
|
| 2457 |
-// GetDeviceStatus provides size, mapped sectors |
|
| 2458 |
-func (devices *DeviceSet) GetDeviceStatus(hash string) (*DevStatus, error) {
|
|
| 2459 |
- info, err := devices.lookupDeviceWithLock(hash) |
|
| 2460 |
- if err != nil {
|
|
| 2461 |
- return nil, err |
|
| 2462 |
- } |
|
| 2463 |
- |
|
| 2464 |
- info.lock.Lock() |
|
| 2465 |
- defer info.lock.Unlock() |
|
| 2466 |
- |
|
| 2467 |
- devices.Lock() |
|
| 2468 |
- defer devices.Unlock() |
|
| 2469 |
- |
|
| 2470 |
- status := &DevStatus{
|
|
| 2471 |
- DeviceID: info.DeviceID, |
|
| 2472 |
- Size: info.Size, |
|
| 2473 |
- TransactionID: info.TransactionID, |
|
| 2474 |
- } |
|
| 2475 |
- |
|
| 2476 |
- if err := devices.activateDeviceIfNeeded(info, false); err != nil {
|
|
| 2477 |
- return nil, fmt.Errorf("devmapper: Error activating devmapper device for '%s': %s", hash, err)
|
|
| 2478 |
- } |
|
| 2479 |
- |
|
| 2480 |
- sizeInSectors, mappedSectors, highestMappedSector, err := devices.deviceStatus(info.DevName()) |
|
| 2481 |
- |
|
| 2482 |
- if err != nil {
|
|
| 2483 |
- return nil, err |
|
| 2484 |
- } |
|
| 2485 |
- |
|
| 2486 |
- status.SizeInSectors = sizeInSectors |
|
| 2487 |
- status.MappedSectors = mappedSectors |
|
| 2488 |
- status.HighestMappedSector = highestMappedSector |
|
| 2489 |
- |
|
| 2490 |
- return status, nil |
|
| 2491 |
-} |
|
| 2492 |
- |
|
| 2493 |
-func (devices *DeviceSet) poolStatus() (totalSizeInSectors, transactionID, dataUsed, dataTotal, metadataUsed, metadataTotal uint64, err error) {
|
|
| 2494 |
- var params string |
|
| 2495 |
- if _, totalSizeInSectors, _, params, err = devicemapper.GetStatus(devices.getPoolName()); err == nil {
|
|
| 2496 |
- _, err = fmt.Sscanf(params, "%d %d/%d %d/%d", &transactionID, &metadataUsed, &metadataTotal, &dataUsed, &dataTotal) |
|
| 2497 |
- } |
|
| 2498 |
- return |
|
| 2499 |
-} |
|
| 2500 |
- |
|
| 2501 |
-// DataDevicePath returns the path to the data storage for this deviceset, |
|
| 2502 |
-// regardless of loopback or block device |
|
| 2503 |
-func (devices *DeviceSet) DataDevicePath() string {
|
|
| 2504 |
- return devices.dataDevice |
|
| 2505 |
-} |
|
| 2506 |
- |
|
| 2507 |
-// MetadataDevicePath returns the path to the metadata storage for this deviceset, |
|
| 2508 |
-// regardless of loopback or block device |
|
| 2509 |
-func (devices *DeviceSet) MetadataDevicePath() string {
|
|
| 2510 |
- return devices.metadataDevice |
|
| 2511 |
-} |
|
| 2512 |
- |
|
| 2513 |
-func (devices *DeviceSet) getUnderlyingAvailableSpace(loopFile string) (uint64, error) {
|
|
| 2514 |
- buf := new(unix.Statfs_t) |
|
| 2515 |
- if err := unix.Statfs(loopFile, buf); err != nil {
|
|
| 2516 |
- logrus.WithField("storage-driver", "devicemapper").Warnf("Couldn't stat loopfile filesystem %v: %v", loopFile, err)
|
|
| 2517 |
- return 0, err |
|
| 2518 |
- } |
|
| 2519 |
- return buf.Bfree * uint64(buf.Bsize), nil |
|
| 2520 |
-} |
|
| 2521 |
- |
|
| 2522 |
-func (devices *DeviceSet) isRealFile(loopFile string) (bool, error) {
|
|
| 2523 |
- if loopFile != "" {
|
|
| 2524 |
- fi, err := os.Stat(loopFile) |
|
| 2525 |
- if err != nil {
|
|
| 2526 |
- logrus.WithField("storage-driver", "devicemapper").Warnf("Couldn't stat loopfile %v: %v", loopFile, err)
|
|
| 2527 |
- return false, err |
|
| 2528 |
- } |
|
| 2529 |
- return fi.Mode().IsRegular(), nil |
|
| 2530 |
- } |
|
| 2531 |
- return false, nil |
|
| 2532 |
-} |
|
| 2533 |
- |
|
| 2534 |
-// Status returns the current status of this deviceset |
|
| 2535 |
-func (devices *DeviceSet) Status() *Status {
|
|
| 2536 |
- devices.Lock() |
|
| 2537 |
- defer devices.Unlock() |
|
| 2538 |
- |
|
| 2539 |
- status := &Status{}
|
|
| 2540 |
- |
|
| 2541 |
- status.PoolName = devices.getPoolName() |
|
| 2542 |
- status.DataFile = devices.DataDevicePath() |
|
| 2543 |
- status.DataLoopback = devices.dataLoopFile |
|
| 2544 |
- status.MetadataFile = devices.MetadataDevicePath() |
|
| 2545 |
- status.MetadataLoopback = devices.metadataLoopFile |
|
| 2546 |
- status.UdevSyncSupported = devicemapper.UdevSyncSupported() |
|
| 2547 |
- status.DeferredRemoveEnabled = devices.deferredRemove |
|
| 2548 |
- status.DeferredDeleteEnabled = devices.deferredDelete |
|
| 2549 |
- status.DeferredDeletedDeviceCount = devices.nrDeletedDevices |
|
| 2550 |
- status.BaseDeviceSize = devices.getBaseDeviceSize() |
|
| 2551 |
- status.BaseDeviceFS = devices.getBaseDeviceFS() |
|
| 2552 |
- |
|
| 2553 |
- totalSizeInSectors, _, dataUsed, dataTotal, metadataUsed, metadataTotal, err := devices.poolStatus() |
|
| 2554 |
- if err == nil {
|
|
| 2555 |
- // Convert from blocks to bytes |
|
| 2556 |
- blockSizeInSectors := totalSizeInSectors / dataTotal |
|
| 2557 |
- |
|
| 2558 |
- status.Data.Used = dataUsed * blockSizeInSectors * 512 |
|
| 2559 |
- status.Data.Total = dataTotal * blockSizeInSectors * 512 |
|
| 2560 |
- status.Data.Available = status.Data.Total - status.Data.Used |
|
| 2561 |
- |
|
| 2562 |
- // metadata blocks are always 4k |
|
| 2563 |
- status.Metadata.Used = metadataUsed * 4096 |
|
| 2564 |
- status.Metadata.Total = metadataTotal * 4096 |
|
| 2565 |
- status.Metadata.Available = status.Metadata.Total - status.Metadata.Used |
|
| 2566 |
- |
|
| 2567 |
- status.SectorSize = blockSizeInSectors * 512 |
|
| 2568 |
- |
|
| 2569 |
- if check, _ := devices.isRealFile(devices.dataLoopFile); check {
|
|
| 2570 |
- actualSpace, err := devices.getUnderlyingAvailableSpace(devices.dataLoopFile) |
|
| 2571 |
- if err == nil && actualSpace < status.Data.Available {
|
|
| 2572 |
- status.Data.Available = actualSpace |
|
| 2573 |
- } |
|
| 2574 |
- } |
|
| 2575 |
- |
|
| 2576 |
- if check, _ := devices.isRealFile(devices.metadataLoopFile); check {
|
|
| 2577 |
- actualSpace, err := devices.getUnderlyingAvailableSpace(devices.metadataLoopFile) |
|
| 2578 |
- if err == nil && actualSpace < status.Metadata.Available {
|
|
| 2579 |
- status.Metadata.Available = actualSpace |
|
| 2580 |
- } |
|
| 2581 |
- } |
|
| 2582 |
- |
|
| 2583 |
- minFreeData := (dataTotal * uint64(devices.minFreeSpacePercent)) / 100 |
|
| 2584 |
- status.MinFreeSpace = minFreeData * blockSizeInSectors * 512 |
|
| 2585 |
- } |
|
| 2586 |
- |
|
| 2587 |
- return status |
|
| 2588 |
-} |
|
| 2589 |
- |
|
| 2590 |
-// Status returns the current status of this deviceset |
|
| 2591 |
-func (devices *DeviceSet) exportDeviceMetadata(hash string) (*deviceMetadata, error) {
|
|
| 2592 |
- info, err := devices.lookupDeviceWithLock(hash) |
|
| 2593 |
- if err != nil {
|
|
| 2594 |
- return nil, err |
|
| 2595 |
- } |
|
| 2596 |
- |
|
| 2597 |
- info.lock.Lock() |
|
| 2598 |
- defer info.lock.Unlock() |
|
| 2599 |
- |
|
| 2600 |
- metadata := &deviceMetadata{info.DeviceID, info.Size, info.Name()}
|
|
| 2601 |
- return metadata, nil |
|
| 2602 |
-} |
|
| 2603 |
- |
|
| 2604 |
-// NewDeviceSet creates the device set based on the options provided. |
|
| 2605 |
-func NewDeviceSet(root string, doInit bool, options []string, idMap idtools.IdentityMapping) (*DeviceSet, error) {
|
|
| 2606 |
- devicemapper.SetDevDir("/dev")
|
|
| 2607 |
- |
|
| 2608 |
- devices := &DeviceSet{
|
|
| 2609 |
- root: root, |
|
| 2610 |
- metaData: metaData{Devices: make(map[string]*devInfo)},
|
|
| 2611 |
- dataLoopbackSize: defaultDataLoopbackSize, |
|
| 2612 |
- metaDataLoopbackSize: defaultMetaDataLoopbackSize, |
|
| 2613 |
- baseFsSize: defaultBaseFsSize, |
|
| 2614 |
- overrideUdevSyncCheck: defaultUdevSyncOverride, |
|
| 2615 |
- doBlkDiscard: true, |
|
| 2616 |
- thinpBlockSize: defaultThinpBlockSize, |
|
| 2617 |
- deviceIDMap: make([]byte, deviceIDMapSz), |
|
| 2618 |
- deletionWorkerTicker: time.NewTicker(time.Second * 30), |
|
| 2619 |
- idMap: idMap, |
|
| 2620 |
- minFreeSpacePercent: defaultMinFreeSpacePercent, |
|
| 2621 |
- } |
|
| 2622 |
- |
|
| 2623 |
- version, err := devicemapper.GetDriverVersion() |
|
| 2624 |
- if err != nil {
|
|
| 2625 |
- // Can't even get driver version, assume not supported |
|
| 2626 |
- return nil, graphdriver.ErrNotSupported |
|
| 2627 |
- } |
|
| 2628 |
- |
|
| 2629 |
- if err := determineDriverCapabilities(version); err != nil {
|
|
| 2630 |
- return nil, graphdriver.ErrNotSupported |
|
| 2631 |
- } |
|
| 2632 |
- |
|
| 2633 |
- if driverDeferredRemovalSupport && devicemapper.LibraryDeferredRemovalSupport {
|
|
| 2634 |
- // enable deferred stuff by default |
|
| 2635 |
- enableDeferredDeletion = true |
|
| 2636 |
- enableDeferredRemoval = true |
|
| 2637 |
- } |
|
| 2638 |
- |
|
| 2639 |
- foundBlkDiscard := false |
|
| 2640 |
- var lvmSetupConfig directLVMConfig |
|
| 2641 |
- for _, option := range options {
|
|
| 2642 |
- key, val, err := parsers.ParseKeyValueOpt(option) |
|
| 2643 |
- if err != nil {
|
|
| 2644 |
- return nil, err |
|
| 2645 |
- } |
|
| 2646 |
- key = strings.ToLower(key) |
|
| 2647 |
- switch key {
|
|
| 2648 |
- case "dm.basesize": |
|
| 2649 |
- size, err := units.RAMInBytes(val) |
|
| 2650 |
- if err != nil {
|
|
| 2651 |
- return nil, err |
|
| 2652 |
- } |
|
| 2653 |
- userBaseSize = true |
|
| 2654 |
- devices.baseFsSize = uint64(size) |
|
| 2655 |
- case "dm.loopdatasize": |
|
| 2656 |
- size, err := units.RAMInBytes(val) |
|
| 2657 |
- if err != nil {
|
|
| 2658 |
- return nil, err |
|
| 2659 |
- } |
|
| 2660 |
- devices.dataLoopbackSize = size |
|
| 2661 |
- case "dm.loopmetadatasize": |
|
| 2662 |
- size, err := units.RAMInBytes(val) |
|
| 2663 |
- if err != nil {
|
|
| 2664 |
- return nil, err |
|
| 2665 |
- } |
|
| 2666 |
- devices.metaDataLoopbackSize = size |
|
| 2667 |
- case "dm.fs": |
|
| 2668 |
- if val != "ext4" && val != "xfs" {
|
|
| 2669 |
- return nil, fmt.Errorf("devmapper: Unsupported filesystem %s", val)
|
|
| 2670 |
- } |
|
| 2671 |
- devices.filesystem = val |
|
| 2672 |
- case "dm.mkfsarg": |
|
| 2673 |
- devices.mkfsArgs = append(devices.mkfsArgs, val) |
|
| 2674 |
- case "dm.mountopt": |
|
| 2675 |
- devices.mountOptions = joinMountOptions(devices.mountOptions, val) |
|
| 2676 |
- case "dm.metadatadev": |
|
| 2677 |
- devices.metadataDevice = val |
|
| 2678 |
- case "dm.datadev": |
|
| 2679 |
- devices.dataDevice = val |
|
| 2680 |
- case "dm.thinpooldev": |
|
| 2681 |
- devices.thinPoolDevice = strings.TrimPrefix(val, "/dev/mapper/") |
|
| 2682 |
- case "dm.blkdiscard": |
|
| 2683 |
- foundBlkDiscard = true |
|
| 2684 |
- devices.doBlkDiscard, err = strconv.ParseBool(val) |
|
| 2685 |
- if err != nil {
|
|
| 2686 |
- return nil, err |
|
| 2687 |
- } |
|
| 2688 |
- case "dm.blocksize": |
|
| 2689 |
- size, err := units.RAMInBytes(val) |
|
| 2690 |
- if err != nil {
|
|
| 2691 |
- return nil, err |
|
| 2692 |
- } |
|
| 2693 |
- // convert to 512b sectors |
|
| 2694 |
- devices.thinpBlockSize = uint32(size) >> 9 |
|
| 2695 |
- case "dm.override_udev_sync_check": |
|
| 2696 |
- devices.overrideUdevSyncCheck, err = strconv.ParseBool(val) |
|
| 2697 |
- if err != nil {
|
|
| 2698 |
- return nil, err |
|
| 2699 |
- } |
|
| 2700 |
- |
|
| 2701 |
- case "dm.use_deferred_removal": |
|
| 2702 |
- enableDeferredRemoval, err = strconv.ParseBool(val) |
|
| 2703 |
- if err != nil {
|
|
| 2704 |
- return nil, err |
|
| 2705 |
- } |
|
| 2706 |
- |
|
| 2707 |
- case "dm.use_deferred_deletion": |
|
| 2708 |
- enableDeferredDeletion, err = strconv.ParseBool(val) |
|
| 2709 |
- if err != nil {
|
|
| 2710 |
- return nil, err |
|
| 2711 |
- } |
|
| 2712 |
- |
|
| 2713 |
- case "dm.min_free_space": |
|
| 2714 |
- if !strings.HasSuffix(val, "%") {
|
|
| 2715 |
- return nil, fmt.Errorf("devmapper: Option dm.min_free_space requires %% suffix")
|
|
| 2716 |
- } |
|
| 2717 |
- |
|
| 2718 |
- valstring := strings.TrimSuffix(val, "%") |
|
| 2719 |
- minFreeSpacePercent, err := strconv.ParseUint(valstring, 10, 32) |
|
| 2720 |
- if err != nil {
|
|
| 2721 |
- return nil, err |
|
| 2722 |
- } |
|
| 2723 |
- |
|
| 2724 |
- if minFreeSpacePercent >= 100 {
|
|
| 2725 |
- return nil, fmt.Errorf("devmapper: Invalid value %v for option dm.min_free_space", val)
|
|
| 2726 |
- } |
|
| 2727 |
- |
|
| 2728 |
- devices.minFreeSpacePercent = uint32(minFreeSpacePercent) |
|
| 2729 |
- case "dm.xfs_nospace_max_retries": |
|
| 2730 |
- _, err := strconv.ParseUint(val, 10, 64) |
|
| 2731 |
- if err != nil {
|
|
| 2732 |
- return nil, err |
|
| 2733 |
- } |
|
| 2734 |
- devices.xfsNospaceRetries = val |
|
| 2735 |
- case "dm.directlvm_device": |
|
| 2736 |
- lvmSetupConfig.Device = val |
|
| 2737 |
- case "dm.directlvm_device_force": |
|
| 2738 |
- lvmSetupConfigForce, err = strconv.ParseBool(val) |
|
| 2739 |
- if err != nil {
|
|
| 2740 |
- return nil, err |
|
| 2741 |
- } |
|
| 2742 |
- case "dm.thinp_percent": |
|
| 2743 |
- per, err := strconv.ParseUint(strings.TrimSuffix(val, "%"), 10, 32) |
|
| 2744 |
- if err != nil {
|
|
| 2745 |
- return nil, errors.Wrapf(err, "could not parse `dm.thinp_percent=%s`", val) |
|
| 2746 |
- } |
|
| 2747 |
- if per >= 100 {
|
|
| 2748 |
- return nil, errors.New("dm.thinp_percent must be greater than 0 and less than 100")
|
|
| 2749 |
- } |
|
| 2750 |
- lvmSetupConfig.ThinpPercent = per |
|
| 2751 |
- case "dm.thinp_metapercent": |
|
| 2752 |
- per, err := strconv.ParseUint(strings.TrimSuffix(val, "%"), 10, 32) |
|
| 2753 |
- if err != nil {
|
|
| 2754 |
- return nil, errors.Wrapf(err, "could not parse `dm.thinp_metapercent=%s`", val) |
|
| 2755 |
- } |
|
| 2756 |
- if per >= 100 {
|
|
| 2757 |
- return nil, errors.New("dm.thinp_metapercent must be greater than 0 and less than 100")
|
|
| 2758 |
- } |
|
| 2759 |
- lvmSetupConfig.ThinpMetaPercent = per |
|
| 2760 |
- case "dm.thinp_autoextend_percent": |
|
| 2761 |
- per, err := strconv.ParseUint(strings.TrimSuffix(val, "%"), 10, 32) |
|
| 2762 |
- if err != nil {
|
|
| 2763 |
- return nil, errors.Wrapf(err, "could not parse `dm.thinp_autoextend_percent=%s`", val) |
|
| 2764 |
- } |
|
| 2765 |
- if per > 100 {
|
|
| 2766 |
- return nil, errors.New("dm.thinp_autoextend_percent must be greater than 0 and less than 100")
|
|
| 2767 |
- } |
|
| 2768 |
- lvmSetupConfig.AutoExtendPercent = per |
|
| 2769 |
- case "dm.thinp_autoextend_threshold": |
|
| 2770 |
- per, err := strconv.ParseUint(strings.TrimSuffix(val, "%"), 10, 32) |
|
| 2771 |
- if err != nil {
|
|
| 2772 |
- return nil, errors.Wrapf(err, "could not parse `dm.thinp_autoextend_threshold=%s`", val) |
|
| 2773 |
- } |
|
| 2774 |
- if per > 100 {
|
|
| 2775 |
- return nil, errors.New("dm.thinp_autoextend_threshold must be greater than 0 and less than 100")
|
|
| 2776 |
- } |
|
| 2777 |
- lvmSetupConfig.AutoExtendThreshold = per |
|
| 2778 |
- case "dm.libdm_log_level": |
|
| 2779 |
- level, err := strconv.ParseInt(val, 10, 32) |
|
| 2780 |
- if err != nil {
|
|
| 2781 |
- return nil, errors.Wrapf(err, "could not parse `dm.libdm_log_level=%s`", val) |
|
| 2782 |
- } |
|
| 2783 |
- if level < devicemapper.LogLevelFatal || level > devicemapper.LogLevelDebug {
|
|
| 2784 |
- return nil, errors.Errorf("dm.libdm_log_level must be in range [%d,%d]", devicemapper.LogLevelFatal, devicemapper.LogLevelDebug)
|
|
| 2785 |
- } |
|
| 2786 |
- // Register a new logging callback with the specified level. |
|
| 2787 |
- devicemapper.LogInit(devicemapper.DefaultLogger{
|
|
| 2788 |
- Level: int(level), |
|
| 2789 |
- }) |
|
| 2790 |
- default: |
|
| 2791 |
- return nil, fmt.Errorf("devmapper: Unknown option %s", key)
|
|
| 2792 |
- } |
|
| 2793 |
- } |
|
| 2794 |
- |
|
| 2795 |
- if err := validateLVMConfig(lvmSetupConfig); err != nil {
|
|
| 2796 |
- return nil, err |
|
| 2797 |
- } |
|
| 2798 |
- |
|
| 2799 |
- devices.lvmSetupConfig = lvmSetupConfig |
|
| 2800 |
- |
|
| 2801 |
- // By default, don't do blk discard hack on raw devices, its rarely useful and is expensive |
|
| 2802 |
- if !foundBlkDiscard && (devices.dataDevice != "" || devices.thinPoolDevice != "") {
|
|
| 2803 |
- devices.doBlkDiscard = false |
|
| 2804 |
- } |
|
| 2805 |
- |
|
| 2806 |
- if err := devices.initDevmapper(doInit); err != nil {
|
|
| 2807 |
- return nil, err |
|
| 2808 |
- } |
|
| 2809 |
- |
|
| 2810 |
- return devices, nil |
|
| 2811 |
-} |
| 2812 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,106 +0,0 @@ |
| 1 |
-package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper" |
|
| 2 |
- |
|
| 3 |
-// Definition of struct dm_task and sub structures (from lvm2) |
|
| 4 |
-// |
|
| 5 |
-// struct dm_ioctl {
|
|
| 6 |
-// /* |
|
| 7 |
-// * The version number is made up of three parts: |
|
| 8 |
-// * major - no backward or forward compatibility, |
|
| 9 |
-// * minor - only backwards compatible, |
|
| 10 |
-// * patch - both backwards and forwards compatible. |
|
| 11 |
-// * |
|
| 12 |
-// * All clients of the ioctl interface should fill in the |
|
| 13 |
-// * version number of the interface that they were |
|
| 14 |
-// * compiled with. |
|
| 15 |
-// * |
|
| 16 |
-// * All recognized ioctl commands (ie. those that don't |
|
| 17 |
-// * return -ENOTTY) fill out this field, even if the |
|
| 18 |
-// * command failed. |
|
| 19 |
-// */ |
|
| 20 |
-// uint32_t version[3]; /* in/out */ |
|
| 21 |
-// uint32_t data_size; /* total size of data passed in |
|
| 22 |
-// * including this struct */ |
|
| 23 |
- |
|
| 24 |
-// uint32_t data_start; /* offset to start of data |
|
| 25 |
-// * relative to start of this struct */ |
|
| 26 |
- |
|
| 27 |
-// uint32_t target_count; /* in/out */ |
|
| 28 |
-// int32_t open_count; /* out */ |
|
| 29 |
-// uint32_t flags; /* in/out */ |
|
| 30 |
- |
|
| 31 |
-// /* |
|
| 32 |
-// * event_nr holds either the event number (input and output) or the |
|
| 33 |
-// * udev cookie value (input only). |
|
| 34 |
-// * The DM_DEV_WAIT ioctl takes an event number as input. |
|
| 35 |
-// * The DM_SUSPEND, DM_DEV_REMOVE and DM_DEV_RENAME ioctls |
|
| 36 |
-// * use the field as a cookie to return in the DM_COOKIE |
|
| 37 |
-// * variable with the uevents they issue. |
|
| 38 |
-// * For output, the ioctls return the event number, not the cookie. |
|
| 39 |
-// */ |
|
| 40 |
-// uint32_t event_nr; /* in/out */ |
|
| 41 |
-// uint32_t padding; |
|
| 42 |
- |
|
| 43 |
-// uint64_t dev; /* in/out */ |
|
| 44 |
- |
|
| 45 |
-// char name[DM_NAME_LEN]; /* device name */ |
|
| 46 |
-// char uuid[DM_UUID_LEN]; /* unique identifier for |
|
| 47 |
-// * the block device */ |
|
| 48 |
-// char data[7]; /* padding or data */ |
|
| 49 |
-// }; |
|
| 50 |
- |
|
| 51 |
-// struct target {
|
|
| 52 |
-// uint64_t start; |
|
| 53 |
-// uint64_t length; |
|
| 54 |
-// char *type; |
|
| 55 |
-// char *params; |
|
| 56 |
- |
|
| 57 |
-// struct target *next; |
|
| 58 |
-// }; |
|
| 59 |
- |
|
| 60 |
-// typedef enum {
|
|
| 61 |
-// DM_ADD_NODE_ON_RESUME, /* add /dev/mapper node with dmsetup resume */ |
|
| 62 |
-// DM_ADD_NODE_ON_CREATE /* add /dev/mapper node with dmsetup create */ |
|
| 63 |
-// } dm_add_node_t; |
|
| 64 |
- |
|
| 65 |
-// struct dm_task {
|
|
| 66 |
-// int type; |
|
| 67 |
-// char *dev_name; |
|
| 68 |
-// char *mangled_dev_name; |
|
| 69 |
- |
|
| 70 |
-// struct target *head, *tail; |
|
| 71 |
- |
|
| 72 |
-// int read_only; |
|
| 73 |
-// uint32_t event_nr; |
|
| 74 |
-// int major; |
|
| 75 |
-// int minor; |
|
| 76 |
-// int allow_default_major_fallback; |
|
| 77 |
-// uid_t uid; |
|
| 78 |
-// gid_t gid; |
|
| 79 |
-// mode_t mode; |
|
| 80 |
-// uint32_t read_ahead; |
|
| 81 |
-// uint32_t read_ahead_flags; |
|
| 82 |
-// union {
|
|
| 83 |
-// struct dm_ioctl *v4; |
|
| 84 |
-// } dmi; |
|
| 85 |
-// char *newname; |
|
| 86 |
-// char *message; |
|
| 87 |
-// char *geometry; |
|
| 88 |
-// uint64_t sector; |
|
| 89 |
-// int no_flush; |
|
| 90 |
-// int no_open_count; |
|
| 91 |
-// int skip_lockfs; |
|
| 92 |
-// int query_inactive_table; |
|
| 93 |
-// int suppress_identical_reload; |
|
| 94 |
-// dm_add_node_t add_node; |
|
| 95 |
-// uint64_t existing_table_size; |
|
| 96 |
-// int cookie_set; |
|
| 97 |
-// int new_uuid; |
|
| 98 |
-// int secure_data; |
|
| 99 |
-// int retry_remove; |
|
| 100 |
-// int enable_checks; |
|
| 101 |
-// int expected_errno; |
|
| 102 |
- |
|
| 103 |
-// char *uuid; |
|
| 104 |
-// char *mangled_uuid; |
|
| 105 |
-// }; |
|
| 106 |
-// |
| 107 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,208 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
-// +build linux |
|
| 3 |
- |
|
| 4 |
-package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper" |
|
| 5 |
- |
|
| 6 |
-import ( |
|
| 7 |
- "fmt" |
|
| 8 |
- "os" |
|
| 9 |
- "os/exec" |
|
| 10 |
- "syscall" |
|
| 11 |
- "testing" |
|
| 12 |
- "time" |
|
| 13 |
- |
|
| 14 |
- "github.com/docker/docker/daemon/graphdriver" |
|
| 15 |
- "github.com/docker/docker/daemon/graphdriver/graphtest" |
|
| 16 |
- "github.com/docker/docker/pkg/idtools" |
|
| 17 |
- "github.com/docker/docker/pkg/parsers/kernel" |
|
| 18 |
- "golang.org/x/sys/unix" |
|
| 19 |
-) |
|
| 20 |
- |
|
| 21 |
-func init() {
|
|
| 22 |
- // Reduce the size of the base fs and loopback for the tests |
|
| 23 |
- defaultDataLoopbackSize = 300 * 1024 * 1024 |
|
| 24 |
- defaultMetaDataLoopbackSize = 200 * 1024 * 1024 |
|
| 25 |
- defaultBaseFsSize = 300 * 1024 * 1024 |
|
| 26 |
- defaultUdevSyncOverride = true |
|
| 27 |
- if err := initLoopbacks(); err != nil {
|
|
| 28 |
- panic(err) |
|
| 29 |
- } |
|
| 30 |
-} |
|
| 31 |
- |
|
| 32 |
-// initLoopbacks ensures that the loopback devices are properly created within |
|
| 33 |
-// the system running the device mapper tests. |
|
| 34 |
-func initLoopbacks() error {
|
|
| 35 |
- statT, err := getBaseLoopStats() |
|
| 36 |
- if err != nil {
|
|
| 37 |
- return err |
|
| 38 |
- } |
|
| 39 |
- // create at least 128 loopback files, since a few first ones |
|
| 40 |
- // might be already in use by the host OS |
|
| 41 |
- for i := 0; i < 128; i++ {
|
|
| 42 |
- loopPath := fmt.Sprintf("/dev/loop%d", i)
|
|
| 43 |
- // only create new loopback files if they don't exist |
|
| 44 |
- if _, err := os.Stat(loopPath); err != nil {
|
|
| 45 |
- if mkerr := syscall.Mknod(loopPath, |
|
| 46 |
- uint32(statT.Mode|syscall.S_IFBLK), int((7<<8)|(i&0xff)|((i&0xfff00)<<12))); mkerr != nil { //nolint: unconvert
|
|
| 47 |
- return mkerr |
|
| 48 |
- } |
|
| 49 |
- os.Chown(loopPath, int(statT.Uid), int(statT.Gid)) |
|
| 50 |
- } |
|
| 51 |
- } |
|
| 52 |
- return nil |
|
| 53 |
-} |
|
| 54 |
- |
|
| 55 |
-// getBaseLoopStats inspects /dev/loop0 to collect uid,gid, and mode for the |
|
| 56 |
-// loop0 device on the system. If it does not exist we assume 0,0,0660 for the |
|
| 57 |
-// stat data |
|
| 58 |
-func getBaseLoopStats() (*syscall.Stat_t, error) {
|
|
| 59 |
- loop0, err := os.Stat("/dev/loop0")
|
|
| 60 |
- if err != nil {
|
|
| 61 |
- if os.IsNotExist(err) {
|
|
| 62 |
- return &syscall.Stat_t{
|
|
| 63 |
- Uid: 0, |
|
| 64 |
- Gid: 0, |
|
| 65 |
- Mode: 0660, |
|
| 66 |
- }, nil |
|
| 67 |
- } |
|
| 68 |
- return nil, err |
|
| 69 |
- } |
|
| 70 |
- return loop0.Sys().(*syscall.Stat_t), nil |
|
| 71 |
-} |
|
| 72 |
- |
|
| 73 |
-// This avoids creating a new driver for each test if all tests are run |
|
| 74 |
-// Make sure to put new tests between TestDevmapperSetup and TestDevmapperTeardown |
|
| 75 |
-func TestDevmapperSetup(t *testing.T) {
|
|
| 76 |
- graphtest.GetDriver(t, "devicemapper") |
|
| 77 |
-} |
|
| 78 |
- |
|
| 79 |
-func TestDevmapperCreateEmpty(t *testing.T) {
|
|
| 80 |
- graphtest.DriverTestCreateEmpty(t, "devicemapper") |
|
| 81 |
-} |
|
| 82 |
- |
|
| 83 |
-func TestDevmapperCreateBase(t *testing.T) {
|
|
| 84 |
- graphtest.DriverTestCreateBase(t, "devicemapper") |
|
| 85 |
-} |
|
| 86 |
- |
|
| 87 |
-func TestDevmapperCreateSnap(t *testing.T) {
|
|
| 88 |
- graphtest.DriverTestCreateSnap(t, "devicemapper") |
|
| 89 |
-} |
|
| 90 |
- |
|
| 91 |
-func TestDevmapperTeardown(t *testing.T) {
|
|
| 92 |
- graphtest.PutDriver(t) |
|
| 93 |
-} |
|
| 94 |
- |
|
| 95 |
-func TestDevmapperReduceLoopBackSize(t *testing.T) {
|
|
| 96 |
- tenMB := int64(10 * 1024 * 1024) |
|
| 97 |
- testChangeLoopBackSize(t, -tenMB, defaultDataLoopbackSize, defaultMetaDataLoopbackSize) |
|
| 98 |
-} |
|
| 99 |
- |
|
| 100 |
-func TestDevmapperIncreaseLoopBackSize(t *testing.T) {
|
|
| 101 |
- tenMB := int64(10 * 1024 * 1024) |
|
| 102 |
- testChangeLoopBackSize(t, tenMB, defaultDataLoopbackSize+tenMB, defaultMetaDataLoopbackSize+tenMB) |
|
| 103 |
-} |
|
| 104 |
- |
|
| 105 |
-func testChangeLoopBackSize(t *testing.T, delta, expectDataSize, expectMetaDataSize int64) {
|
|
| 106 |
- driver := graphtest.GetDriver(t, "devicemapper").(*graphtest.Driver).Driver.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver) |
|
| 107 |
- defer graphtest.PutDriver(t) |
|
| 108 |
- // make sure data or metadata loopback size are the default size |
|
| 109 |
- if s := driver.DeviceSet.Status(); s.Data.Total != uint64(defaultDataLoopbackSize) || s.Metadata.Total != uint64(defaultMetaDataLoopbackSize) {
|
|
| 110 |
- t.Fatal("data or metadata loop back size is incorrect")
|
|
| 111 |
- } |
|
| 112 |
- if err := driver.Cleanup(); err != nil {
|
|
| 113 |
- t.Fatal(err) |
|
| 114 |
- } |
|
| 115 |
- // Reload |
|
| 116 |
- d, err := Init(driver.home, []string{
|
|
| 117 |
- fmt.Sprintf("dm.loopdatasize=%d", defaultDataLoopbackSize+delta),
|
|
| 118 |
- fmt.Sprintf("dm.loopmetadatasize=%d", defaultMetaDataLoopbackSize+delta),
|
|
| 119 |
- }, idtools.IdentityMapping{})
|
|
| 120 |
- if err != nil {
|
|
| 121 |
- t.Fatalf("error creating devicemapper driver: %v", err)
|
|
| 122 |
- } |
|
| 123 |
- driver = d.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver) |
|
| 124 |
- if s := driver.DeviceSet.Status(); s.Data.Total != uint64(expectDataSize) || s.Metadata.Total != uint64(expectMetaDataSize) {
|
|
| 125 |
- t.Fatal("data or metadata loop back size is incorrect")
|
|
| 126 |
- } |
|
| 127 |
- if err := driver.Cleanup(); err != nil {
|
|
| 128 |
- t.Fatal(err) |
|
| 129 |
- } |
|
| 130 |
-} |
|
| 131 |
- |
|
| 132 |
-// Make sure devices.Lock() has been release upon return from cleanupDeletedDevices() function |
|
| 133 |
-func TestDevmapperLockReleasedDeviceDeletion(t *testing.T) {
|
|
| 134 |
- driver := graphtest.GetDriver(t, "devicemapper").(*graphtest.Driver).Driver.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver) |
|
| 135 |
- defer graphtest.PutDriver(t) |
|
| 136 |
- |
|
| 137 |
- // Call cleanupDeletedDevices() and after the call take and release |
|
| 138 |
- // DeviceSet Lock. If lock has not been released, this will hang. |
|
| 139 |
- driver.DeviceSet.cleanupDeletedDevices() |
|
| 140 |
- |
|
| 141 |
- doneChan := make(chan bool, 1) |
|
| 142 |
- |
|
| 143 |
- go func() {
|
|
| 144 |
- driver.DeviceSet.Lock() |
|
| 145 |
- defer driver.DeviceSet.Unlock() |
|
| 146 |
- doneChan <- true |
|
| 147 |
- }() |
|
| 148 |
- |
|
| 149 |
- select {
|
|
| 150 |
- case <-time.After(time.Second * 5): |
|
| 151 |
- // Timer expired. That means lock was not released upon |
|
| 152 |
- // function return and we are deadlocked. Release lock |
|
| 153 |
- // here so that cleanup could succeed and fail the test. |
|
| 154 |
- driver.DeviceSet.Unlock() |
|
| 155 |
- t.Fatal("Could not acquire devices lock after call to cleanupDeletedDevices()")
|
|
| 156 |
- case <-doneChan: |
|
| 157 |
- } |
|
| 158 |
-} |
|
| 159 |
- |
|
| 160 |
-// Ensure that mounts aren't leakedriver. It's non-trivial for us to test the full |
|
| 161 |
-// reproducer of #34573 in a unit test, but we can at least make sure that a |
|
| 162 |
-// simple command run in a new namespace doesn't break things horribly. |
|
| 163 |
-func TestDevmapperMountLeaks(t *testing.T) {
|
|
| 164 |
- if !kernel.CheckKernelVersion(3, 18, 0) {
|
|
| 165 |
- t.Skipf("kernel version <3.18.0 and so is missing torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe.")
|
|
| 166 |
- } |
|
| 167 |
- |
|
| 168 |
- driver := graphtest.GetDriver(t, "devicemapper", "dm.use_deferred_removal=false", "dm.use_deferred_deletion=false").(*graphtest.Driver).Driver.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver) |
|
| 169 |
- defer graphtest.PutDriver(t) |
|
| 170 |
- |
|
| 171 |
- // We need to create a new (dummy) device. |
|
| 172 |
- if err := driver.Create("some-layer", "", nil); err != nil {
|
|
| 173 |
- t.Fatalf("setting up some-layer: %v", err)
|
|
| 174 |
- } |
|
| 175 |
- |
|
| 176 |
- // Mount the device. |
|
| 177 |
- _, err := driver.Get("some-layer", "")
|
|
| 178 |
- if err != nil {
|
|
| 179 |
- t.Fatalf("mounting some-layer: %v", err)
|
|
| 180 |
- } |
|
| 181 |
- |
|
| 182 |
- // Create a new subprocess which will inherit our mountpoint, then |
|
| 183 |
- // intentionally leak it and stick around. We can't do this entirely within |
|
| 184 |
- // Go because forking and namespaces in Go are really not handled well at |
|
| 185 |
- // all. |
|
| 186 |
- cmd := exec.Cmd{
|
|
| 187 |
- Path: "/bin/sh", |
|
| 188 |
- Args: []string{
|
|
| 189 |
- "/bin/sh", "-c", |
|
| 190 |
- "mount --make-rprivate / && sleep 1000s", |
|
| 191 |
- }, |
|
| 192 |
- SysProcAttr: &syscall.SysProcAttr{
|
|
| 193 |
- Unshareflags: syscall.CLONE_NEWNS, |
|
| 194 |
- }, |
|
| 195 |
- } |
|
| 196 |
- if err := cmd.Start(); err != nil {
|
|
| 197 |
- t.Fatalf("starting sub-command: %v", err)
|
|
| 198 |
- } |
|
| 199 |
- defer func() {
|
|
| 200 |
- unix.Kill(cmd.Process.Pid, unix.SIGKILL) |
|
| 201 |
- cmd.Wait() |
|
| 202 |
- }() |
|
| 203 |
- |
|
| 204 |
- // Now try to "drop" the device. |
|
| 205 |
- if err := driver.Put("some-layer"); err != nil {
|
|
| 206 |
- t.Fatalf("unmounting some-layer: %v", err)
|
|
| 207 |
- } |
|
| 208 |
-} |
| 209 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,244 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
-// +build linux |
|
| 3 |
- |
|
| 4 |
-package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper" |
|
| 5 |
- |
|
| 6 |
-import ( |
|
| 7 |
- "fmt" |
|
| 8 |
- "os" |
|
| 9 |
- "path" |
|
| 10 |
- "strconv" |
|
| 11 |
- |
|
| 12 |
- "github.com/docker/docker/daemon/graphdriver" |
|
| 13 |
- "github.com/docker/docker/pkg/devicemapper" |
|
| 14 |
- "github.com/docker/docker/pkg/idtools" |
|
| 15 |
- units "github.com/docker/go-units" |
|
| 16 |
- "github.com/moby/locker" |
|
| 17 |
- "github.com/moby/sys/mount" |
|
| 18 |
- "github.com/sirupsen/logrus" |
|
| 19 |
- "golang.org/x/sys/unix" |
|
| 20 |
-) |
|
| 21 |
- |
|
| 22 |
-func init() {
|
|
| 23 |
- graphdriver.Register("devicemapper", Init)
|
|
| 24 |
-} |
|
| 25 |
- |
|
| 26 |
-// Driver contains the device set mounted and the home directory |
|
| 27 |
-type Driver struct {
|
|
| 28 |
- *DeviceSet |
|
| 29 |
- home string |
|
| 30 |
- ctr *graphdriver.RefCounter |
|
| 31 |
- locker *locker.Locker |
|
| 32 |
-} |
|
| 33 |
- |
|
| 34 |
-// Init creates a driver with the given home and the set of options. |
|
| 35 |
-func Init(home string, options []string, idMap idtools.IdentityMapping) (graphdriver.Driver, error) {
|
|
| 36 |
- deviceSet, err := NewDeviceSet(home, true, options, idMap) |
|
| 37 |
- if err != nil {
|
|
| 38 |
- return nil, err |
|
| 39 |
- } |
|
| 40 |
- |
|
| 41 |
- d := &Driver{
|
|
| 42 |
- DeviceSet: deviceSet, |
|
| 43 |
- home: home, |
|
| 44 |
- ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()), |
|
| 45 |
- locker: locker.New(), |
|
| 46 |
- } |
|
| 47 |
- |
|
| 48 |
- return graphdriver.NewNaiveDiffDriver(d, d.idMap), nil |
|
| 49 |
-} |
|
| 50 |
- |
|
| 51 |
-func (d *Driver) String() string {
|
|
| 52 |
- return "devicemapper" |
|
| 53 |
-} |
|
| 54 |
- |
|
| 55 |
-// Status returns the status about the driver in a printable format. |
|
| 56 |
-// Information returned contains Pool Name, Data File, Metadata file, disk usage by |
|
| 57 |
-// the data and metadata, etc. |
|
| 58 |
-func (d *Driver) Status() [][2]string {
|
|
| 59 |
- s := d.DeviceSet.Status() |
|
| 60 |
- |
|
| 61 |
- status := [][2]string{
|
|
| 62 |
- {"Pool Name", s.PoolName},
|
|
| 63 |
- {"Pool Blocksize", units.HumanSize(float64(s.SectorSize))},
|
|
| 64 |
- {"Base Device Size", units.HumanSize(float64(s.BaseDeviceSize))},
|
|
| 65 |
- {"Backing Filesystem", s.BaseDeviceFS},
|
|
| 66 |
- {"Udev Sync Supported", fmt.Sprintf("%v", s.UdevSyncSupported)},
|
|
| 67 |
- } |
|
| 68 |
- |
|
| 69 |
- if len(s.DataFile) > 0 {
|
|
| 70 |
- status = append(status, [2]string{"Data file", s.DataFile})
|
|
| 71 |
- } |
|
| 72 |
- if len(s.MetadataFile) > 0 {
|
|
| 73 |
- status = append(status, [2]string{"Metadata file", s.MetadataFile})
|
|
| 74 |
- } |
|
| 75 |
- if len(s.DataLoopback) > 0 {
|
|
| 76 |
- status = append(status, [2]string{"Data loop file", s.DataLoopback})
|
|
| 77 |
- } |
|
| 78 |
- if len(s.MetadataLoopback) > 0 {
|
|
| 79 |
- status = append(status, [2]string{"Metadata loop file", s.MetadataLoopback})
|
|
| 80 |
- } |
|
| 81 |
- |
|
| 82 |
- status = append(status, [][2]string{
|
|
| 83 |
- {"Data Space Used", units.HumanSize(float64(s.Data.Used))},
|
|
| 84 |
- {"Data Space Total", units.HumanSize(float64(s.Data.Total))},
|
|
| 85 |
- {"Data Space Available", units.HumanSize(float64(s.Data.Available))},
|
|
| 86 |
- {"Metadata Space Used", units.HumanSize(float64(s.Metadata.Used))},
|
|
| 87 |
- {"Metadata Space Total", units.HumanSize(float64(s.Metadata.Total))},
|
|
| 88 |
- {"Metadata Space Available", units.HumanSize(float64(s.Metadata.Available))},
|
|
| 89 |
- {"Thin Pool Minimum Free Space", units.HumanSize(float64(s.MinFreeSpace))},
|
|
| 90 |
- {"Deferred Removal Enabled", fmt.Sprintf("%v", s.DeferredRemoveEnabled)},
|
|
| 91 |
- {"Deferred Deletion Enabled", fmt.Sprintf("%v", s.DeferredDeleteEnabled)},
|
|
| 92 |
- {"Deferred Deleted Device Count", fmt.Sprintf("%v", s.DeferredDeletedDeviceCount)},
|
|
| 93 |
- }...) |
|
| 94 |
- |
|
| 95 |
- if vStr, err := devicemapper.GetLibraryVersion(); err == nil {
|
|
| 96 |
- status = append(status, [2]string{"Library Version", vStr})
|
|
| 97 |
- } |
|
| 98 |
- return status |
|
| 99 |
-} |
|
| 100 |
- |
|
| 101 |
-// GetMetadata returns a map of information about the device. |
|
| 102 |
-func (d *Driver) GetMetadata(id string) (map[string]string, error) {
|
|
| 103 |
- m, err := d.DeviceSet.exportDeviceMetadata(id) |
|
| 104 |
- |
|
| 105 |
- if err != nil {
|
|
| 106 |
- return nil, err |
|
| 107 |
- } |
|
| 108 |
- |
|
| 109 |
- metadata := make(map[string]string) |
|
| 110 |
- metadata["DeviceId"] = strconv.Itoa(m.deviceID) |
|
| 111 |
- metadata["DeviceSize"] = strconv.FormatUint(m.deviceSize, 10) |
|
| 112 |
- metadata["DeviceName"] = m.deviceName |
|
| 113 |
- return metadata, nil |
|
| 114 |
-} |
|
| 115 |
- |
|
| 116 |
-// Cleanup unmounts a device. |
|
| 117 |
-func (d *Driver) Cleanup() error {
|
|
| 118 |
- err := d.DeviceSet.Shutdown(d.home) |
|
| 119 |
- umountErr := mount.RecursiveUnmount(d.home) |
|
| 120 |
- |
|
| 121 |
- // in case we have two errors, prefer the one from Shutdown() |
|
| 122 |
- if err != nil {
|
|
| 123 |
- return err |
|
| 124 |
- } |
|
| 125 |
- |
|
| 126 |
- return umountErr |
|
| 127 |
-} |
|
| 128 |
- |
|
| 129 |
-// CreateReadWrite creates a layer that is writable for use as a container |
|
| 130 |
-// file system. |
|
| 131 |
-func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
|
|
| 132 |
- return d.Create(id, parent, opts) |
|
| 133 |
-} |
|
| 134 |
- |
|
| 135 |
-// Create adds a device with a given id and the parent. |
|
| 136 |
-func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
|
|
| 137 |
- var storageOpt map[string]string |
|
| 138 |
- if opts != nil {
|
|
| 139 |
- storageOpt = opts.StorageOpt |
|
| 140 |
- } |
|
| 141 |
- return d.DeviceSet.AddDevice(id, parent, storageOpt) |
|
| 142 |
-} |
|
| 143 |
- |
|
| 144 |
-// Remove removes a device with a given id, unmounts the filesystem, and removes the mount point. |
|
| 145 |
-func (d *Driver) Remove(id string) error {
|
|
| 146 |
- d.locker.Lock(id) |
|
| 147 |
- defer d.locker.Unlock(id) |
|
| 148 |
- if !d.DeviceSet.HasDevice(id) {
|
|
| 149 |
- // Consider removing a non-existing device a no-op |
|
| 150 |
- // This is useful to be able to progress on container removal |
|
| 151 |
- // if the underlying device has gone away due to earlier errors |
|
| 152 |
- return nil |
|
| 153 |
- } |
|
| 154 |
- |
|
| 155 |
- // This assumes the device has been properly Get/Put:ed and thus is unmounted |
|
| 156 |
- if err := d.DeviceSet.DeleteDevice(id, false); err != nil {
|
|
| 157 |
- return fmt.Errorf("failed to remove device %s: %v", id, err)
|
|
| 158 |
- } |
|
| 159 |
- |
|
| 160 |
- // Most probably the mount point is already removed on Put() |
|
| 161 |
- // (see DeviceSet.UnmountDevice()), but just in case it was not |
|
| 162 |
- // let's try to remove it here as well, ignoring errors as |
|
| 163 |
- // an older kernel can return EBUSY if e.g. the mount was leaked |
|
| 164 |
- // to other mount namespaces. A failure to remove the container's |
|
| 165 |
- // mount point is not important and should not be treated |
|
| 166 |
- // as a failure to remove the container. |
|
| 167 |
- mp := path.Join(d.home, "mnt", id) |
|
| 168 |
- err := unix.Rmdir(mp) |
|
| 169 |
- if err != nil && !os.IsNotExist(err) {
|
|
| 170 |
- logrus.WithField("storage-driver", "devicemapper").Warnf("unable to remove mount point %q: %s", mp, err)
|
|
| 171 |
- } |
|
| 172 |
- |
|
| 173 |
- return nil |
|
| 174 |
-} |
|
| 175 |
- |
|
| 176 |
-// Get mounts a device with given id into the root filesystem |
|
| 177 |
-func (d *Driver) Get(id, mountLabel string) (string, error) {
|
|
| 178 |
- d.locker.Lock(id) |
|
| 179 |
- defer d.locker.Unlock(id) |
|
| 180 |
- mp := path.Join(d.home, "mnt", id) |
|
| 181 |
- rootFs := path.Join(mp, "rootfs") |
|
| 182 |
- if count := d.ctr.Increment(mp); count > 1 {
|
|
| 183 |
- return rootFs, nil |
|
| 184 |
- } |
|
| 185 |
- |
|
| 186 |
- root := d.idMap.RootPair() |
|
| 187 |
- |
|
| 188 |
- // Create the target directories if they don't exist |
|
| 189 |
- if err := idtools.MkdirAllAndChown(path.Join(d.home, "mnt"), 0755, root); err != nil {
|
|
| 190 |
- d.ctr.Decrement(mp) |
|
| 191 |
- return "", err |
|
| 192 |
- } |
|
| 193 |
- if err := idtools.MkdirAndChown(mp, 0755, root); err != nil && !os.IsExist(err) {
|
|
| 194 |
- d.ctr.Decrement(mp) |
|
| 195 |
- return "", err |
|
| 196 |
- } |
|
| 197 |
- |
|
| 198 |
- // Mount the device |
|
| 199 |
- if err := d.DeviceSet.MountDevice(id, mp, mountLabel); err != nil {
|
|
| 200 |
- d.ctr.Decrement(mp) |
|
| 201 |
- return "", err |
|
| 202 |
- } |
|
| 203 |
- |
|
| 204 |
- if err := idtools.MkdirAllAndChown(rootFs, 0755, root); err != nil {
|
|
| 205 |
- d.ctr.Decrement(mp) |
|
| 206 |
- d.DeviceSet.UnmountDevice(id, mp) |
|
| 207 |
- return "", err |
|
| 208 |
- } |
|
| 209 |
- |
|
| 210 |
- idFile := path.Join(mp, "id") |
|
| 211 |
- if _, err := os.Stat(idFile); err != nil && os.IsNotExist(err) {
|
|
| 212 |
- // Create an "id" file with the container/image id in it to help reconstruct this in case |
|
| 213 |
- // of later problems |
|
| 214 |
- if err := os.WriteFile(idFile, []byte(id), 0600); err != nil {
|
|
| 215 |
- d.ctr.Decrement(mp) |
|
| 216 |
- d.DeviceSet.UnmountDevice(id, mp) |
|
| 217 |
- return "", err |
|
| 218 |
- } |
|
| 219 |
- } |
|
| 220 |
- |
|
| 221 |
- return rootFs, nil |
|
| 222 |
-} |
|
| 223 |
- |
|
| 224 |
-// Put unmounts a device and removes it. |
|
| 225 |
-func (d *Driver) Put(id string) error {
|
|
| 226 |
- d.locker.Lock(id) |
|
| 227 |
- defer d.locker.Unlock(id) |
|
| 228 |
- mp := path.Join(d.home, "mnt", id) |
|
| 229 |
- if count := d.ctr.Decrement(mp); count > 0 {
|
|
| 230 |
- return nil |
|
| 231 |
- } |
|
| 232 |
- |
|
| 233 |
- err := d.DeviceSet.UnmountDevice(id, mp) |
|
| 234 |
- if err != nil {
|
|
| 235 |
- logrus.WithField("storage-driver", "devicemapper").Errorf("Error unmounting device %s: %v", id, err)
|
|
| 236 |
- } |
|
| 237 |
- |
|
| 238 |
- return err |
|
| 239 |
-} |
|
| 240 |
- |
|
| 241 |
-// Exists checks to see if the device exists. |
|
| 242 |
-func (d *Driver) Exists(id string) bool {
|
|
| 243 |
- return d.DeviceSet.HasDevice(id) |
|
| 244 |
-} |
| 245 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,67 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
-// +build linux |
|
| 3 |
- |
|
| 4 |
-package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper" |
|
| 5 |
- |
|
| 6 |
-import ( |
|
| 7 |
- "bytes" |
|
| 8 |
- "fmt" |
|
| 9 |
- "os" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-type probeData struct {
|
|
| 13 |
- fsName string |
|
| 14 |
- magic string |
|
| 15 |
- offset uint64 |
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-// ProbeFsType returns the filesystem name for the given device id. |
|
| 19 |
-func ProbeFsType(device string) (string, error) {
|
|
| 20 |
- probes := []probeData{
|
|
| 21 |
- {"btrfs", "_BHRfS_M", 0x10040},
|
|
| 22 |
- {"ext4", "\123\357", 0x438},
|
|
| 23 |
- {"xfs", "XFSB", 0},
|
|
| 24 |
- } |
|
| 25 |
- |
|
| 26 |
- maxLen := uint64(0) |
|
| 27 |
- for _, p := range probes {
|
|
| 28 |
- l := p.offset + uint64(len(p.magic)) |
|
| 29 |
- if l > maxLen {
|
|
| 30 |
- maxLen = l |
|
| 31 |
- } |
|
| 32 |
- } |
|
| 33 |
- |
|
| 34 |
- file, err := os.Open(device) |
|
| 35 |
- if err != nil {
|
|
| 36 |
- return "", err |
|
| 37 |
- } |
|
| 38 |
- defer file.Close() |
|
| 39 |
- |
|
| 40 |
- buffer := make([]byte, maxLen) |
|
| 41 |
- l, err := file.Read(buffer) |
|
| 42 |
- if err != nil {
|
|
| 43 |
- return "", err |
|
| 44 |
- } |
|
| 45 |
- |
|
| 46 |
- if uint64(l) != maxLen {
|
|
| 47 |
- return "", fmt.Errorf("devmapper: unable to detect filesystem type of %s, short read", device)
|
|
| 48 |
- } |
|
| 49 |
- |
|
| 50 |
- for _, p := range probes {
|
|
| 51 |
- if bytes.Equal([]byte(p.magic), buffer[p.offset:p.offset+uint64(len(p.magic))]) {
|
|
| 52 |
- return p.fsName, nil |
|
| 53 |
- } |
|
| 54 |
- } |
|
| 55 |
- |
|
| 56 |
- return "", fmt.Errorf("devmapper: Unknown filesystem type on %s", device)
|
|
| 57 |
-} |
|
| 58 |
- |
|
| 59 |
-func joinMountOptions(a, b string) string {
|
|
| 60 |
- if a == "" {
|
|
| 61 |
- return b |
|
| 62 |
- } |
|
| 63 |
- if b == "" {
|
|
| 64 |
- return a |
|
| 65 |
- } |
|
| 66 |
- return a + "," + b |
|
| 67 |
-} |
| ... | ... |
@@ -316,18 +316,14 @@ func isEmptyDir(name string) bool {
|
| 316 | 316 |
|
| 317 | 317 |
// isDeprecated checks if a storage-driver is marked "deprecated" |
| 318 | 318 |
func isDeprecated(name string) bool {
|
| 319 |
- switch name {
|
|
| 320 | 319 |
// NOTE: when deprecating a driver, update daemon.fillDriverInfo() accordingly |
| 321 |
- case "devicemapper": |
|
| 322 |
- return true |
|
| 323 |
- } |
|
| 324 | 320 |
return false |
| 325 | 321 |
} |
| 326 | 322 |
|
| 327 | 323 |
// checkRemoved checks if a storage-driver has been deprecated (and removed) |
| 328 | 324 |
func checkRemoved(name string) error {
|
| 329 | 325 |
switch name {
|
| 330 |
- case "aufs", "overlay": |
|
| 326 |
+ case "aufs", "devicemapper", "overlay": |
|
| 331 | 327 |
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))
|
| 332 | 328 |
} |
| 333 | 329 |
return nil |
| ... | ... |
@@ -50,7 +50,7 @@ const ( |
| 50 | 50 |
|
| 51 | 51 |
var ( |
| 52 | 52 |
// List of drivers that should be used in an order |
| 53 |
- priority = "overlay2,fuse-overlayfs,btrfs,zfs,devicemapper,vfs" |
|
| 53 |
+ priority = "overlay2,fuse-overlayfs,btrfs,zfs,vfs" |
|
| 54 | 54 |
|
| 55 | 55 |
// FsNames maps filesystem id to name of the filesystem. |
| 56 | 56 |
FsNames = map[FsMagic]string{
|
| 57 | 57 |
deleted file mode 100644 |
| ... | ... |
@@ -1,9 +0,0 @@ |
| 1 |
-//go:build !exclude_graphdriver_devicemapper && !static_build && linux |
|
| 2 |
-// +build !exclude_graphdriver_devicemapper,!static_build,linux |
|
| 3 |
- |
|
| 4 |
-package register // import "github.com/docker/docker/daemon/graphdriver/register" |
|
| 5 |
- |
|
| 6 |
-import ( |
|
| 7 |
- // register the devmapper graphdriver |
|
| 8 |
- _ "github.com/docker/docker/daemon/graphdriver/devmapper" |
|
| 9 |
-) |
| ... | ... |
@@ -128,7 +128,7 @@ WARNING: The %s storage-driver is deprecated, and will be removed in a future re |
| 128 | 128 |
Refer to the documentation for more information: https://docs.docker.com/go/storage-driver/` |
| 129 | 129 |
|
| 130 | 130 |
switch v.Driver {
|
| 131 |
- case "devicemapper", "overlay": |
|
| 131 |
+ case "overlay": |
|
| 132 | 132 |
v.Warnings = append(v.Warnings, fmt.Sprintf(warnMsg, v.Driver)) |
| 133 | 133 |
} |
| 134 | 134 |
|
| ... | ... |
@@ -377,7 +377,7 @@ Return low-level information on the container `id` |
| 377 | 377 |
"WorkingDir": "" |
| 378 | 378 |
}, |
| 379 | 379 |
"Created": "2015-01-06T15:47:31.485331387Z", |
| 380 |
- "Driver": "devicemapper", |
|
| 380 |
+ "Driver": "overlay2", |
|
| 381 | 381 |
"ExecDriver": "native-0.2", |
| 382 | 382 |
"ExecIDs": null, |
| 383 | 383 |
"HostConfig": {
|
| ... | ... |
@@ -387,7 +387,7 @@ Return low-level information on the container `id` |
| 387 | 387 |
"WorkingDir": "" |
| 388 | 388 |
}, |
| 389 | 389 |
"Created": "2015-01-06T15:47:31.485331387Z", |
| 390 |
- "Driver": "devicemapper", |
|
| 390 |
+ "Driver": "overlay2", |
|
| 391 | 391 |
"ExecDriver": "native-0.2", |
| 392 | 392 |
"ExecIDs": null, |
| 393 | 393 |
"HostConfig": {
|
| ... | ... |
@@ -390,7 +390,7 @@ Return low-level information on the container `id` |
| 390 | 390 |
"WorkingDir": "" |
| 391 | 391 |
}, |
| 392 | 392 |
"Created": "2015-01-06T15:47:31.485331387Z", |
| 393 |
- "Driver": "devicemapper", |
|
| 393 |
+ "Driver": "overlay2", |
|
| 394 | 394 |
"ExecDriver": "native-0.2", |
| 395 | 395 |
"ExecIDs": null, |
| 396 | 396 |
"HostConfig": {
|
| ... | ... |
@@ -412,7 +412,7 @@ Return low-level information on the container `id` |
| 412 | 412 |
"StopSignal": "SIGTERM" |
| 413 | 413 |
}, |
| 414 | 414 |
"Created": "2015-01-06T15:47:31.485331387Z", |
| 415 |
- "Driver": "devicemapper", |
|
| 415 |
+ "Driver": "overlay2", |
|
| 416 | 416 |
"ExecDriver": "native-0.2", |
| 417 | 417 |
"ExecIDs": null, |
| 418 | 418 |
"HostConfig": {
|
| ... | ... |
@@ -529,7 +529,7 @@ Return low-level information on the container `id` |
| 529 | 529 |
"StopSignal": "SIGTERM" |
| 530 | 530 |
}, |
| 531 | 531 |
"Created": "2015-01-06T15:47:31.485331387Z", |
| 532 |
- "Driver": "devicemapper", |
|
| 532 |
+ "Driver": "overlay2", |
|
| 533 | 533 |
"ExecIDs": null, |
| 534 | 534 |
"HostConfig": {
|
| 535 | 535 |
"Binds": null, |
| ... | ... |
@@ -555,7 +555,7 @@ Return low-level information on the container `id` |
| 555 | 555 |
"StopSignal": "SIGTERM" |
| 556 | 556 |
}, |
| 557 | 557 |
"Created": "2015-01-06T15:47:31.485331387Z", |
| 558 |
- "Driver": "devicemapper", |
|
| 558 |
+ "Driver": "overlay2", |
|
| 559 | 559 |
"ExecIDs": null, |
| 560 | 560 |
"HostConfig": {
|
| 561 | 561 |
"Binds": null, |
| ... | ... |
@@ -597,7 +597,7 @@ Return low-level information on the container `id` |
| 597 | 597 |
"StopSignal": "SIGTERM" |
| 598 | 598 |
}, |
| 599 | 599 |
"Created": "2015-01-06T15:47:31.485331387Z", |
| 600 |
- "Driver": "devicemapper", |
|
| 600 |
+ "Driver": "overlay2", |
|
| 601 | 601 |
"ExecIDs": null, |
| 602 | 602 |
"HostConfig": {
|
| 603 | 603 |
"Binds": null, |
| ... | ... |
@@ -4832,7 +4832,7 @@ paths: |
| 4832 | 4832 |
StopSignal: "SIGTERM" |
| 4833 | 4833 |
StopTimeout: 10 |
| 4834 | 4834 |
Created: "2015-01-06T15:47:31.485331387Z" |
| 4835 |
- Driver: "devicemapper" |
|
| 4835 |
+ Driver: "overlay2" |
|
| 4836 | 4836 |
ExecIDs: |
| 4837 | 4837 |
- "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca" |
| 4838 | 4838 |
- "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4" |
| ... | ... |
@@ -5824,7 +5824,7 @@ paths: |
| 5824 | 5824 |
StopSignal: "SIGTERM" |
| 5825 | 5825 |
StopTimeout: 10 |
| 5826 | 5826 |
Created: "2015-01-06T15:47:31.485331387Z" |
| 5827 |
- Driver: "devicemapper" |
|
| 5827 |
+ Driver: "overlay2" |
|
| 5828 | 5828 |
ExecIDs: |
| 5829 | 5829 |
- "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca" |
| 5830 | 5830 |
- "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4" |
| ... | ... |
@@ -6125,7 +6125,7 @@ paths: |
| 6125 | 6125 |
StopSignal: "SIGTERM" |
| 6126 | 6126 |
StopTimeout: 10 |
| 6127 | 6127 |
Created: "2015-01-06T15:47:31.485331387Z" |
| 6128 |
- Driver: "devicemapper" |
|
| 6128 |
+ Driver: "overlay2" |
|
| 6129 | 6129 |
ExecIDs: |
| 6130 | 6130 |
- "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca" |
| 6131 | 6131 |
- "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4" |
| ... | ... |
@@ -6317,7 +6317,7 @@ paths: |
| 6317 | 6317 |
StopSignal: "SIGTERM" |
| 6318 | 6318 |
StopTimeout: 10 |
| 6319 | 6319 |
Created: "2015-01-06T15:47:31.485331387Z" |
| 6320 |
- Driver: "devicemapper" |
|
| 6320 |
+ Driver: "overlay2" |
|
| 6321 | 6321 |
ExecIDs: |
| 6322 | 6322 |
- "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca" |
| 6323 | 6323 |
- "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4" |
| ... | ... |
@@ -130,7 +130,7 @@ can take over 15 minutes to complete. |
| 130 | 130 |
```none |
| 131 | 131 |
Successfully built 3d872560918e |
| 132 | 132 |
Successfully tagged docker-dev:dry-run-test |
| 133 |
- 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 |
|
| 133 |
+ 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 |
|
| 134 | 134 |
# |
| 135 | 135 |
``` |
| 136 | 136 |
|
| ... | ... |
@@ -63,30 +63,6 @@ func (s *DockerCLICreateSuite) TestCreateArgs(c *testing.T) {
|
| 63 | 63 |
} |
| 64 | 64 |
} |
| 65 | 65 |
|
| 66 |
-// Make sure we can grow the container's rootfs at creation time. |
|
| 67 |
-func (s *DockerCLICreateSuite) TestCreateGrowRootfs(c *testing.T) {
|
|
| 68 |
- // Windows and Devicemapper support growing the rootfs |
|
| 69 |
- if testEnv.OSType != "windows" {
|
|
| 70 |
- testRequires(c, Devicemapper) |
|
| 71 |
- } |
|
| 72 |
- out, _ := dockerCmd(c, "create", "--storage-opt", "size=120G", "busybox") |
|
| 73 |
- |
|
| 74 |
- cleanedContainerID := strings.TrimSpace(out) |
|
| 75 |
- |
|
| 76 |
- inspectOut := inspectField(c, cleanedContainerID, "HostConfig.StorageOpt") |
|
| 77 |
- assert.Equal(c, inspectOut, "map[size:120G]") |
|
| 78 |
-} |
|
| 79 |
- |
|
| 80 |
-// Make sure we cannot shrink the container's rootfs at creation time. |
|
| 81 |
-func (s *DockerCLICreateSuite) TestCreateShrinkRootfs(c *testing.T) {
|
|
| 82 |
- testRequires(c, Devicemapper) |
|
| 83 |
- |
|
| 84 |
- // Ensure this fails because of the defaultBaseFsSize is 10G |
|
| 85 |
- out, _, err := dockerCmdWithError("create", "--storage-opt", "size=5G", "busybox")
|
|
| 86 |
- assert.ErrorContains(c, err, "", out) |
|
| 87 |
- assert.Assert(c, strings.Contains(out, "Container size cannot be smaller than")) |
|
| 88 |
-} |
|
| 89 |
- |
|
| 90 | 66 |
// Make sure we can set hostconfig options too |
| 91 | 67 |
func (s *DockerCLICreateSuite) TestCreateHostConfig(c *testing.T) {
|
| 92 | 68 |
out, _ := dockerCmd(c, "create", "-P", "busybox", "echo") |
| ... | ... |
@@ -34,7 +34,6 @@ import ( |
| 34 | 34 |
"github.com/docker/docker/libnetwork/iptables" |
| 35 | 35 |
"github.com/docker/docker/opts" |
| 36 | 36 |
testdaemon "github.com/docker/docker/testutil/daemon" |
| 37 |
- units "github.com/docker/go-units" |
|
| 38 | 37 |
"github.com/moby/sys/mount" |
| 39 | 38 |
"golang.org/x/sys/unix" |
| 40 | 39 |
"gotest.tools/v3/assert" |
| ... | ... |
@@ -203,77 +202,6 @@ func (s *DockerDaemonSuite) TestDaemonStartIptablesFalse(c *testing.T) {
|
| 203 | 203 |
s.d.Start(c, "--iptables=false") |
| 204 | 204 |
} |
| 205 | 205 |
|
| 206 |
-// Make sure we cannot shrink base device at daemon restart. |
|
| 207 |
-func (s *DockerDaemonSuite) TestDaemonRestartWithInvalidBasesize(c *testing.T) {
|
|
| 208 |
- testRequires(c, Devicemapper) |
|
| 209 |
- s.d.Start(c) |
|
| 210 |
- |
|
| 211 |
- oldBasesizeBytes := getBaseDeviceSize(c, s.d) |
|
| 212 |
- var newBasesizeBytes int64 = 1073741824 // 1GB in bytes |
|
| 213 |
- |
|
| 214 |
- if newBasesizeBytes < oldBasesizeBytes {
|
|
| 215 |
- err := s.d.RestartWithError("--storage-opt", fmt.Sprintf("dm.basesize=%d", newBasesizeBytes))
|
|
| 216 |
- assert.Assert(c, err != nil, "daemon should not have started as new base device size is less than existing base device size: %v", err) |
|
| 217 |
- // 'err != nil' is expected behaviour, no new daemon started, |
|
| 218 |
- // so no need to stop daemon. |
|
| 219 |
- if err != nil {
|
|
| 220 |
- return |
|
| 221 |
- } |
|
| 222 |
- } |
|
| 223 |
- s.d.Stop(c) |
|
| 224 |
-} |
|
| 225 |
- |
|
| 226 |
-// Make sure we can grow base device at daemon restart. |
|
| 227 |
-func (s *DockerDaemonSuite) TestDaemonRestartWithIncreasedBasesize(c *testing.T) {
|
|
| 228 |
- testRequires(c, Devicemapper) |
|
| 229 |
- s.d.Start(c) |
|
| 230 |
- |
|
| 231 |
- oldBasesizeBytes := getBaseDeviceSize(c, s.d) |
|
| 232 |
- |
|
| 233 |
- var newBasesizeBytes int64 = 53687091200 // 50GB in bytes |
|
| 234 |
- |
|
| 235 |
- if newBasesizeBytes < oldBasesizeBytes {
|
|
| 236 |
- c.Skipf("New base device size (%v) must be greater than (%s)", units.HumanSize(float64(newBasesizeBytes)), units.HumanSize(float64(oldBasesizeBytes)))
|
|
| 237 |
- } |
|
| 238 |
- |
|
| 239 |
- err := s.d.RestartWithError("--storage-opt", fmt.Sprintf("dm.basesize=%d", newBasesizeBytes))
|
|
| 240 |
- assert.Assert(c, err == nil, "we should have been able to start the daemon with increased base device size: %v", err) |
|
| 241 |
- |
|
| 242 |
- basesizeAfterRestart := getBaseDeviceSize(c, s.d) |
|
| 243 |
- newBasesize, err := convertBasesize(newBasesizeBytes) |
|
| 244 |
- assert.Assert(c, err == nil, "Error in converting base device size: %v", err) |
|
| 245 |
- assert.Equal(c, newBasesize, basesizeAfterRestart, "Basesize passed is not equal to Basesize set") |
|
| 246 |
- s.d.Stop(c) |
|
| 247 |
-} |
|
| 248 |
- |
|
| 249 |
-func getBaseDeviceSize(c *testing.T, d *daemon.Daemon) int64 {
|
|
| 250 |
- info := d.Info(c) |
|
| 251 |
- for _, statusLine := range info.DriverStatus {
|
|
| 252 |
- key, value := statusLine[0], statusLine[1] |
|
| 253 |
- if key == "Base Device Size" {
|
|
| 254 |
- return parseDeviceSize(c, value) |
|
| 255 |
- } |
|
| 256 |
- } |
|
| 257 |
- c.Fatal("failed to parse Base Device Size from info")
|
|
| 258 |
- return int64(0) |
|
| 259 |
-} |
|
| 260 |
- |
|
| 261 |
-func parseDeviceSize(c *testing.T, raw string) int64 {
|
|
| 262 |
- size, err := units.RAMInBytes(strings.TrimSpace(raw)) |
|
| 263 |
- assert.NilError(c, err) |
|
| 264 |
- return size |
|
| 265 |
-} |
|
| 266 |
- |
|
| 267 |
-func convertBasesize(basesizeBytes int64) (int64, error) {
|
|
| 268 |
- basesize := units.HumanSize(float64(basesizeBytes)) |
|
| 269 |
- basesize = strings.Trim(basesize, " ")[:len(basesize)-3] |
|
| 270 |
- basesizeFloat, err := strconv.ParseFloat(strings.Trim(basesize, " "), 64) |
|
| 271 |
- if err != nil {
|
|
| 272 |
- return 0, err |
|
| 273 |
- } |
|
| 274 |
- return int64(basesizeFloat) * 1024 * 1024 * 1024, nil |
|
| 275 |
-} |
|
| 276 |
- |
|
| 277 | 206 |
// Issue #8444: If docker0 bridge is modified (intentionally or unintentionally) and |
| 278 | 207 |
// no longer has an IP associated, we should gracefully handle that case and associate |
| 279 | 208 |
// an IP with it rather than fail daemon start |
| ... | ... |
@@ -27,12 +27,6 @@ func (s *DockerCLIInspectSuite) OnTimeout(c *testing.T) {
|
| 27 | 27 |
s.ds.OnTimeout(c) |
| 28 | 28 |
} |
| 29 | 29 |
|
| 30 |
-func checkValidGraphDriver(c *testing.T, name string) {
|
|
| 31 |
- if name != "devicemapper" && name != "overlay" && name != "vfs" && name != "zfs" && name != "btrfs" && name != "aufs" {
|
|
| 32 |
- c.Fatalf("%v is not a valid graph driver name", name)
|
|
| 33 |
- } |
|
| 34 |
-} |
|
| 35 |
- |
|
| 36 | 30 |
func (s *DockerCLIInspectSuite) TestInspectImage(c *testing.T) {
|
| 37 | 31 |
testRequires(c, DaemonIsLinux) |
| 38 | 32 |
imageTest := "emptyfs" |
| ... | ... |
@@ -177,49 +171,6 @@ func (s *DockerCLIInspectSuite) TestInspectContainerFilterInt(c *testing.T) {
|
| 177 | 177 |
assert.Equal(c, inspectResult, true) |
| 178 | 178 |
} |
| 179 | 179 |
|
| 180 |
-func (s *DockerCLIInspectSuite) TestInspectImageGraphDriver(c *testing.T) {
|
|
| 181 |
- testRequires(c, DaemonIsLinux, Devicemapper) |
|
| 182 |
- imageTest := "emptyfs" |
|
| 183 |
- name := inspectField(c, imageTest, "GraphDriver.Name") |
|
| 184 |
- |
|
| 185 |
- checkValidGraphDriver(c, name) |
|
| 186 |
- |
|
| 187 |
- deviceID := inspectField(c, imageTest, "GraphDriver.Data.DeviceId") |
|
| 188 |
- |
|
| 189 |
- _, err := strconv.Atoi(deviceID) |
|
| 190 |
- assert.Assert(c, err == nil, "failed to inspect DeviceId of the image: %s, %v", deviceID, err) |
|
| 191 |
- |
|
| 192 |
- deviceSize := inspectField(c, imageTest, "GraphDriver.Data.DeviceSize") |
|
| 193 |
- |
|
| 194 |
- _, err = strconv.ParseUint(deviceSize, 10, 64) |
|
| 195 |
- assert.Assert(c, err == nil, "failed to inspect DeviceSize of the image: %s, %v", deviceSize, err) |
|
| 196 |
-} |
|
| 197 |
- |
|
| 198 |
-func (s *DockerCLIInspectSuite) TestInspectContainerGraphDriver(c *testing.T) {
|
|
| 199 |
- testRequires(c, DaemonIsLinux, Devicemapper) |
|
| 200 |
- |
|
| 201 |
- out, _ := dockerCmd(c, "run", "-d", "busybox", "true") |
|
| 202 |
- out = strings.TrimSpace(out) |
|
| 203 |
- |
|
| 204 |
- name := inspectField(c, out, "GraphDriver.Name") |
|
| 205 |
- |
|
| 206 |
- checkValidGraphDriver(c, name) |
|
| 207 |
- |
|
| 208 |
- imageDeviceID := inspectField(c, "busybox", "GraphDriver.Data.DeviceId") |
|
| 209 |
- |
|
| 210 |
- deviceID := inspectField(c, out, "GraphDriver.Data.DeviceId") |
|
| 211 |
- |
|
| 212 |
- assert.Assert(c, imageDeviceID != deviceID) |
|
| 213 |
- |
|
| 214 |
- _, err := strconv.Atoi(deviceID) |
|
| 215 |
- assert.Assert(c, err == nil, "failed to inspect DeviceId of the image: %s, %v", deviceID, err) |
|
| 216 |
- |
|
| 217 |
- deviceSize := inspectField(c, out, "GraphDriver.Data.DeviceSize") |
|
| 218 |
- |
|
| 219 |
- _, err = strconv.ParseUint(deviceSize, 10, 64) |
|
| 220 |
- assert.Assert(c, err == nil, "failed to inspect DeviceSize of the image: %s, %v", deviceSize, err) |
|
| 221 |
-} |
|
| 222 |
- |
|
| 223 | 180 |
func (s *DockerCLIInspectSuite) TestInspectBindMountPoint(c *testing.T) {
|
| 224 | 181 |
modifier := ",z" |
| 225 | 182 |
prefix, slash := getPrefixAndSlashFromDaemonPlatform() |
| ... | ... |
@@ -100,10 +100,6 @@ func Apparmor() bool {
|
| 100 | 100 |
return err == nil && len(buf) > 1 && buf[0] == 'Y' |
| 101 | 101 |
} |
| 102 | 102 |
|
| 103 |
-func Devicemapper() bool {
|
|
| 104 |
- return strings.HasPrefix(testEnv.DaemonInfo.Driver, "devicemapper") |
|
| 105 |
-} |
|
| 106 |
- |
|
| 107 | 103 |
// containerdSnapshotterEnabled checks if the daemon in the test-environment is |
| 108 | 104 |
// configured with containerd-snapshotters enabled. |
| 109 | 105 |
func containerdSnapshotterEnabled() bool {
|
| ... | ... |
@@ -2,19 +2,14 @@ package container // import "github.com/docker/docker/integration/container" |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"context" |
| 5 |
- "fmt" |
|
| 6 | 5 |
"strconv" |
| 7 |
- "strings" |
|
| 8 | 6 |
"testing" |
| 9 | 7 |
"time" |
| 10 | 8 |
|
| 11 |
- "github.com/docker/docker/api/types" |
|
| 12 | 9 |
containertypes "github.com/docker/docker/api/types/container" |
| 13 | 10 |
"github.com/docker/docker/integration/internal/container" |
| 14 | 11 |
"gotest.tools/v3/assert" |
| 15 |
- "gotest.tools/v3/icmd" |
|
| 16 | 12 |
"gotest.tools/v3/poll" |
| 17 |
- "gotest.tools/v3/skip" |
|
| 18 | 13 |
) |
| 19 | 14 |
|
| 20 | 15 |
// TestStopContainerWithTimeout checks that ContainerStop with |
| ... | ... |
@@ -69,32 +64,3 @@ func TestStopContainerWithTimeout(t *testing.T) {
|
| 69 | 69 |
}) |
| 70 | 70 |
} |
| 71 | 71 |
} |
| 72 |
- |
|
| 73 |
-func TestDeleteDevicemapper(t *testing.T) {
|
|
| 74 |
- skip.If(t, testEnv.DaemonInfo.Driver != "devicemapper") |
|
| 75 |
- skip.If(t, testEnv.IsRemoteDaemon) |
|
| 76 |
- |
|
| 77 |
- defer setupTest(t)() |
|
| 78 |
- client := testEnv.APIClient() |
|
| 79 |
- ctx := context.Background() |
|
| 80 |
- |
|
| 81 |
- id := container.Run(ctx, t, client, container.WithName("foo-"+t.Name()), container.WithCmd("echo"))
|
|
| 82 |
- |
|
| 83 |
- poll.WaitOn(t, container.IsStopped(ctx, client, id), poll.WithDelay(100*time.Millisecond)) |
|
| 84 |
- |
|
| 85 |
- inspect, err := client.ContainerInspect(ctx, id) |
|
| 86 |
- assert.NilError(t, err) |
|
| 87 |
- |
|
| 88 |
- deviceID := inspect.GraphDriver.Data["DeviceId"] |
|
| 89 |
- |
|
| 90 |
- // Find pool name from device name |
|
| 91 |
- deviceName := inspect.GraphDriver.Data["DeviceName"] |
|
| 92 |
- devicePrefix := deviceName[:strings.LastIndex(deviceName, "-")] |
|
| 93 |
- devicePool := fmt.Sprintf("/dev/mapper/%s-pool", devicePrefix)
|
|
| 94 |
- |
|
| 95 |
- result := icmd.RunCommand("dmsetup", "message", devicePool, "0", fmt.Sprintf("delete %s", deviceID))
|
|
| 96 |
- result.Assert(t, icmd.Success) |
|
| 97 |
- |
|
| 98 |
- err = client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{})
|
|
| 99 |
- assert.NilError(t, err) |
|
| 100 |
-} |
| 101 | 72 |
deleted file mode 100644 |
| ... | ... |
@@ -1,811 +0,0 @@ |
| 1 |
-//go:build linux && cgo |
|
| 2 |
-// +build linux,cgo |
|
| 3 |
- |
|
| 4 |
-package devicemapper // import "github.com/docker/docker/pkg/devicemapper" |
|
| 5 |
- |
|
| 6 |
-import ( |
|
| 7 |
- "errors" |
|
| 8 |
- "fmt" |
|
| 9 |
- "os" |
|
| 10 |
- "runtime" |
|
| 11 |
- "unsafe" |
|
| 12 |
- |
|
| 13 |
- "github.com/sirupsen/logrus" |
|
| 14 |
- "golang.org/x/sys/unix" |
|
| 15 |
-) |
|
| 16 |
- |
|
| 17 |
-// Same as DM_DEVICE_* enum values from libdevmapper.h |
|
| 18 |
-// |
|
| 19 |
-//nolint:unused |
|
| 20 |
-const ( |
|
| 21 |
- deviceCreate TaskType = iota |
|
| 22 |
- deviceReload |
|
| 23 |
- deviceRemove |
|
| 24 |
- deviceRemoveAll |
|
| 25 |
- deviceSuspend |
|
| 26 |
- deviceResume |
|
| 27 |
- deviceInfo |
|
| 28 |
- deviceDeps |
|
| 29 |
- deviceRename |
|
| 30 |
- deviceVersion |
|
| 31 |
- deviceStatus |
|
| 32 |
- deviceTable |
|
| 33 |
- deviceWaitevent |
|
| 34 |
- deviceList |
|
| 35 |
- deviceClear |
|
| 36 |
- deviceMknodes |
|
| 37 |
- deviceListVersions |
|
| 38 |
- deviceTargetMsg |
|
| 39 |
- deviceSetGeometry |
|
| 40 |
-) |
|
| 41 |
- |
|
| 42 |
-const ( |
|
| 43 |
- addNodeOnResume AddNodeType = iota |
|
| 44 |
- addNodeOnCreate |
|
| 45 |
-) |
|
| 46 |
- |
|
| 47 |
-// List of errors returned when using devicemapper. |
|
| 48 |
-var ( |
|
| 49 |
- ErrTaskRun = errors.New("dm_task_run failed")
|
|
| 50 |
- ErrTaskSetName = errors.New("dm_task_set_name failed")
|
|
| 51 |
- ErrTaskSetMessage = errors.New("dm_task_set_message failed")
|
|
| 52 |
- ErrTaskSetAddNode = errors.New("dm_task_set_add_node failed")
|
|
| 53 |
- ErrTaskAddTarget = errors.New("dm_task_add_target failed")
|
|
| 54 |
- ErrTaskSetSector = errors.New("dm_task_set_sector failed")
|
|
| 55 |
- ErrTaskGetDeps = errors.New("dm_task_get_deps failed")
|
|
| 56 |
- ErrTaskGetInfo = errors.New("dm_task_get_info failed")
|
|
| 57 |
- ErrTaskGetDriverVersion = errors.New("dm_task_get_driver_version failed")
|
|
| 58 |
- ErrTaskDeferredRemove = errors.New("dm_task_deferred_remove failed")
|
|
| 59 |
- ErrTaskSetCookie = errors.New("dm_task_set_cookie failed")
|
|
| 60 |
- ErrNilCookie = errors.New("cookie ptr can't be nil")
|
|
| 61 |
- ErrGetBlockSize = errors.New("Can't get block size")
|
|
| 62 |
- ErrUdevWait = errors.New("wait on udev cookie failed")
|
|
| 63 |
- ErrSetDevDir = errors.New("dm_set_dev_dir failed")
|
|
| 64 |
- ErrGetLibraryVersion = errors.New("dm_get_library_version failed")
|
|
| 65 |
- ErrInvalidAddNode = errors.New("Invalid AddNode type")
|
|
| 66 |
- ErrBusy = errors.New("Device is Busy")
|
|
| 67 |
- ErrDeviceIDExists = errors.New("Device Id Exists")
|
|
| 68 |
- ErrEnxio = errors.New("No such device or address")
|
|
| 69 |
-) |
|
| 70 |
- |
|
| 71 |
-var ( |
|
| 72 |
- dmSawBusy bool |
|
| 73 |
- dmSawExist bool |
|
| 74 |
- dmSawEnxio bool // No Such Device or Address |
|
| 75 |
- dmSawEnoData bool // No data available |
|
| 76 |
-) |
|
| 77 |
- |
|
| 78 |
-type ( |
|
| 79 |
- // Task represents a devicemapper task (like lvcreate, etc.) ; a task is needed for each ioctl |
|
| 80 |
- // command to execute. |
|
| 81 |
- Task struct {
|
|
| 82 |
- unmanaged *cdmTask |
|
| 83 |
- } |
|
| 84 |
- // Deps represents dependents (layer) of a device. |
|
| 85 |
- Deps struct {
|
|
| 86 |
- Count uint32 |
|
| 87 |
- Filler uint32 |
|
| 88 |
- Device []uint64 |
|
| 89 |
- } |
|
| 90 |
- // Info represents information about a device. |
|
| 91 |
- Info struct {
|
|
| 92 |
- Exists int |
|
| 93 |
- Suspended int |
|
| 94 |
- LiveTable int |
|
| 95 |
- InactiveTable int |
|
| 96 |
- OpenCount int32 |
|
| 97 |
- EventNr uint32 |
|
| 98 |
- Major uint32 |
|
| 99 |
- Minor uint32 |
|
| 100 |
- ReadOnly int |
|
| 101 |
- TargetCount int32 |
|
| 102 |
- DeferredRemove int |
|
| 103 |
- } |
|
| 104 |
- // TaskType represents a type of task |
|
| 105 |
- TaskType int |
|
| 106 |
- // AddNodeType represents a type of node to be added |
|
| 107 |
- AddNodeType int |
|
| 108 |
-) |
|
| 109 |
- |
|
| 110 |
-// DeviceIDExists returns whether error conveys the information about device Id already |
|
| 111 |
-// exist or not. This will be true if device creation or snap creation |
|
| 112 |
-// operation fails if device or snap device already exists in pool. |
|
| 113 |
-// Current implementation is little crude as it scans the error string |
|
| 114 |
-// for exact pattern match. Replacing it with more robust implementation |
|
| 115 |
-// is desirable. |
|
| 116 |
-func DeviceIDExists(err error) bool {
|
|
| 117 |
- return fmt.Sprint(err) == fmt.Sprint(ErrDeviceIDExists) |
|
| 118 |
-} |
|
| 119 |
- |
|
| 120 |
-func (t *Task) destroy() {
|
|
| 121 |
- if t != nil {
|
|
| 122 |
- DmTaskDestroy(t.unmanaged) |
|
| 123 |
- runtime.SetFinalizer(t, nil) |
|
| 124 |
- } |
|
| 125 |
-} |
|
| 126 |
- |
|
| 127 |
-// TaskCreateNamed is a convenience function for TaskCreate when a name |
|
| 128 |
-// will be set on the task as well |
|
| 129 |
-func TaskCreateNamed(t TaskType, name string) (*Task, error) {
|
|
| 130 |
- task := TaskCreate(t) |
|
| 131 |
- if task == nil {
|
|
| 132 |
- return nil, fmt.Errorf("devicemapper: Can't create task of type %d", int(t))
|
|
| 133 |
- } |
|
| 134 |
- if err := task.setName(name); err != nil {
|
|
| 135 |
- return nil, fmt.Errorf("devicemapper: Can't set task name %s", name)
|
|
| 136 |
- } |
|
| 137 |
- return task, nil |
|
| 138 |
-} |
|
| 139 |
- |
|
| 140 |
-// TaskCreate initializes a devicemapper task of tasktype |
|
| 141 |
-func TaskCreate(tasktype TaskType) *Task {
|
|
| 142 |
- Ctask := DmTaskCreate(int(tasktype)) |
|
| 143 |
- if Ctask == nil {
|
|
| 144 |
- return nil |
|
| 145 |
- } |
|
| 146 |
- task := &Task{unmanaged: Ctask}
|
|
| 147 |
- runtime.SetFinalizer(task, (*Task).destroy) |
|
| 148 |
- return task |
|
| 149 |
-} |
|
| 150 |
- |
|
| 151 |
-func (t *Task) run() error {
|
|
| 152 |
- if res := DmTaskRun(t.unmanaged); res != 1 {
|
|
| 153 |
- return ErrTaskRun |
|
| 154 |
- } |
|
| 155 |
- runtime.KeepAlive(t) |
|
| 156 |
- return nil |
|
| 157 |
-} |
|
| 158 |
- |
|
| 159 |
-func (t *Task) setName(name string) error {
|
|
| 160 |
- if res := DmTaskSetName(t.unmanaged, name); res != 1 {
|
|
| 161 |
- return ErrTaskSetName |
|
| 162 |
- } |
|
| 163 |
- return nil |
|
| 164 |
-} |
|
| 165 |
- |
|
| 166 |
-func (t *Task) setMessage(message string) error {
|
|
| 167 |
- if res := DmTaskSetMessage(t.unmanaged, message); res != 1 {
|
|
| 168 |
- return ErrTaskSetMessage |
|
| 169 |
- } |
|
| 170 |
- return nil |
|
| 171 |
-} |
|
| 172 |
- |
|
| 173 |
-func (t *Task) setSector(sector uint64) error {
|
|
| 174 |
- if res := DmTaskSetSector(t.unmanaged, sector); res != 1 {
|
|
| 175 |
- return ErrTaskSetSector |
|
| 176 |
- } |
|
| 177 |
- return nil |
|
| 178 |
-} |
|
| 179 |
- |
|
| 180 |
-func (t *Task) setCookie(cookie *uint, flags uint16) error {
|
|
| 181 |
- if cookie == nil {
|
|
| 182 |
- return ErrNilCookie |
|
| 183 |
- } |
|
| 184 |
- if res := DmTaskSetCookie(t.unmanaged, cookie, flags); res != 1 {
|
|
| 185 |
- return ErrTaskSetCookie |
|
| 186 |
- } |
|
| 187 |
- return nil |
|
| 188 |
-} |
|
| 189 |
- |
|
| 190 |
-func (t *Task) setAddNode(addNode AddNodeType) error {
|
|
| 191 |
- if addNode != addNodeOnResume && addNode != addNodeOnCreate {
|
|
| 192 |
- return ErrInvalidAddNode |
|
| 193 |
- } |
|
| 194 |
- if res := DmTaskSetAddNode(t.unmanaged, addNode); res != 1 {
|
|
| 195 |
- return ErrTaskSetAddNode |
|
| 196 |
- } |
|
| 197 |
- return nil |
|
| 198 |
-} |
|
| 199 |
- |
|
| 200 |
-func (t *Task) addTarget(start, size uint64, ttype, params string) error {
|
|
| 201 |
- if res := DmTaskAddTarget(t.unmanaged, start, size, |
|
| 202 |
- ttype, params); res != 1 {
|
|
| 203 |
- return ErrTaskAddTarget |
|
| 204 |
- } |
|
| 205 |
- return nil |
|
| 206 |
-} |
|
| 207 |
- |
|
| 208 |
-func (t *Task) getDeps() (*Deps, error) {
|
|
| 209 |
- var deps *Deps |
|
| 210 |
- if deps = DmTaskGetDeps(t.unmanaged); deps == nil {
|
|
| 211 |
- return nil, ErrTaskGetDeps |
|
| 212 |
- } |
|
| 213 |
- return deps, nil |
|
| 214 |
-} |
|
| 215 |
- |
|
| 216 |
-func (t *Task) getInfo() (*Info, error) {
|
|
| 217 |
- info := &Info{}
|
|
| 218 |
- if res := DmTaskGetInfo(t.unmanaged, info); res != 1 {
|
|
| 219 |
- return nil, ErrTaskGetInfo |
|
| 220 |
- } |
|
| 221 |
- return info, nil |
|
| 222 |
-} |
|
| 223 |
- |
|
| 224 |
-func (t *Task) getInfoWithDeferred() (*Info, error) {
|
|
| 225 |
- info := &Info{}
|
|
| 226 |
- if res := DmTaskGetInfoWithDeferred(t.unmanaged, info); res != 1 {
|
|
| 227 |
- return nil, ErrTaskGetInfo |
|
| 228 |
- } |
|
| 229 |
- return info, nil |
|
| 230 |
-} |
|
| 231 |
- |
|
| 232 |
-func (t *Task) getDriverVersion() (string, error) {
|
|
| 233 |
- res := DmTaskGetDriverVersion(t.unmanaged) |
|
| 234 |
- if res == "" {
|
|
| 235 |
- return "", ErrTaskGetDriverVersion |
|
| 236 |
- } |
|
| 237 |
- return res, nil |
|
| 238 |
-} |
|
| 239 |
- |
|
| 240 |
-func (t *Task) getNextTarget(next unsafe.Pointer) (nextPtr unsafe.Pointer, start uint64, length uint64, targetType string, params string) {
|
|
| 241 |
- return DmGetNextTarget(t.unmanaged, next, &start, &length, &targetType, ¶ms), start, length, targetType, params |
|
| 242 |
-} |
|
| 243 |
- |
|
| 244 |
-// UdevWait waits for any processes that are waiting for udev to complete the specified cookie. |
|
| 245 |
-func UdevWait(cookie *uint) error {
|
|
| 246 |
- if res := DmUdevWait(*cookie); res != 1 {
|
|
| 247 |
- logrus.Debugf("devicemapper: Failed to wait on udev cookie %d, %d", *cookie, res)
|
|
| 248 |
- return ErrUdevWait |
|
| 249 |
- } |
|
| 250 |
- return nil |
|
| 251 |
-} |
|
| 252 |
- |
|
| 253 |
-// SetDevDir sets the dev folder for the device mapper library (usually /dev). |
|
| 254 |
-func SetDevDir(dir string) error {
|
|
| 255 |
- if res := DmSetDevDir(dir); res != 1 {
|
|
| 256 |
- logrus.Debug("devicemapper: Error dm_set_dev_dir")
|
|
| 257 |
- return ErrSetDevDir |
|
| 258 |
- } |
|
| 259 |
- return nil |
|
| 260 |
-} |
|
| 261 |
- |
|
| 262 |
-// GetLibraryVersion returns the device mapper library version. |
|
| 263 |
-func GetLibraryVersion() (string, error) {
|
|
| 264 |
- var version string |
|
| 265 |
- if res := DmGetLibraryVersion(&version); res != 1 {
|
|
| 266 |
- return "", ErrGetLibraryVersion |
|
| 267 |
- } |
|
| 268 |
- return version, nil |
|
| 269 |
-} |
|
| 270 |
- |
|
| 271 |
-// UdevSyncSupported returns whether device-mapper is able to sync with udev |
|
| 272 |
-// |
|
| 273 |
-// This is essential otherwise race conditions can arise where both udev and |
|
| 274 |
-// device-mapper attempt to create and destroy devices. |
|
| 275 |
-func UdevSyncSupported() bool {
|
|
| 276 |
- return DmUdevGetSyncSupport() != 0 |
|
| 277 |
-} |
|
| 278 |
- |
|
| 279 |
-// UdevSetSyncSupport allows setting whether the udev sync should be enabled. |
|
| 280 |
-// The return bool indicates the state of whether the sync is enabled. |
|
| 281 |
-func UdevSetSyncSupport(enable bool) bool {
|
|
| 282 |
- if enable {
|
|
| 283 |
- DmUdevSetSyncSupport(1) |
|
| 284 |
- } else {
|
|
| 285 |
- DmUdevSetSyncSupport(0) |
|
| 286 |
- } |
|
| 287 |
- |
|
| 288 |
- return UdevSyncSupported() |
|
| 289 |
-} |
|
| 290 |
- |
|
| 291 |
-// CookieSupported returns whether the version of device-mapper supports the |
|
| 292 |
-// use of cookie's in the tasks. |
|
| 293 |
-// This is largely a lower level call that other functions use. |
|
| 294 |
-func CookieSupported() bool {
|
|
| 295 |
- return DmCookieSupported() != 0 |
|
| 296 |
-} |
|
| 297 |
- |
|
| 298 |
-// RemoveDevice is a useful helper for cleaning up a device. |
|
| 299 |
-func RemoveDevice(name string) error {
|
|
| 300 |
- task, err := TaskCreateNamed(deviceRemove, name) |
|
| 301 |
- if task == nil {
|
|
| 302 |
- return err |
|
| 303 |
- } |
|
| 304 |
- |
|
| 305 |
- cookie := new(uint) |
|
| 306 |
- if err := task.setCookie(cookie, 0); err != nil {
|
|
| 307 |
- return fmt.Errorf("devicemapper: Can not set cookie: %s", err)
|
|
| 308 |
- } |
|
| 309 |
- defer UdevWait(cookie) |
|
| 310 |
- |
|
| 311 |
- dmSawBusy = false // reset before the task is run |
|
| 312 |
- dmSawEnxio = false |
|
| 313 |
- if err = task.run(); err != nil {
|
|
| 314 |
- if dmSawBusy {
|
|
| 315 |
- return ErrBusy |
|
| 316 |
- } |
|
| 317 |
- if dmSawEnxio {
|
|
| 318 |
- return ErrEnxio |
|
| 319 |
- } |
|
| 320 |
- return fmt.Errorf("devicemapper: Error running RemoveDevice %s", err)
|
|
| 321 |
- } |
|
| 322 |
- |
|
| 323 |
- return nil |
|
| 324 |
-} |
|
| 325 |
- |
|
| 326 |
-// RemoveDeviceDeferred is a useful helper for cleaning up a device, but deferred. |
|
| 327 |
-func RemoveDeviceDeferred(name string) error {
|
|
| 328 |
- logrus.Debugf("devicemapper: RemoveDeviceDeferred START(%s)", name)
|
|
| 329 |
- defer logrus.Debugf("devicemapper: RemoveDeviceDeferred END(%s)", name)
|
|
| 330 |
- task, err := TaskCreateNamed(deviceRemove, name) |
|
| 331 |
- if task == nil {
|
|
| 332 |
- return err |
|
| 333 |
- } |
|
| 334 |
- |
|
| 335 |
- if err := DmTaskDeferredRemove(task.unmanaged); err != 1 {
|
|
| 336 |
- return ErrTaskDeferredRemove |
|
| 337 |
- } |
|
| 338 |
- |
|
| 339 |
- // set a task cookie and disable library fallback, or else libdevmapper will |
|
| 340 |
- // disable udev dm rules and delete the symlink under /dev/mapper by itself, |
|
| 341 |
- // even if the removal is deferred by the kernel. |
|
| 342 |
- cookie := new(uint) |
|
| 343 |
- flags := uint16(DmUdevDisableLibraryFallback) |
|
| 344 |
- if err := task.setCookie(cookie, flags); err != nil {
|
|
| 345 |
- return fmt.Errorf("devicemapper: Can not set cookie: %s", err)
|
|
| 346 |
- } |
|
| 347 |
- |
|
| 348 |
- // libdevmapper and udev relies on System V semaphore for synchronization, |
|
| 349 |
- // semaphores created in `task.setCookie` will be cleaned up in `UdevWait`. |
|
| 350 |
- // So these two function call must come in pairs, otherwise semaphores will |
|
| 351 |
- // be leaked, and the limit of number of semaphores defined in `/proc/sys/kernel/sem` |
|
| 352 |
- // will be reached, which will eventually make all following calls to 'task.SetCookie' |
|
| 353 |
- // fail. |
|
| 354 |
- // this call will not wait for the deferred removal's final executing, since no |
|
| 355 |
- // udev event will be generated, and the semaphore's value will not be incremented |
|
| 356 |
- // by udev, what UdevWait is just cleaning up the semaphore. |
|
| 357 |
- defer UdevWait(cookie) |
|
| 358 |
- |
|
| 359 |
- dmSawEnxio = false |
|
| 360 |
- if err = task.run(); err != nil {
|
|
| 361 |
- if dmSawEnxio {
|
|
| 362 |
- return ErrEnxio |
|
| 363 |
- } |
|
| 364 |
- return fmt.Errorf("devicemapper: Error running RemoveDeviceDeferred %s", err)
|
|
| 365 |
- } |
|
| 366 |
- |
|
| 367 |
- return nil |
|
| 368 |
-} |
|
| 369 |
- |
|
| 370 |
-// CancelDeferredRemove cancels a deferred remove for a device. |
|
| 371 |
-func CancelDeferredRemove(deviceName string) error {
|
|
| 372 |
- task, err := TaskCreateNamed(deviceTargetMsg, deviceName) |
|
| 373 |
- if task == nil {
|
|
| 374 |
- return err |
|
| 375 |
- } |
|
| 376 |
- |
|
| 377 |
- if err := task.setSector(0); err != nil {
|
|
| 378 |
- return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
|
| 379 |
- } |
|
| 380 |
- |
|
| 381 |
- if err := task.setMessage("@cancel_deferred_remove"); err != nil {
|
|
| 382 |
- return fmt.Errorf("devicemapper: Can't set message %s", err)
|
|
| 383 |
- } |
|
| 384 |
- |
|
| 385 |
- dmSawBusy = false |
|
| 386 |
- dmSawEnxio = false |
|
| 387 |
- if err := task.run(); err != nil {
|
|
| 388 |
- // A device might be being deleted already |
|
| 389 |
- if dmSawBusy {
|
|
| 390 |
- return ErrBusy |
|
| 391 |
- } else if dmSawEnxio {
|
|
| 392 |
- return ErrEnxio |
|
| 393 |
- } |
|
| 394 |
- return fmt.Errorf("devicemapper: Error running CancelDeferredRemove %s", err)
|
|
| 395 |
- } |
|
| 396 |
- return nil |
|
| 397 |
-} |
|
| 398 |
- |
|
| 399 |
-// GetBlockDeviceSize returns the size of a block device identified by the specified file. |
|
| 400 |
-func GetBlockDeviceSize(file *os.File) (uint64, error) {
|
|
| 401 |
- size, err := ioctlBlkGetSize64(file.Fd()) |
|
| 402 |
- if err != nil {
|
|
| 403 |
- logrus.Errorf("devicemapper: Error getblockdevicesize: %s", err)
|
|
| 404 |
- return 0, ErrGetBlockSize |
|
| 405 |
- } |
|
| 406 |
- return uint64(size), nil |
|
| 407 |
-} |
|
| 408 |
- |
|
| 409 |
-// BlockDeviceDiscard runs discard for the given path. |
|
| 410 |
-// This is used as a workaround for the kernel not discarding block so |
|
| 411 |
-// on the thin pool when we remove a thinp device, so we do it |
|
| 412 |
-// manually |
|
| 413 |
-func BlockDeviceDiscard(path string) error {
|
|
| 414 |
- file, err := os.OpenFile(path, os.O_RDWR, 0) |
|
| 415 |
- if err != nil {
|
|
| 416 |
- return err |
|
| 417 |
- } |
|
| 418 |
- defer file.Close() |
|
| 419 |
- |
|
| 420 |
- size, err := GetBlockDeviceSize(file) |
|
| 421 |
- if err != nil {
|
|
| 422 |
- return err |
|
| 423 |
- } |
|
| 424 |
- |
|
| 425 |
- if err := ioctlBlkDiscard(file.Fd(), 0, size); err != nil {
|
|
| 426 |
- return err |
|
| 427 |
- } |
|
| 428 |
- |
|
| 429 |
- // Without this sometimes the remove of the device that happens after |
|
| 430 |
- // discard fails with EBUSY. |
|
| 431 |
- unix.Sync() |
|
| 432 |
- |
|
| 433 |
- return nil |
|
| 434 |
-} |
|
| 435 |
- |
|
| 436 |
-// CreatePool is the programmatic example of "dmsetup create". |
|
| 437 |
-// It creates a device with the specified poolName, data and metadata file and block size. |
|
| 438 |
-func CreatePool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
|
|
| 439 |
- task, err := TaskCreateNamed(deviceCreate, poolName) |
|
| 440 |
- if task == nil {
|
|
| 441 |
- return err |
|
| 442 |
- } |
|
| 443 |
- |
|
| 444 |
- size, err := GetBlockDeviceSize(dataFile) |
|
| 445 |
- if err != nil {
|
|
| 446 |
- return fmt.Errorf("devicemapper: Can't get data size %s", err)
|
|
| 447 |
- } |
|
| 448 |
- |
|
| 449 |
- params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
|
|
| 450 |
- if err := task.addTarget(0, size/512, "thin-pool", params); err != nil {
|
|
| 451 |
- return fmt.Errorf("devicemapper: Can't add target %s", err)
|
|
| 452 |
- } |
|
| 453 |
- |
|
| 454 |
- cookie := new(uint) |
|
| 455 |
- flags := uint16(DmUdevDisableSubsystemRulesFlag | DmUdevDisableDiskRulesFlag | DmUdevDisableOtherRulesFlag) |
|
| 456 |
- if err := task.setCookie(cookie, flags); err != nil {
|
|
| 457 |
- return fmt.Errorf("devicemapper: Can't set cookie %s", err)
|
|
| 458 |
- } |
|
| 459 |
- defer UdevWait(cookie) |
|
| 460 |
- |
|
| 461 |
- if err := task.run(); err != nil {
|
|
| 462 |
- return fmt.Errorf("devicemapper: Error running deviceCreate (CreatePool) %s", err)
|
|
| 463 |
- } |
|
| 464 |
- |
|
| 465 |
- return nil |
|
| 466 |
-} |
|
| 467 |
- |
|
| 468 |
-// ReloadPool is the programmatic example of "dmsetup reload". |
|
| 469 |
-// It reloads the table with the specified poolName, data and metadata file and block size. |
|
| 470 |
-func ReloadPool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
|
|
| 471 |
- task, err := TaskCreateNamed(deviceReload, poolName) |
|
| 472 |
- if task == nil {
|
|
| 473 |
- return err |
|
| 474 |
- } |
|
| 475 |
- |
|
| 476 |
- size, err := GetBlockDeviceSize(dataFile) |
|
| 477 |
- if err != nil {
|
|
| 478 |
- return fmt.Errorf("devicemapper: Can't get data size %s", err)
|
|
| 479 |
- } |
|
| 480 |
- |
|
| 481 |
- params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
|
|
| 482 |
- if err := task.addTarget(0, size/512, "thin-pool", params); err != nil {
|
|
| 483 |
- return fmt.Errorf("devicemapper: Can't add target %s", err)
|
|
| 484 |
- } |
|
| 485 |
- |
|
| 486 |
- if err := task.run(); err != nil {
|
|
| 487 |
- return fmt.Errorf("devicemapper: Error running ReloadPool %s", err)
|
|
| 488 |
- } |
|
| 489 |
- |
|
| 490 |
- return nil |
|
| 491 |
-} |
|
| 492 |
- |
|
| 493 |
-// GetDeps is the programmatic example of "dmsetup deps". |
|
| 494 |
-// It outputs a list of devices referenced by the live table for the specified device. |
|
| 495 |
-func GetDeps(name string) (*Deps, error) {
|
|
| 496 |
- task, err := TaskCreateNamed(deviceDeps, name) |
|
| 497 |
- if task == nil {
|
|
| 498 |
- return nil, err |
|
| 499 |
- } |
|
| 500 |
- if err := task.run(); err != nil {
|
|
| 501 |
- return nil, err |
|
| 502 |
- } |
|
| 503 |
- return task.getDeps() |
|
| 504 |
-} |
|
| 505 |
- |
|
| 506 |
-// GetInfo is the programmatic example of "dmsetup info". |
|
| 507 |
-// It outputs some brief information about the device. |
|
| 508 |
-func GetInfo(name string) (*Info, error) {
|
|
| 509 |
- task, err := TaskCreateNamed(deviceInfo, name) |
|
| 510 |
- if task == nil {
|
|
| 511 |
- return nil, err |
|
| 512 |
- } |
|
| 513 |
- if err := task.run(); err != nil {
|
|
| 514 |
- return nil, err |
|
| 515 |
- } |
|
| 516 |
- return task.getInfo() |
|
| 517 |
-} |
|
| 518 |
- |
|
| 519 |
-// GetInfoWithDeferred is the programmatic example of "dmsetup info", but deferred. |
|
| 520 |
-// It outputs some brief information about the device. |
|
| 521 |
-func GetInfoWithDeferred(name string) (*Info, error) {
|
|
| 522 |
- task, err := TaskCreateNamed(deviceInfo, name) |
|
| 523 |
- if task == nil {
|
|
| 524 |
- return nil, err |
|
| 525 |
- } |
|
| 526 |
- if err := task.run(); err != nil {
|
|
| 527 |
- return nil, err |
|
| 528 |
- } |
|
| 529 |
- return task.getInfoWithDeferred() |
|
| 530 |
-} |
|
| 531 |
- |
|
| 532 |
-// GetDriverVersion is the programmatic example of "dmsetup version". |
|
| 533 |
-// It outputs version information of the driver. |
|
| 534 |
-func GetDriverVersion() (string, error) {
|
|
| 535 |
- task := TaskCreate(deviceVersion) |
|
| 536 |
- if task == nil {
|
|
| 537 |
- return "", fmt.Errorf("devicemapper: Can't create deviceVersion task")
|
|
| 538 |
- } |
|
| 539 |
- if err := task.run(); err != nil {
|
|
| 540 |
- return "", err |
|
| 541 |
- } |
|
| 542 |
- return task.getDriverVersion() |
|
| 543 |
-} |
|
| 544 |
- |
|
| 545 |
-// GetStatus is the programmatic example of "dmsetup status". |
|
| 546 |
-// It outputs status information for the specified device name. |
|
| 547 |
-func GetStatus(name string) (uint64, uint64, string, string, error) {
|
|
| 548 |
- task, err := TaskCreateNamed(deviceStatus, name) |
|
| 549 |
- if task == nil {
|
|
| 550 |
- logrus.Debugf("devicemapper: GetStatus() Error TaskCreateNamed: %s", err)
|
|
| 551 |
- return 0, 0, "", "", err |
|
| 552 |
- } |
|
| 553 |
- if err := task.run(); err != nil {
|
|
| 554 |
- logrus.Debugf("devicemapper: GetStatus() Error Run: %s", err)
|
|
| 555 |
- return 0, 0, "", "", err |
|
| 556 |
- } |
|
| 557 |
- |
|
| 558 |
- devinfo, err := task.getInfo() |
|
| 559 |
- if err != nil {
|
|
| 560 |
- logrus.Debugf("devicemapper: GetStatus() Error GetInfo: %s", err)
|
|
| 561 |
- return 0, 0, "", "", err |
|
| 562 |
- } |
|
| 563 |
- if devinfo.Exists == 0 {
|
|
| 564 |
- logrus.Debugf("devicemapper: GetStatus() Non existing device %s", name)
|
|
| 565 |
- return 0, 0, "", "", fmt.Errorf("devicemapper: Non existing device %s", name)
|
|
| 566 |
- } |
|
| 567 |
- |
|
| 568 |
- _, start, length, targetType, params := task.getNextTarget(unsafe.Pointer(nil)) |
|
| 569 |
- return start, length, targetType, params, nil |
|
| 570 |
-} |
|
| 571 |
- |
|
| 572 |
-// GetTable is the programmatic example for "dmsetup table". |
|
| 573 |
-// It outputs the current table for the specified device name. |
|
| 574 |
-func GetTable(name string) (uint64, uint64, string, string, error) {
|
|
| 575 |
- task, err := TaskCreateNamed(deviceTable, name) |
|
| 576 |
- if task == nil {
|
|
| 577 |
- logrus.Debugf("devicemapper: GetTable() Error TaskCreateNamed: %s", err)
|
|
| 578 |
- return 0, 0, "", "", err |
|
| 579 |
- } |
|
| 580 |
- if err := task.run(); err != nil {
|
|
| 581 |
- logrus.Debugf("devicemapper: GetTable() Error Run: %s", err)
|
|
| 582 |
- return 0, 0, "", "", err |
|
| 583 |
- } |
|
| 584 |
- |
|
| 585 |
- devinfo, err := task.getInfo() |
|
| 586 |
- if err != nil {
|
|
| 587 |
- logrus.Debugf("devicemapper: GetTable() Error GetInfo: %s", err)
|
|
| 588 |
- return 0, 0, "", "", err |
|
| 589 |
- } |
|
| 590 |
- if devinfo.Exists == 0 {
|
|
| 591 |
- logrus.Debugf("devicemapper: GetTable() Non existing device %s", name)
|
|
| 592 |
- return 0, 0, "", "", fmt.Errorf("devicemapper: Non existing device %s", name)
|
|
| 593 |
- } |
|
| 594 |
- |
|
| 595 |
- _, start, length, targetType, params := task.getNextTarget(unsafe.Pointer(nil)) |
|
| 596 |
- return start, length, targetType, params, nil |
|
| 597 |
-} |
|
| 598 |
- |
|
| 599 |
-// SetTransactionID sets a transaction id for the specified device name. |
|
| 600 |
-func SetTransactionID(poolName string, oldID uint64, newID uint64) error {
|
|
| 601 |
- task, err := TaskCreateNamed(deviceTargetMsg, poolName) |
|
| 602 |
- if task == nil {
|
|
| 603 |
- return err |
|
| 604 |
- } |
|
| 605 |
- |
|
| 606 |
- if err := task.setSector(0); err != nil {
|
|
| 607 |
- return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
|
| 608 |
- } |
|
| 609 |
- |
|
| 610 |
- if err := task.setMessage(fmt.Sprintf("set_transaction_id %d %d", oldID, newID)); err != nil {
|
|
| 611 |
- return fmt.Errorf("devicemapper: Can't set message %s", err)
|
|
| 612 |
- } |
|
| 613 |
- |
|
| 614 |
- if err := task.run(); err != nil {
|
|
| 615 |
- return fmt.Errorf("devicemapper: Error running SetTransactionID %s", err)
|
|
| 616 |
- } |
|
| 617 |
- return nil |
|
| 618 |
-} |
|
| 619 |
- |
|
| 620 |
-// SuspendDevice is the programmatic example of "dmsetup suspend". |
|
| 621 |
-// It suspends the specified device. |
|
| 622 |
-func SuspendDevice(name string) error {
|
|
| 623 |
- task, err := TaskCreateNamed(deviceSuspend, name) |
|
| 624 |
- if task == nil {
|
|
| 625 |
- return err |
|
| 626 |
- } |
|
| 627 |
- if err := task.run(); err != nil {
|
|
| 628 |
- return fmt.Errorf("devicemapper: Error running deviceSuspend %s", err)
|
|
| 629 |
- } |
|
| 630 |
- return nil |
|
| 631 |
-} |
|
| 632 |
- |
|
| 633 |
-// ResumeDevice is the programmatic example of "dmsetup resume". |
|
| 634 |
-// It un-suspends the specified device. |
|
| 635 |
-func ResumeDevice(name string) error {
|
|
| 636 |
- task, err := TaskCreateNamed(deviceResume, name) |
|
| 637 |
- if task == nil {
|
|
| 638 |
- return err |
|
| 639 |
- } |
|
| 640 |
- |
|
| 641 |
- cookie := new(uint) |
|
| 642 |
- if err := task.setCookie(cookie, 0); err != nil {
|
|
| 643 |
- return fmt.Errorf("devicemapper: Can't set cookie %s", err)
|
|
| 644 |
- } |
|
| 645 |
- defer UdevWait(cookie) |
|
| 646 |
- |
|
| 647 |
- if err := task.run(); err != nil {
|
|
| 648 |
- return fmt.Errorf("devicemapper: Error running deviceResume %s", err)
|
|
| 649 |
- } |
|
| 650 |
- |
|
| 651 |
- return nil |
|
| 652 |
-} |
|
| 653 |
- |
|
| 654 |
-// CreateDevice creates a device with the specified poolName with the specified device id. |
|
| 655 |
-func CreateDevice(poolName string, deviceID int) error {
|
|
| 656 |
- logrus.Debugf("devicemapper: CreateDevice(poolName=%v, deviceID=%v)", poolName, deviceID)
|
|
| 657 |
- task, err := TaskCreateNamed(deviceTargetMsg, poolName) |
|
| 658 |
- if task == nil {
|
|
| 659 |
- return err |
|
| 660 |
- } |
|
| 661 |
- |
|
| 662 |
- if err := task.setSector(0); err != nil {
|
|
| 663 |
- return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
|
| 664 |
- } |
|
| 665 |
- |
|
| 666 |
- if err := task.setMessage(fmt.Sprintf("create_thin %d", deviceID)); err != nil {
|
|
| 667 |
- return fmt.Errorf("devicemapper: Can't set message %s", err)
|
|
| 668 |
- } |
|
| 669 |
- |
|
| 670 |
- dmSawExist = false // reset before the task is run |
|
| 671 |
- if err := task.run(); err != nil {
|
|
| 672 |
- // Caller wants to know about ErrDeviceIDExists so that it can try with a different device id. |
|
| 673 |
- if dmSawExist {
|
|
| 674 |
- return ErrDeviceIDExists |
|
| 675 |
- } |
|
| 676 |
- |
|
| 677 |
- return fmt.Errorf("devicemapper: Error running CreateDevice %s", err)
|
|
| 678 |
- } |
|
| 679 |
- return nil |
|
| 680 |
-} |
|
| 681 |
- |
|
| 682 |
-// DeleteDevice deletes a device with the specified poolName with the specified device id. |
|
| 683 |
-func DeleteDevice(poolName string, deviceID int) error {
|
|
| 684 |
- task, err := TaskCreateNamed(deviceTargetMsg, poolName) |
|
| 685 |
- if task == nil {
|
|
| 686 |
- return err |
|
| 687 |
- } |
|
| 688 |
- |
|
| 689 |
- if err := task.setSector(0); err != nil {
|
|
| 690 |
- return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
|
| 691 |
- } |
|
| 692 |
- |
|
| 693 |
- if err := task.setMessage(fmt.Sprintf("delete %d", deviceID)); err != nil {
|
|
| 694 |
- return fmt.Errorf("devicemapper: Can't set message %s", err)
|
|
| 695 |
- } |
|
| 696 |
- |
|
| 697 |
- dmSawBusy = false |
|
| 698 |
- dmSawEnoData = false |
|
| 699 |
- if err := task.run(); err != nil {
|
|
| 700 |
- if dmSawBusy {
|
|
| 701 |
- return ErrBusy |
|
| 702 |
- } |
|
| 703 |
- if dmSawEnoData {
|
|
| 704 |
- logrus.Debugf("devicemapper: Device(id: %d) from pool(%s) does not exist", deviceID, poolName)
|
|
| 705 |
- return nil |
|
| 706 |
- } |
|
| 707 |
- return fmt.Errorf("devicemapper: Error running DeleteDevice %s", err)
|
|
| 708 |
- } |
|
| 709 |
- return nil |
|
| 710 |
-} |
|
| 711 |
- |
|
| 712 |
-// ActivateDevice activates the device identified by the specified |
|
| 713 |
-// poolName, name and deviceID with the specified size. |
|
| 714 |
-func ActivateDevice(poolName string, name string, deviceID int, size uint64) error {
|
|
| 715 |
- return activateDevice(poolName, name, deviceID, size, "") |
|
| 716 |
-} |
|
| 717 |
- |
|
| 718 |
-// ActivateDeviceWithExternal activates the device identified by the specified |
|
| 719 |
-// poolName, name and deviceID with the specified size. |
|
| 720 |
-func ActivateDeviceWithExternal(poolName string, name string, deviceID int, size uint64, external string) error {
|
|
| 721 |
- return activateDevice(poolName, name, deviceID, size, external) |
|
| 722 |
-} |
|
| 723 |
- |
|
| 724 |
-func activateDevice(poolName string, name string, deviceID int, size uint64, external string) error {
|
|
| 725 |
- task, err := TaskCreateNamed(deviceCreate, name) |
|
| 726 |
- if task == nil {
|
|
| 727 |
- return err |
|
| 728 |
- } |
|
| 729 |
- |
|
| 730 |
- var params string |
|
| 731 |
- if len(external) > 0 {
|
|
| 732 |
- params = fmt.Sprintf("%s %d %s", poolName, deviceID, external)
|
|
| 733 |
- } else {
|
|
| 734 |
- params = fmt.Sprintf("%s %d", poolName, deviceID)
|
|
| 735 |
- } |
|
| 736 |
- if err := task.addTarget(0, size/512, "thin", params); err != nil {
|
|
| 737 |
- return fmt.Errorf("devicemapper: Can't add target %s", err)
|
|
| 738 |
- } |
|
| 739 |
- if err := task.setAddNode(addNodeOnCreate); err != nil {
|
|
| 740 |
- return fmt.Errorf("devicemapper: Can't add node %s", err)
|
|
| 741 |
- } |
|
| 742 |
- |
|
| 743 |
- cookie := new(uint) |
|
| 744 |
- if err := task.setCookie(cookie, 0); err != nil {
|
|
| 745 |
- return fmt.Errorf("devicemapper: Can't set cookie %s", err)
|
|
| 746 |
- } |
|
| 747 |
- |
|
| 748 |
- defer UdevWait(cookie) |
|
| 749 |
- |
|
| 750 |
- if err := task.run(); err != nil {
|
|
| 751 |
- return fmt.Errorf("devicemapper: Error running deviceCreate (ActivateDevice) %s", err)
|
|
| 752 |
- } |
|
| 753 |
- |
|
| 754 |
- return nil |
|
| 755 |
-} |
|
| 756 |
- |
|
| 757 |
-// CreateSnapDeviceRaw creates a snapshot device. Caller needs to suspend and resume the origin device if it is active. |
|
| 758 |
-func CreateSnapDeviceRaw(poolName string, deviceID int, baseDeviceID int) error {
|
|
| 759 |
- task, err := TaskCreateNamed(deviceTargetMsg, poolName) |
|
| 760 |
- if task == nil {
|
|
| 761 |
- return err |
|
| 762 |
- } |
|
| 763 |
- |
|
| 764 |
- if err := task.setSector(0); err != nil {
|
|
| 765 |
- return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
|
| 766 |
- } |
|
| 767 |
- |
|
| 768 |
- if err := task.setMessage(fmt.Sprintf("create_snap %d %d", deviceID, baseDeviceID)); err != nil {
|
|
| 769 |
- return fmt.Errorf("devicemapper: Can't set message %s", err)
|
|
| 770 |
- } |
|
| 771 |
- |
|
| 772 |
- dmSawExist = false // reset before the task is run |
|
| 773 |
- if err := task.run(); err != nil {
|
|
| 774 |
- // Caller wants to know about ErrDeviceIDExists so that it can try with a different device id. |
|
| 775 |
- if dmSawExist {
|
|
| 776 |
- return ErrDeviceIDExists |
|
| 777 |
- } |
|
| 778 |
- return fmt.Errorf("devicemapper: Error running deviceCreate (CreateSnapDeviceRaw) %s", err)
|
|
| 779 |
- } |
|
| 780 |
- |
|
| 781 |
- return nil |
|
| 782 |
-} |
|
| 783 |
- |
|
| 784 |
-// CreateSnapDevice creates a snapshot based on the device identified by the baseName and baseDeviceId, |
|
| 785 |
-func CreateSnapDevice(poolName string, deviceID int, baseName string, baseDeviceID int) error {
|
|
| 786 |
- devinfo, _ := GetInfo(baseName) |
|
| 787 |
- doSuspend := devinfo != nil && devinfo.Exists != 0 |
|
| 788 |
- |
|
| 789 |
- if doSuspend {
|
|
| 790 |
- if err := SuspendDevice(baseName); err != nil {
|
|
| 791 |
- return err |
|
| 792 |
- } |
|
| 793 |
- } |
|
| 794 |
- |
|
| 795 |
- if err := CreateSnapDeviceRaw(poolName, deviceID, baseDeviceID); err != nil {
|
|
| 796 |
- if doSuspend {
|
|
| 797 |
- if err2 := ResumeDevice(baseName); err2 != nil {
|
|
| 798 |
- return fmt.Errorf("CreateSnapDeviceRaw Error: (%v): ResumeDevice Error: (%v)", err, err2)
|
|
| 799 |
- } |
|
| 800 |
- } |
|
| 801 |
- return err |
|
| 802 |
- } |
|
| 803 |
- |
|
| 804 |
- if doSuspend {
|
|
| 805 |
- if err := ResumeDevice(baseName); err != nil {
|
|
| 806 |
- return err |
|
| 807 |
- } |
|
| 808 |
- } |
|
| 809 |
- |
|
| 810 |
- return nil |
|
| 811 |
-} |
| 812 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,126 +0,0 @@ |
| 1 |
-//go:build linux && cgo |
|
| 2 |
-// +build linux,cgo |
|
| 3 |
- |
|
| 4 |
-package devicemapper // import "github.com/docker/docker/pkg/devicemapper" |
|
| 5 |
- |
|
| 6 |
-import "C" |
|
| 7 |
- |
|
| 8 |
-import ( |
|
| 9 |
- "fmt" |
|
| 10 |
- "strings" |
|
| 11 |
- |
|
| 12 |
- "github.com/sirupsen/logrus" |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-// DevmapperLogger defines methods required to register as a callback for |
|
| 16 |
-// logging events received from devicemapper. Note that devicemapper will send |
|
| 17 |
-// *all* logs regardless to callbacks (including debug logs) so it's |
|
| 18 |
-// recommended to not spam the console with the outputs. |
|
| 19 |
-type DevmapperLogger interface {
|
|
| 20 |
- // DMLog is the logging callback containing all of the information from |
|
| 21 |
- // devicemapper. The interface is identical to the C libdm counterpart. |
|
| 22 |
- DMLog(level int, file string, line int, dmError int, message string) |
|
| 23 |
-} |
|
| 24 |
- |
|
| 25 |
-// dmLogger is the current logger in use that is being forwarded our messages. |
|
| 26 |
-var dmLogger DevmapperLogger |
|
| 27 |
- |
|
| 28 |
-// LogInit changes the logging callback called after processing libdm logs for |
|
| 29 |
-// error message information. The default logger simply forwards all logs to |
|
| 30 |
-// logrus. Calling LogInit(nil) disables the calling of callbacks. |
|
| 31 |
-func LogInit(logger DevmapperLogger) {
|
|
| 32 |
- dmLogger = logger |
|
| 33 |
-} |
|
| 34 |
- |
|
| 35 |
-// Due to the way cgo works this has to be in a separate file, as devmapper.go has |
|
| 36 |
-// definitions in the cgo block, which is incompatible with using "//export" |
|
| 37 |
- |
|
| 38 |
-// DevmapperLogCallback exports the devmapper log callback for cgo. Note that |
|
| 39 |
-// because we are using callbacks, this function will be called for *every* log |
|
| 40 |
-// in libdm (even debug ones because there's no way of setting the verbosity |
|
| 41 |
-// level for an external logging callback). |
|
| 42 |
-// |
|
| 43 |
-//export DevmapperLogCallback |
|
| 44 |
-func DevmapperLogCallback(level C.int, file *C.char, line, dmErrnoOrClass C.int, message *C.char) {
|
|
| 45 |
- msg := C.GoString(message) |
|
| 46 |
- |
|
| 47 |
- // Track what errno libdm saw, because the library only gives us 0 or 1. |
|
| 48 |
- if level < LogLevelDebug {
|
|
| 49 |
- if strings.Contains(msg, "busy") {
|
|
| 50 |
- dmSawBusy = true |
|
| 51 |
- } |
|
| 52 |
- |
|
| 53 |
- if strings.Contains(msg, "File exists") {
|
|
| 54 |
- dmSawExist = true |
|
| 55 |
- } |
|
| 56 |
- |
|
| 57 |
- if strings.Contains(msg, "No such device or address") {
|
|
| 58 |
- dmSawEnxio = true |
|
| 59 |
- } |
|
| 60 |
- if strings.Contains(msg, "No data available") {
|
|
| 61 |
- dmSawEnoData = true |
|
| 62 |
- } |
|
| 63 |
- } |
|
| 64 |
- |
|
| 65 |
- if dmLogger != nil {
|
|
| 66 |
- dmLogger.DMLog(int(level), C.GoString(file), int(line), int(dmErrnoOrClass), msg) |
|
| 67 |
- } |
|
| 68 |
-} |
|
| 69 |
- |
|
| 70 |
-// DefaultLogger is the default logger used by pkg/devicemapper. It forwards |
|
| 71 |
-// all logs that are of higher or equal priority to the given level to the |
|
| 72 |
-// corresponding logrus level. |
|
| 73 |
-type DefaultLogger struct {
|
|
| 74 |
- // Level corresponds to the highest libdm level that will be forwarded to |
|
| 75 |
- // logrus. In order to change this, register a new DefaultLogger. |
|
| 76 |
- Level int |
|
| 77 |
-} |
|
| 78 |
- |
|
| 79 |
-// DMLog is the logging callback containing all of the information from |
|
| 80 |
-// devicemapper. The interface is identical to the C libdm counterpart. |
|
| 81 |
-func (l DefaultLogger) DMLog(level int, file string, line, dmError int, message string) {
|
|
| 82 |
- if level <= l.Level {
|
|
| 83 |
- // Forward the log to the correct logrus level, if allowed by dmLogLevel. |
|
| 84 |
- logMsg := fmt.Sprintf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
|
|
| 85 |
- switch level {
|
|
| 86 |
- case LogLevelFatal, LogLevelErr: |
|
| 87 |
- logrus.Error(logMsg) |
|
| 88 |
- case LogLevelWarn: |
|
| 89 |
- logrus.Warn(logMsg) |
|
| 90 |
- case LogLevelNotice, LogLevelInfo: |
|
| 91 |
- logrus.Info(logMsg) |
|
| 92 |
- case LogLevelDebug: |
|
| 93 |
- logrus.Debug(logMsg) |
|
| 94 |
- default: |
|
| 95 |
- // Don't drop any "unknown" levels. |
|
| 96 |
- logrus.Info(logMsg) |
|
| 97 |
- } |
|
| 98 |
- } |
|
| 99 |
-} |
|
| 100 |
- |
|
| 101 |
-// registerLogCallback registers our own logging callback function for libdm |
|
| 102 |
-// (which is DevmapperLogCallback). |
|
| 103 |
-// |
|
| 104 |
-// Because libdm only gives us {0,1} error codes we need to parse the logs
|
|
| 105 |
-// produced by libdm (to set dmSawBusy and so on). Note that by registering a |
|
| 106 |
-// callback using DevmapperLogCallback, libdm will no longer output logs to |
|
| 107 |
-// stderr so we have to log everything ourselves. None of this handling is |
|
| 108 |
-// optional because we depend on log callbacks to parse the logs, and if we |
|
| 109 |
-// don't forward the log information we'll be in a lot of trouble when |
|
| 110 |
-// debugging things. |
|
| 111 |
-func registerLogCallback() {
|
|
| 112 |
- LogWithErrnoInit() |
|
| 113 |
-} |
|
| 114 |
- |
|
| 115 |
-func init() {
|
|
| 116 |
- // Use the default logger by default. We only allow LogLevelFatal by |
|
| 117 |
- // default, because internally we mask a lot of libdm errors by retrying |
|
| 118 |
- // and similar tricks. Also, libdm is very chatty and we don't want to |
|
| 119 |
- // worry users for no reason. |
|
| 120 |
- dmLogger = DefaultLogger{
|
|
| 121 |
- Level: LogLevelFatal, |
|
| 122 |
- } |
|
| 123 |
- |
|
| 124 |
- // Register as early as possible so we don't miss anything. |
|
| 125 |
- registerLogCallback() |
|
| 126 |
-} |
| 127 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,246 +0,0 @@ |
| 1 |
-//go:build linux && cgo |
|
| 2 |
-// +build linux,cgo |
|
| 3 |
- |
|
| 4 |
-package devicemapper // import "github.com/docker/docker/pkg/devicemapper" |
|
| 5 |
- |
|
| 6 |
-/* |
|
| 7 |
-#define _GNU_SOURCE |
|
| 8 |
-#include <libdevmapper.h> |
|
| 9 |
-#include <linux/fs.h> // FIXME: present only for BLKGETSIZE64, maybe we can remove it? |
|
| 10 |
- |
|
| 11 |
-// FIXME: Can't we find a way to do the logging in pure Go? |
|
| 12 |
-extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str); |
|
| 13 |
- |
|
| 14 |
-static void log_cb(int level, const char *file, int line, int dm_errno_or_class, const char *f, ...) |
|
| 15 |
-{
|
|
| 16 |
- char *buffer = NULL; |
|
| 17 |
- va_list ap; |
|
| 18 |
- int ret; |
|
| 19 |
- |
|
| 20 |
- va_start(ap, f); |
|
| 21 |
- ret = vasprintf(&buffer, f, ap); |
|
| 22 |
- va_end(ap); |
|
| 23 |
- if (ret < 0) {
|
|
| 24 |
- // memory allocation failed -- should never happen? |
|
| 25 |
- return; |
|
| 26 |
- } |
|
| 27 |
- |
|
| 28 |
- DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer); |
|
| 29 |
- free(buffer); |
|
| 30 |
-} |
|
| 31 |
- |
|
| 32 |
-static void log_with_errno_init() |
|
| 33 |
-{
|
|
| 34 |
- dm_log_with_errno_init(log_cb); |
|
| 35 |
-} |
|
| 36 |
-*/ |
|
| 37 |
-import "C" |
|
| 38 |
- |
|
| 39 |
-import ( |
|
| 40 |
- "reflect" |
|
| 41 |
- "unsafe" |
|
| 42 |
-) |
|
| 43 |
- |
|
| 44 |
-type ( |
|
| 45 |
- cdmTask C.struct_dm_task |
|
| 46 |
-) |
|
| 47 |
- |
|
| 48 |
-// IOCTL consts |
|
| 49 |
-const ( |
|
| 50 |
- BlkGetSize64 = C.BLKGETSIZE64 |
|
| 51 |
- BlkDiscard = C.BLKDISCARD |
|
| 52 |
-) |
|
| 53 |
- |
|
| 54 |
-// Devicemapper cookie flags. |
|
| 55 |
-const ( |
|
| 56 |
- DmUdevDisableSubsystemRulesFlag = C.DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG |
|
| 57 |
- DmUdevDisableDiskRulesFlag = C.DM_UDEV_DISABLE_DISK_RULES_FLAG |
|
| 58 |
- DmUdevDisableOtherRulesFlag = C.DM_UDEV_DISABLE_OTHER_RULES_FLAG |
|
| 59 |
- DmUdevDisableLibraryFallback = C.DM_UDEV_DISABLE_LIBRARY_FALLBACK |
|
| 60 |
-) |
|
| 61 |
- |
|
| 62 |
-// DeviceMapper mapped functions. |
|
| 63 |
-var ( |
|
| 64 |
- DmGetLibraryVersion = dmGetLibraryVersionFct |
|
| 65 |
- DmGetNextTarget = dmGetNextTargetFct |
|
| 66 |
- DmSetDevDir = dmSetDevDirFct |
|
| 67 |
- DmTaskAddTarget = dmTaskAddTargetFct |
|
| 68 |
- DmTaskCreate = dmTaskCreateFct |
|
| 69 |
- DmTaskDestroy = dmTaskDestroyFct |
|
| 70 |
- DmTaskGetDeps = dmTaskGetDepsFct |
|
| 71 |
- DmTaskGetInfo = dmTaskGetInfoFct |
|
| 72 |
- DmTaskGetDriverVersion = dmTaskGetDriverVersionFct |
|
| 73 |
- DmTaskRun = dmTaskRunFct |
|
| 74 |
- DmTaskSetAddNode = dmTaskSetAddNodeFct |
|
| 75 |
- DmTaskSetCookie = dmTaskSetCookieFct |
|
| 76 |
- DmTaskSetMessage = dmTaskSetMessageFct |
|
| 77 |
- DmTaskSetName = dmTaskSetNameFct |
|
| 78 |
- DmTaskSetSector = dmTaskSetSectorFct |
|
| 79 |
- DmUdevWait = dmUdevWaitFct |
|
| 80 |
- DmUdevSetSyncSupport = dmUdevSetSyncSupportFct |
|
| 81 |
- DmUdevGetSyncSupport = dmUdevGetSyncSupportFct |
|
| 82 |
- DmCookieSupported = dmCookieSupportedFct |
|
| 83 |
- LogWithErrnoInit = logWithErrnoInitFct |
|
| 84 |
- DmTaskDeferredRemove = dmTaskDeferredRemoveFct |
|
| 85 |
- DmTaskGetInfoWithDeferred = dmTaskGetInfoWithDeferredFct |
|
| 86 |
-) |
|
| 87 |
- |
|
| 88 |
-func free(p *C.char) {
|
|
| 89 |
- C.free(unsafe.Pointer(p)) |
|
| 90 |
-} |
|
| 91 |
- |
|
| 92 |
-func dmTaskDestroyFct(task *cdmTask) {
|
|
| 93 |
- C.dm_task_destroy((*C.struct_dm_task)(task)) |
|
| 94 |
-} |
|
| 95 |
- |
|
| 96 |
-func dmTaskCreateFct(taskType int) *cdmTask {
|
|
| 97 |
- return (*cdmTask)(C.dm_task_create(C.int(taskType))) |
|
| 98 |
-} |
|
| 99 |
- |
|
| 100 |
-func dmTaskRunFct(task *cdmTask) int {
|
|
| 101 |
- ret, _ := C.dm_task_run((*C.struct_dm_task)(task)) |
|
| 102 |
- return int(ret) |
|
| 103 |
-} |
|
| 104 |
- |
|
| 105 |
-func dmTaskSetNameFct(task *cdmTask, name string) int {
|
|
| 106 |
- Cname := C.CString(name) |
|
| 107 |
- defer free(Cname) |
|
| 108 |
- |
|
| 109 |
- return int(C.dm_task_set_name((*C.struct_dm_task)(task), Cname)) |
|
| 110 |
-} |
|
| 111 |
- |
|
| 112 |
-func dmTaskSetMessageFct(task *cdmTask, message string) int {
|
|
| 113 |
- Cmessage := C.CString(message) |
|
| 114 |
- defer free(Cmessage) |
|
| 115 |
- |
|
| 116 |
- return int(C.dm_task_set_message((*C.struct_dm_task)(task), Cmessage)) |
|
| 117 |
-} |
|
| 118 |
- |
|
| 119 |
-func dmTaskSetSectorFct(task *cdmTask, sector uint64) int {
|
|
| 120 |
- return int(C.dm_task_set_sector((*C.struct_dm_task)(task), C.uint64_t(sector))) |
|
| 121 |
-} |
|
| 122 |
- |
|
| 123 |
-func dmTaskSetCookieFct(task *cdmTask, cookie *uint, flags uint16) int {
|
|
| 124 |
- cCookie := C.uint32_t(*cookie) |
|
| 125 |
- defer func() {
|
|
| 126 |
- *cookie = uint(cCookie) |
|
| 127 |
- }() |
|
| 128 |
- return int(C.dm_task_set_cookie((*C.struct_dm_task)(task), &cCookie, C.uint16_t(flags))) |
|
| 129 |
-} |
|
| 130 |
- |
|
| 131 |
-func dmTaskSetAddNodeFct(task *cdmTask, addNode AddNodeType) int {
|
|
| 132 |
- return int(C.dm_task_set_add_node((*C.struct_dm_task)(task), C.dm_add_node_t(addNode))) |
|
| 133 |
-} |
|
| 134 |
- |
|
| 135 |
-func dmTaskAddTargetFct(task *cdmTask, start, size uint64, ttype, params string) int {
|
|
| 136 |
- Cttype := C.CString(ttype) |
|
| 137 |
- defer free(Cttype) |
|
| 138 |
- |
|
| 139 |
- Cparams := C.CString(params) |
|
| 140 |
- defer free(Cparams) |
|
| 141 |
- |
|
| 142 |
- return int(C.dm_task_add_target((*C.struct_dm_task)(task), C.uint64_t(start), C.uint64_t(size), Cttype, Cparams)) |
|
| 143 |
-} |
|
| 144 |
- |
|
| 145 |
-func dmTaskGetDepsFct(task *cdmTask) *Deps {
|
|
| 146 |
- Cdeps := C.dm_task_get_deps((*C.struct_dm_task)(task)) |
|
| 147 |
- if Cdeps == nil {
|
|
| 148 |
- return nil |
|
| 149 |
- } |
|
| 150 |
- |
|
| 151 |
- // golang issue: https://github.com/golang/go/issues/11925 |
|
| 152 |
- var devices []C.uint64_t |
|
| 153 |
- devicesHdr := (*reflect.SliceHeader)(unsafe.Pointer(&devices)) |
|
| 154 |
- devicesHdr.Data = uintptr(unsafe.Pointer(uintptr(unsafe.Pointer(Cdeps)) + unsafe.Sizeof(*Cdeps))) |
|
| 155 |
- devicesHdr.Len = int(Cdeps.count) |
|
| 156 |
- devicesHdr.Cap = int(Cdeps.count) |
|
| 157 |
- |
|
| 158 |
- deps := &Deps{
|
|
| 159 |
- Count: uint32(Cdeps.count), |
|
| 160 |
- Filler: uint32(Cdeps.filler), |
|
| 161 |
- } |
|
| 162 |
- for _, device := range devices {
|
|
| 163 |
- deps.Device = append(deps.Device, uint64(device)) |
|
| 164 |
- } |
|
| 165 |
- return deps |
|
| 166 |
-} |
|
| 167 |
- |
|
| 168 |
-func dmTaskGetInfoFct(task *cdmTask, info *Info) int {
|
|
| 169 |
- Cinfo := C.struct_dm_info{}
|
|
| 170 |
- defer func() {
|
|
| 171 |
- info.Exists = int(Cinfo.exists) |
|
| 172 |
- info.Suspended = int(Cinfo.suspended) |
|
| 173 |
- info.LiveTable = int(Cinfo.live_table) |
|
| 174 |
- info.InactiveTable = int(Cinfo.inactive_table) |
|
| 175 |
- info.OpenCount = int32(Cinfo.open_count) |
|
| 176 |
- info.EventNr = uint32(Cinfo.event_nr) |
|
| 177 |
- info.Major = uint32(Cinfo.major) |
|
| 178 |
- info.Minor = uint32(Cinfo.minor) |
|
| 179 |
- info.ReadOnly = int(Cinfo.read_only) |
|
| 180 |
- info.TargetCount = int32(Cinfo.target_count) |
|
| 181 |
- }() |
|
| 182 |
- return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo)) |
|
| 183 |
-} |
|
| 184 |
- |
|
| 185 |
-func dmTaskGetDriverVersionFct(task *cdmTask) string {
|
|
| 186 |
- buffer := C.malloc(128) |
|
| 187 |
- defer C.free(buffer) |
|
| 188 |
- res := C.dm_task_get_driver_version((*C.struct_dm_task)(task), (*C.char)(buffer), 128) |
|
| 189 |
- if res == 0 {
|
|
| 190 |
- return "" |
|
| 191 |
- } |
|
| 192 |
- return C.GoString((*C.char)(buffer)) |
|
| 193 |
-} |
|
| 194 |
- |
|
| 195 |
-func dmGetNextTargetFct(task *cdmTask, next unsafe.Pointer, start, length *uint64, target, params *string) unsafe.Pointer {
|
|
| 196 |
- var ( |
|
| 197 |
- Cstart, Clength C.uint64_t |
|
| 198 |
- CtargetType, Cparams *C.char |
|
| 199 |
- ) |
|
| 200 |
- defer func() {
|
|
| 201 |
- *start = uint64(Cstart) |
|
| 202 |
- *length = uint64(Clength) |
|
| 203 |
- *target = C.GoString(CtargetType) |
|
| 204 |
- *params = C.GoString(Cparams) |
|
| 205 |
- }() |
|
| 206 |
- |
|
| 207 |
- //lint:ignore SA4000 false positive on (identical expressions on the left and right side of the '==' operator) (staticcheck) |
|
| 208 |
- nextp := C.dm_get_next_target((*C.struct_dm_task)(task), next, &Cstart, &Clength, &CtargetType, &Cparams) |
|
| 209 |
- return nextp |
|
| 210 |
-} |
|
| 211 |
- |
|
| 212 |
-func dmUdevSetSyncSupportFct(syncWithUdev int) {
|
|
| 213 |
- C.dm_udev_set_sync_support(C.int(syncWithUdev)) |
|
| 214 |
-} |
|
| 215 |
- |
|
| 216 |
-func dmUdevGetSyncSupportFct() int {
|
|
| 217 |
- return int(C.dm_udev_get_sync_support()) |
|
| 218 |
-} |
|
| 219 |
- |
|
| 220 |
-func dmUdevWaitFct(cookie uint) int {
|
|
| 221 |
- return int(C.dm_udev_wait(C.uint32_t(cookie))) |
|
| 222 |
-} |
|
| 223 |
- |
|
| 224 |
-func dmCookieSupportedFct() int {
|
|
| 225 |
- return int(C.dm_cookie_supported()) |
|
| 226 |
-} |
|
| 227 |
- |
|
| 228 |
-func logWithErrnoInitFct() {
|
|
| 229 |
- C.log_with_errno_init() |
|
| 230 |
-} |
|
| 231 |
- |
|
| 232 |
-func dmSetDevDirFct(dir string) int {
|
|
| 233 |
- Cdir := C.CString(dir) |
|
| 234 |
- defer free(Cdir) |
|
| 235 |
- |
|
| 236 |
- return int(C.dm_set_dev_dir(Cdir)) |
|
| 237 |
-} |
|
| 238 |
- |
|
| 239 |
-func dmGetLibraryVersionFct(version *string) int {
|
|
| 240 |
- buffer := C.CString(string(make([]byte, 128))) |
|
| 241 |
- defer free(buffer) |
|
| 242 |
- defer func() {
|
|
| 243 |
- *version = C.GoString(buffer) |
|
| 244 |
- }() |
|
| 245 |
- return int(C.dm_get_library_version(buffer, 128)) |
|
| 246 |
-} |
| 8 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,35 +0,0 @@ |
| 1 |
-//go:build linux && cgo && !static_build && !libdm_dlsym_deferred_remove && !libdm_no_deferred_remove |
|
| 2 |
-// +build linux,cgo,!static_build,!libdm_dlsym_deferred_remove,!libdm_no_deferred_remove |
|
| 3 |
- |
|
| 4 |
-package devicemapper // import "github.com/docker/docker/pkg/devicemapper" |
|
| 5 |
- |
|
| 6 |
-/* |
|
| 7 |
-#include <libdevmapper.h> |
|
| 8 |
-*/ |
|
| 9 |
-import "C" |
|
| 10 |
- |
|
| 11 |
-// LibraryDeferredRemovalSupport tells if the feature is supported by the |
|
| 12 |
-// current Docker invocation. |
|
| 13 |
-const LibraryDeferredRemovalSupport = true |
|
| 14 |
- |
|
| 15 |
-func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
|
| 16 |
- return int(C.dm_task_deferred_remove((*C.struct_dm_task)(task))) |
|
| 17 |
-} |
|
| 18 |
- |
|
| 19 |
-func dmTaskGetInfoWithDeferredFct(task *cdmTask, info *Info) int {
|
|
| 20 |
- Cinfo := C.struct_dm_info{}
|
|
| 21 |
- defer func() {
|
|
| 22 |
- info.Exists = int(Cinfo.exists) |
|
| 23 |
- info.Suspended = int(Cinfo.suspended) |
|
| 24 |
- info.LiveTable = int(Cinfo.live_table) |
|
| 25 |
- info.InactiveTable = int(Cinfo.inactive_table) |
|
| 26 |
- info.OpenCount = int32(Cinfo.open_count) |
|
| 27 |
- info.EventNr = uint32(Cinfo.event_nr) |
|
| 28 |
- info.Major = uint32(Cinfo.major) |
|
| 29 |
- info.Minor = uint32(Cinfo.minor) |
|
| 30 |
- info.ReadOnly = int(Cinfo.read_only) |
|
| 31 |
- info.TargetCount = int32(Cinfo.target_count) |
|
| 32 |
- info.DeferredRemove = int(Cinfo.deferred_remove) |
|
| 33 |
- }() |
|
| 34 |
- return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo)) |
|
| 35 |
-} |
| 36 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,128 +0,0 @@ |
| 1 |
-//go:build linux && cgo && !static_build && libdm_dlsym_deferred_remove && !libdm_no_deferred_remove |
|
| 2 |
-// +build linux,cgo,!static_build,libdm_dlsym_deferred_remove,!libdm_no_deferred_remove |
|
| 3 |
- |
|
| 4 |
-package devicemapper |
|
| 5 |
- |
|
| 6 |
-/* |
|
| 7 |
-#cgo LDFLAGS: -ldl |
|
| 8 |
-#include <stdlib.h> |
|
| 9 |
-#include <dlfcn.h> |
|
| 10 |
-#include <libdevmapper.h> |
|
| 11 |
- |
|
| 12 |
-// Yes, I know this looks scary. In order to be able to fill our own internal |
|
| 13 |
-// dm_info with deferred_remove we need to have a struct definition that is |
|
| 14 |
-// correct (regardless of the version of libdm that was used to compile it). To |
|
| 15 |
-// this end, we define struct_backport_dm_info. This code comes from lvm2, and |
|
| 16 |
-// I have verified that the structure has only ever had elements *appended* to |
|
| 17 |
-// it (since 2001). |
|
| 18 |
-// |
|
| 19 |
-// It is also important that this structure be _larger_ than the dm_info that |
|
| 20 |
-// libdevmapper expected. Otherwise libdm might try to write to memory it |
|
| 21 |
-// shouldn't (they don't have a "known size" API). |
|
| 22 |
-struct backport_dm_info {
|
|
| 23 |
- int exists; |
|
| 24 |
- int suspended; |
|
| 25 |
- int live_table; |
|
| 26 |
- int inactive_table; |
|
| 27 |
- int32_t open_count; |
|
| 28 |
- uint32_t event_nr; |
|
| 29 |
- uint32_t major; |
|
| 30 |
- uint32_t minor; |
|
| 31 |
- int read_only; |
|
| 32 |
- |
|
| 33 |
- int32_t target_count; |
|
| 34 |
- |
|
| 35 |
- int deferred_remove; |
|
| 36 |
- int internal_suspend; |
|
| 37 |
- |
|
| 38 |
- // Padding, purely for our own safety. This is to avoid cases where libdm |
|
| 39 |
- // was updated underneath us and we call into dm_task_get_info() with too |
|
| 40 |
- // small of a buffer. |
|
| 41 |
- char _[512]; |
|
| 42 |
-}; |
|
| 43 |
- |
|
| 44 |
-// We have to wrap this in CGo, because Go really doesn't like function pointers. |
|
| 45 |
-int call_dm_task_deferred_remove(void *fn, struct dm_task *task) |
|
| 46 |
-{
|
|
| 47 |
- int (*_dm_task_deferred_remove)(struct dm_task *task) = fn; |
|
| 48 |
- return _dm_task_deferred_remove(task); |
|
| 49 |
-} |
|
| 50 |
-*/ |
|
| 51 |
-import "C" |
|
| 52 |
- |
|
| 53 |
-import ( |
|
| 54 |
- "unsafe" |
|
| 55 |
- |
|
| 56 |
- "github.com/sirupsen/logrus" |
|
| 57 |
-) |
|
| 58 |
- |
|
| 59 |
-// dm_task_deferred_remove is not supported by all distributions, due to |
|
| 60 |
-// out-dated versions of devicemapper. However, in the case where the |
|
| 61 |
-// devicemapper library was updated without rebuilding Docker (which can happen |
|
| 62 |
-// in some distributions) then we should attempt to dynamically load the |
|
| 63 |
-// relevant object rather than try to link to it. |
|
| 64 |
- |
|
| 65 |
-// dmTaskDeferredRemoveFct is a "bound" version of dm_task_deferred_remove. |
|
| 66 |
-// It is nil if dm_task_deferred_remove was not found in the libdevmapper that |
|
| 67 |
-// is currently loaded. |
|
| 68 |
-var dmTaskDeferredRemovePtr unsafe.Pointer |
|
| 69 |
- |
|
| 70 |
-// LibraryDeferredRemovalSupport tells if the feature is supported by the |
|
| 71 |
-// current Docker invocation. This value is fixed during init. |
|
| 72 |
-var LibraryDeferredRemovalSupport bool |
|
| 73 |
- |
|
| 74 |
-func init() {
|
|
| 75 |
- // Clear any errors. |
|
| 76 |
- var err *C.char |
|
| 77 |
- C.dlerror() |
|
| 78 |
- |
|
| 79 |
- // The symbol we want to fetch. |
|
| 80 |
- symName := C.CString("dm_task_deferred_remove")
|
|
| 81 |
- defer C.free(unsafe.Pointer(symName)) |
|
| 82 |
- |
|
| 83 |
- // See if we can find dm_task_deferred_remove. Since we already are linked |
|
| 84 |
- // to libdevmapper, we can search our own address space (rather than trying |
|
| 85 |
- // to guess what libdevmapper is called). We use NULL here, as RTLD_DEFAULT |
|
| 86 |
- // is not available in CGO (even if you set _GNU_SOURCE for some reason). |
|
| 87 |
- // The semantics are identical on glibc. |
|
| 88 |
- sym := C.dlsym(nil, symName) |
|
| 89 |
- err = C.dlerror() |
|
| 90 |
- if err != nil {
|
|
| 91 |
- logrus.Debugf("devmapper: could not load dm_task_deferred_remove: %s", C.GoString(err))
|
|
| 92 |
- return |
|
| 93 |
- } |
|
| 94 |
- |
|
| 95 |
- logrus.Debugf("devmapper: found dm_task_deferred_remove at %x", uintptr(sym))
|
|
| 96 |
- dmTaskDeferredRemovePtr = sym |
|
| 97 |
- LibraryDeferredRemovalSupport = true |
|
| 98 |
-} |
|
| 99 |
- |
|
| 100 |
-func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
|
| 101 |
- sym := dmTaskDeferredRemovePtr |
|
| 102 |
- if sym == nil || !LibraryDeferredRemovalSupport {
|
|
| 103 |
- return -1 |
|
| 104 |
- } |
|
| 105 |
- return int(C.call_dm_task_deferred_remove(sym, (*C.struct_dm_task)(task))) |
|
| 106 |
-} |
|
| 107 |
- |
|
| 108 |
-func dmTaskGetInfoWithDeferredFct(task *cdmTask, info *Info) int {
|
|
| 109 |
- if !LibraryDeferredRemovalSupport {
|
|
| 110 |
- return -1 |
|
| 111 |
- } |
|
| 112 |
- |
|
| 113 |
- Cinfo := C.struct_backport_dm_info{}
|
|
| 114 |
- defer func() {
|
|
| 115 |
- info.Exists = int(Cinfo.exists) |
|
| 116 |
- info.Suspended = int(Cinfo.suspended) |
|
| 117 |
- info.LiveTable = int(Cinfo.live_table) |
|
| 118 |
- info.InactiveTable = int(Cinfo.inactive_table) |
|
| 119 |
- info.OpenCount = int32(Cinfo.open_count) |
|
| 120 |
- info.EventNr = uint32(Cinfo.event_nr) |
|
| 121 |
- info.Major = uint32(Cinfo.major) |
|
| 122 |
- info.Minor = uint32(Cinfo.minor) |
|
| 123 |
- info.ReadOnly = int(Cinfo.read_only) |
|
| 124 |
- info.TargetCount = int32(Cinfo.target_count) |
|
| 125 |
- info.DeferredRemove = int(Cinfo.deferred_remove) |
|
| 126 |
- }() |
|
| 127 |
- return int(C.dm_task_get_info((*C.struct_dm_task)(task), (*C.struct_dm_info)(unsafe.Pointer(&Cinfo)))) |
|
| 128 |
-} |
| 129 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,17 +0,0 @@ |
| 1 |
-//go:build linux && cgo && !libdm_dlsym_deferred_remove && libdm_no_deferred_remove |
|
| 2 |
-// +build linux,cgo,!libdm_dlsym_deferred_remove,libdm_no_deferred_remove |
|
| 3 |
- |
|
| 4 |
-package devicemapper // import "github.com/docker/docker/pkg/devicemapper" |
|
| 5 |
- |
|
| 6 |
-// LibraryDeferredRemovalSupport tells if the feature is supported by the |
|
| 7 |
-// current Docker invocation. |
|
| 8 |
-const LibraryDeferredRemovalSupport = false |
|
| 9 |
- |
|
| 10 |
-func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
|
| 11 |
- // Error. Nobody should be calling it. |
|
| 12 |
- return -1 |
|
| 13 |
-} |
|
| 14 |
- |
|
| 15 |
-func dmTaskGetInfoWithDeferredFct(task *cdmTask, info *Info) int {
|
|
| 16 |
- return -1 |
|
| 17 |
-} |
| 18 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,29 +0,0 @@ |
| 1 |
-//go:build linux && cgo |
|
| 2 |
-// +build linux,cgo |
|
| 3 |
- |
|
| 4 |
-package devicemapper // import "github.com/docker/docker/pkg/devicemapper" |
|
| 5 |
- |
|
| 6 |
-import ( |
|
| 7 |
- "unsafe" |
|
| 8 |
- |
|
| 9 |
- "golang.org/x/sys/unix" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-func ioctlBlkGetSize64(fd uintptr) (int64, error) {
|
|
| 13 |
- var size int64 |
|
| 14 |
- if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, BlkGetSize64, uintptr(unsafe.Pointer(&size))); err != 0 {
|
|
| 15 |
- return 0, err |
|
| 16 |
- } |
|
| 17 |
- return size, nil |
|
| 18 |
-} |
|
| 19 |
- |
|
| 20 |
-func ioctlBlkDiscard(fd uintptr, offset, length uint64) error {
|
|
| 21 |
- var r [2]uint64 |
|
| 22 |
- r[0] = offset |
|
| 23 |
- r[1] = length |
|
| 24 |
- |
|
| 25 |
- if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, BlkDiscard, uintptr(unsafe.Pointer(&r[0]))); err != 0 {
|
|
| 26 |
- return err |
|
| 27 |
- } |
|
| 28 |
- return nil |
|
| 29 |
-} |
| 30 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,11 +0,0 @@ |
| 1 |
-package devicemapper // import "github.com/docker/docker/pkg/devicemapper" |
|
| 2 |
- |
|
| 3 |
-// definitions from lvm2 lib/log/log.h |
|
| 4 |
-const ( |
|
| 5 |
- LogLevelFatal = 2 + iota // _LOG_FATAL |
|
| 6 |
- LogLevelErr // _LOG_ERR |
|
| 7 |
- LogLevelWarn // _LOG_WARN |
|
| 8 |
- LogLevelNotice // _LOG_NOTICE |
|
| 9 |
- LogLevelInfo // _LOG_INFO |
|
| 10 |
- LogLevelDebug // _LOG_DEBUG |
|
| 11 |
-) |
| ... | ... |
@@ -89,14 +89,14 @@ To disable btrfs: |
| 89 | 89 |
export DOCKER_BUILDTAGS='exclude_graphdriver_btrfs' |
| 90 | 90 |
``` |
| 91 | 91 |
|
| 92 |
-To disable devicemapper: |
|
| 92 |
+To disable zfs: |
|
| 93 | 93 |
```bash |
| 94 |
-export DOCKER_BUILDTAGS='exclude_graphdriver_devicemapper' |
|
| 94 |
+export DOCKER_BUILDTAGS='exclude_graphdriver_zfs' |
|
| 95 | 95 |
``` |
| 96 | 96 |
|
| 97 | 97 |
NOTE: if you need to set more than one build tag, space separate them: |
| 98 | 98 |
```bash |
| 99 |
-export DOCKER_BUILDTAGS='exclude_graphdriver_devicemapper exclude_graphdriver_btrfs' |
|
| 99 |
+export DOCKER_BUILDTAGS='exclude_graphdriver_btrfs exclude_graphdriver_zfs' |
|
| 100 | 100 |
``` |
| 101 | 101 |
|
| 102 | 102 |
## System Dependencies |