Vendor in container storage
This should add quota support to cri-o Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
parent
e838611fdd
commit
29bd1c79dd
52 changed files with 2751 additions and 1881 deletions
236
vendor/github.com/containers/storage/drivers/devmapper/device_setup.go
generated
vendored
Normal file
236
vendor/github.com/containers/storage/drivers/devmapper/device_setup.go
generated
vendored
Normal file
|
@ -0,0 +1,236 @@
|
|||
package devmapper
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type directLVMConfig struct {
|
||||
Device string
|
||||
ThinpPercent uint64
|
||||
ThinpMetaPercent uint64
|
||||
AutoExtendPercent uint64
|
||||
AutoExtendThreshold uint64
|
||||
}
|
||||
|
||||
var (
|
||||
errThinpPercentMissing = errors.New("must set both `dm.thinp_percent` and `dm.thinp_metapercent` if either is specified")
|
||||
errThinpPercentTooBig = errors.New("combined `dm.thinp_percent` and `dm.thinp_metapercent` must not be greater than 100")
|
||||
errMissingSetupDevice = errors.New("must provide device path in `dm.setup_device` in order to configure direct-lvm")
|
||||
)
|
||||
|
||||
func validateLVMConfig(cfg directLVMConfig) error {
|
||||
if reflect.DeepEqual(cfg, directLVMConfig{}) {
|
||||
return nil
|
||||
}
|
||||
if cfg.Device == "" {
|
||||
return errMissingSetupDevice
|
||||
}
|
||||
if (cfg.ThinpPercent > 0 && cfg.ThinpMetaPercent == 0) || cfg.ThinpMetaPercent > 0 && cfg.ThinpPercent == 0 {
|
||||
return errThinpPercentMissing
|
||||
}
|
||||
|
||||
if cfg.ThinpPercent+cfg.ThinpMetaPercent > 100 {
|
||||
return errThinpPercentTooBig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkDevAvailable(dev string) error {
|
||||
lvmScan, err := exec.LookPath("lvmdiskscan")
|
||||
if err != nil {
|
||||
logrus.Debug("could not find lvmdiskscan")
|
||||
return nil
|
||||
}
|
||||
|
||||
out, err := exec.Command(lvmScan).CombinedOutput()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error(string(out))
|
||||
return nil
|
||||
}
|
||||
|
||||
if !bytes.Contains(out, []byte(dev)) {
|
||||
return errors.Errorf("%s is not available for use with devicemapper", dev)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkDevInVG(dev string) error {
|
||||
pvDisplay, err := exec.LookPath("pvdisplay")
|
||||
if err != nil {
|
||||
logrus.Debug("could not find pvdisplay")
|
||||
return nil
|
||||
}
|
||||
|
||||
out, err := exec.Command(pvDisplay, dev).CombinedOutput()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error(string(out))
|
||||
return nil
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(bytes.NewReader(bytes.TrimSpace(out)))
|
||||
for scanner.Scan() {
|
||||
fields := strings.SplitAfter(strings.TrimSpace(scanner.Text()), "VG Name")
|
||||
if len(fields) > 1 {
|
||||
// got "VG Name" line"
|
||||
vg := strings.TrimSpace(fields[1])
|
||||
if len(vg) > 0 {
|
||||
return errors.Errorf("%s is already part of a volume group %q: must remove this device from any volume group or provide a different device", dev, vg)
|
||||
}
|
||||
logrus.Error(fields)
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkDevHasFS(dev string) error {
|
||||
blkid, err := exec.LookPath("blkid")
|
||||
if err != nil {
|
||||
logrus.Debug("could not find blkid")
|
||||
return nil
|
||||
}
|
||||
|
||||
out, err := exec.Command(blkid, dev).CombinedOutput()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error(string(out))
|
||||
return nil
|
||||
}
|
||||
|
||||
fields := bytes.Fields(out)
|
||||
for _, f := range fields {
|
||||
kv := bytes.Split(f, []byte{'='})
|
||||
if bytes.Equal(kv[0], []byte("TYPE")) {
|
||||
v := bytes.Trim(kv[1], "\"")
|
||||
if len(v) > 0 {
|
||||
return errors.Errorf("%s has a filesystem already, use dm.directlvm_device_force=true if you want to wipe the device", dev)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyBlockDevice(dev string, force bool) error {
|
||||
if err := checkDevAvailable(dev); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkDevInVG(dev); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if force {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := checkDevHasFS(dev); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readLVMConfig(root string) (directLVMConfig, error) {
|
||||
var cfg directLVMConfig
|
||||
|
||||
p := filepath.Join(root, "setup-config.json")
|
||||
b, err := ioutil.ReadFile(p)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return cfg, nil
|
||||
}
|
||||
return cfg, errors.Wrap(err, "error reading existing setup config")
|
||||
}
|
||||
|
||||
// check if this is just an empty file, no need to produce a json error later if so
|
||||
if len(b) == 0 {
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &cfg)
|
||||
return cfg, errors.Wrap(err, "error unmarshaling previous device setup config")
|
||||
}
|
||||
|
||||
func writeLVMConfig(root string, cfg directLVMConfig) error {
|
||||
p := filepath.Join(root, "setup-config.json")
|
||||
b, err := json.Marshal(cfg)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error marshalling direct lvm config")
|
||||
}
|
||||
err = ioutil.WriteFile(p, b, 0600)
|
||||
return errors.Wrap(err, "error writing direct lvm config to file")
|
||||
}
|
||||
|
||||
func setupDirectLVM(cfg directLVMConfig) error {
|
||||
lvmProfileDir := "/etc/lvm/profile"
|
||||
binaries := []string{"pvcreate", "vgcreate", "lvcreate", "lvconvert", "lvchange", "thin_check"}
|
||||
|
||||
for _, bin := range binaries {
|
||||
if _, err := exec.LookPath(bin); err != nil {
|
||||
return errors.Wrap(err, "error looking up command `"+bin+"` while setting up direct lvm")
|
||||
}
|
||||
}
|
||||
|
||||
err := os.MkdirAll(lvmProfileDir, 0755)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error creating lvm profile directory")
|
||||
}
|
||||
|
||||
if cfg.AutoExtendPercent == 0 {
|
||||
cfg.AutoExtendPercent = 20
|
||||
}
|
||||
|
||||
if cfg.AutoExtendThreshold == 0 {
|
||||
cfg.AutoExtendThreshold = 80
|
||||
}
|
||||
|
||||
if cfg.ThinpPercent == 0 {
|
||||
cfg.ThinpPercent = 95
|
||||
}
|
||||
if cfg.ThinpMetaPercent == 0 {
|
||||
cfg.ThinpMetaPercent = 1
|
||||
}
|
||||
|
||||
out, err := exec.Command("pvcreate", "-f", cfg.Device).CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, string(out))
|
||||
}
|
||||
|
||||
out, err = exec.Command("vgcreate", "storage", cfg.Device).CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, string(out))
|
||||
}
|
||||
|
||||
out, err = exec.Command("lvcreate", "--wipesignatures", "y", "-n", "thinpool", "storage", "--extents", fmt.Sprintf("%d%%VG", cfg.ThinpPercent)).CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, string(out))
|
||||
}
|
||||
out, err = exec.Command("lvcreate", "--wipesignatures", "y", "-n", "thinpoolmeta", "storage", "--extents", fmt.Sprintf("%d%%VG", cfg.ThinpMetaPercent)).CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, string(out))
|
||||
}
|
||||
|
||||
out, err = exec.Command("lvconvert", "-y", "--zero", "n", "-c", "512K", "--thinpool", "storage/thinpool", "--poolmetadata", "storage/thinpoolmeta").CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, string(out))
|
||||
}
|
||||
|
||||
profile := fmt.Sprintf("activation{\nthin_pool_autoextend_threshold=%d\nthin_pool_autoextend_percent=%d\n}", cfg.AutoExtendThreshold, cfg.AutoExtendPercent)
|
||||
err = ioutil.WriteFile(lvmProfileDir+"/storage-thinpool.profile", []byte(profile), 0600)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error writing storage thinp autoextend profile")
|
||||
}
|
||||
|
||||
out, err = exec.Command("lvchange", "--metadataprofile", "storage-thinpool", "storage/thinpool").CombinedOutput()
|
||||
return errors.Wrap(err, string(out))
|
||||
}
|
574
vendor/github.com/containers/storage/drivers/devmapper/deviceset.go
generated
vendored
574
vendor/github.com/containers/storage/drivers/devmapper/deviceset.go
generated
vendored
|
@ -12,44 +12,41 @@ import (
|
|||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/containers/storage/drivers"
|
||||
"github.com/containers/storage/pkg/devicemapper"
|
||||
"github.com/containers/storage/pkg/dmesg"
|
||||
"github.com/containers/storage/pkg/idtools"
|
||||
"github.com/containers/storage/pkg/ioutils"
|
||||
"github.com/containers/storage/pkg/loopback"
|
||||
"github.com/containers/storage/pkg/mount"
|
||||
"github.com/containers/storage/pkg/parsers"
|
||||
"github.com/docker/go-units"
|
||||
|
||||
"github.com/containers/storage/pkg/parsers/kernel"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultDataLoopbackSize int64 = 100 * 1024 * 1024 * 1024
|
||||
defaultMetaDataLoopbackSize int64 = 2 * 1024 * 1024 * 1024
|
||||
defaultBaseFsSize uint64 = 10 * 1024 * 1024 * 1024
|
||||
defaultThinpBlockSize uint32 = 128 // 64K = 128 512b sectors
|
||||
defaultUdevSyncOverride = false
|
||||
maxDeviceID = 0xffffff // 24 bit, pool limit
|
||||
deviceIDMapSz = (maxDeviceID + 1) / 8
|
||||
// We retry device removal so many a times that even error messages
|
||||
// will fill up console during normal operation. So only log Fatal
|
||||
// messages by default.
|
||||
logLevel = devicemapper.LogLevelFatal
|
||||
defaultDataLoopbackSize int64 = 100 * 1024 * 1024 * 1024
|
||||
defaultMetaDataLoopbackSize int64 = 2 * 1024 * 1024 * 1024
|
||||
defaultBaseFsSize uint64 = 10 * 1024 * 1024 * 1024
|
||||
defaultThinpBlockSize uint32 = 128 // 64K = 128 512b sectors
|
||||
defaultUdevSyncOverride = false
|
||||
maxDeviceID = 0xffffff // 24 bit, pool limit
|
||||
deviceIDMapSz = (maxDeviceID + 1) / 8
|
||||
driverDeferredRemovalSupport = false
|
||||
enableDeferredRemoval = false
|
||||
enableDeferredDeletion = false
|
||||
userBaseSize = false
|
||||
defaultMinFreeSpacePercent uint32 = 10
|
||||
lvmSetupConfigForce bool
|
||||
)
|
||||
|
||||
const deviceSetMetaFile string = "deviceset-metadata"
|
||||
|
@ -122,6 +119,8 @@ type DeviceSet struct {
|
|||
uidMaps []idtools.IDMap
|
||||
gidMaps []idtools.IDMap
|
||||
minFreeSpacePercent uint32 //min free space percentage in thinpool
|
||||
xfsNospaceRetries string // max retries when xfs receives ENOSPC
|
||||
lvmSetupConfig directLVMConfig
|
||||
}
|
||||
|
||||
// DiskUsage contains information about disk usage and is used when reporting Status of a device.
|
||||
|
@ -170,7 +169,7 @@ type Status struct {
|
|||
MinFreeSpace uint64
|
||||
}
|
||||
|
||||
// Structure used to export image/container metadata in docker inspect.
|
||||
// Structure used to export image/container metadata in inspect.
|
||||
type deviceMetadata struct {
|
||||
deviceID int
|
||||
deviceSize uint64 // size in bytes
|
||||
|
@ -379,10 +378,7 @@ func (devices *DeviceSet) isDeviceIDFree(deviceID int) bool {
|
|||
var mask byte
|
||||
i := deviceID % 8
|
||||
mask = (1 << uint(i))
|
||||
if (devices.deviceIDMap[deviceID/8] & mask) != 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return (devices.deviceIDMap[deviceID/8] & mask) == 0
|
||||
}
|
||||
|
||||
// Should be called with devices.Lock() held.
|
||||
|
@ -409,8 +405,8 @@ func (devices *DeviceSet) lookupDeviceWithLock(hash string) (*devInfo, error) {
|
|||
// This function relies on that device hash map has been loaded in advance.
|
||||
// Should be called with devices.Lock() held.
|
||||
func (devices *DeviceSet) constructDeviceIDMap() {
|
||||
logrus.Debugf("devmapper: constructDeviceIDMap()")
|
||||
defer logrus.Debugf("devmapper: constructDeviceIDMap() END")
|
||||
logrus.Debug("devmapper: constructDeviceIDMap()")
|
||||
defer logrus.Debug("devmapper: constructDeviceIDMap() END")
|
||||
|
||||
for _, info := range devices.Devices {
|
||||
devices.markDeviceIDUsed(info.DeviceID)
|
||||
|
@ -458,8 +454,8 @@ func (devices *DeviceSet) deviceFileWalkFunction(path string, finfo os.FileInfo)
|
|||
}
|
||||
|
||||
func (devices *DeviceSet) loadDeviceFilesOnStart() error {
|
||||
logrus.Debugf("devmapper: loadDeviceFilesOnStart()")
|
||||
defer logrus.Debugf("devmapper: loadDeviceFilesOnStart() END")
|
||||
logrus.Debug("devmapper: loadDeviceFilesOnStart()")
|
||||
defer logrus.Debug("devmapper: loadDeviceFilesOnStart() END")
|
||||
|
||||
var scan = func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
|
@ -479,11 +475,10 @@ func (devices *DeviceSet) loadDeviceFilesOnStart() error {
|
|||
}
|
||||
|
||||
// Should be called with devices.Lock() held.
|
||||
func (devices *DeviceSet) unregisterDevice(id int, hash string) error {
|
||||
logrus.Debugf("devmapper: unregisterDevice(%v, %v)", id, hash)
|
||||
func (devices *DeviceSet) unregisterDevice(hash string) error {
|
||||
logrus.Debugf("devmapper: unregisterDevice(%v)", hash)
|
||||
info := &devInfo{
|
||||
Hash: hash,
|
||||
DeviceID: id,
|
||||
Hash: hash,
|
||||
}
|
||||
|
||||
delete(devices.Devices, hash)
|
||||
|
@ -528,7 +523,7 @@ func (devices *DeviceSet) activateDeviceIfNeeded(info *devInfo, ignoreDeleted bo
|
|||
|
||||
// Make sure deferred removal on device is canceled, if one was
|
||||
// scheduled.
|
||||
if err := devices.cancelDeferredRemoval(info); err != nil {
|
||||
if err := devices.cancelDeferredRemovalIfNeeded(info); err != nil {
|
||||
return fmt.Errorf("devmapper: Device Deferred Removal Cancellation Failed: %s", err)
|
||||
}
|
||||
|
||||
|
@ -539,11 +534,11 @@ func (devices *DeviceSet) activateDeviceIfNeeded(info *devInfo, ignoreDeleted bo
|
|||
return devicemapper.ActivateDevice(devices.getPoolDevName(), info.Name(), info.DeviceID, info.Size)
|
||||
}
|
||||
|
||||
// Return true only if kernel supports xfs and mkfs.xfs is available
|
||||
func xfsSupported() bool {
|
||||
// xfsSupported checks if xfs is supported, returns nil if it is, otherwise an error
|
||||
func xfsSupported() error {
|
||||
// Make sure mkfs.xfs is available
|
||||
if _, err := exec.LookPath("mkfs.xfs"); err != nil {
|
||||
return false
|
||||
return err // error text is descriptive enough
|
||||
}
|
||||
|
||||
// Check if kernel supports xfs filesystem or not.
|
||||
|
@ -551,43 +546,48 @@ func xfsSupported() bool {
|
|||
|
||||
f, err := os.Open("/proc/filesystems")
|
||||
if err != nil {
|
||||
logrus.Warnf("devmapper: Could not check if xfs is supported: %v", err)
|
||||
return false
|
||||
return errors.Wrapf(err, "error checking for xfs support")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
if strings.HasSuffix(s.Text(), "\txfs") {
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.Err(); err != nil {
|
||||
logrus.Warnf("devmapper: Could not check if xfs is supported: %v", err)
|
||||
return errors.Wrapf(err, "error checking for xfs support")
|
||||
}
|
||||
return false
|
||||
|
||||
return errors.New(`kernel does not support xfs, or "modprobe xfs" failed`)
|
||||
}
|
||||
|
||||
func determineDefaultFS() string {
|
||||
if xfsSupported() {
|
||||
err := xfsSupported()
|
||||
if err == nil {
|
||||
return "xfs"
|
||||
}
|
||||
|
||||
logrus.Warn("devmapper: XFS is not supported in your system. Either the kernel doesn't support it or mkfs.xfs is not in your PATH. Defaulting to ext4 filesystem")
|
||||
logrus.Warnf("devmapper: XFS is not supported in your system (%v). Defaulting to ext4 filesystem", err)
|
||||
return "ext4"
|
||||
}
|
||||
|
||||
// mkfsOptions tries to figure out whether some additional mkfs options are required
|
||||
func mkfsOptions(fs string) []string {
|
||||
if fs == "xfs" && !kernel.CheckKernelVersion(3, 16, 0) {
|
||||
// For kernels earlier than 3.16 (and newer xfsutils),
|
||||
// some xfs features need to be explicitly disabled.
|
||||
return []string{"-m", "crc=0,finobt=0"}
|
||||
}
|
||||
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) createFilesystem(info *devInfo) (err error) {
|
||||
devname := info.DevName()
|
||||
|
||||
args := []string{}
|
||||
for _, arg := range devices.mkfsArgs {
|
||||
args = append(args, arg)
|
||||
}
|
||||
|
||||
args = append(args, devname)
|
||||
|
||||
if devices.filesystem == "" {
|
||||
devices.filesystem = determineDefaultFS()
|
||||
}
|
||||
|
@ -595,7 +595,11 @@ func (devices *DeviceSet) createFilesystem(info *devInfo) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
logrus.Infof("devmapper: Creating filesystem %s on device %s", devices.filesystem, info.Name())
|
||||
args := mkfsOptions(devices.filesystem)
|
||||
args = append(args, devices.mkfsArgs...)
|
||||
args = append(args, devname)
|
||||
|
||||
logrus.Infof("devmapper: Creating filesystem %s on device %s, mkfs args: %v", devices.filesystem, info.Name(), args)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
logrus.Infof("devmapper: Error while creating filesystem %s on device %s: %v", devices.filesystem, info.Name(), err)
|
||||
|
@ -833,7 +837,7 @@ func (devices *DeviceSet) createRegisterDevice(hash string) (*devInfo, error) {
|
|||
}
|
||||
|
||||
if err := devices.closeTransaction(); err != nil {
|
||||
devices.unregisterDevice(deviceID, hash)
|
||||
devices.unregisterDevice(hash)
|
||||
devicemapper.DeleteDevice(devices.getPoolDevName(), deviceID)
|
||||
devices.markDeviceIDFree(deviceID)
|
||||
return nil, err
|
||||
|
@ -841,11 +845,57 @@ func (devices *DeviceSet) createRegisterDevice(hash string) (*devInfo, error) {
|
|||
return info, nil
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInfo, size uint64) error {
|
||||
if err := devices.poolHasFreeSpace(); err != nil {
|
||||
func (devices *DeviceSet) takeSnapshot(hash string, baseInfo *devInfo, size uint64) error {
|
||||
var (
|
||||
devinfo *devicemapper.Info
|
||||
err error
|
||||
)
|
||||
|
||||
if err = devices.poolHasFreeSpace(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if devices.deferredRemove {
|
||||
devinfo, err = devicemapper.GetInfoWithDeferred(baseInfo.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if devinfo != nil && devinfo.DeferredRemove != 0 {
|
||||
err = devices.cancelDeferredRemoval(baseInfo)
|
||||
if err != nil {
|
||||
// If Error is ErrEnxio. Device is probably already gone. Continue.
|
||||
if errors.Cause(err) != devicemapper.ErrEnxio {
|
||||
return err
|
||||
}
|
||||
devinfo = nil
|
||||
} else {
|
||||
defer devices.deactivateDevice(baseInfo)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
devinfo, err = devicemapper.GetInfo(baseInfo.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
doSuspend := devinfo != nil && devinfo.Exists != 0
|
||||
|
||||
if doSuspend {
|
||||
if err = devicemapper.SuspendDevice(baseInfo.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
defer devicemapper.ResumeDevice(baseInfo.Name())
|
||||
}
|
||||
|
||||
if err = devices.createRegisterSnapDevice(hash, baseInfo, size); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInfo, size uint64) error {
|
||||
deviceID, err := devices.getNextFreeDeviceID()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -858,7 +908,7 @@ func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInf
|
|||
}
|
||||
|
||||
for {
|
||||
if err := devicemapper.CreateSnapDevice(devices.getPoolDevName(), deviceID, baseInfo.Name(), baseInfo.DeviceID); err != nil {
|
||||
if err := devicemapper.CreateSnapDeviceRaw(devices.getPoolDevName(), deviceID, baseInfo.DeviceID); err != nil {
|
||||
if devicemapper.DeviceIDExists(err) {
|
||||
// Device ID already exists. This should not
|
||||
// happen. Now we have a mechanism to find
|
||||
|
@ -888,7 +938,7 @@ func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInf
|
|||
}
|
||||
|
||||
if err := devices.closeTransaction(); err != nil {
|
||||
devices.unregisterDevice(deviceID, hash)
|
||||
devices.unregisterDevice(hash)
|
||||
devicemapper.DeleteDevice(devices.getPoolDevName(), deviceID)
|
||||
devices.markDeviceIDFree(deviceID)
|
||||
return err
|
||||
|
@ -1134,7 +1184,7 @@ func (devices *DeviceSet) growFS(info *devInfo) error {
|
|||
|
||||
defer devices.deactivateDevice(info)
|
||||
|
||||
fsMountPoint := "/run/containers/mnt"
|
||||
fsMountPoint := "/run/containers/storage/mnt"
|
||||
if _, err := os.Stat(fsMountPoint); os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(fsMountPoint, 0700); err != nil {
|
||||
return err
|
||||
|
@ -1150,10 +1200,10 @@ func (devices *DeviceSet) growFS(info *devInfo) error {
|
|||
options = joinMountOptions(options, devices.mountOptions)
|
||||
|
||||
if err := mount.Mount(info.DevName(), fsMountPoint, devices.BaseDeviceFilesystem, options); err != nil {
|
||||
return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), fsMountPoint, err)
|
||||
return fmt.Errorf("Error mounting '%s' on '%s': %s\n%v", info.DevName(), fsMountPoint, err, string(dmesg.Dmesg(256)))
|
||||
}
|
||||
|
||||
defer syscall.Unmount(fsMountPoint, syscall.MNT_DETACH)
|
||||
defer unix.Unmount(fsMountPoint, unix.MNT_DETACH)
|
||||
|
||||
switch devices.BaseDeviceFilesystem {
|
||||
case "ext4":
|
||||
|
@ -1216,39 +1266,18 @@ func (devices *DeviceSet) setupBaseImage() error {
|
|||
}
|
||||
|
||||
func setCloseOnExec(name string) {
|
||||
if fileInfos, _ := ioutil.ReadDir("/proc/self/fd"); fileInfos != nil {
|
||||
for _, i := range fileInfos {
|
||||
link, _ := os.Readlink(filepath.Join("/proc/self/fd", i.Name()))
|
||||
if link == name {
|
||||
fd, err := strconv.Atoi(i.Name())
|
||||
if err == nil {
|
||||
syscall.CloseOnExec(fd)
|
||||
}
|
||||
fileInfos, _ := ioutil.ReadDir("/proc/self/fd")
|
||||
for _, i := range fileInfos {
|
||||
link, _ := os.Readlink(filepath.Join("/proc/self/fd", i.Name()))
|
||||
if link == name {
|
||||
fd, err := strconv.Atoi(i.Name())
|
||||
if err == nil {
|
||||
unix.CloseOnExec(fd)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DMLog implements logging using DevMapperLogger interface.
|
||||
func (devices *DeviceSet) DMLog(level int, file string, line int, dmError int, message string) {
|
||||
// By default libdm sends us all the messages including debug ones.
|
||||
// We need to filter out messages here and figure out which one
|
||||
// should be printed.
|
||||
if level > logLevel {
|
||||
return
|
||||
}
|
||||
|
||||
// FIXME(vbatts) push this back into ./pkg/devicemapper/
|
||||
if level <= devicemapper.LogLevelErr {
|
||||
logrus.Errorf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
|
||||
} else if level <= devicemapper.LogLevelInfo {
|
||||
logrus.Infof("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
|
||||
} else {
|
||||
// FIXME(vbatts) push this back into ./pkg/devicemapper/
|
||||
logrus.Debugf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
|
||||
}
|
||||
}
|
||||
|
||||
func major(device uint64) uint64 {
|
||||
return (device >> 8) & 0xfff
|
||||
}
|
||||
|
@ -1356,10 +1385,7 @@ func (devices *DeviceSet) saveTransactionMetaData() error {
|
|||
}
|
||||
|
||||
func (devices *DeviceSet) removeTransactionMetaData() error {
|
||||
if err := os.RemoveAll(devices.transactionMetaFile()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return os.RemoveAll(devices.transactionMetaFile())
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) rollbackTransaction() error {
|
||||
|
@ -1464,12 +1490,9 @@ func (devices *DeviceSet) closeTransaction() error {
|
|||
}
|
||||
|
||||
func determineDriverCapabilities(version string) error {
|
||||
/*
|
||||
* Driver version 4.27.0 and greater support deferred activation
|
||||
* feature.
|
||||
*/
|
||||
// Kernel driver version >= 4.27.0 support deferred removal
|
||||
|
||||
logrus.Debugf("devicemapper: driver version is %s", version)
|
||||
logrus.Debugf("devicemapper: kernel dm driver version is %s", version)
|
||||
|
||||
versionSplit := strings.Split(version, ".")
|
||||
major, err := strconv.Atoi(versionSplit[0])
|
||||
|
@ -1505,12 +1528,13 @@ func determineDriverCapabilities(version string) error {
|
|||
|
||||
// Determine the major and minor number of loopback device
|
||||
func getDeviceMajorMinor(file *os.File) (uint64, uint64, error) {
|
||||
stat, err := file.Stat()
|
||||
var stat unix.Stat_t
|
||||
err := unix.Stat(file.Name(), &stat)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
dev := stat.Sys().(*syscall.Stat_t).Rdev
|
||||
dev := stat.Rdev
|
||||
majorNum := major(dev)
|
||||
minorNum := minor(dev)
|
||||
|
||||
|
@ -1648,36 +1672,19 @@ func (devices *DeviceSet) enableDeferredRemovalDeletion() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
||||
// give ourselves to libdm as a log handler
|
||||
devicemapper.LogInit(devices)
|
||||
|
||||
version, err := devicemapper.GetDriverVersion()
|
||||
if err != nil {
|
||||
// Can't even get driver version, assume not supported
|
||||
return errors.Wrap(graphdriver.ErrNotSupported, "unable to determine version of device mapper")
|
||||
}
|
||||
|
||||
if err := determineDriverCapabilities(version); err != nil {
|
||||
return errors.Wrap(graphdriver.ErrNotSupported, "unable to determine device mapper driver capabilities")
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) {
|
||||
if err := devices.enableDeferredRemovalDeletion(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// https://github.com/docker/docker/issues/4036
|
||||
// if supported := devicemapper.UdevSetSyncSupport(true); !supported {
|
||||
// if storageversion.IAmStatic == "true" {
|
||||
// logrus.Errorf("devmapper: Udev sync is not supported. This will lead to data loss and unexpected behavior. Install a dynamic binary to use devicemapper or select a different storage driver. For more information, see https://docs.docker.com/engine/reference/commandline/daemon/#daemon-storage-driver-option")
|
||||
// } else {
|
||||
// logrus.Errorf("devmapper: Udev sync is not supported. This will lead to data loss and unexpected behavior. Install a more recent version of libdevmapper or select a different storage driver. For more information, see https://docs.docker.com/engine/reference/commandline/daemon/#daemon-storage-driver-option")
|
||||
// }
|
||||
//
|
||||
// if !devices.overrideUdevSyncCheck {
|
||||
// return graphdriver.ErrNotSupported
|
||||
// }
|
||||
// }
|
||||
if supported := devicemapper.UdevSetSyncSupport(true); !supported {
|
||||
logrus.Error("devmapper: Udev sync is not supported. This will lead to data loss and unexpected behavior. Install a more recent version of libdevmapper or select a different storage driver. For more information, see https://docs.docker.com/engine/reference/commandline/dockerd/#storage-driver-options")
|
||||
|
||||
if !devices.overrideUdevSyncCheck {
|
||||
return graphdriver.ErrNotSupported
|
||||
}
|
||||
}
|
||||
|
||||
//create the root dir of the devmapper driver ownership to match this
|
||||
//daemon's remapped root uid/gid so containers can start properly
|
||||
|
@ -1692,20 +1699,47 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Set the device prefix from the device id and inode of the container root dir
|
||||
|
||||
st, err := os.Stat(devices.root)
|
||||
prevSetupConfig, err := readLVMConfig(devices.root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(devices.lvmSetupConfig, directLVMConfig{}) {
|
||||
if devices.thinPoolDevice != "" {
|
||||
return errors.New("cannot setup direct-lvm when `dm.thinpooldev` is also specified")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(prevSetupConfig, devices.lvmSetupConfig) {
|
||||
if !reflect.DeepEqual(prevSetupConfig, directLVMConfig{}) {
|
||||
return errors.New("changing direct-lvm config is not supported")
|
||||
}
|
||||
logrus.WithField("storage-driver", "devicemapper").WithField("direct-lvm-config", devices.lvmSetupConfig).Debugf("Setting up direct lvm mode")
|
||||
if err := verifyBlockDevice(devices.lvmSetupConfig.Device, lvmSetupConfigForce); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := setupDirectLVM(devices.lvmSetupConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeLVMConfig(devices.root, devices.lvmSetupConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
devices.thinPoolDevice = "storage-thinpool"
|
||||
logrus.WithField("storage-driver", "devicemapper").Debugf("Setting dm.thinpooldev to %q", devices.thinPoolDevice)
|
||||
}
|
||||
|
||||
// Set the device prefix from the device id and inode of the storage root dir
|
||||
var st unix.Stat_t
|
||||
if err := unix.Stat(devices.root, &st); err != nil {
|
||||
return fmt.Errorf("devmapper: Error looking up dir %s: %s", devices.root, err)
|
||||
}
|
||||
sysSt := st.Sys().(*syscall.Stat_t)
|
||||
// "reg-" stands for "regular file".
|
||||
// In the future we might use "dev-" for "device file", etc.
|
||||
// container-maj,min[-inode] stands for:
|
||||
// - Managed by container storage
|
||||
// - The target of this device is at major <maj> and minor <min>
|
||||
// - If <inode> is defined, use that file inside the device as a loopback image. Otherwise use the device itself.
|
||||
devices.devicePrefix = fmt.Sprintf("container-%d:%d-%d", major(sysSt.Dev), minor(sysSt.Dev), sysSt.Ino)
|
||||
devices.devicePrefix = fmt.Sprintf("container-%d:%d-%d", major(st.Dev), minor(st.Dev), st.Ino)
|
||||
logrus.Debugf("devmapper: Generated prefix: %s", devices.devicePrefix)
|
||||
|
||||
// Check for the existence of the thin-pool device
|
||||
|
@ -1748,7 +1782,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
|||
hasData := devices.hasImage("data")
|
||||
|
||||
if !doInit && !hasData {
|
||||
return errors.New("Loopback data file not found")
|
||||
return errors.New("loopback data file not found")
|
||||
}
|
||||
|
||||
if !hasData {
|
||||
|
@ -1781,7 +1815,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
|||
hasMetadata := devices.hasImage("metadata")
|
||||
|
||||
if !doInit && !hasMetadata {
|
||||
return errors.New("Loopback metadata file not found")
|
||||
return errors.New("loopback metadata file not found")
|
||||
}
|
||||
|
||||
if !hasMetadata {
|
||||
|
@ -1811,6 +1845,14 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
|||
if err := devicemapper.CreatePool(devices.getPoolName(), dataFile, metadataFile, devices.thinpBlockSize); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
err = devices.deactivatePool()
|
||||
if err != nil {
|
||||
logrus.Warnf("devmapper: Failed to deactivatePool: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Pool already exists and caller did not pass us a pool. That means
|
||||
|
@ -1857,8 +1899,8 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
|||
|
||||
// AddDevice adds a device and registers in the hash.
|
||||
func (devices *DeviceSet) AddDevice(hash, baseHash string, storageOpt map[string]string) error {
|
||||
logrus.Debugf("devmapper: AddDevice(hash=%s basehash=%s)", hash, baseHash)
|
||||
defer logrus.Debugf("devmapper: AddDevice(hash=%s basehash=%s) END", hash, baseHash)
|
||||
logrus.Debugf("devmapper: AddDevice START(hash=%s basehash=%s)", hash, baseHash)
|
||||
defer logrus.Debugf("devmapper: AddDevice END(hash=%s basehash=%s)", hash, baseHash)
|
||||
|
||||
// If a deleted device exists, return error.
|
||||
baseInfo, err := devices.lookupDeviceWithLock(baseHash)
|
||||
|
@ -1895,7 +1937,7 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string, storageOpt map[string
|
|||
return fmt.Errorf("devmapper: Container size cannot be smaller than %s", units.HumanSize(float64(baseInfo.Size)))
|
||||
}
|
||||
|
||||
if err := devices.createRegisterSnapDevice(hash, baseInfo, size); err != nil {
|
||||
if err := devices.takeSnapshot(hash, baseInfo, size); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -1975,7 +2017,7 @@ func (devices *DeviceSet) deleteTransaction(info *devInfo, syncDelete bool) erro
|
|||
}
|
||||
|
||||
if err == nil {
|
||||
if err := devices.unregisterDevice(info.DeviceID, info.Hash); err != nil {
|
||||
if err := devices.unregisterDevice(info.Hash); err != nil {
|
||||
return err
|
||||
}
|
||||
// If device was already in deferred delete state that means
|
||||
|
@ -1996,8 +2038,8 @@ func (devices *DeviceSet) deleteTransaction(info *devInfo, syncDelete bool) erro
|
|||
|
||||
// Issue discard only if device open count is zero.
|
||||
func (devices *DeviceSet) issueDiscard(info *devInfo) error {
|
||||
logrus.Debugf("devmapper: issueDiscard(device: %s). START", info.Hash)
|
||||
defer logrus.Debugf("devmapper: issueDiscard(device: %s). END", info.Hash)
|
||||
logrus.Debugf("devmapper: issueDiscard START(device: %s).", info.Hash)
|
||||
defer logrus.Debugf("devmapper: issueDiscard END(device: %s).", info.Hash)
|
||||
// This is a workaround for the kernel not discarding block so
|
||||
// on the thin pool when we remove a thinp device, so we do it
|
||||
// manually.
|
||||
|
@ -2030,7 +2072,16 @@ func (devices *DeviceSet) deleteDevice(info *devInfo, syncDelete bool) error {
|
|||
}
|
||||
|
||||
// Try to deactivate device in case it is active.
|
||||
if err := devices.deactivateDevice(info); err != nil {
|
||||
// If deferred removal is enabled and deferred deletion is disabled
|
||||
// then make sure device is removed synchronously. There have been
|
||||
// some cases of device being busy for short duration and we would
|
||||
// rather busy wait for device removal to take care of these cases.
|
||||
deferredRemove := devices.deferredRemove
|
||||
if !devices.deferredDelete {
|
||||
deferredRemove = false
|
||||
}
|
||||
|
||||
if err := devices.deactivateDeviceMode(info, deferredRemove); err != nil {
|
||||
logrus.Debugf("devmapper: Error deactivating device: %s", err)
|
||||
return err
|
||||
}
|
||||
|
@ -2046,8 +2097,8 @@ func (devices *DeviceSet) deleteDevice(info *devInfo, syncDelete bool) error {
|
|||
// removal. If one wants to override that and want DeleteDevice() to fail if
|
||||
// device was busy and could not be deleted, set syncDelete=true.
|
||||
func (devices *DeviceSet) DeleteDevice(hash string, syncDelete bool) error {
|
||||
logrus.Debugf("devmapper: DeleteDevice(hash=%v syncDelete=%v) START", hash, syncDelete)
|
||||
defer logrus.Debugf("devmapper: DeleteDevice(hash=%v syncDelete=%v) END", hash, syncDelete)
|
||||
logrus.Debugf("devmapper: DeleteDevice START(hash=%v syncDelete=%v)", hash, syncDelete)
|
||||
defer logrus.Debugf("devmapper: DeleteDevice END(hash=%v syncDelete=%v)", hash, syncDelete)
|
||||
info, err := devices.lookupDeviceWithLock(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -2063,8 +2114,8 @@ func (devices *DeviceSet) DeleteDevice(hash string, syncDelete bool) error {
|
|||
}
|
||||
|
||||
func (devices *DeviceSet) deactivatePool() error {
|
||||
logrus.Debug("devmapper: deactivatePool()")
|
||||
defer logrus.Debug("devmapper: deactivatePool END")
|
||||
logrus.Debug("devmapper: deactivatePool() START")
|
||||
defer logrus.Debug("devmapper: deactivatePool() END")
|
||||
devname := devices.getPoolDevName()
|
||||
|
||||
devinfo, err := devicemapper.GetInfo(devname)
|
||||
|
@ -2087,7 +2138,12 @@ func (devices *DeviceSet) deactivatePool() error {
|
|||
}
|
||||
|
||||
func (devices *DeviceSet) deactivateDevice(info *devInfo) error {
|
||||
logrus.Debugf("devmapper: deactivateDevice(%s)", info.Hash)
|
||||
return devices.deactivateDeviceMode(info, devices.deferredRemove)
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) deactivateDeviceMode(info *devInfo, deferredRemove bool) error {
|
||||
var err error
|
||||
logrus.Debugf("devmapper: deactivateDevice START(%s)", info.Hash)
|
||||
defer logrus.Debugf("devmapper: deactivateDevice END(%s)", info.Hash)
|
||||
|
||||
devinfo, err := devicemapper.GetInfo(info.Name())
|
||||
|
@ -2099,14 +2155,17 @@ func (devices *DeviceSet) deactivateDevice(info *devInfo) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if devices.deferredRemove {
|
||||
if err := devicemapper.RemoveDeviceDeferred(info.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
if deferredRemove {
|
||||
err = devicemapper.RemoveDeviceDeferred(info.Name())
|
||||
} else {
|
||||
if err := devices.removeDevice(info.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
err = devices.removeDevice(info.Name())
|
||||
}
|
||||
|
||||
// This function's semantics is such that it does not return an
|
||||
// error if device does not exist. So if device went away by
|
||||
// the time we actually tried to remove it, do not return error.
|
||||
if errors.Cause(err) != devicemapper.ErrEnxio {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -2137,41 +2196,53 @@ func (devices *DeviceSet) removeDevice(devname string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (devices *DeviceSet) cancelDeferredRemoval(info *devInfo) error {
|
||||
func (devices *DeviceSet) cancelDeferredRemovalIfNeeded(info *devInfo) error {
|
||||
if !devices.deferredRemove {
|
||||
return nil
|
||||
}
|
||||
|
||||
logrus.Debugf("devmapper: cancelDeferredRemoval START(%s)", info.Name())
|
||||
defer logrus.Debugf("devmapper: cancelDeferredRemoval END(%s)", info.Name())
|
||||
logrus.Debugf("devmapper: cancelDeferredRemovalIfNeeded START(%s)", info.Name())
|
||||
defer logrus.Debugf("devmapper: cancelDeferredRemovalIfNeeded END(%s)", info.Name())
|
||||
|
||||
devinfo, err := devicemapper.GetInfoWithDeferred(info.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if devinfo != nil && devinfo.DeferredRemove == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cancel deferred remove
|
||||
for i := 0; i < 100; i++ {
|
||||
err = devicemapper.CancelDeferredRemove(info.Name())
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if errors.Cause(err) == devicemapper.ErrEnxio {
|
||||
// Device is probably already gone. Return success.
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := devices.cancelDeferredRemoval(info); err != nil {
|
||||
// If Error is ErrEnxio. Device is probably already gone. Continue.
|
||||
if errors.Cause(err) != devicemapper.ErrBusy {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// If we see EBUSY it may be a transient error,
|
||||
// sleep a bit a retry a few times.
|
||||
devices.Unlock()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
devices.Lock()
|
||||
func (devices *DeviceSet) cancelDeferredRemoval(info *devInfo) error {
|
||||
logrus.Debugf("devmapper: cancelDeferredRemoval START(%s)", info.Name())
|
||||
defer logrus.Debugf("devmapper: cancelDeferredRemoval END(%s)", info.Name())
|
||||
|
||||
var err error
|
||||
|
||||
// Cancel deferred remove
|
||||
for i := 0; i < 100; i++ {
|
||||
err = devicemapper.CancelDeferredRemove(info.Name())
|
||||
if err != nil {
|
||||
if errors.Cause(err) != devicemapper.ErrBusy {
|
||||
// If we see EBUSY it may be a transient error,
|
||||
// sleep a bit a retry a few times.
|
||||
devices.Unlock()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
devices.Lock()
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -2209,9 +2280,6 @@ func (devices *DeviceSet) Shutdown(home string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if p == path.Join(home, "mnt") {
|
||||
return nil
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
@ -2220,7 +2288,7 @@ func (devices *DeviceSet) Shutdown(home string) error {
|
|||
// We use MNT_DETACH here in case it is still busy in some running
|
||||
// container. This means it'll go away from the global scope directly,
|
||||
// and the device will be released when that container dies.
|
||||
if err := syscall.Unmount(p, syscall.MNT_DETACH); err != nil {
|
||||
if err := unix.Unmount(p, unix.MNT_DETACH); err != nil {
|
||||
logrus.Debugf("devmapper: Shutdown unmounting %s, error: %s", p, err)
|
||||
}
|
||||
}
|
||||
|
@ -2263,6 +2331,34 @@ func (devices *DeviceSet) Shutdown(home string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Recent XFS changes allow changing behavior of filesystem in case of errors.
|
||||
// When thin pool gets full and XFS gets ENOSPC error, currently it tries
|
||||
// IO infinitely and sometimes it can block the container process
|
||||
// and process can't be killWith 0 value, XFS will not retry upon error
|
||||
// and instead will shutdown filesystem.
|
||||
|
||||
func (devices *DeviceSet) xfsSetNospaceRetries(info *devInfo) error {
|
||||
dmDevicePath, err := os.Readlink(info.DevName())
|
||||
if err != nil {
|
||||
return fmt.Errorf("devmapper: readlink failed for device %v:%v", info.DevName(), err)
|
||||
}
|
||||
|
||||
dmDeviceName := path.Base(dmDevicePath)
|
||||
filePath := "/sys/fs/xfs/" + dmDeviceName + "/error/metadata/ENOSPC/max_retries"
|
||||
maxRetriesFile, err := os.OpenFile(filePath, os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
defer maxRetriesFile.Close()
|
||||
|
||||
// Set max retries to 0
|
||||
_, err = maxRetriesFile.WriteString(devices.xfsNospaceRetries)
|
||||
if err != nil {
|
||||
return fmt.Errorf("devmapper: Failed to write string %v to file %v:%v", devices.xfsNospaceRetries, filePath, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MountDevice mounts the device if not already mounted.
|
||||
func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error {
|
||||
info, err := devices.lookupDeviceWithLock(hash)
|
||||
|
@ -2300,7 +2396,15 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error {
|
|||
options = joinMountOptions(options, label.FormatMountLabel("", mountLabel))
|
||||
|
||||
if err := mount.Mount(info.DevName(), path, fstype, options); err != nil {
|
||||
return fmt.Errorf("devmapper: Error mounting '%s' on '%s': %s", info.DevName(), path, err)
|
||||
return fmt.Errorf("devmapper: Error mounting '%s' on '%s': %s\n%v", info.DevName(), path, err, string(dmesg.Dmesg(256)))
|
||||
}
|
||||
|
||||
if fstype == "xfs" && devices.xfsNospaceRetries != "" {
|
||||
if err := devices.xfsSetNospaceRetries(info); err != nil {
|
||||
unix.Unmount(path, unix.MNT_DETACH)
|
||||
devices.deactivateDevice(info)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -2308,8 +2412,8 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error {
|
|||
|
||||
// UnmountDevice unmounts the device and removes it from hash.
|
||||
func (devices *DeviceSet) UnmountDevice(hash, mountPath string) error {
|
||||
logrus.Debugf("devmapper: UnmountDevice(hash=%s)", hash)
|
||||
defer logrus.Debugf("devmapper: UnmountDevice(hash=%s) END", hash)
|
||||
logrus.Debugf("devmapper: UnmountDevice START(hash=%s)", hash)
|
||||
defer logrus.Debugf("devmapper: UnmountDevice END(hash=%s)", hash)
|
||||
|
||||
info, err := devices.lookupDeviceWithLock(hash)
|
||||
if err != nil {
|
||||
|
@ -2323,16 +2427,12 @@ func (devices *DeviceSet) UnmountDevice(hash, mountPath string) error {
|
|||
defer devices.Unlock()
|
||||
|
||||
logrus.Debugf("devmapper: Unmount(%s)", mountPath)
|
||||
if err := syscall.Unmount(mountPath, syscall.MNT_DETACH); err != nil {
|
||||
if err := unix.Unmount(mountPath, unix.MNT_DETACH); err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Debug("devmapper: Unmount done")
|
||||
|
||||
if err := devices.deactivateDevice(info); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return devices.deactivateDevice(info)
|
||||
}
|
||||
|
||||
// HasDevice returns true if the device metadata exists.
|
||||
|
@ -2424,8 +2524,8 @@ func (devices *DeviceSet) MetadataDevicePath() string {
|
|||
}
|
||||
|
||||
func (devices *DeviceSet) getUnderlyingAvailableSpace(loopFile string) (uint64, error) {
|
||||
buf := new(syscall.Statfs_t)
|
||||
if err := syscall.Statfs(loopFile, buf); err != nil {
|
||||
buf := new(unix.Statfs_t)
|
||||
if err := unix.Statfs(loopFile, buf); err != nil {
|
||||
logrus.Warnf("devmapper: Couldn't stat loopfile filesystem %v: %v", loopFile, err)
|
||||
return 0, err
|
||||
}
|
||||
|
@ -2534,22 +2634,25 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [
|
|||
minFreeSpacePercent: defaultMinFreeSpacePercent,
|
||||
}
|
||||
|
||||
// Pick up initialization settings, if any were saved before
|
||||
defaultsFile := path.Join(root, "defaults")
|
||||
defaultsBytes, err := ioutil.ReadFile(defaultsFile)
|
||||
defaults := []string{}
|
||||
settings := map[string]string{}
|
||||
if err == nil && len(defaultsBytes) > 0 {
|
||||
defaults = strings.Split(string(defaultsBytes), "\n")
|
||||
version, err := devicemapper.GetDriverVersion()
|
||||
if err != nil {
|
||||
// Can't even get driver version, assume not supported
|
||||
return nil, graphdriver.ErrNotSupported
|
||||
}
|
||||
|
||||
if err := determineDriverCapabilities(version); err != nil {
|
||||
return nil, graphdriver.ErrNotSupported
|
||||
}
|
||||
|
||||
if driverDeferredRemovalSupport && devicemapper.LibraryDeferredRemovalSupport {
|
||||
// enable deferred stuff by default
|
||||
enableDeferredDeletion = true
|
||||
enableDeferredRemoval = true
|
||||
}
|
||||
|
||||
foundBlkDiscard := false
|
||||
nthOption := 0
|
||||
for _, option := range append(defaults, options...) {
|
||||
nthOption = nthOption + 1
|
||||
if len(option) == 0 {
|
||||
continue
|
||||
}
|
||||
var lvmSetupConfig directLVMConfig
|
||||
for _, option := range options {
|
||||
key, val, err := parsers.ParseKeyValueOpt(option)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -2637,15 +2740,78 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [
|
|||
}
|
||||
|
||||
devices.minFreeSpacePercent = uint32(minFreeSpacePercent)
|
||||
default:
|
||||
if nthOption > len(defaults) {
|
||||
return nil, fmt.Errorf("devmapper: Unknown option %s", key)
|
||||
case "dm.xfs_nospace_max_retries":
|
||||
_, err := strconv.ParseUint(val, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logrus.Errorf("devmapper: Unknown option %s, ignoring", key)
|
||||
devices.xfsNospaceRetries = val
|
||||
case "dm.directlvm_device":
|
||||
lvmSetupConfig.Device = val
|
||||
case "dm.directlvm_device_force":
|
||||
lvmSetupConfigForce, err = strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "dm.thinp_percent":
|
||||
per, err := strconv.ParseUint(strings.TrimSuffix(val, "%"), 10, 32)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not parse `dm.thinp_percent=%s`", val)
|
||||
}
|
||||
if per >= 100 {
|
||||
return nil, errors.New("dm.thinp_percent must be greater than 0 and less than 100")
|
||||
}
|
||||
lvmSetupConfig.ThinpPercent = per
|
||||
case "dm.thinp_metapercent":
|
||||
per, err := strconv.ParseUint(strings.TrimSuffix(val, "%"), 10, 32)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not parse `dm.thinp_metapercent=%s`", val)
|
||||
}
|
||||
if per >= 100 {
|
||||
return nil, errors.New("dm.thinp_metapercent must be greater than 0 and less than 100")
|
||||
}
|
||||
lvmSetupConfig.ThinpMetaPercent = per
|
||||
case "dm.thinp_autoextend_percent":
|
||||
per, err := strconv.ParseUint(strings.TrimSuffix(val, "%"), 10, 32)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not parse `dm.thinp_autoextend_percent=%s`", val)
|
||||
}
|
||||
if per > 100 {
|
||||
return nil, errors.New("dm.thinp_autoextend_percent must be greater than 0 and less than 100")
|
||||
}
|
||||
lvmSetupConfig.AutoExtendPercent = per
|
||||
case "dm.thinp_autoextend_threshold":
|
||||
per, err := strconv.ParseUint(strings.TrimSuffix(val, "%"), 10, 32)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not parse `dm.thinp_autoextend_threshold=%s`", val)
|
||||
}
|
||||
if per > 100 {
|
||||
return nil, errors.New("dm.thinp_autoextend_threshold must be greater than 0 and less than 100")
|
||||
}
|
||||
lvmSetupConfig.AutoExtendThreshold = per
|
||||
case "dm.libdm_log_level":
|
||||
level, err := strconv.ParseInt(val, 10, 32)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not parse `dm.libdm_log_level=%s`", val)
|
||||
}
|
||||
if level < devicemapper.LogLevelFatal || level > devicemapper.LogLevelDebug {
|
||||
return nil, errors.Errorf("dm.libdm_log_level must be in range [%d,%d]", devicemapper.LogLevelFatal, devicemapper.LogLevelDebug)
|
||||
}
|
||||
// Register a new logging callback with the specified level.
|
||||
devicemapper.LogInit(devicemapper.DefaultLogger{
|
||||
Level: int(level),
|
||||
})
|
||||
default:
|
||||
return nil, fmt.Errorf("devmapper: Unknown option %s", key)
|
||||
}
|
||||
settings[key] = val
|
||||
}
|
||||
|
||||
if err := validateLVMConfig(lvmSetupConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
devices.lvmSetupConfig = lvmSetupConfig
|
||||
|
||||
// By default, don't do blk discard hack on raw devices, its rarely useful and is expensive
|
||||
if !foundBlkDiscard && (devices.dataDevice != "" || devices.thinPoolDevice != "") {
|
||||
devices.doBlkDiscard = false
|
||||
|
@ -2655,15 +2821,5 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Save these settings along with the other metadata
|
||||
defaults = []string{}
|
||||
for key, val := range settings {
|
||||
defaults = append(defaults, key+"="+val)
|
||||
}
|
||||
defaultsBytes = []byte(strings.Join(defaults, "\n") + "\n")
|
||||
if err := ioutils.AtomicWriteFile(defaultsFile, defaultsBytes, 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return devices, nil
|
||||
}
|
||||
|
|
54
vendor/github.com/containers/storage/drivers/devmapper/driver.go
generated
vendored
54
vendor/github.com/containers/storage/drivers/devmapper/driver.go
generated
vendored
|
@ -14,8 +14,10 @@ import (
|
|||
"github.com/containers/storage/drivers"
|
||||
"github.com/containers/storage/pkg/devicemapper"
|
||||
"github.com/containers/storage/pkg/idtools"
|
||||
"github.com/containers/storage/pkg/locker"
|
||||
"github.com/containers/storage/pkg/mount"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/containers/storage/pkg/system"
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -29,6 +31,7 @@ type Driver struct {
|
|||
uidMaps []idtools.IDMap
|
||||
gidMaps []idtools.IDMap
|
||||
ctr *graphdriver.RefCounter
|
||||
locker *locker.Locker
|
||||
}
|
||||
|
||||
// Init creates a driver with the given home and the set of options.
|
||||
|
@ -48,6 +51,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
|||
uidMaps: uidMaps,
|
||||
gidMaps: gidMaps,
|
||||
ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()),
|
||||
locker: locker.New(),
|
||||
}
|
||||
|
||||
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
|
||||
|
@ -65,18 +69,18 @@ func (d *Driver) Status() [][2]string {
|
|||
|
||||
status := [][2]string{
|
||||
{"Pool Name", s.PoolName},
|
||||
{"Pool Blocksize", fmt.Sprintf("%s", units.HumanSize(float64(s.SectorSize)))},
|
||||
{"Base Device Size", fmt.Sprintf("%s", units.HumanSize(float64(s.BaseDeviceSize)))},
|
||||
{"Pool Blocksize", units.HumanSize(float64(s.SectorSize))},
|
||||
{"Base Device Size", units.HumanSize(float64(s.BaseDeviceSize))},
|
||||
{"Backing Filesystem", s.BaseDeviceFS},
|
||||
{"Data file", s.DataFile},
|
||||
{"Metadata file", s.MetadataFile},
|
||||
{"Data Space Used", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Used)))},
|
||||
{"Data Space Total", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Total)))},
|
||||
{"Data Space Available", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Available)))},
|
||||
{"Metadata Space Used", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Used)))},
|
||||
{"Metadata Space Total", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Total)))},
|
||||
{"Metadata Space Available", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Available)))},
|
||||
{"Thin Pool Minimum Free Space", fmt.Sprintf("%s", units.HumanSize(float64(s.MinFreeSpace)))},
|
||||
{"Data Space Used", units.HumanSize(float64(s.Data.Used))},
|
||||
{"Data Space Total", units.HumanSize(float64(s.Data.Total))},
|
||||
{"Data Space Available", units.HumanSize(float64(s.Data.Available))},
|
||||
{"Metadata Space Used", units.HumanSize(float64(s.Metadata.Used))},
|
||||
{"Metadata Space Total", units.HumanSize(float64(s.Metadata.Total))},
|
||||
{"Metadata Space Available", units.HumanSize(float64(s.Metadata.Available))},
|
||||
{"Thin Pool Minimum Free Space", units.HumanSize(float64(s.MinFreeSpace))},
|
||||
{"Udev Sync Supported", fmt.Sprintf("%v", s.UdevSyncSupported)},
|
||||
{"Deferred Removal Enabled", fmt.Sprintf("%v", s.DeferredRemoveEnabled)},
|
||||
{"Deferred Deletion Enabled", fmt.Sprintf("%v", s.DeferredDeleteEnabled)},
|
||||
|
@ -122,12 +126,17 @@ func (d *Driver) Cleanup() error {
|
|||
|
||||
// CreateReadWrite creates a layer that is writable for use as a container
|
||||
// file system.
|
||||
func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error {
|
||||
return d.Create(id, parent, mountLabel, storageOpt)
|
||||
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
|
||||
return d.Create(id, parent, opts)
|
||||
}
|
||||
|
||||
// Create adds a device with a given id and the parent.
|
||||
func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
|
||||
func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
|
||||
var storageOpt map[string]string
|
||||
if opts != nil {
|
||||
storageOpt = opts.StorageOpt
|
||||
}
|
||||
|
||||
if err := d.DeviceSet.AddDevice(id, parent, storageOpt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -137,6 +146,8 @@ func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]str
|
|||
|
||||
// Remove removes a device with a given id, unmounts the filesystem.
|
||||
func (d *Driver) Remove(id string) error {
|
||||
d.locker.Lock(id)
|
||||
defer d.locker.Unlock(id)
|
||||
if !d.DeviceSet.HasDevice(id) {
|
||||
// Consider removing a non-existing device a no-op
|
||||
// This is useful to be able to progress on container removal
|
||||
|
@ -146,19 +157,15 @@ func (d *Driver) Remove(id string) error {
|
|||
|
||||
// This assumes the device has been properly Get/Put:ed and thus is unmounted
|
||||
if err := d.DeviceSet.DeleteDevice(id, false); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to remove device %s: %v", id, err)
|
||||
}
|
||||
|
||||
mp := path.Join(d.home, "mnt", id)
|
||||
if err := os.RemoveAll(mp); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return system.EnsureRemoveAll(path.Join(d.home, "mnt", id))
|
||||
}
|
||||
|
||||
// Get mounts a device with given id into the root filesystem
|
||||
func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
d.locker.Lock(id)
|
||||
defer d.locker.Unlock(id)
|
||||
mp := path.Join(d.home, "mnt", id)
|
||||
rootFs := path.Join(mp, "rootfs")
|
||||
if count := d.ctr.Increment(mp); count > 1 {
|
||||
|
@ -209,6 +216,8 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
|
|||
|
||||
// Put unmounts a device and removes it.
|
||||
func (d *Driver) Put(id string) error {
|
||||
d.locker.Lock(id)
|
||||
defer d.locker.Unlock(id)
|
||||
mp := path.Join(d.home, "mnt", id)
|
||||
if count := d.ctr.Decrement(mp); count > 0 {
|
||||
return nil
|
||||
|
@ -227,6 +236,5 @@ func (d *Driver) Exists(id string) bool {
|
|||
|
||||
// AdditionalImageStores returns additional image stores supported by the driver
|
||||
func (d *Driver) AdditionalImageStores() []string {
|
||||
var imageStores []string
|
||||
return imageStores
|
||||
return nil
|
||||
}
|
||||
|
|
13
vendor/github.com/containers/storage/drivers/devmapper/mount.go
generated
vendored
13
vendor/github.com/containers/storage/drivers/devmapper/mount.go
generated
vendored
|
@ -7,7 +7,8 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// FIXME: this is copy-pasted from the aufs driver.
|
||||
|
@ -15,19 +16,17 @@ import (
|
|||
|
||||
// Mounted returns true if a mount point exists.
|
||||
func Mounted(mountpoint string) (bool, error) {
|
||||
mntpoint, err := os.Stat(mountpoint)
|
||||
if err != nil {
|
||||
var mntpointSt unix.Stat_t
|
||||
if err := unix.Stat(mountpoint, &mntpointSt); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
parent, err := os.Stat(filepath.Join(mountpoint, ".."))
|
||||
if err != nil {
|
||||
var parentSt unix.Stat_t
|
||||
if err := unix.Stat(filepath.Join(mountpoint, ".."), &parentSt); err != nil {
|
||||
return false, err
|
||||
}
|
||||
mntpointSt := mntpoint.Sys().(*syscall.Stat_t)
|
||||
parentSt := parent.Sys().(*syscall.Stat_t)
|
||||
return mntpointSt.Dev != parentSt.Dev, nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue