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.

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