| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 | package mainimport (	"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 neededfunc 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 poolfunc 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 namefunc 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 databasefunc 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()	}}
 |