storage.go 11 KB

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