storage.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. package main
  2. import (
  3. "errors"
  4. "io/ioutil"
  5. "log"
  6. "os"
  7. "path/filepath"
  8. "runtime"
  9. "strings"
  10. "time"
  11. "imuslab.com/arozos/mod/filesystem/arozfs"
  12. "imuslab.com/arozos/mod/permission"
  13. "imuslab.com/arozos/mod/storage/bridge"
  14. fs "imuslab.com/arozos/mod/filesystem"
  15. storage "imuslab.com/arozos/mod/storage"
  16. )
  17. var (
  18. baseStoragePool *storage.StoragePool //base storage pool, all user can access these virtual roots
  19. //fsHandlers []*fs.FileSystemHandler //All File system handlers. All opened handles must be registered in here
  20. //storagePools []*storage.StoragePool //All Storage pool opened
  21. bridgeManager *bridge.Record //Manager to handle bridged FSH
  22. )
  23. func StorageInit() {
  24. //Load the default handler for the user storage root
  25. if !fs.FileExists(filepath.Clean(*root_directory) + "/") {
  26. os.MkdirAll(filepath.Clean(*root_directory)+"/", 0755)
  27. }
  28. //Start loading the base storage pool
  29. err := LoadBaseStoragePool()
  30. if err != nil {
  31. panic(err)
  32. }
  33. //Create a brdige record manager
  34. bm := bridge.NewBridgeRecord("system/bridge.json")
  35. bridgeManager = bm
  36. }
  37. func LoadBaseStoragePool() error {
  38. //All fsh for the base pool
  39. fsHandlers := []*fs.FileSystemHandler{}
  40. //Use for Debian buster local file system
  41. localFileSystem := "ext4"
  42. if runtime.GOOS == "windows" {
  43. localFileSystem = "ntfs"
  44. }
  45. baseHandler, err := fs.NewFileSystemHandler(fs.FileSystemOption{
  46. Name: "User",
  47. Uuid: "user",
  48. Path: filepath.ToSlash(filepath.Clean(*root_directory)) + "/",
  49. Hierarchy: "user",
  50. Automount: false,
  51. Filesystem: localFileSystem,
  52. })
  53. if err != nil {
  54. systemWideLogger.PrintAndLog("Storage", "Failed to initiate user root storage directory: "+*root_directory+err.Error(), err)
  55. return err
  56. }
  57. fsHandlers = append(fsHandlers, baseHandler)
  58. //Load the tmp folder as storage unit
  59. tmpHandler, err := fs.NewFileSystemHandler(fs.FileSystemOption{
  60. Name: "tmp",
  61. Uuid: "tmp",
  62. Path: filepath.ToSlash(filepath.Clean(*tmp_directory)) + "/",
  63. Hierarchy: "user",
  64. Automount: false,
  65. Filesystem: localFileSystem,
  66. })
  67. if err != nil {
  68. systemWideLogger.PrintAndLog("Storage", "Failed to initiate tmp storage directory: "+*tmp_directory+err.Error(), err)
  69. return err
  70. }
  71. fsHandlers = append(fsHandlers, tmpHandler)
  72. //Load all the storage config from file
  73. rawConfig, err := ioutil.ReadFile(*storage_config_file)
  74. if err != nil {
  75. //File not found. Use internal storage only
  76. systemWideLogger.PrintAndLog("Storage", "Storage configuration file not found. Using internal storage only.", err)
  77. } else {
  78. //Configuration loaded. Initializing handler
  79. externalHandlers, err := fs.NewFileSystemHandlersFromJSON(rawConfig)
  80. if err != nil {
  81. systemWideLogger.PrintAndLog("Storage", "Failed to load storage configuration: "+err.Error()+" -- Skipping", err)
  82. } else {
  83. for _, thisHandler := range externalHandlers {
  84. fsHandlers = append(fsHandlers, thisHandler)
  85. systemWideLogger.PrintAndLog("Storage", thisHandler.Name+" Mounted as "+thisHandler.UUID+":/", err)
  86. }
  87. }
  88. }
  89. //Create a base storage pool for all users
  90. sp, err := storage.NewStoragePool(fsHandlers, "system")
  91. if err != nil {
  92. systemWideLogger.PrintAndLog("Storage", "Failed to create base Storaeg Pool", err)
  93. return err
  94. }
  95. //Update the storage pool permission to readwrite
  96. sp.OtherPermission = arozfs.FsReadWrite
  97. baseStoragePool = sp
  98. return nil
  99. }
  100. //Initialize the storage connection health check for all fsh.
  101. func storageHeartbeatTickerInit() {
  102. ticker := time.NewTicker(60 * time.Second)
  103. done := make(chan bool)
  104. go func() {
  105. for {
  106. select {
  107. case <-done:
  108. return
  109. case <-ticker.C:
  110. StoragePerformFileSystemAbstractionConnectionHeartbeat()
  111. }
  112. }
  113. }()
  114. }
  115. //Perform heartbeat to all connected file system abstraction.
  116. //Blocking function, use with go routine if needed
  117. func StoragePerformFileSystemAbstractionConnectionHeartbeat() {
  118. allFsh := GetAllLoadedFsh()
  119. for _, thisFsh := range allFsh {
  120. err := thisFsh.FileSystemAbstraction.Heartbeat()
  121. if err != nil {
  122. log.Println("[Storage] File System Abstraction from " + thisFsh.Name + " report an error: " + err.Error())
  123. //Reload this storage pool abstraction
  124. thisFsh.ReloadFileSystelAbstraction()
  125. }
  126. }
  127. }
  128. //Initialize group storage pool
  129. func GroupStoragePoolInit() {
  130. //Mount permission groups
  131. for _, pg := range permissionHandler.PermissionGroups {
  132. //For each group, check does this group has a config file
  133. err := LoadStoragePoolForGroup(pg)
  134. if err != nil {
  135. continue
  136. }
  137. //Do something else, WIP
  138. }
  139. //Start editing interface for Storage Pool Editor
  140. StoragePoolEditorInit()
  141. }
  142. func LoadStoragePoolForGroup(pg *permission.PermissionGroup) error {
  143. expectedConfigPath := "./system/storage/" + pg.Name + ".json"
  144. if fs.FileExists(expectedConfigPath) {
  145. //Read the config file
  146. pgStorageConfig, err := os.ReadFile(expectedConfigPath)
  147. if err != nil {
  148. systemWideLogger.PrintAndLog("Storage", "Failed to read config for "+pg.Name+": "+err.Error(), err)
  149. return errors.New("Failed to read config for " + pg.Name + ": " + err.Error())
  150. }
  151. //Generate fsHandler form json
  152. thisGroupFsHandlers, err := fs.NewFileSystemHandlersFromJSON(pgStorageConfig)
  153. if err != nil {
  154. systemWideLogger.PrintAndLog("Storage", "Failed to load storage configuration: "+err.Error(), err)
  155. return errors.New("Failed to load storage configuration: " + err.Error())
  156. }
  157. //Show debug message
  158. for _, thisHandler := range thisGroupFsHandlers {
  159. systemWideLogger.PrintAndLog("Storage", thisHandler.Name+" Mounted as "+thisHandler.UUID+":/ for group "+pg.Name, err)
  160. }
  161. //Create a storage pool from these handlers
  162. sp, err := storage.NewStoragePool(thisGroupFsHandlers, pg.Name)
  163. if err != nil {
  164. systemWideLogger.PrintAndLog("Storage", "Failed to create storage pool for "+pg.Name, err)
  165. return errors.New("Failed to create storage pool for " + pg.Name)
  166. }
  167. //Set other permission to denied by default
  168. sp.OtherPermission = arozfs.FsDenied
  169. //Assign storage pool to group
  170. pg.StoragePool = sp
  171. } else {
  172. //Storage configuration not exists. Fill in the basic information and move to next storage pool
  173. //Create a new empty storage pool for this group
  174. sp, err := storage.NewStoragePool([]*fs.FileSystemHandler{}, pg.Name)
  175. if err != nil {
  176. systemWideLogger.PrintAndLog("Storage", "Failed to create empty storage pool for group: "+pg.Name, err)
  177. }
  178. pg.StoragePool = sp
  179. pg.StoragePool.OtherPermission = arozfs.FsDenied
  180. }
  181. return nil
  182. }
  183. //Check if a storage pool exists by its group owner name
  184. func StoragePoolExists(poolOwner string) bool {
  185. _, err := GetStoragePoolByOwner(poolOwner)
  186. return err == nil
  187. }
  188. func GetAllStoragePools() []*storage.StoragePool {
  189. //Append the base pool
  190. results := []*storage.StoragePool{baseStoragePool}
  191. //Add each permissionGroup's pool
  192. for _, pg := range permissionHandler.PermissionGroups {
  193. results = append(results, pg.StoragePool)
  194. }
  195. return results
  196. }
  197. func GetStoragePoolByOwner(owner string) (*storage.StoragePool, error) {
  198. sps := GetAllStoragePools()
  199. for _, pool := range sps {
  200. if pool.Owner == owner {
  201. return pool, nil
  202. }
  203. }
  204. return nil, errors.New("Storage pool owned by " + owner + " not found")
  205. }
  206. func GetFSHandlerSubpathFromVpath(vpath string) (*fs.FileSystemHandler, string, error) {
  207. VirtualRootID, subpath, err := fs.GetIDFromVirtualPath(vpath)
  208. if err != nil {
  209. return nil, "", errors.New("Unable to resolve requested path: " + err.Error())
  210. }
  211. fsh, err := GetFsHandlerByUUID(VirtualRootID)
  212. if err != nil {
  213. return nil, "", errors.New("Unable to resolve requested path: " + err.Error())
  214. }
  215. if fsh == nil || fsh.FileSystemAbstraction == nil {
  216. return nil, "", errors.New("Unable to resolve requested path: " + err.Error())
  217. }
  218. if fsh.Closed {
  219. return nil, "", errors.New("Target file system handler already closed")
  220. }
  221. return fsh, subpath, nil
  222. }
  223. func GetFsHandlerByUUID(uuid string) (*fs.FileSystemHandler, error) {
  224. //Filter out the :/ fropm uuid if exists
  225. if strings.Contains(uuid, ":") {
  226. uuid = strings.Split(uuid, ":")[0]
  227. }
  228. var resultFsh *fs.FileSystemHandler = nil
  229. allFsh := GetAllLoadedFsh()
  230. for _, fsh := range allFsh {
  231. if fsh.UUID == uuid && !fsh.Closed {
  232. resultFsh = fsh
  233. }
  234. }
  235. if resultFsh == nil {
  236. return nil, errors.New("Filesystem handler with given UUID not found")
  237. } else {
  238. return resultFsh, nil
  239. }
  240. }
  241. func GetAllLoadedFsh() []*fs.FileSystemHandler {
  242. fshTmp := map[string]*fs.FileSystemHandler{}
  243. allFsh := []*fs.FileSystemHandler{}
  244. allStoragePools := GetAllStoragePools()
  245. for _, thisSP := range allStoragePools {
  246. for _, thisFsh := range thisSP.Storages {
  247. fshPointer := thisFsh
  248. fshTmp[thisFsh.UUID] = fshPointer
  249. }
  250. }
  251. //Restructure the map to slice
  252. for _, fsh := range fshTmp {
  253. allFsh = append(allFsh, fsh)
  254. }
  255. return allFsh
  256. }
  257. func RegisterStorageSettings() {
  258. //Storage Pool Configuration
  259. registerSetting(settingModule{
  260. Name: "Storage Pools",
  261. Desc: "Storage Pool Mounting Configuration",
  262. IconPath: "SystemAO/disk/smart/img/small_icon.png",
  263. Group: "Disk",
  264. StartDir: "SystemAO/storage/poolList.html",
  265. RequireAdmin: true,
  266. })
  267. }
  268. //CloseAllStorages Close all storage database
  269. func CloseAllStorages() {
  270. allFsh := GetAllLoadedFsh()
  271. for _, fsh := range allFsh {
  272. fsh.FilesystemDatabase.Close()
  273. }
  274. }
  275. func closeAllStoragePools() {
  276. for _, sp := range GetAllStoragePools() {
  277. sp.Close()
  278. }
  279. }