123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- package main
- import (
- "encoding/json"
- "errors"
- "log"
- "os"
- "path/filepath"
- "runtime"
- "strings"
- "time"
- "imuslab.com/arozos/mod/filesystem"
- "imuslab.com/arozos/mod/filesystem/arozfs"
- "imuslab.com/arozos/mod/permission"
- "imuslab.com/arozos/mod/storage/bridge"
- fs "imuslab.com/arozos/mod/filesystem"
- storage "imuslab.com/arozos/mod/storage"
- )
- var (
- baseStoragePool *storage.StoragePool //base storage pool, all user can access these virtual roots
- //fsHandlers []*fs.FileSystemHandler //All File system handlers. All opened handles must be registered in here
- //storagePools []*storage.StoragePool //All Storage pool opened
- bridgeManager *bridge.Record //Manager to handle bridged FSH
- storageHeartbeatTickerChan chan bool //Channel to stop the storage heartbeat ticker
- )
- func StorageInit() {
- //Load the default handler for the user storage root
- if !fs.FileExists(filepath.Clean(*root_directory) + "/") {
- os.MkdirAll(filepath.Clean(*root_directory)+"/", 0755)
- }
- //Start loading the base storage pool
- err := LoadBaseStoragePool()
- if err != nil {
- panic(err)
- }
- //Create a brdige record manager
- bm := bridge.NewBridgeRecord("system/bridge.json")
- bridgeManager = bm
- }
- func LoadBaseStoragePool() error {
- //All fsh for the base pool
- fsHandlers := []*fs.FileSystemHandler{}
- //Use for Debian buster local file system
- localFileSystem := "ext4"
- if runtime.GOOS == "windows" {
- localFileSystem = "ntfs"
- }
- baseHandler, err := fs.NewFileSystemHandler(fs.FileSystemOption{
- Name: "User",
- Uuid: "user",
- Path: filepath.ToSlash(filepath.Clean(*root_directory)) + "/",
- Hierarchy: "user",
- Automount: false,
- Filesystem: localFileSystem,
- })
- if err != nil {
- systemWideLogger.PrintAndLog("Storage", "Failed to initiate user root storage directory: "+*root_directory+err.Error(), err)
- return err
- }
- fsHandlers = append(fsHandlers, baseHandler)
- //Load the tmp folder as storage unit
- tmpHandler, err := fs.NewFileSystemHandler(fs.FileSystemOption{
- Name: "tmp",
- Uuid: "tmp",
- Path: filepath.ToSlash(filepath.Clean(*tmp_directory)) + "/",
- Hierarchy: "user",
- Automount: false,
- Filesystem: localFileSystem,
- })
- if err != nil {
- systemWideLogger.PrintAndLog("Storage", "Failed to initiate tmp storage directory: "+*tmp_directory+err.Error(), err)
- return err
- }
- fsHandlers = append(fsHandlers, tmpHandler)
- //Load all the storage config from file
- rawConfig, err := os.ReadFile(*storage_config_file)
- if err != nil {
- //File not found. Use internal storage only
- systemWideLogger.PrintAndLog("Storage", "Storage configuration file not found. Using internal storage only.", err)
- } else {
- //Configuration loaded. Initializing handler
- externalHandlers, err := fs.NewFileSystemHandlersFromJSON(rawConfig)
- if err != nil {
- systemWideLogger.PrintAndLog("Storage", "Failed to load storage configuration: "+err.Error()+" -- Skipping", err)
- } else {
- for _, thisHandler := range externalHandlers {
- fsHandlers = append(fsHandlers, thisHandler)
- systemWideLogger.PrintAndLog("Storage", thisHandler.Name+" Mounted as "+thisHandler.UUID+":/", err)
- }
- }
- }
- //Create a base storage pool for all users
- sp, err := storage.NewStoragePool(fsHandlers, "system")
- if err != nil {
- systemWideLogger.PrintAndLog("Storage", "Failed to create base Storaeg Pool", err)
- return err
- }
- //Update the storage pool permission to readwrite
- sp.OtherPermission = arozfs.FsReadWrite
- baseStoragePool = sp
- return nil
- }
- // Initialize the storage connection health check for all fsh.
- func storageHeartbeatTickerInit() {
- ticker := time.NewTicker(60 * time.Second)
- done := make(chan bool)
- go func() {
- for {
- select {
- case <-done:
- return
- case <-ticker.C:
- StoragePerformFileSystemAbstractionConnectionHeartbeat()
- }
- }
- }()
- storageHeartbeatTickerChan = done
- }
- // Perform heartbeat to all connected file system abstraction.
- // Blocking function, use with go routine if needed
- func StoragePerformFileSystemAbstractionConnectionHeartbeat() {
- allFsh := GetAllLoadedFsh()
- for _, thisFsh := range allFsh {
- err := thisFsh.FileSystemAbstraction.Heartbeat()
- if err != nil {
- log.Println("[Storage] File System Abstraction from " + thisFsh.Name + " report an error: " + err.Error())
- //Retreive the old startup config and close the pool
- originalStartOption := filesystem.FileSystemOption{}
- js, _ := json.Marshal(thisFsh.StartOptions)
- json.Unmarshal(js, &originalStartOption)
- //Create a new fsh from original start options
- newfsh, err := filesystem.NewFileSystemHandler(originalStartOption)
- if err != nil {
- log.Println("[Storage] Unable to reconnect " + thisFsh.Name + ": " + err.Error())
- continue
- } else {
- //New fsh created. Close the old one
- thisFsh.Close()
- }
- //Pop this fsh from all storage pool that mounted this
- sp := GetAllStoragePools()
- parentsp := []*storage.StoragePool{}
- for _, thissp := range sp {
- if thissp.ContainDiskID(originalStartOption.Uuid) {
- parentsp = append(parentsp, thissp)
- thissp.DetachFsHandler(originalStartOption.Uuid)
- }
- }
- //Add the new fsh to all the storage pools that have it originally
- for _, pool := range parentsp {
- err := pool.AttachFsHandler(newfsh)
- if err != nil {
- log.Println("[Storage] Attach fsh to pool failed: " + err.Error())
- }
- }
- }
- }
- }
- // Initialize group storage pool
- func GroupStoragePoolInit() {
- //Mount permission groups
- for _, pg := range permissionHandler.PermissionGroups {
- //For each group, check does this group has a config file
- err := LoadStoragePoolForGroup(pg)
- if err != nil {
- continue
- }
- //Do something else, WIP
- }
- //Start editing interface for Storage Pool Editor
- StoragePoolEditorInit()
- }
- func LoadStoragePoolForGroup(pg *permission.PermissionGroup) error {
- expectedConfigPath := "./system/storage/" + pg.Name + ".json"
- if fs.FileExists(expectedConfigPath) {
- //Read the config file
- pgStorageConfig, err := os.ReadFile(expectedConfigPath)
- if err != nil {
- systemWideLogger.PrintAndLog("Storage", "Failed to read config for "+pg.Name+": "+err.Error(), err)
- return errors.New("Failed to read config for " + pg.Name + ": " + err.Error())
- }
- //Generate fsHandler form json
- thisGroupFsHandlers, err := fs.NewFileSystemHandlersFromJSON(pgStorageConfig)
- if err != nil {
- systemWideLogger.PrintAndLog("Storage", "Failed to load storage configuration: "+err.Error(), err)
- return errors.New("Failed to load storage configuration: " + err.Error())
- }
- //Show debug message
- for _, thisHandler := range thisGroupFsHandlers {
- systemWideLogger.PrintAndLog("Storage", thisHandler.Name+" Mounted as "+thisHandler.UUID+":/ for group "+pg.Name, err)
- }
- //Create a storage pool from these handlers
- sp, err := storage.NewStoragePool(thisGroupFsHandlers, pg.Name)
- if err != nil {
- systemWideLogger.PrintAndLog("Storage", "Failed to create storage pool for "+pg.Name, err)
- return errors.New("Failed to create storage pool for " + pg.Name)
- }
- //Set other permission to denied by default
- sp.OtherPermission = arozfs.FsDenied
- //Assign storage pool to group
- pg.StoragePool = sp
- } else {
- //Storage configuration not exists. Fill in the basic information and move to next storage pool
- //Create a new empty storage pool for this group
- sp, err := storage.NewStoragePool([]*fs.FileSystemHandler{}, pg.Name)
- if err != nil {
- systemWideLogger.PrintAndLog("Storage", "Failed to create empty storage pool for group: "+pg.Name, err)
- }
- pg.StoragePool = sp
- pg.StoragePool.OtherPermission = arozfs.FsDenied
- }
- return nil
- }
- // Check if a storage pool exists by its group owner name
- func StoragePoolExists(poolOwner string) bool {
- _, err := GetStoragePoolByOwner(poolOwner)
- return err == nil
- }
- func GetAllStoragePools() []*storage.StoragePool {
- //Append the base pool
- results := []*storage.StoragePool{baseStoragePool}
- //Add each permissionGroup's pool
- for _, pg := range permissionHandler.PermissionGroups {
- results = append(results, pg.StoragePool)
- }
- return results
- }
- func GetStoragePoolByOwner(owner string) (*storage.StoragePool, error) {
- sps := GetAllStoragePools()
- for _, pool := range sps {
- if pool.Owner == owner {
- return pool, nil
- }
- }
- return nil, errors.New("Storage pool owned by " + owner + " not found")
- }
- func GetFSHandlerSubpathFromVpath(vpath string) (*fs.FileSystemHandler, string, error) {
- VirtualRootID, subpath, err := fs.GetIDFromVirtualPath(vpath)
- if err != nil {
- return nil, "", errors.New("Unable to resolve requested path: " + err.Error())
- }
- fsh, err := GetFsHandlerByUUID(VirtualRootID)
- if err != nil {
- return nil, "", errors.New("Unable to resolve requested path: " + err.Error())
- }
- if fsh == nil || fsh.FileSystemAbstraction == nil {
- return nil, "", errors.New("Unable to resolve requested path: " + err.Error())
- }
- if fsh.Closed {
- return nil, "", errors.New("Target file system handler already closed")
- }
- return fsh, subpath, nil
- }
- func GetFsHandlerByUUID(uuid string) (*fs.FileSystemHandler, error) {
- //Filter out the :/ fropm uuid if exists
- if strings.Contains(uuid, ":") {
- uuid = strings.Split(uuid, ":")[0]
- }
- var resultFsh *fs.FileSystemHandler = nil
- allFsh := GetAllLoadedFsh()
- for _, fsh := range allFsh {
- if fsh.UUID == uuid && !fsh.Closed {
- resultFsh = fsh
- }
- }
- if resultFsh == nil {
- return nil, errors.New("Filesystem handler with given UUID not found")
- } else {
- return resultFsh, nil
- }
- }
- func GetAllLoadedFsh() []*fs.FileSystemHandler {
- fshTmp := map[string]*fs.FileSystemHandler{}
- allFsh := []*fs.FileSystemHandler{}
- allStoragePools := GetAllStoragePools()
- for _, thisSP := range allStoragePools {
- for _, thisFsh := range thisSP.Storages {
- fshPointer := thisFsh
- fshTmp[thisFsh.UUID] = fshPointer
- }
- }
- //Restructure the map to slice
- for _, fsh := range fshTmp {
- allFsh = append(allFsh, fsh)
- }
- return allFsh
- }
- func RegisterStorageSettings() {
- //Storage Pool Configuration
- registerSetting(settingModule{
- Name: "Storage Pools",
- Desc: "Storage Pool Mounting Configuration",
- IconPath: "SystemAO/disk/smart/img/small_icon.png",
- Group: "Disk",
- StartDir: "SystemAO/storage/poolList.html",
- RequireAdmin: true,
- })
- }
- // CloseAllStorages Close all storage database
- func CloseAllStorages() {
- allFsh := GetAllLoadedFsh()
- for _, fsh := range allFsh {
- fsh.FilesystemDatabase.Close()
- }
- }
- func closeAllStoragePools() {
- //Stop the storage pool heartbeat
- storageHeartbeatTickerChan <- true
- //Close all storage pools
- for _, sp := range GetAllStoragePools() {
- sp.Close()
- }
- }
|