Explorar o código

Merge pull request #14021 from rhvgoyal/detect-pool-loopback-devices

devicemapper: Check loop devices of existing pool
Vincent Batts %!s(int64=10) %!d(string=hai) anos
pai
achega
5ca3e7c54c
Modificáronse 2 ficheiros con 156 adicións e 0 borrados
  1. 131 0
      daemon/graphdriver/devmapper/deviceset.go
  2. 25 0
      pkg/devicemapper/devmapper.go

+ 131 - 0
daemon/graphdriver/devmapper/deviceset.go

@@ -1089,6 +1089,126 @@ func determineDriverCapabilities(version string) error {
 	return nil
 }
 
+// Determine the major and minor number of loopback device
+func getDeviceMajorMinor(file *os.File) (uint64, uint64, error) {
+	stat, err := file.Stat()
+	if err != nil {
+		return 0, 0, err
+	}
+
+	dev := stat.Sys().(*syscall.Stat_t).Rdev
+	majorNum := major(dev)
+	minorNum := minor(dev)
+
+	logrus.Debugf("[devmapper]: Major:Minor for device: %s is:%v:%v", file.Name(), majorNum, minorNum)
+	return majorNum, minorNum, nil
+}
+
+// Given a file which is backing file of a loop back device, find the
+// loopback device name and its major/minor number.
+func getLoopFileDeviceMajMin(filename string) (string, uint64, uint64, error) {
+	file, err := os.Open(filename)
+	if err != nil {
+		logrus.Debugf("[devmapper]: Failed to open file %s", filename)
+		return "", 0, 0, err
+	}
+
+	defer file.Close()
+	loopbackDevice := devicemapper.FindLoopDeviceFor(file)
+	if loopbackDevice == nil {
+		return "", 0, 0, fmt.Errorf("[devmapper]: Unable to find loopback mount for: %s", filename)
+	}
+	defer loopbackDevice.Close()
+
+	Major, Minor, err := getDeviceMajorMinor(loopbackDevice)
+	if err != nil {
+		return "", 0, 0, err
+	}
+	return loopbackDevice.Name(), Major, Minor, nil
+}
+
+// Get the major/minor numbers of thin pool data and metadata devices
+func (devices *DeviceSet) getThinPoolDataMetaMajMin() (uint64, uint64, uint64, uint64, error) {
+	var params, poolDataMajMin, poolMetadataMajMin string
+
+	_, _, _, params, err := devicemapper.GetTable(devices.getPoolName())
+	if err != nil {
+		return 0, 0, 0, 0, err
+	}
+
+	if _, err = fmt.Sscanf(params, "%s %s", &poolMetadataMajMin, &poolDataMajMin); err != nil {
+		return 0, 0, 0, 0, err
+	}
+
+	logrus.Debugf("[devmapper]: poolDataMajMin=%s poolMetaMajMin=%s\n", poolDataMajMin, poolMetadataMajMin)
+
+	poolDataMajMinorSplit := strings.Split(poolDataMajMin, ":")
+	poolDataMajor, err := strconv.ParseUint(poolDataMajMinorSplit[0], 10, 32)
+	if err != nil {
+		return 0, 0, 0, 0, err
+	}
+
+	poolDataMinor, err := strconv.ParseUint(poolDataMajMinorSplit[1], 10, 32)
+	if err != nil {
+		return 0, 0, 0, 0, err
+	}
+
+	poolMetadataMajMinorSplit := strings.Split(poolMetadataMajMin, ":")
+	poolMetadataMajor, err := strconv.ParseUint(poolMetadataMajMinorSplit[0], 10, 32)
+	if err != nil {
+		return 0, 0, 0, 0, err
+	}
+
+	poolMetadataMinor, err := strconv.ParseUint(poolMetadataMajMinorSplit[1], 10, 32)
+	if err != nil {
+		return 0, 0, 0, 0, err
+	}
+
+	return poolDataMajor, poolDataMinor, poolMetadataMajor, poolMetadataMinor, nil
+}
+
+func (devices *DeviceSet) loadThinPoolLoopBackInfo() error {
+	poolDataMajor, poolDataMinor, poolMetadataMajor, poolMetadataMinor, err := devices.getThinPoolDataMetaMajMin()
+	if err != nil {
+		return err
+	}
+
+	dirname := devices.loopbackDir()
+
+	// data device has not been passed in. So there should be a data file
+	// which is being mounted as loop device.
+	if devices.dataDevice == "" {
+		datafilename := path.Join(dirname, "data")
+		dataLoopDevice, dataMajor, dataMinor, err := getLoopFileDeviceMajMin(datafilename)
+		if err != nil {
+			return err
+		}
+
+		// Compare the two
+		if poolDataMajor == dataMajor && poolDataMinor == dataMinor {
+			devices.dataDevice = dataLoopDevice
+			devices.dataLoopFile = datafilename
+		}
+
+	}
+
+	// metadata device has not been passed in. So there should be a
+	// metadata file which is being mounted as loop device.
+	if devices.metadataDevice == "" {
+		metadatafilename := path.Join(dirname, "metadata")
+		metadataLoopDevice, metadataMajor, metadataMinor, err := getLoopFileDeviceMajMin(metadatafilename)
+		if err != nil {
+			return err
+		}
+		if poolMetadataMajor == metadataMajor && poolMetadataMinor == metadataMinor {
+			devices.metadataDevice = metadataLoopDevice
+			devices.metadataLoopFile = metadatafilename
+		}
+	}
+
+	return nil
+}
+
 func (devices *DeviceSet) initDevmapper(doInit bool) error {
 	// give ourselves to libdm as a log handler
 	devicemapper.LogInit(devices)
@@ -1233,6 +1353,17 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
 		}
 	}
 
+	// Pool already exists and caller did not pass us a pool. That means
+	// we probably created pool earlier and could not remove it as some
+	// containers were still using it. Detect some of the properties of
+	// pool, like is it using loop devices.
+	if info.Exists != 0 && devices.thinPoolDevice == "" {
+		if err := devices.loadThinPoolLoopBackInfo(); err != nil {
+			logrus.Debugf("Failed to load thin pool loopback device information:%v", err)
+			return err
+		}
+	}
+
 	// If we didn't just create the data or metadata image, we need to
 	// load the transaction id and migrate old metadata
 	if !createdLoopback {

+ 25 - 0
pkg/devicemapper/devmapper.go

@@ -587,6 +587,31 @@ func GetStatus(name string) (uint64, uint64, string, string, error) {
 	return start, length, targetType, params, nil
 }
 
+func GetTable(name string) (uint64, uint64, string, string, error) {
+	task, err := TaskCreateNamed(DeviceTable, name)
+	if task == nil {
+		logrus.Debugf("GetTable: Error TaskCreateNamed: %s", err)
+		return 0, 0, "", "", err
+	}
+	if err := task.Run(); err != nil {
+		logrus.Debugf("GetTable: Error Run: %s", err)
+		return 0, 0, "", "", err
+	}
+
+	devinfo, err := task.GetInfo()
+	if err != nil {
+		logrus.Debugf("GetTable: Error GetInfo: %s", err)
+		return 0, 0, "", "", err
+	}
+	if devinfo.Exists == 0 {
+		logrus.Debugf("GetTable: Non existing device %s", name)
+		return 0, 0, "", "", fmt.Errorf("Non existing device %s", name)
+	}
+
+	_, start, length, targetType, params := task.GetNextTarget(unsafe.Pointer(nil))
+	return start, length, targetType, params, nil
+}
+
 func SetTransactionId(poolName string, oldId uint64, newId uint64) error {
 	task, err := TaskCreateNamed(DeviceTargetMsg, poolName)
 	if task == nil {