Files
zoraxy/src/mod/database/database_openwrt.go
2023-05-22 23:05:59 +08:00

209 lines
4.8 KiB
Go

//go:build mipsle || riscv64
// +build mipsle riscv64
package database
import (
"encoding/json"
"errors"
"log"
"os"
"path/filepath"
"strings"
"sync"
)
func newDatabase(dbfile string, readOnlyMode bool) (*Database, error) {
dbRootPath := filepath.ToSlash(filepath.Clean(dbfile))
dbRootPath = "fsdb/" + dbRootPath
err := os.MkdirAll(dbRootPath, 0755)
if err != nil {
return nil, err
}
tableMap := sync.Map{}
//build the table list from file system
files, err := filepath.Glob(filepath.Join(dbRootPath, "/*"))
if err != nil {
return nil, err
}
for _, file := range files {
if isDirectory(file) {
tableMap.Store(filepath.Base(file), "")
}
}
log.Println("Filesystem Emulated Key-value Database Service Started: " + dbRootPath)
return &Database{
Db: dbRootPath,
Tables: tableMap,
ReadOnly: readOnlyMode,
}, nil
}
func (d *Database) dump(filename string) ([]string, error) {
//Get all file objects from root
rootfiles, err := filepath.Glob(filepath.Join(d.Db.(string), "/*"))
if err != nil {
return []string{}, err
}
//Filter out the folders
rootFolders := []string{}
for _, file := range rootfiles {
if !isDirectory(file) {
rootFolders = append(rootFolders, filepath.Base(file))
}
}
return rootFolders, nil
}
func (d *Database) newTable(tableName string) error {
if d.ReadOnly {
return errors.New("Operation rejected in ReadOnly mode")
}
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
if !fileExists(tablePath) {
return os.MkdirAll(tablePath, 0755)
}
return nil
}
func (d *Database) tableExists(tableName string) bool {
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
if _, err := os.Stat(tablePath); errors.Is(err, os.ErrNotExist) {
return false
}
if !isDirectory(tablePath) {
return false
}
return true
}
func (d *Database) dropTable(tableName string) error {
if d.ReadOnly {
return errors.New("Operation rejected in ReadOnly mode")
}
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
if d.tableExists(tableName) {
return os.RemoveAll(tablePath)
} else {
return errors.New("table not exists")
}
}
func (d *Database) write(tableName string, key string, value interface{}) error {
if d.ReadOnly {
return errors.New("Operation rejected in ReadOnly mode")
}
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
js, err := json.Marshal(value)
if err != nil {
return err
}
key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
return os.WriteFile(filepath.Join(tablePath, key+".entry"), js, 0755)
}
func (d *Database) read(tableName string, key string, assignee interface{}) error {
if !d.keyExists(tableName, key) {
return errors.New("key not exists")
}
key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
entryPath := filepath.Join(tablePath, key+".entry")
content, err := os.ReadFile(entryPath)
if err != nil {
return err
}
err = json.Unmarshal(content, &assignee)
return err
}
func (d *Database) keyExists(tableName string, key string) bool {
key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
entryPath := filepath.Join(tablePath, key+".entry")
return fileExists(entryPath)
}
func (d *Database) delete(tableName string, key string) error {
if d.ReadOnly {
return errors.New("Operation rejected in ReadOnly mode")
}
if !d.keyExists(tableName, key) {
return errors.New("key not exists")
}
key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
entryPath := filepath.Join(tablePath, key+".entry")
return os.Remove(entryPath)
}
func (d *Database) listTable(tableName string) ([][][]byte, error) {
if !d.tableExists(tableName) {
return [][][]byte{}, errors.New("table not exists")
}
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
entries, err := filepath.Glob(filepath.Join(tablePath, "/*.entry"))
if err != nil {
return [][][]byte{}, err
}
var results [][][]byte = [][][]byte{}
for _, entry := range entries {
if !isDirectory(entry) {
//Read it
key := filepath.Base(entry)
key = strings.TrimSuffix(key, filepath.Ext(key))
key = strings.ReplaceAll(key, "-SLASH_SIGN-", "/")
bkey := []byte(key)
bval := []byte("")
c, err := os.ReadFile(entry)
if err != nil {
break
}
bval = c
results = append(results, [][]byte{bkey, bval})
}
}
return results, nil
}
func (d *Database) close() {
//Nothing to close as it is file system
}
func isDirectory(path string) bool {
fileInfo, err := os.Stat(path)
if err != nil {
return false
}
return fileInfo.IsDir()
}
func fileExists(name string) bool {
_, err := os.Stat(name)
if err == nil {
return true
}
if errors.Is(err, os.ErrNotExist) {
return false
}
return false
}