loop.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. package manager
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "strconv"
  6. "strings"
  7. "sync"
  8. "yunion.io/x/log"
  9. "yunion.io/x/pkg/errors"
  10. "yunion.io/x/onecloud/pkg/util/fileutils2"
  11. "yunion.io/x/onecloud/pkg/util/losetup"
  12. "yunion.io/x/onecloud/pkg/util/losetup/ioctl"
  13. "yunion.io/x/onecloud/pkg/util/mountutils"
  14. "yunion.io/x/onecloud/pkg/util/procutils"
  15. )
  16. type ILoopDevice interface {
  17. // GetDevicePath 返回设备路径
  18. GetDevicePath() string
  19. // IsUsed 检查设备是否在使用中
  20. IsUsed() bool
  21. // SetUsed 设置设备使用状态
  22. SetUsed(used bool)
  23. }
  24. type ILoopManager interface {
  25. AttachDevice(filePath string, partScan bool) (*losetup.Device, error)
  26. DetachDevice(devPath string) error
  27. }
  28. // loopDevice Loop设备实现
  29. type loopDevice struct {
  30. devicePath string
  31. used bool
  32. lock *sync.Mutex
  33. }
  34. // NewLoopDevice 创建新的Loop设备
  35. func NewLoopDevice(devicePath string) ILoopDevice {
  36. return &loopDevice{
  37. devicePath: devicePath,
  38. used: fileutils2.IsBlockDeviceUsed(devicePath),
  39. lock: &sync.Mutex{},
  40. }
  41. }
  42. func (d *loopDevice) GetDevicePath() string {
  43. return d.devicePath
  44. }
  45. func (d *loopDevice) IsUsed() bool {
  46. // 修复在 host 服务运行期间,有些 loop 设备被其他程序使用的情况
  47. if fileutils2.IsBlockDeviceUsed(d.devicePath) {
  48. return true
  49. }
  50. return d.used
  51. }
  52. func (d *loopDevice) SetUsed(used bool) {
  53. d.lock.Lock()
  54. defer d.lock.Unlock()
  55. d.used = used
  56. }
  57. func (d *loopDevice) Detach() error {
  58. // 获取所有挂载点
  59. mountPoints, err := d.getMountPoints()
  60. if err != nil {
  61. return errors.Wrap(err, "getMountPoints")
  62. }
  63. // 卸载所有挂载点
  64. for _, mountPoint := range mountPoints {
  65. if err := mountutils.Unmount(mountPoint, false); err != nil {
  66. return errors.Wrapf(err, "umount %s", mountPoint)
  67. }
  68. }
  69. // 断开loop设备
  70. if err := losetup.DetachDevice(d.GetDevicePath()); err != nil {
  71. return errors.Wrapf(err, "detach loop device %s", d.GetDevicePath())
  72. }
  73. return nil
  74. }
  75. func (d *loopDevice) getMountPoints() ([]string, error) {
  76. cmd := fmt.Sprintf("mount | grep %sp1 | awk '{print $3}'", d.GetDevicePath())
  77. output, err := procutils.NewRemoteCommandAsFarAsPossible("sh", "-c", cmd).Output()
  78. if err != nil {
  79. return nil, errors.Wrapf(err, "exec cmd %s: %s", cmd, output)
  80. }
  81. return strings.Split(string(output), "\n"), nil
  82. }
  83. func (m *loopManager) initDevices() error {
  84. // 使用 ls 和 grep 命令列出所有以数字结尾的 /dev/loop* 设备
  85. cmd := "ls /dev/loop* | grep -E 'loop[0-9]+$'"
  86. output, err := procutils.NewRemoteCommandAsFarAsPossible("sh", "-c", cmd).Output()
  87. if err != nil {
  88. log.Errorf("list loop devices error: %v", err)
  89. output = make([]byte, 0)
  90. }
  91. // 按行分割输出
  92. devices := strings.Split(string(output), "\n")
  93. for _, dev := range devices {
  94. dev = strings.TrimSpace(dev)
  95. if dev == "" {
  96. continue
  97. }
  98. m.devices[dev] = NewLoopDevice(dev)
  99. }
  100. return nil
  101. }
  102. // loopManager Loop设备管理器
  103. type loopManager struct {
  104. devices map[string]ILoopDevice
  105. actionLock *sync.Mutex
  106. mapLock *sync.Mutex
  107. }
  108. // NewLoopManager 创建新的Loop管理器
  109. func newLoopManager() (ILoopManager, error) {
  110. ret := &loopManager{
  111. devices: make(map[string]ILoopDevice),
  112. actionLock: &sync.Mutex{},
  113. mapLock: new(sync.Mutex),
  114. }
  115. if err := ret.initDevices(); err != nil {
  116. return ret, errors.Wrap(err, "initDevices")
  117. }
  118. return ret, nil
  119. }
  120. const (
  121. MAX_LOOPDEV_COUNT = 512
  122. )
  123. func (m *loopManager) AttachDevice(filePath string, partScan bool) (*losetup.Device, error) {
  124. m.actionLock.Lock()
  125. defer m.actionLock.Unlock()
  126. dev, err := m.acquireDevice()
  127. if err != nil {
  128. return nil, errors.Wrap(err, "AcquireDevice")
  129. }
  130. loDev, err := losetup.AttachDeviceWithPath(dev.GetDevicePath(), filePath, partScan)
  131. if err != nil {
  132. return nil, errors.Wrapf(err, "AttachDeviceWithPath: %s, filePath: %s", dev.GetDevicePath(), filePath)
  133. }
  134. return loDev, nil
  135. }
  136. func (m *loopManager) DetachDevice(devPath string) error {
  137. m.actionLock.Lock()
  138. defer m.actionLock.Unlock()
  139. if err := losetup.DetachDevice(devPath); err != nil {
  140. return errors.Wrapf(err, "DetachDevice: %s", devPath)
  141. }
  142. m.releaseDevice(devPath)
  143. return nil
  144. }
  145. func (m *loopManager) findNewDevice() (ILoopDevice, error) {
  146. // 获取所有已使用的设备号
  147. usedNumbers := make(map[int]bool)
  148. for name := range m.devices {
  149. // 从设备名称中提取数字,例如从 /dev/loop0 中提取 0
  150. numStr := strings.TrimPrefix(name, "/dev/loop")
  151. num, err := strconv.Atoi(numStr)
  152. if err != nil {
  153. return nil, errors.Wrapf(err, "parse device number from %s", name)
  154. }
  155. usedNumbers[num] = true
  156. }
  157. notFound := true
  158. errs := make([]error, 0)
  159. // 从0开始查找第一个未使用的设备号
  160. for i := 0; i < MAX_LOOPDEV_COUNT; i++ {
  161. if usedNumbers[i] {
  162. continue
  163. }
  164. notFound = false
  165. // 创建新的loop设备
  166. devPath, err := ioctl.AddDevice(i)
  167. if err != nil {
  168. errs = append(errs, errors.Wrapf(err, "add device %d", i))
  169. continue
  170. }
  171. device := NewLoopDevice(devPath)
  172. m.devices[devPath] = device
  173. device.SetUsed(true)
  174. return device, nil
  175. }
  176. if notFound {
  177. return nil, errors.Wrap(errors.ErrNotFound, "No available device found")
  178. } else {
  179. return nil, errors.NewAggregate(errs)
  180. }
  181. }
  182. func (m *loopManager) acquireDevice() (ILoopDevice, error) {
  183. m.mapLock.Lock()
  184. defer m.mapLock.Unlock()
  185. freeDevices := make([]ILoopDevice, 0)
  186. for _, device := range m.devices {
  187. if !device.IsUsed() {
  188. freeDevices = append(freeDevices, device)
  189. }
  190. }
  191. if len(freeDevices) > 0 {
  192. device := freeDevices[rand.Intn(len(freeDevices))]
  193. device.SetUsed(true)
  194. return device, nil
  195. }
  196. // create new device
  197. device, err := m.findNewDevice()
  198. if err != nil {
  199. return nil, errors.Wrap(err, "findNewDevice")
  200. }
  201. return device, nil
  202. }
  203. func (m *loopManager) releaseDevice(devPath string) {
  204. m.mapLock.Lock()
  205. defer m.mapLock.Unlock()
  206. if dev, ok := m.devices[devPath]; ok {
  207. dev.SetUsed(false)
  208. }
  209. }