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
5.0 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/tmlibs/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. db.mtx.Lock()
  125. defer db.mtx.Unlock()
  126. // We need a copy of all of the keys.
  127. // Not the best, but probably not a bottleneck depending.
  128. keys, err := list(db.dir, start, end)
  129. if err != nil {
  130. panic(errors.Wrapf(err, "Listing keys in %s", db.dir))
  131. }
  132. sort.Strings(keys)
  133. return newMemDBIterator(db, keys, start, end)
  134. }
  135. func (db *FSDB) ReverseIterator(start, end []byte) Iterator {
  136. panic("not implemented yet") // XXX
  137. }
  138. func (db *FSDB) nameToPath(name []byte) string {
  139. n := url.PathEscape(string(name))
  140. return filepath.Join(db.dir, n)
  141. }
  142. // Read some bytes to a file.
  143. // CONTRACT: returns os errors directly without wrapping.
  144. func read(path string) ([]byte, error) {
  145. f, err := os.Open(path)
  146. if err != nil {
  147. return nil, err
  148. }
  149. defer f.Close()
  150. d, err := ioutil.ReadAll(f)
  151. if err != nil {
  152. return nil, err
  153. }
  154. return d, nil
  155. }
  156. // Write some bytes from a file.
  157. // CONTRACT: returns os errors directly without wrapping.
  158. func write(path string, d []byte) error {
  159. f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, keyPerm)
  160. if err != nil {
  161. return err
  162. }
  163. defer f.Close()
  164. _, err = f.Write(d)
  165. if err != nil {
  166. return err
  167. }
  168. err = f.Sync()
  169. return err
  170. }
  171. // Remove a file.
  172. // CONTRACT: returns os errors directly without wrapping.
  173. func remove(path string) error {
  174. return os.Remove(path)
  175. }
  176. // List keys in a directory, stripping of escape sequences and dir portions.
  177. // CONTRACT: returns os errors directly without wrapping.
  178. func list(dirPath string, start, end []byte) ([]string, error) {
  179. dir, err := os.Open(dirPath)
  180. if err != nil {
  181. return nil, err
  182. }
  183. defer dir.Close()
  184. names, err := dir.Readdirnames(0)
  185. if err != nil {
  186. return nil, err
  187. }
  188. var keys []string
  189. for _, name := range names {
  190. n, err := url.PathUnescape(name)
  191. if err != nil {
  192. return nil, fmt.Errorf("Failed to unescape %s while listing", name)
  193. }
  194. key := unescapeKey([]byte(n))
  195. if IsKeyInDomain(key, start, end, false) {
  196. keys = append(keys, string(key))
  197. }
  198. }
  199. return keys, nil
  200. }
  201. // To support empty or nil keys, while the file system doesn't allow empty
  202. // filenames.
  203. func escapeKey(key []byte) []byte {
  204. return []byte("k_" + string(key))
  205. }
  206. func unescapeKey(escKey []byte) []byte {
  207. if len(escKey) < 2 {
  208. panic(fmt.Sprintf("Invalid esc key: %x", escKey))
  209. }
  210. if string(escKey[:2]) != "k_" {
  211. panic(fmt.Sprintf("Invalid esc key: %x", escKey))
  212. }
  213. return escKey[2:]
  214. }