losetup.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package raid
  2. import (
  3. "fmt"
  4. "os"
  5. "os/exec"
  6. "path/filepath"
  7. "strings"
  8. )
  9. /*
  10. losetup.go
  11. This script handle losetup loopback interface setup and listing
  12. */
  13. type LoopDevice struct {
  14. Device string
  15. PartitionRange string
  16. ImageFile string
  17. }
  18. // List all the loop devices
  19. func ListAllLoopDevices() ([]*LoopDevice, error) {
  20. cmd := exec.Command("sudo", "losetup", "-a")
  21. output, err := cmd.CombinedOutput()
  22. if err != nil {
  23. return nil, fmt.Errorf("error running losetup -a command: %v", err)
  24. }
  25. //Example of returned values
  26. // /dev/loop0: [2049]:265955 (/home/aroz/test/sdX.img)
  27. // Split the output into lines and extract device names
  28. lines := strings.Split(string(output), "\n")
  29. var devices []*LoopDevice = []*LoopDevice{}
  30. for _, line := range lines {
  31. fields := strings.Fields(line)
  32. if len(fields) >= 3 {
  33. //As the image name contains a bracket, that needs to be trimmed off
  34. imageName := strings.TrimPrefix(strings.TrimSpace(fields[2]), "(")
  35. imageName = strings.TrimSuffix(imageName, ")")
  36. devices = append(devices, &LoopDevice{
  37. Device: strings.TrimSuffix(fields[0], ":"),
  38. PartitionRange: fields[1],
  39. ImageFile: imageName,
  40. })
  41. }
  42. }
  43. return devices, nil
  44. }
  45. // Mount an given image path as loopback device
  46. func MountImageAsLoopDevice(imagePath string) error {
  47. cmd := exec.Command("sudo", "losetup", "-f", imagePath)
  48. cmd.Stdout = os.Stdout
  49. cmd.Stderr = os.Stderr
  50. err := cmd.Run()
  51. if err != nil {
  52. return fmt.Errorf("creating loopback device failed: %v", err)
  53. }
  54. return nil
  55. }
  56. // Unmount a loop device by the image path
  57. func UnmountLoopDeviceByImagePath(imagePath string) error {
  58. imagePathIsMounted, err := ImageMountedAsLoopDevice(imagePath)
  59. if err != nil {
  60. return err
  61. }
  62. if !imagePathIsMounted {
  63. //Image already unmounted. No need to unmount
  64. return nil
  65. }
  66. loopDriveID, err := GetLoopDriveIDFromImagePath(imagePath)
  67. if err != nil {
  68. return err
  69. }
  70. //As we checked for mounted above, no need to check if loopDriveID is empty string
  71. return UnmountLoopDeviceByID(loopDriveID)
  72. }
  73. // Unmount the loop device by id e.g. /dev/loop1
  74. func UnmountLoopDeviceByID(loopDevId string) error {
  75. cmd := exec.Command("sudo", "losetup", "-d", loopDevId)
  76. cmd.Stdout = os.Stdout
  77. cmd.Stderr = os.Stderr
  78. err := cmd.Run()
  79. if err != nil {
  80. return fmt.Errorf("delete loopback device failed: %v", err)
  81. }
  82. return nil
  83. }
  84. // Get loopdrive ID (/dev/loop1) by the image path, return empty string if not found error if load failed
  85. func GetLoopDriveIDFromImagePath(imagePath string) (string, error) {
  86. absPath, err := filepath.Abs(imagePath)
  87. if err != nil {
  88. return "", err
  89. }
  90. devs, err := ListAllLoopDevices()
  91. if err != nil {
  92. return "", err
  93. }
  94. for _, dev := range devs {
  95. if filepath.ToSlash(dev.ImageFile) == filepath.ToSlash(absPath) {
  96. //Found. already mounted
  97. return dev.Device, nil
  98. }
  99. }
  100. return "", nil
  101. }
  102. // Check if an image file is already mounted as loop drive
  103. func ImageMountedAsLoopDevice(imagePath string) (bool, error) {
  104. loopDriveId, err := GetLoopDriveIDFromImagePath(imagePath)
  105. if err != nil {
  106. return false, err
  107. }
  108. return loopDriveId != "", nil
  109. }