diff --git a/lib/blockstore/file.js b/lib/blockstore/file.js index e91da649..231dc573 100644 --- a/lib/blockstore/file.js +++ b/lib/blockstore/file.js @@ -191,9 +191,12 @@ class FileBlockStore extends AbstractBlockStore { const rec = await this.db.get(layout.f.encode(fileno)); + let touch = false; + if (rec) { filerecord = FileRecord.fromRaw(rec); } else { + touch = true; filerecord = new FileRecord({ blocks: 0, used: 0, @@ -204,6 +207,7 @@ class FileBlockStore extends AbstractBlockStore { if (filerecord.used + length > filerecord.length) { fileno += 1; filepath = this.filepath(fileno); + touch = true; filerecord = new FileRecord({ blocks: 0, used: 0, @@ -211,6 +215,11 @@ class FileBlockStore extends AbstractBlockStore { }); } + if (touch) { + const fd = await fs.open(filepath, 'w'); + await fs.close(fd); + } + return {fileno, filerecord, filepath}; } @@ -240,7 +249,7 @@ class FileBlockStore extends AbstractBlockStore { bwm.writeU32(blength); const magic = bwm.render(); - const fd = await fs.open(filepath, 'a'); + const fd = await fs.open(filepath, 'r+'); const mwritten = await fs.write(fd, magic, 0, mlength, mposition); const bwritten = await fs.write(fd, data, 0, blength, bposition); diff --git a/test/blockstore-test.js b/test/blockstore-test.js index 2dead60e..53043516 100644 --- a/test/blockstore-test.js +++ b/test/blockstore-test.js @@ -4,6 +4,7 @@ 'use strict'; const Logger = require('blgr'); +const bio = require('bufio'); const assert = require('./util/assert'); const common = require('./util/common'); const {resolve} = require('path'); @@ -428,6 +429,54 @@ describe('BlockStore', function() { } }); + it('will recover from interrupt during block write', async () => { + { + const block = random.randomBytes(128); + const hash = random.randomBytes(32); + await store.write(hash, block); + + const block2 = await store.read(hash); + assert.bufferEqual(block2, block); + } + + // Manually insert a partially written block to the + // end of file as would be the case of an untimely + // interrupted write of a block. The file record + // would not be updated to include the used bytes and + // thus this data should be overwritten. + { + const filepath = store.filepath(0); + + const fd = await fs.open(filepath, 'a'); + + const bw = bio.write(8); + bw.writeU32(store.network.magic); + bw.writeU32(73); + const magic = bw.render(); + + const failblock = random.randomBytes(73); + + const mwritten = await fs.write(fd, magic, 0, 8); + const bwritten = await fs.write(fd, failblock, 0, 73); + + await fs.close(fd); + + assert.equal(mwritten, 8); + assert.equal(bwritten, 73); + } + + // Now check that this block has the correct position + // in the file and that it can be read correctly. + { + const block = random.randomBytes(128); + const hash = random.randomBytes(32); + await store.write(hash, block); + + const block2 = await store.read(hash); + assert.bufferEqual(block2, block); + } + }); + it('will return null if block not found', async () => { const hash = random.randomBytes(32); const block = await store.read(hash);