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.

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