You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

254 lines
4.8 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. package db
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/url"
  6. "os"
  7. "path/filepath"
  8. "sort"
  9. "sync"
  10. "github.com/pkg/errors"
  11. )
  12. const (
  13. keyPerm = os.FileMode(0600)
  14. dirPerm = os.FileMode(0700)
  15. )
  16. func init() {
  17. registerDBCreator(FSDBBackendStr, func(name string, dir string) (DB, error) {
  18. dbPath := filepath.Join(dir, name+".db")
  19. return NewFSDB(dbPath), nil
  20. }, false)
  21. }
  22. var _ DB = (*FSDB)(nil)
  23. // It's slow.
  24. type FSDB struct {
  25. mtx sync.Mutex
  26. dir string
  27. }
  28. func NewFSDB(dir string) *FSDB {
  29. err := os.MkdirAll(dir, dirPerm)
  30. if err != nil {
  31. panic(errors.Wrap(err, "Creating FSDB dir "+dir))
  32. }
  33. database := &FSDB{
  34. dir: dir,
  35. }
  36. return database
  37. }
  38. func (db *FSDB) Get(key []byte) []byte {
  39. db.mtx.Lock()
  40. defer db.mtx.Unlock()
  41. panicNilKey(key)
  42. path := db.nameToPath(key)
  43. value, err := read(path)
  44. if os.IsNotExist(err) {
  45. return nil
  46. } else if err != nil {
  47. panic(errors.Wrapf(err, "Getting key %s (0x%X)", string(key), key))
  48. }
  49. return value
  50. }
  51. func (db *FSDB) Has(key []byte) bool {
  52. db.mtx.Lock()
  53. defer db.mtx.Unlock()
  54. panicNilKey(key)
  55. path := db.nameToPath(key)
  56. _, err := read(path)
  57. if os.IsNotExist(err) {
  58. return false
  59. } else if err != nil {
  60. panic(errors.Wrapf(err, "Getting key %s (0x%X)", string(key), key))
  61. }
  62. return true
  63. }
  64. func (db *FSDB) Set(key []byte, value []byte) {
  65. db.mtx.Lock()
  66. defer db.mtx.Unlock()
  67. panicNilKey(key)
  68. db.SetNoLock(key, value)
  69. }
  70. func (db *FSDB) SetSync(key []byte, value []byte) {
  71. db.mtx.Lock()
  72. defer db.mtx.Unlock()
  73. panicNilKey(key)
  74. db.SetNoLock(key, value)
  75. }
  76. // NOTE: Implements atomicSetDeleter.
  77. func (db *FSDB) SetNoLock(key []byte, value []byte) {
  78. panicNilKey(key)
  79. if value == nil {
  80. value = []byte{}
  81. }
  82. path := db.nameToPath(key)
  83. err := write(path, value)
  84. if err != nil {
  85. panic(errors.Wrapf(err, "Setting key %s (0x%X)", string(key), key))
  86. }
  87. }
  88. func (db *FSDB) Delete(key []byte) {
  89. db.mtx.Lock()
  90. defer db.mtx.Unlock()
  91. panicNilKey(key)
  92. db.DeleteNoLock(key)
  93. }
  94. func (db *FSDB) DeleteSync(key []byte) {
  95. db.mtx.Lock()
  96. defer db.mtx.Unlock()
  97. panicNilKey(key)
  98. db.DeleteNoLock(key)
  99. }
  100. // NOTE: Implements atomicSetDeleter.
  101. func (db *FSDB) DeleteNoLock(key []byte) {
  102. panicNilKey(key)
  103. path := db.nameToPath(key)
  104. err := remove(path)
  105. if os.IsNotExist(err) {
  106. return
  107. } else if err != nil {
  108. panic(errors.Wrapf(err, "Removing key %s (0x%X)", string(key), key))
  109. }
  110. }
  111. func (db *FSDB) Close() {
  112. // Nothing to do.
  113. }
  114. func (db *FSDB) Print() {
  115. db.mtx.Lock()
  116. defer db.mtx.Unlock()
  117. panic("FSDB.Print not yet implemented")
  118. }
  119. func (db *FSDB) Stats() map[string]string {
  120. db.mtx.Lock()
  121. defer db.mtx.Unlock()
  122. panic("FSDB.Stats not yet implemented")
  123. }
  124. func (db *FSDB) NewBatch() Batch {
  125. db.mtx.Lock()
  126. defer db.mtx.Unlock()
  127. // Not sure we would ever want to try...
  128. // It doesn't seem easy for general filesystems.
  129. panic("FSDB.NewBatch not yet implemented")
  130. }
  131. func (db *FSDB) Mutex() *sync.Mutex {
  132. return &(db.mtx)
  133. }
  134. func (db *FSDB) Iterator(start, end []byte) Iterator {
  135. it := newMemDBIterator(db, start, end)
  136. db.mtx.Lock()
  137. defer db.mtx.Unlock()
  138. // We need a copy of all of the keys.
  139. // Not the best, but probably not a bottleneck depending.
  140. keys, err := list(db.dir, start, end)
  141. if err != nil {
  142. panic(errors.Wrapf(err, "Listing keys in %s", db.dir))
  143. }
  144. sort.Strings(keys)
  145. it.keys = keys
  146. return it
  147. }
  148. func (db *FSDB) ReverseIterator(start, end []byte) Iterator {
  149. // XXX
  150. return nil
  151. }
  152. func (db *FSDB) nameToPath(name []byte) string {
  153. n := url.PathEscape(string(name))
  154. return filepath.Join(db.dir, n)
  155. }
  156. // Read some bytes to a file.
  157. // CONTRACT: returns os errors directly without wrapping.
  158. func read(path string) ([]byte, error) {
  159. f, err := os.Open(path)
  160. if err != nil {
  161. return nil, err
  162. }
  163. defer f.Close()
  164. d, err := ioutil.ReadAll(f)
  165. if err != nil {
  166. return nil, err
  167. }
  168. return d, nil
  169. }
  170. // Write some bytes from a file.
  171. // CONTRACT: returns os errors directly without wrapping.
  172. func write(path string, d []byte) error {
  173. f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, keyPerm)
  174. if err != nil {
  175. return err
  176. }
  177. defer f.Close()
  178. _, err = f.Write(d)
  179. if err != nil {
  180. return err
  181. }
  182. err = f.Sync()
  183. return err
  184. }
  185. // Remove a file.
  186. // CONTRACT: returns os errors directly without wrapping.
  187. func remove(path string) error {
  188. return os.Remove(path)
  189. }
  190. // List files of a path.
  191. // Paths will NOT include dir as the prefix.
  192. // CONTRACT: returns os errors directly without wrapping.
  193. func list(dirPath string, start, end []byte) ([]string, error) {
  194. dir, err := os.Open(dirPath)
  195. if err != nil {
  196. return nil, err
  197. }
  198. defer dir.Close()
  199. names, err := dir.Readdirnames(0)
  200. if err != nil {
  201. return nil, err
  202. }
  203. var paths []string
  204. for _, name := range names {
  205. n, err := url.PathUnescape(name)
  206. if err != nil {
  207. return nil, fmt.Errorf("Failed to unescape %s while listing", name)
  208. }
  209. if IsKeyInDomain(n, start, end) {
  210. paths = append(paths, n)
  211. }
  212. }
  213. return paths, nil
  214. }