database_openwrt.go 4.8 KB

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