remove.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package ioctl
  15. /*
  16. #include <linux/loop.h>
  17. */
  18. //import "C"
  19. import (
  20. "fmt"
  21. "os"
  22. "path/filepath"
  23. "strconv"
  24. "strings"
  25. "syscall"
  26. "time"
  27. "yunion.io/x/log"
  28. "yunion.io/x/pkg/errors"
  29. fileutils "yunion.io/x/onecloud/pkg/util/fileutils2"
  30. "yunion.io/x/onecloud/pkg/util/losetup"
  31. "yunion.io/x/onecloud/pkg/util/mountutils"
  32. )
  33. const (
  34. LOOP_CTL_PATH = "/dev/loop-control"
  35. LOOP_CTL_REMOVE = 0x4c81 // 19585 C.LOOP_CTL_REMOVE
  36. // LOOP_CTL_GET_FREE = C.LOOP_CTL_GET_FREE
  37. LOOP_CTL_ADD = 0x4c80 // 19584 C.LOOP_CTL_ADD
  38. )
  39. func AddDevice(devNumber int) (string, error) {
  40. fd, err := os.OpenFile(LOOP_CTL_PATH, os.O_RDWR, 0644)
  41. if err != nil {
  42. return "", errors.Wrapf(err, "Open %s", LOOP_CTL_PATH)
  43. }
  44. defer fd.Close()
  45. retNu, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), LOOP_CTL_ADD, uintptr(devNumber))
  46. if err != nil && !strings.Contains(err.Error(), "errno 0") {
  47. return "", errors.Wrapf(err, "IOCTL ADD DEVICE")
  48. }
  49. return fmt.Sprintf("/dev/loop%d", retNu), nil
  50. }
  51. /*func FindFreeDevice() (string, error) {
  52. fd, err := os.OpenFile(LOOP_CTL_PATH, os.O_RDWR, 0644)
  53. if err != nil {
  54. return "", errors.Wrapf(err, "Open %s", LOOP_CTL_PATH)
  55. }
  56. defer fd.Close()
  57. var devNr int
  58. retNu, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), LOOP_CTL_GET_FREE, uintptr(devNr))
  59. if err != nil {
  60. return "", errors.Wrapf(err, "IOCTL GET FREE 2")
  61. }
  62. log.Infof("using ioctl get free loop %v", retNu)
  63. return fmt.Sprintf("/dev/loop%d", retNu), nil
  64. }*/
  65. func RemoveDevice(devNumber int) error {
  66. fd, err := os.OpenFile(LOOP_CTL_PATH, os.O_RDWR, 0644)
  67. if err != nil {
  68. return errors.Wrapf(err, "Open %s", LOOP_CTL_PATH)
  69. }
  70. defer fd.Close()
  71. retNum, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), LOOP_CTL_REMOVE, uintptr(devNumber))
  72. if err != nil {
  73. return errors.Wrapf(err, "IOCT REMOVE %d", devNumber)
  74. }
  75. log.Infof("using ioctl remove loop %v", retNum)
  76. return nil
  77. }
  78. func DetachAndRemoveDevice(devPath string) error {
  79. getDev := func() (*losetup.Device, error) {
  80. devs, err := losetup.ListDevices()
  81. if err != nil {
  82. return nil, errors.Wrapf(err, "list devices")
  83. }
  84. dev := devs.GetDeviceByName(devPath)
  85. return dev, nil
  86. }
  87. dev, err := getDev()
  88. if err != nil {
  89. return err
  90. }
  91. if dev == nil {
  92. return nil
  93. }
  94. // check mountpoints
  95. partions, err := losetup.GetDevicePartions(devPath)
  96. if err != nil {
  97. return errors.Wrapf(err, "get device %s partions", devPath)
  98. }
  99. for _, part := range partions {
  100. checkMntCmd, _ := losetup.NewCommand("sh", "-c", fmt.Sprintf("mount | grep %s | awk '{print $3}'", part)).Run()
  101. if out := checkMntCmd.Output(); out != "" {
  102. mntPoints := strings.Split(out, "\n")
  103. for _, mntPoint := range mntPoints {
  104. if mntPoint != "" {
  105. if err := mountutils.Unmount(mntPoint, false); err != nil {
  106. return errors.Wrapf(err, "umount %s of dev: %s, part: %s", mntPoint, dev.Name, part)
  107. }
  108. }
  109. }
  110. }
  111. }
  112. _, err = losetup.NewLosetupCommand().AddArgs("-d", dev.Name).Run()
  113. if err != nil {
  114. return errors.Wrapf(err, "detach device")
  115. }
  116. removeDev := func() error {
  117. log.Infof("Start removing device %s", dev.Name)
  118. if fileutils.Exists(dev.Name) {
  119. devNumStr := strings.TrimPrefix(filepath.Base(dev.Name), "loop")
  120. devNum, err := strconv.Atoi(devNumStr)
  121. if err != nil {
  122. return errors.Wrapf(err, "remove device: invalid device number: %s", devNumStr)
  123. }
  124. if err := RemoveDevice(devNum); err != nil {
  125. return errors.Wrapf(err, "remove device: %s", devNumStr)
  126. }
  127. } else {
  128. log.Infof("Loop device %s removed", dev.Name)
  129. }
  130. return nil
  131. }
  132. errs := []error{}
  133. for i := 1; i <= 5; i++ {
  134. if err := removeDev(); err != nil {
  135. err = errors.Wrapf(err, "remove loop device, %d times", i)
  136. log.Warningf("remove device %s: %v", devPath, err)
  137. errs = append(errs, err)
  138. } else {
  139. return nil
  140. }
  141. time.Sleep(time.Duration(i) * time.Second)
  142. }
  143. return errors.NewAggregate(errs)
  144. // recheck
  145. /*dev, err = getDev()
  146. if err != nil {
  147. return errors.Wrapf(err, "get device by %s for rechecking", devPath)
  148. }
  149. if dev != nil {
  150. return errors.Errorf("device %s still exists, %s", devPath, jsonutils.Marshal(dev))
  151. }*/
  152. }