+ /*Low level block fini*/
+ return bdev->close(bdev);
+}
+
+int ext4_block_flush_buf(struct ext4_blockdev *bdev, struct ext4_buf *buf)
+{
+ int r;
+ struct ext4_bcache *bc = bdev->bc;
+ /*Only flushing unreferenced buffer is allowed.*/
+ ext4_assert(!buf->refctr);
+
+ if (ext4_bcache_test_flag(buf, BC_DIRTY)) {
+ r = ext4_blocks_set_direct(bdev, buf->data, buf->lba, 1);
+
+ if (r) {
+ if (buf->end_write)
+ buf->end_write(bc, buf, r, buf->end_write_arg);
+
+ return r;
+ }
+
+ ext4_bcache_remove_dirty_node(bc, buf);
+ ext4_bcache_clear_flag(buf, BC_DIRTY);
+ if (buf->end_write)
+ buf->end_write(bc, buf, r, buf->end_write_arg);
+
+ }
+ return EOK;
+}
+
+int ext4_block_cache_shake(struct ext4_blockdev *bdev)
+{
+ struct ext4_buf *buf;
+ while (!RB_EMPTY(&bdev->bc->lru_root) &&
+ ext4_bcache_is_full(bdev->bc)) {
+
+ buf = ext4_buf_lowest_lru(bdev->bc);
+ ext4_assert(buf);
+ if (ext4_bcache_test_flag(buf, BC_DIRTY)) {
+ int r = ext4_block_flush_buf(bdev, buf);
+ if (r != EOK)
+ return r;
+
+ }
+
+ ext4_bcache_drop_buf(bdev->bc, buf);
+ }
+ return EOK;
+}
+
+int ext4_block_get_noread(struct ext4_blockdev *bdev, struct ext4_block *b,
+ uint64_t lba)
+{
+ bool is_new;
+ int r;
+
+ ext4_assert(bdev && b);
+
+ if (!(bdev->flags & EXT4_BDEV_INITIALIZED))
+ return EIO;
+
+ if (!(lba < bdev->lg_bcnt))
+ return ERANGE;
+
+ b->dirty = 0;
+ b->lb_id = lba;
+
+ /*If cache is full we have to (flush and) drop it anyway :(*/
+ r = ext4_block_cache_shake(bdev);
+ if (r != EOK)
+ return r;
+
+ r = ext4_bcache_alloc(bdev->bc, b, &is_new);
+ if (r != EOK)
+ return r;
+
+ if (!b->data)
+ return ENOMEM;
+
+ return EOK;