nim-codex/dagger/stores/fsstore.nim

157 lines
3.6 KiB
Nim

## Nim-Dagger
## Copyright (c) 2021 Status Research & Development GmbH
## Licensed under either of
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
## at your option.
## This file may not be copied, modified, or distributed except according to
## those terms.
import pkg/upraises
push: {.upraises: [].}
import std/os
import pkg/chronos
import pkg/chronicles
import pkg/libp2p
import pkg/questionable
import pkg/questionable/results
import pkg/stew/io2
import ./cachestore
import ./blockstore
export blockstore
logScope:
topics = "dagger fsstore"
type
FSStore* = ref object of BlockStore
cache: BlockStore
repoDir: string
postfixLen*: int
template blockPath*(self: FSStore, cid: Cid): string =
self.repoDir / ($cid)[^self.postfixLen..^1] / $cid
method getBlock*(
self: FSStore,
cid: Cid): Future[?!Block] {.async.} =
## Get a block from the stores
##
if cid.isEmpty:
trace "Empty block, ignoring"
return cid.emptyBlock.success
if cid in self.cache:
return await self.cache.getBlock(cid)
if cid notin self:
return Block.failure("Couldn't find block in fs store")
var data: seq[byte]
let path = self.blockPath(cid)
if (
let res = io2.readFile(path, data);
res.isErr):
let error = io2.ioErrorMsg(res.error)
trace "Cannot read file from fs store", path , error
return Block.failure("Cannot read file from fs store")
return Block.new(cid, data)
method putBlock*(
self: FSStore,
blk: Block): Future[bool] {.async.} =
## Put a block to the blockstore
##
if blk.isEmpty:
trace "Empty block, ignoring"
return true
if blk.cid in self:
return true
# if directory exists it wont fail
if io2.createPath(self.blockPath(blk.cid).parentDir).isErr:
trace "Unable to create block prefix dir", dir = self.blockPath(blk.cid).parentDir
return false
let path = self.blockPath(blk.cid)
if (
let res = io2.writeFile(path, blk.data);
res.isErr):
let error = io2.ioErrorMsg(res.error)
trace "Unable to store block", path, cid = blk.cid, error
return false
if await self.cache.putBlock(blk):
trace "Unable to store block in cache", cid = blk.cid
return true
method delBlock*(
self: FSStore,
cid: Cid): Future[bool] {.async.} =
## Delete a block/s from the block store
##
if cid.isEmpty:
trace "Empty block, ignoring"
return true
let path = self.blockPath(cid)
if (
let res = io2.removeFile(path);
res.isErr):
let error = io2.ioErrorMsg(res.error)
trace "Unable to delete block", path, cid, error
return false
if await self.cache.delBlock(cid):
trace "Unable to store block in cache", cid
return true
method hasBlock*(self: FSStore, cid: Cid): bool =
## Check if the block exists in the blockstore
##
trace "Checking for block existence", cid
if cid.isEmpty:
trace "Empty block, ignoring"
return true
self.blockPath(cid).isFile()
method blockList*(s: FSStore): Future[seq[Cid]] {.async.} =
## Very expensive AND blocking!
debug "finding all blocks in store"
for (pkind, folderPath) in s.repoDir.walkDir():
if pkind != pcDir: continue
let baseName = basename(folderPath)
if baseName.len != s.postfixLen: continue
for (fkind, filePath) in folderPath.walkDir(false):
if fkind != pcFile: continue
let cid = Cid.init(basename(filePath))
if cid.isOk:
result.add(cid.get())
return result
proc new*(
T: type FSStore,
repoDir: string,
postfixLen = 2,
cache: BlockStore = CacheStore.new()): T =
T(
postfixLen: postfixLen,
repoDir: repoDir,
cache: cache)