diskcapacity.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package diskcapacity
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "net/http"
  6. "path/filepath"
  7. "runtime"
  8. "strings"
  9. "log"
  10. "strconv"
  11. "os/exec"
  12. "imuslab.com/arozos/mod/common"
  13. "imuslab.com/arozos/mod/disk/diskspace"
  14. "imuslab.com/arozos/mod/user"
  15. )
  16. /*
  17. Disk Capacity
  18. This is a simple module to check how many storage space is remaining
  19. on a given directory in accessiable file system paths
  20. Author: tobychui
  21. */
  22. type Resolver struct {
  23. UserHandler *user.UserHandler
  24. }
  25. type Capacity struct {
  26. PhysicalDevice string //The ID of the physical device, like C:/ or /dev/sda1
  27. MountingHierarchy string //The Mounting Hierarchy of the vroot
  28. Used int64 //Used capacity in bytes
  29. Avilable int64 //Avilable capacity in bytes
  30. Total int64 //Total capacity in bytes
  31. }
  32. //Create a new Capacity Resolver with the given user handler
  33. func NewCapacityResolver(u *user.UserHandler) *Resolver {
  34. return &Resolver{
  35. UserHandler: u,
  36. }
  37. }
  38. func (cr *Resolver) HandleCapacityResolving(w http.ResponseWriter, r *http.Request) {
  39. //Check if the request user is authenticated
  40. userinfo, err := cr.UserHandler.GetUserInfoFromRequest(w, r)
  41. if err != nil {
  42. common.SendErrorResponse(w, "User not logged in")
  43. return
  44. }
  45. //Get vpath from paramter
  46. vpath, err := common.Mv(r, "path", true)
  47. if err != nil {
  48. common.SendErrorResponse(w, "Vpath is not defined")
  49. return
  50. }
  51. capinfo, err := cr.ResolveCapacityInfo(userinfo.Username, vpath)
  52. if err != nil {
  53. common.SendErrorResponse(w, "Unable to resolve path capacity information: "+err.Error())
  54. return
  55. }
  56. //Get Storage Hierarcy
  57. fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
  58. if err != nil {
  59. capinfo.MountingHierarchy = "Unknown"
  60. } else {
  61. capinfo.MountingHierarchy = fsh.Hierarchy
  62. }
  63. //Send the requested path capacity information
  64. js, _ := json.Marshal(capinfo)
  65. common.SendJSONResponse(w, string(js))
  66. }
  67. func (cr *Resolver) ResolveCapacityInfo(username string, vpath string) (*Capacity, error) {
  68. //Resolve the vpath for this user
  69. userinfo, err := cr.UserHandler.GetUserInfoFromUsername(username)
  70. if err != nil {
  71. return nil, err
  72. }
  73. realpath, err := userinfo.VirtualPathToRealPath(vpath)
  74. if err != nil {
  75. return nil, err
  76. }
  77. realpath = filepath.ToSlash(filepath.Clean(realpath))
  78. return cr.GetCapacityInfo(realpath)
  79. }
  80. func (cr *Resolver) GetCapacityInfo(realpath string) (*Capacity, error) {
  81. rpathAbs, err := filepath.Abs(realpath)
  82. if err != nil {
  83. return nil, err
  84. }
  85. if runtime.GOOS == "windows" {
  86. //Windows
  87. //Extract disk ID from path
  88. rpathAbs = filepath.ToSlash(filepath.Clean(rpathAbs))
  89. diskRoot := strings.Split(rpathAbs, "/")[0]
  90. //Match the disk space info generated from diskspace
  91. logicDiskInfo := diskspace.GetAllLogicDiskInfo()
  92. for _, ldi := range logicDiskInfo {
  93. if strings.TrimSpace(ldi.Device) == strings.TrimSpace(diskRoot) {
  94. //Matching device ID
  95. return &Capacity{
  96. PhysicalDevice: ldi.Device,
  97. Used: ldi.Used,
  98. Avilable: ldi.Available,
  99. Total: ldi.Volume,
  100. }, nil
  101. }
  102. }
  103. } else {
  104. //Assume Linux or Mac
  105. //Use command: df -P {abs_path}
  106. cmd := exec.Command("df", "-P", rpathAbs)
  107. log.Println("df", "-P", rpathAbs)
  108. out, err := cmd.CombinedOutput()
  109. if err != nil {
  110. return nil, err
  111. }
  112. //Get the last line of the output
  113. diskInfo := strings.TrimSpace(string(out))
  114. tmp := strings.Split(diskInfo, "\n")
  115. targetDiskInfo := strings.Join(tmp[len(tmp) - 1:], " ");
  116. for strings.Contains(targetDiskInfo, " "){
  117. targetDiskInfo = strings.ReplaceAll(targetDiskInfo, " ", " ")
  118. }
  119. diskInfoSlice := strings.Split(targetDiskInfo, " ")
  120. if len(diskInfoSlice) < 4{
  121. return nil, errors.New("Malformed output for df -P")
  122. }
  123. //Extract capacity information from df output
  124. total, err := strconv.ParseInt(diskInfoSlice[1], 10, 64)
  125. if err != nil{
  126. return nil, errors.New("Malformed output for df -P")
  127. }
  128. used, err := strconv.ParseInt(diskInfoSlice[2], 10, 64)
  129. if err != nil{
  130. return nil, errors.New("Malformed output for df -P")
  131. }
  132. availbe, err := strconv.ParseInt(diskInfoSlice[3], 10, 64)
  133. if err != nil{
  134. return nil, errors.New("Malformed output for df -P")
  135. }
  136. //Return the capacity info struct, capacity is reported in 1024 bytes block
  137. return &Capacity{
  138. PhysicalDevice: diskInfoSlice[0],
  139. Used: used * 1024,
  140. Avilable: availbe * 1024,
  141. Total: total * 1024,
  142. }, nil
  143. }
  144. return nil, errors.New("Unable to resolve matching disk capacity information")
  145. }