dataStore_bolt.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. // +build !PSIPHON_USE_BADGER_DB,!PSIPHON_USE_FILES_DB
  2. /*
  3. * Copyright (c) 2018, Psiphon Inc.
  4. * All rights reserved.
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. package psiphon
  21. import (
  22. std_errors "errors"
  23. "os"
  24. "path/filepath"
  25. "runtime/debug"
  26. "sync/atomic"
  27. "time"
  28. "github.com/Psiphon-Labs/bolt"
  29. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
  30. )
  31. const (
  32. OPEN_DB_RETRIES = 2
  33. )
  34. type datastoreDB struct {
  35. boltDB *bolt.DB
  36. isFailed int32
  37. }
  38. type datastoreTx struct {
  39. db *datastoreDB
  40. boltTx *bolt.Tx
  41. }
  42. type datastoreBucket struct {
  43. db *datastoreDB
  44. boltBucket *bolt.Bucket
  45. }
  46. type datastoreCursor struct {
  47. db *datastoreDB
  48. boltCursor *bolt.Cursor
  49. }
  50. func datastoreOpenDB(
  51. rootDataDirectory string, retryAndReset bool) (*datastoreDB, error) {
  52. var db *datastoreDB
  53. var err error
  54. attempts := 1
  55. if retryAndReset {
  56. attempts += OPEN_DB_RETRIES
  57. }
  58. for attempt := 0; attempt < attempts; attempt++ {
  59. db, err = tryDatastoreOpenDB(rootDataDirectory, attempt > 0)
  60. if err == nil {
  61. break
  62. }
  63. NoticeWarning("tryDatastoreOpenDB failed: %s", err)
  64. // The datastore file may be corrupt, so, in subsequent iterations, set the
  65. // "reset" flag and attempt to delete the file and try again.
  66. }
  67. return db, err
  68. }
  69. func tryDatastoreOpenDB(
  70. rootDataDirectory string, reset bool) (retdb *datastoreDB, reterr error) {
  71. // Testing indicates that the bolt Check function can raise SIGSEGV due to
  72. // invalid mmap buffer accesses in cases such as opening a valid but
  73. // truncated datastore file.
  74. //
  75. // To handle this, we temporarily set SetPanicOnFault in order to treat the
  76. // fault as a panic, recover any panic, and return an error which will result
  77. // in a retry with reset.
  78. // Begin recovery preamble
  79. panicOnFault := debug.SetPanicOnFault(true)
  80. defer debug.SetPanicOnFault(panicOnFault)
  81. defer func() {
  82. if r := recover(); r != nil {
  83. retdb = nil
  84. reterr = errors.Tracef("panic: %v", r)
  85. }
  86. }()
  87. // End recovery preamble
  88. filename := filepath.Join(rootDataDirectory, "psiphon.boltdb")
  89. if reset {
  90. NoticeWarning("tryDatastoreOpenDB: reset")
  91. os.Remove(filename)
  92. }
  93. newDB, err := bolt.Open(filename, 0600, &bolt.Options{Timeout: 1 * time.Second})
  94. if err != nil {
  95. return nil, errors.Trace(err)
  96. }
  97. // Run consistency checks on datastore and emit errors for diagnostics
  98. // purposes. We assume this will complete quickly for typical size Psiphon
  99. // datastores and wait for the check to complete before proceeding.
  100. err = newDB.View(func(tx *bolt.Tx) error {
  101. return tx.SynchronousCheck()
  102. })
  103. if err != nil {
  104. return nil, errors.Trace(err)
  105. }
  106. err = newDB.Update(func(tx *bolt.Tx) error {
  107. requiredBuckets := [][]byte{
  108. datastoreServerEntriesBucket,
  109. datastoreServerEntryTagsBucket,
  110. datastoreServerEntryTombstoneTagsBucket,
  111. datastoreSplitTunnelRouteETagsBucket,
  112. datastoreSplitTunnelRouteDataBucket,
  113. datastoreUrlETagsBucket,
  114. datastoreKeyValueBucket,
  115. datastoreRemoteServerListStatsBucket,
  116. datastoreFailedTunnelStatsBucket,
  117. datastoreSLOKsBucket,
  118. datastoreTacticsBucket,
  119. datastoreSpeedTestSamplesBucket,
  120. datastoreDialParametersBucket,
  121. }
  122. for _, bucket := range requiredBuckets {
  123. _, err := tx.CreateBucketIfNotExists(bucket)
  124. if err != nil {
  125. return err
  126. }
  127. }
  128. return nil
  129. })
  130. if err != nil {
  131. return nil, errors.Trace(err)
  132. }
  133. // Cleanup obsolete buckets
  134. err = newDB.Update(func(tx *bolt.Tx) error {
  135. obsoleteBuckets := [][]byte{
  136. []byte("tunnelStats"),
  137. []byte("rankedServerEntries"),
  138. }
  139. for _, obsoleteBucket := range obsoleteBuckets {
  140. if tx.Bucket(obsoleteBucket) != nil {
  141. err := tx.DeleteBucket(obsoleteBucket)
  142. if err != nil {
  143. NoticeWarning("DeleteBucket %s error: %s", obsoleteBucket, err)
  144. // Continue, since this is not fatal
  145. }
  146. }
  147. }
  148. return nil
  149. })
  150. if err != nil {
  151. return nil, errors.Trace(err)
  152. }
  153. return &datastoreDB{boltDB: newDB}, nil
  154. }
  155. var errDatastoreFailed = std_errors.New("datastore has failed")
  156. func (db *datastoreDB) isDatastoreFailed() bool {
  157. return atomic.LoadInt32(&db.isFailed) == 1
  158. }
  159. func (db *datastoreDB) setDatastoreFailed(r interface{}) {
  160. atomic.StoreInt32(&db.isFailed, 1)
  161. NoticeWarning("Datastore failed: %s", errors.Tracef("panic: %v", r))
  162. }
  163. func (db *datastoreDB) close() error {
  164. // Limitation: there is no panic recover in this case. We assume boltDB.Close
  165. // does not make mmap accesses and prefer to not continue with the datastore
  166. // file in a locked or open state. We also assume that any locks aquired by
  167. // boltDB.Close, held by transactions, will be released even if the
  168. // transaction panics and the database is in the failed state.
  169. return db.boltDB.Close()
  170. }
  171. func (db *datastoreDB) view(fn func(tx *datastoreTx) error) (reterr error) {
  172. // Any bolt function that performs mmap buffer accesses can raise SIGBUS due
  173. // to underlying storage changes, such as a truncation of the datastore file
  174. // or removal or network attached storage, etc.
  175. //
  176. // To handle this, we temporarily set SetPanicOnFault in order to treat the
  177. // fault as a panic, recover any panic to avoid crashing the process, and
  178. // putting this datastoreDB instance into a failed state. All subsequent
  179. // calls to this datastoreDBinstance or its related datastoreTx and
  180. // datastoreBucket instances will fail.
  181. // Begin recovery preamble
  182. if db.isDatastoreFailed() {
  183. return errDatastoreFailed
  184. }
  185. panicOnFault := debug.SetPanicOnFault(true)
  186. defer debug.SetPanicOnFault(panicOnFault)
  187. defer func() {
  188. if r := recover(); r != nil {
  189. db.setDatastoreFailed(r)
  190. reterr = errDatastoreFailed
  191. }
  192. }()
  193. // End recovery preamble
  194. return db.boltDB.View(
  195. func(tx *bolt.Tx) error {
  196. err := fn(&datastoreTx{db: db, boltTx: tx})
  197. if err != nil {
  198. return errors.Trace(err)
  199. }
  200. return nil
  201. })
  202. }
  203. func (db *datastoreDB) update(fn func(tx *datastoreTx) error) (reterr error) {
  204. // Begin recovery preamble
  205. if db.isDatastoreFailed() {
  206. return errDatastoreFailed
  207. }
  208. panicOnFault := debug.SetPanicOnFault(true)
  209. defer debug.SetPanicOnFault(panicOnFault)
  210. defer func() {
  211. if r := recover(); r != nil {
  212. db.setDatastoreFailed(r)
  213. reterr = errDatastoreFailed
  214. }
  215. }()
  216. // End recovery preamble
  217. return db.boltDB.Update(
  218. func(tx *bolt.Tx) error {
  219. err := fn(&datastoreTx{db: db, boltTx: tx})
  220. if err != nil {
  221. return errors.Trace(err)
  222. }
  223. return nil
  224. })
  225. }
  226. func (tx *datastoreTx) bucket(name []byte) (retbucket *datastoreBucket) {
  227. // Begin recovery preamble
  228. if tx.db.isDatastoreFailed() {
  229. return &datastoreBucket{db: tx.db, boltBucket: nil}
  230. }
  231. panicOnFault := debug.SetPanicOnFault(true)
  232. defer debug.SetPanicOnFault(panicOnFault)
  233. defer func() {
  234. if r := recover(); r != nil {
  235. tx.db.setDatastoreFailed(r)
  236. retbucket = &datastoreBucket{db: tx.db, boltBucket: nil}
  237. }
  238. }()
  239. // End recovery preamble
  240. return &datastoreBucket{db: tx.db, boltBucket: tx.boltTx.Bucket(name)}
  241. }
  242. func (tx *datastoreTx) clearBucket(name []byte) (reterr error) {
  243. // Begin recovery preamble
  244. if tx.db.isDatastoreFailed() {
  245. return errDatastoreFailed
  246. }
  247. panicOnFault := debug.SetPanicOnFault(true)
  248. defer debug.SetPanicOnFault(panicOnFault)
  249. defer func() {
  250. if r := recover(); r != nil {
  251. tx.db.setDatastoreFailed(r)
  252. reterr = errDatastoreFailed
  253. }
  254. }()
  255. // End recovery preamble
  256. err := tx.boltTx.DeleteBucket(name)
  257. if err != nil {
  258. return errors.Trace(err)
  259. }
  260. _, err = tx.boltTx.CreateBucket(name)
  261. if err != nil {
  262. return errors.Trace(err)
  263. }
  264. return nil
  265. }
  266. func (b *datastoreBucket) get(key []byte) (retvalue []byte) {
  267. // Begin recovery preamble
  268. if b.db.isDatastoreFailed() {
  269. return nil
  270. }
  271. panicOnFault := debug.SetPanicOnFault(true)
  272. defer debug.SetPanicOnFault(panicOnFault)
  273. defer func() {
  274. if r := recover(); r != nil {
  275. b.db.setDatastoreFailed(r)
  276. retvalue = nil
  277. }
  278. }()
  279. // End recovery preamble
  280. return b.boltBucket.Get(key)
  281. }
  282. func (b *datastoreBucket) put(key, value []byte) (reterr error) {
  283. // Begin recovery preamble
  284. if b.db.isDatastoreFailed() {
  285. return errDatastoreFailed
  286. }
  287. panicOnFault := debug.SetPanicOnFault(true)
  288. defer debug.SetPanicOnFault(panicOnFault)
  289. defer func() {
  290. if r := recover(); r != nil {
  291. b.db.setDatastoreFailed(r)
  292. reterr = errDatastoreFailed
  293. }
  294. }()
  295. // End recovery preamble
  296. err := b.boltBucket.Put(key, value)
  297. if err != nil {
  298. return errors.Trace(err)
  299. }
  300. return nil
  301. }
  302. func (b *datastoreBucket) delete(key []byte) (reterr error) {
  303. // Begin recovery preamble
  304. if b.db.isDatastoreFailed() {
  305. return errDatastoreFailed
  306. }
  307. panicOnFault := debug.SetPanicOnFault(true)
  308. defer debug.SetPanicOnFault(panicOnFault)
  309. defer func() {
  310. if r := recover(); r != nil {
  311. b.db.setDatastoreFailed(r)
  312. reterr = errDatastoreFailed
  313. }
  314. }()
  315. // End recovery preamble
  316. err := b.boltBucket.Delete(key)
  317. if err != nil {
  318. return errors.Trace(err)
  319. }
  320. return nil
  321. }
  322. func (b *datastoreBucket) cursor() (retcursor datastoreCursor) {
  323. // Begin recovery preamble
  324. if b.db.isDatastoreFailed() {
  325. return datastoreCursor{db: b.db, boltCursor: nil}
  326. }
  327. panicOnFault := debug.SetPanicOnFault(true)
  328. defer debug.SetPanicOnFault(panicOnFault)
  329. defer func() {
  330. if r := recover(); r != nil {
  331. b.db.setDatastoreFailed(r)
  332. retcursor = datastoreCursor{db: b.db, boltCursor: nil}
  333. }
  334. }()
  335. // End recovery preamble
  336. return datastoreCursor{db: b.db, boltCursor: b.boltBucket.Cursor()}
  337. }
  338. func (c *datastoreCursor) firstKey() (retkey []byte) {
  339. // Begin recovery preamble
  340. if c.db.isDatastoreFailed() {
  341. return nil
  342. }
  343. panicOnFault := debug.SetPanicOnFault(true)
  344. defer debug.SetPanicOnFault(panicOnFault)
  345. defer func() {
  346. if r := recover(); r != nil {
  347. c.db.setDatastoreFailed(r)
  348. retkey = nil
  349. }
  350. }()
  351. // End recovery preamble
  352. key, _ := c.boltCursor.First()
  353. return key
  354. }
  355. func (c *datastoreCursor) nextKey() (retkey []byte) {
  356. // Begin recovery preamble
  357. if c.db.isDatastoreFailed() {
  358. return nil
  359. }
  360. panicOnFault := debug.SetPanicOnFault(true)
  361. defer debug.SetPanicOnFault(panicOnFault)
  362. defer func() {
  363. if r := recover(); r != nil {
  364. c.db.setDatastoreFailed(r)
  365. retkey = nil
  366. }
  367. }()
  368. // End recovery preamble
  369. key, _ := c.boltCursor.Next()
  370. return key
  371. }
  372. func (c *datastoreCursor) first() (retkey, retvalue []byte) {
  373. // Begin recovery preamble
  374. if c.db.isDatastoreFailed() {
  375. return nil, nil
  376. }
  377. panicOnFault := debug.SetPanicOnFault(true)
  378. defer debug.SetPanicOnFault(panicOnFault)
  379. defer func() {
  380. if r := recover(); r != nil {
  381. c.db.setDatastoreFailed(r)
  382. retkey = nil
  383. retvalue = nil
  384. }
  385. }()
  386. // End recovery preamble
  387. return c.boltCursor.First()
  388. }
  389. func (c *datastoreCursor) next() (retkey, retvalue []byte) {
  390. // Begin recovery preamble
  391. if c.db.isDatastoreFailed() {
  392. return nil, nil
  393. }
  394. panicOnFault := debug.SetPanicOnFault(true)
  395. defer debug.SetPanicOnFault(panicOnFault)
  396. defer func() {
  397. if r := recover(); r != nil {
  398. c.db.setDatastoreFailed(r)
  399. retkey = nil
  400. retvalue = nil
  401. }
  402. }()
  403. // End recovery preamble
  404. return c.boltCursor.Next()
  405. }
  406. func (c *datastoreCursor) close() {
  407. // BoltDB doesn't close cursors.
  408. }