database_openwrt.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. //go:build mipsle || riscv64
  2. // +build mipsle riscv64
  3. package database
  4. import (
  5. "encoding/json"
  6. "errors"
  7. "log"
  8. "os"
  9. "path/filepath"
  10. "strings"
  11. "sync"
  12. )
  13. func newDatabase(dbfile string, readOnlyMode bool) (*Database, error) {
  14. dbRootPath := filepath.ToSlash(filepath.Clean(dbfile))
  15. dbRootPath = "fsdb/" + dbRootPath
  16. err := os.MkdirAll(dbRootPath, 0755)
  17. if err != nil {
  18. return nil, err
  19. }
  20. tableMap := sync.Map{}
  21. //build the table list from file system
  22. files, err := filepath.Glob(filepath.Join(dbRootPath, "/*"))
  23. if err != nil {
  24. return nil, err
  25. }
  26. for _, file := range files {
  27. if isDirectory(file) {
  28. tableMap.Store(filepath.Base(file), "")
  29. }
  30. }
  31. log.Println("Filesystem Emulated Key-value Database Service Started: " + dbRootPath)
  32. return &Database{
  33. Db: dbRootPath,
  34. Tables: tableMap,
  35. ReadOnly: readOnlyMode,
  36. }, nil
  37. }
  38. func (d *Database) dump(filename string) ([]string, error) {
  39. //Get all file objects from root
  40. rootfiles, err := filepath.Glob(filepath.Join(d.Db.(string), "/*"))
  41. if err != nil {
  42. return []string{}, err
  43. }
  44. //Filter out the folders
  45. rootFolders := []string{}
  46. for _, file := range rootfiles {
  47. if !isDirectory(file) {
  48. rootFolders = append(rootFolders, filepath.Base(file))
  49. }
  50. }
  51. return rootFolders, nil
  52. }
  53. func (d *Database) newTable(tableName string) error {
  54. if d.ReadOnly {
  55. return errors.New("Operation rejected in ReadOnly mode")
  56. }
  57. tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
  58. if !fileExists(tablePath) {
  59. return os.MkdirAll(tablePath, 0755)
  60. }
  61. return nil
  62. }
  63. func (d *Database) tableExists(tableName string) bool {
  64. tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
  65. if _, err := os.Stat(tablePath); errors.Is(err, os.ErrNotExist) {
  66. return false
  67. }
  68. if !isDirectory(tablePath) {
  69. return false
  70. }
  71. return true
  72. }
  73. func (d *Database) dropTable(tableName string) error {
  74. if d.ReadOnly {
  75. return errors.New("Operation rejected in ReadOnly mode")
  76. }
  77. tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
  78. if d.tableExists(tableName) {
  79. return os.RemoveAll(tablePath)
  80. } else {
  81. return errors.New("table not exists")
  82. }
  83. }
  84. func (d *Database) write(tableName string, key string, value interface{}) error {
  85. if d.ReadOnly {
  86. return errors.New("Operation rejected in ReadOnly mode")
  87. }
  88. tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
  89. js, err := json.Marshal(value)
  90. if err != nil {
  91. return err
  92. }
  93. key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
  94. return os.WriteFile(filepath.Join(tablePath, key+".entry"), js, 0755)
  95. }
  96. func (d *Database) read(tableName string, key string, assignee interface{}) error {
  97. if !d.keyExists(tableName, key) {
  98. return errors.New("key not exists")
  99. }
  100. key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
  101. tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
  102. entryPath := filepath.Join(tablePath, key+".entry")
  103. content, err := os.ReadFile(entryPath)
  104. if err != nil {
  105. return err
  106. }
  107. err = json.Unmarshal(content, &assignee)
  108. return err
  109. }
  110. func (d *Database) keyExists(tableName string, key string) bool {
  111. key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
  112. tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
  113. entryPath := filepath.Join(tablePath, key+".entry")
  114. return fileExists(entryPath)
  115. }
  116. func (d *Database) delete(tableName string, key string) error {
  117. if d.ReadOnly {
  118. return errors.New("Operation rejected in ReadOnly mode")
  119. }
  120. if !d.keyExists(tableName, key) {
  121. return errors.New("key not exists")
  122. }
  123. key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
  124. tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
  125. entryPath := filepath.Join(tablePath, key+".entry")
  126. return os.Remove(entryPath)
  127. }
  128. func (d *Database) listTable(tableName string) ([][][]byte, error) {
  129. if !d.tableExists(tableName) {
  130. return [][][]byte{}, errors.New("table not exists")
  131. }
  132. tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
  133. entries, err := filepath.Glob(filepath.Join(tablePath, "/*.entry"))
  134. if err != nil {
  135. return [][][]byte{}, err
  136. }
  137. var results [][][]byte = [][][]byte{}
  138. for _, entry := range entries {
  139. if !isDirectory(entry) {
  140. //Read it
  141. key := filepath.Base(entry)
  142. key = strings.TrimSuffix(key, filepath.Ext(key))
  143. key = strings.ReplaceAll(key, "-SLASH_SIGN-", "/")
  144. bkey := []byte(key)
  145. bval := []byte("")
  146. c, err := os.ReadFile(entry)
  147. if err != nil {
  148. break
  149. }
  150. bval = c
  151. results = append(results, [][]byte{bkey, bval})
  152. }
  153. }
  154. return results, nil
  155. }
  156. func (d *Database) close() {
  157. //Nothing to close as it is file system
  158. }
  159. func isDirectory(path string) bool {
  160. fileInfo, err := os.Stat(path)
  161. if err != nil {
  162. return false
  163. }
  164. return fileInfo.IsDir()
  165. }
  166. func fileExists(name string) bool {
  167. _, err := os.Stat(name)
  168. if err == nil {
  169. return true
  170. }
  171. if errors.Is(err, os.ErrNotExist) {
  172. return false
  173. }
  174. return false
  175. }