storage.go 11 KB

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