diskfs.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. package diskfs
  2. import (
  3. "bufio"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "os"
  8. "os/exec"
  9. "regexp"
  10. "strings"
  11. "imuslab.com/arozos/mod/utils"
  12. )
  13. /*
  14. diskfs.go
  15. This module handle file system creation and formatting
  16. */
  17. // Storage Device meta was generated by lsblk
  18. // Partitions like sdX0
  19. type PartitionMeta struct {
  20. Name string `json:"name"`
  21. MajMin string `json:"maj:min"`
  22. Rm bool `json:"rm"`
  23. Size int64 `json:"size"`
  24. Ro bool `json:"ro"`
  25. Type string `json:"type"`
  26. Mountpoint string `json:"mountpoint"`
  27. }
  28. // Block device, usually disk or rom, like sdX
  29. type BlockDeviceMeta struct {
  30. Name string `json:"name"`
  31. MajMin string `json:"maj:min"`
  32. Rm bool `json:"rm"`
  33. Size int64 `json:"size"`
  34. Ro bool `json:"ro"`
  35. Type string `json:"type"`
  36. Mountpoint string `json:"mountpoint"`
  37. Children []PartitionMeta `json:"children,omitempty"`
  38. }
  39. // A collection of information for lsblk output
  40. type StorageDevicesMeta struct {
  41. Blockdevices []BlockDeviceMeta `json:"blockdevices"`
  42. }
  43. // Check if the file format driver is installed on this host
  44. // if a format is supported, mkfs.(format) should be symlinked under /sbin
  45. func FormatPackageInstalled(fsType string) bool {
  46. return utils.FileExists("/sbin/mkfs." + fsType)
  47. }
  48. // Create file system, support ntfs, ext4 and fat32 only
  49. func FormatStorageDevice(fsType string, devicePath string) error {
  50. // Check if the filesystem type is supported
  51. switch fsType {
  52. case "ext4":
  53. // Format the device with the specified filesystem type
  54. cmd := exec.Command("sudo", "mkfs."+fsType, devicePath)
  55. output, err := cmd.CombinedOutput()
  56. if err != nil {
  57. return errors.New("unable to format device: " + string(output))
  58. }
  59. return nil
  60. case "vfat", "fat", "fat32":
  61. //Check if mkfs.fat exists
  62. if !FormatPackageInstalled("vfat") {
  63. return errors.New("unable to format device as fat (vfat). dosfstools not installed?")
  64. }
  65. // Format the device with the specified filesystem type
  66. cmd := exec.Command("sudo", "mkfs.vfat", devicePath)
  67. output, err := cmd.CombinedOutput()
  68. if err != nil {
  69. return errors.New("unable to format device: " + string(output))
  70. }
  71. return nil
  72. case "ntfs":
  73. //Check if ntfs-3g exists
  74. if !FormatPackageInstalled("ntfs") {
  75. return errors.New("unable to format device as ntfs: ntfs-3g not installed?")
  76. }
  77. //Format the drive
  78. cmd := exec.Command("sudo", "mkfs.ntfs", devicePath)
  79. output, err := cmd.CombinedOutput()
  80. if err != nil {
  81. return errors.New("unable to format device: " + string(output))
  82. }
  83. return nil
  84. default:
  85. return fmt.Errorf("unsupported filesystem type: %s", fsType)
  86. }
  87. }
  88. // List all the storage device in the system, set minSize to 0 for no filter
  89. func ListAllStorageDevices() (*StorageDevicesMeta, error) {
  90. cmd := exec.Command("sudo", "lsblk", "-b", "--json")
  91. output, err := cmd.CombinedOutput()
  92. if err != nil {
  93. return nil, fmt.Errorf("lsblk error: %v", err)
  94. }
  95. var devices StorageDevicesMeta
  96. err = json.Unmarshal([]byte(output), &devices)
  97. return &devices, err
  98. }
  99. // Get block device (e.g. /dev/sdX) info
  100. func GetBlockDeviceMeta(devicePath string) (*BlockDeviceMeta, error) {
  101. //Trim the /dev/ part of the device path
  102. deviceName := strings.TrimPrefix(devicePath, "/dev/")
  103. if len(deviceName) == 0 {
  104. return nil, errors.New("invalid device path given")
  105. }
  106. re := regexp.MustCompile(`\d+`)
  107. if re.MatchString(deviceName) {
  108. //This is a partition
  109. return nil, errors.New("given device path is a partition not a block device")
  110. }
  111. storageMeta, err := ListAllStorageDevices()
  112. if err != nil {
  113. return nil, err
  114. }
  115. for _, blockdevice := range storageMeta.Blockdevices {
  116. if blockdevice.Name == deviceName {
  117. return &blockdevice, nil
  118. }
  119. }
  120. return nil, errors.New("target block device not found")
  121. }
  122. // Get partition information (e.g. /dev/sdX1)
  123. func GetPartitionMeta(devicePath string) (*PartitionMeta, error) {
  124. //Trim the /dev/ part of the device path
  125. deviceName := strings.TrimPrefix(devicePath, "/dev/")
  126. if len(deviceName) == 0 {
  127. return nil, errors.New("invalid device path given")
  128. }
  129. re := regexp.MustCompile(`\d+`)
  130. if !re.MatchString(deviceName) {
  131. //This is a partition
  132. return nil, errors.New("given device path is a block device not a partition")
  133. }
  134. storageMeta, err := ListAllStorageDevices()
  135. if err != nil {
  136. return nil, err
  137. }
  138. for _, blockdevice := range storageMeta.Blockdevices {
  139. if strings.Contains(deviceName, blockdevice.Name) {
  140. //Matching block device. Check for if there are a matching child
  141. for _, childPartition := range blockdevice.Children {
  142. if childPartition.Name == deviceName {
  143. return &childPartition, nil
  144. }
  145. }
  146. }
  147. }
  148. return nil, errors.New("target partition not found")
  149. }
  150. // Check if a device is mounted given the path name, like /dev/sdc
  151. func DeviceIsMounted(devicePath string) (bool, error) {
  152. // Open the mountinfo file
  153. file, err := os.Open("/proc/mounts")
  154. if err != nil {
  155. return false, fmt.Errorf("error opening /proc/mounts: %v", err)
  156. }
  157. defer file.Close()
  158. // Scan the mountinfo file line by line
  159. scanner := bufio.NewScanner(file)
  160. for scanner.Scan() {
  161. line := scanner.Text()
  162. fields := strings.Fields(line)
  163. if len(fields) >= 2 && fields[0] == devicePath {
  164. // Device is mounted
  165. return true, nil
  166. }
  167. }
  168. // Device is not mounted
  169. return false, nil
  170. }
  171. // UnmountDevice unmounts the specified device.
  172. // Remember to use full path (e.g. /dev/md0) in the devicePath
  173. func UnmountDevice(devicePath string) error {
  174. // Construct the bash command to unmount the device
  175. cmd := exec.Command("sudo", "bash", "-c", fmt.Sprintf("umount %s", devicePath))
  176. // Run the command
  177. err := cmd.Run()
  178. if err != nil {
  179. return fmt.Errorf("error unmounting device: %v", err)
  180. }
  181. return nil
  182. }
  183. // Force Version of Unmount (dangerous)
  184. // Remember to use full path (e.g. /dev/md0) in the devicePath
  185. func ForceUnmountDevice(devicePath string) error {
  186. // Construct the bash command to unmount the device
  187. cmd := exec.Command("sudo", "bash", "-c", fmt.Sprintf("umount -l %s", devicePath))
  188. // Run the command
  189. err := cmd.Run()
  190. if err != nil {
  191. return fmt.Errorf("error unmounting device: %v", err)
  192. }
  193. return nil
  194. }
  195. // DANGER: Wipe the whole disk given the disk path
  196. func WipeDisk(diskPath string) error {
  197. // Unmount the disk
  198. isMounted, _ := DeviceIsMounted(diskPath)
  199. if isMounted {
  200. umountCmd := exec.Command("sudo", "umount", diskPath)
  201. if err := umountCmd.Run(); err != nil {
  202. return fmt.Errorf("error unmounting disk %s: %v", diskPath, err)
  203. }
  204. }
  205. // Wipe all filesystem signatures on the entire disk
  206. wipeCmd := exec.Command("sudo", "wipefs", "--all", "--force", diskPath)
  207. if err := wipeCmd.Run(); err != nil {
  208. return fmt.Errorf("error wiping filesystem signatures on %s: %v", diskPath, err)
  209. }
  210. return nil
  211. }