summaryrefslogtreecommitdiff
path: root/bch_bindgen/src
diff options
context:
space:
mode:
Diffstat (limited to 'bch_bindgen/src')
-rw-r--r--bch_bindgen/src/bcachefs.rs112
-rw-r--r--bch_bindgen/src/bkey.rs125
-rw-r--r--bch_bindgen/src/btree.rs197
-rw-r--r--bch_bindgen/src/errcode.rs40
-rw-r--r--bch_bindgen/src/fs.rs27
-rw-r--r--bch_bindgen/src/keyutils.rs6
-rw-r--r--bch_bindgen/src/keyutils_wrapper.h1
-rw-r--r--bch_bindgen/src/lib.rs168
-rw-r--r--bch_bindgen/src/libbcachefs_wrapper.h22
-rw-r--r--bch_bindgen/src/opts.rs35
-rw-r--r--bch_bindgen/src/rs.rs29
11 files changed, 762 insertions, 0 deletions
diff --git a/bch_bindgen/src/bcachefs.rs b/bch_bindgen/src/bcachefs.rs
new file mode 100644
index 00000000..8e897c08
--- /dev/null
+++ b/bch_bindgen/src/bcachefs.rs
@@ -0,0 +1,112 @@
+#![allow(non_upper_case_globals)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(unused)]
+
+include!(concat!(env!("OUT_DIR"), "/bcachefs.rs"));
+
+use bitfield::bitfield;
+bitfield! {
+ pub struct bch_scrypt_flags(u64);
+ pub N, _: 15, 0;
+ pub R, _: 31, 16;
+ pub P, _: 47, 32;
+}
+bitfield! {
+ pub struct bch_crypt_flags(u64);
+ pub TYPE, _: 4, 0;
+}
+use memoffset::offset_of;
+impl bch_sb_field_crypt {
+ pub fn scrypt_flags(&self) -> Option<bch_scrypt_flags> {
+ use std::convert::TryInto;
+ match bch_kdf_types(bch_crypt_flags(self.flags).TYPE().try_into().ok()?) {
+ bch_kdf_types::BCH_KDF_SCRYPT => Some(bch_scrypt_flags(self.kdf_flags)),
+ _ => None,
+ }
+ }
+ pub fn key(&self) -> &bch_encrypted_key {
+ &self.key
+ }
+}
+impl PartialEq for bch_sb {
+ fn eq(&self, other: &Self) -> bool {
+ self.magic.b == other.magic.b
+ && self.user_uuid.b == other.user_uuid.b
+ && self.block_size == other.block_size
+ && self.version == other.version
+ && self.uuid.b == other.uuid.b
+ && self.seq == other.seq
+ }
+}
+
+impl bch_sb {
+ pub fn crypt(&self) -> Option<&bch_sb_field_crypt> {
+ unsafe {
+ let ptr = bch2_sb_field_get_id(
+ self as *const _ as *mut _,
+ bch_sb_field_type::BCH_SB_FIELD_crypt,
+ ) as *const u8;
+ if ptr.is_null() {
+ None
+ } else {
+ let offset = offset_of!(bch_sb_field_crypt, field);
+ Some(&*((ptr.sub(offset)) as *const _))
+ }
+ }
+ }
+ pub fn uuid(&self) -> uuid::Uuid {
+ uuid::Uuid::from_bytes(self.user_uuid.b)
+ }
+
+ /// Get the nonce used to encrypt the superblock
+ pub fn nonce(&self) -> nonce {
+ use byteorder::{LittleEndian, ReadBytesExt};
+ let mut internal_uuid = &self.uuid.b[..];
+ let dword1 = internal_uuid.read_u32::<LittleEndian>().unwrap();
+ let dword2 = internal_uuid.read_u32::<LittleEndian>().unwrap();
+ nonce {
+ d: [0, 0, dword1, dword2],
+ }
+ }
+}
+impl bch_sb_handle {
+ pub fn sb(&self) -> &bch_sb {
+ unsafe { &*self.sb }
+ }
+
+ pub fn bdev(&self) -> &block_device {
+ unsafe { &*self.bdev }
+ }
+}
+
+#[repr(C)]
+// #[repr(align(8))]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct bch_extent_ptr {
+ pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>,
+}
+
+#[repr(C, packed(8))]
+pub struct btree_node {
+ pub csum: bch_csum,
+ pub magic: __le64,
+ pub flags: __le64,
+ pub min_key: bpos,
+ pub max_key: bpos,
+ pub _ptr: bch_extent_ptr,
+ pub format: bkey_format,
+ pub __bindgen_anon_1: btree_node__bindgen_ty_1,
+}
+
+#[repr(C, packed(8))]
+// #[repr(align(8))]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct bch_extent_crc32 {
+ pub _bitfield_1: __BindgenBitfieldUnit<[u8; 4usize]>,
+ pub csum: __u32,
+}
+
+// #[repr(u8)]
+pub enum rhash_lock_head {}
+pub enum srcu_struct {}
diff --git a/bch_bindgen/src/bkey.rs b/bch_bindgen/src/bkey.rs
new file mode 100644
index 00000000..d4830839
--- /dev/null
+++ b/bch_bindgen/src/bkey.rs
@@ -0,0 +1,125 @@
+#![allow(non_camel_case_types)]
+
+use crate::c;
+use crate::fs::Fs;
+use crate::btree::BtreeIter;
+use crate::printbuf_to_formatter;
+use std::fmt;
+use std::marker::PhantomData;
+use std::mem::transmute;
+
+pub struct BkeySC<'a> {
+ pub k: &'a c::bkey,
+ pub v: &'a c::bch_val,
+ pub(crate) iter: PhantomData<&'a mut BtreeIter<'a>>
+}
+
+pub enum BkeyValC<'a> {
+ deleted,
+ whiteout,
+ error,
+ cookie(&'a c::bch_cookie),
+ hash_whiteout(&'a c::bch_hash_whiteout),
+ btree_ptr(&'a c::bch_btree_ptr),
+ extent(&'a c::bch_extent),
+ reservation(&'a c::bch_reservation),
+ inode(&'a c::bch_inode),
+ inode_generation(&'a c::bch_inode_generation),
+ dirent(&'a c::bch_dirent),
+ xattr(&'a c::bch_xattr),
+ alloc(&'a c::bch_alloc),
+ quota(&'a c::bch_quota),
+ stripe(&'a c::bch_stripe),
+ reflink_p(&'a c::bch_reflink_p),
+ reflink_v(&'a c::bch_reflink_v),
+ inline_data(&'a c::bch_inline_data),
+ btree_ptr_v2(&'a c::bch_btree_ptr_v2),
+ indirect_inline_data(&'a c::bch_indirect_inline_data),
+ alloc_v2(&'a c::bch_alloc_v2),
+ subvolume(&'a c::bch_subvolume),
+ snapshot(&'a c::bch_snapshot),
+ inode_v2(&'a c::bch_inode_v2),
+ alloc_v3(&'a c::bch_alloc_v3),
+ set,
+ lru(&'a c::bch_lru),
+ alloc_v4(&'a c::bch_alloc_v4),
+ backpointer(&'a c::bch_backpointer),
+ inode_v3(&'a c::bch_inode_v3),
+ bucket_gens(&'a c::bch_bucket_gens),
+ snapshot_tree(&'a c::bch_snapshot_tree),
+ logged_op_truncate(&'a c::bch_logged_op_truncate),
+ logged_op_finsert(&'a c::bch_logged_op_finsert),
+}
+
+impl<'a, 'b> BkeySC<'a> {
+ unsafe fn to_raw(&self) -> c::bkey_s_c {
+ c::bkey_s_c { k: self.k, v: self.v }
+ }
+
+ pub fn to_text(&'a self, fs: &'b Fs) -> BkeySCToText<'a, 'b> {
+ BkeySCToText { k: self, fs }
+ }
+
+ pub fn v(&'a self) -> BkeyValC {
+ let ty: c::bch_bkey_type = unsafe { transmute(self.k.type_ as u32) };
+
+ use c::bch_bkey_type::*;
+ use BkeyValC::*;
+ match ty {
+ KEY_TYPE_deleted => deleted,
+ KEY_TYPE_whiteout => whiteout,
+ KEY_TYPE_error => error,
+ KEY_TYPE_cookie => cookie(unsafe { transmute(self.v) }),
+ KEY_TYPE_hash_whiteout => hash_whiteout(unsafe { transmute(self.v) }),
+ KEY_TYPE_btree_ptr => btree_ptr(unsafe { transmute(self.v) }),
+ KEY_TYPE_extent => extent(unsafe { transmute(self.v) }),
+ KEY_TYPE_reservation => reservation(unsafe { transmute(self.v) }),
+ KEY_TYPE_inode => inode(unsafe { transmute(self.v) }),
+ KEY_TYPE_inode_generation => inode_generation(unsafe { transmute(self.v) }),
+ KEY_TYPE_dirent => dirent(unsafe { transmute(self.v) }),
+ KEY_TYPE_xattr => xattr(unsafe { transmute(self.v) }),
+ KEY_TYPE_alloc => alloc(unsafe { transmute(self.v) }),
+ KEY_TYPE_quota => quota(unsafe { transmute(self.v) }),
+ KEY_TYPE_stripe => stripe(unsafe { transmute(self.v) }),
+ KEY_TYPE_reflink_p => reflink_p(unsafe { transmute(self.v) }),
+ KEY_TYPE_reflink_v => reflink_v(unsafe { transmute(self.v) }),
+ KEY_TYPE_inline_data => inline_data(unsafe { transmute(self.v) }),
+ KEY_TYPE_btree_ptr_v2 => btree_ptr_v2(unsafe { transmute(self.v) }),
+ KEY_TYPE_indirect_inline_data => indirect_inline_data(unsafe { transmute(self.v) }),
+ KEY_TYPE_alloc_v2 => alloc_v2(unsafe { transmute(self.v) }),
+ KEY_TYPE_subvolume => subvolume(unsafe { transmute(self.v) }),
+ KEY_TYPE_snapshot => snapshot(unsafe { transmute(self.v) }),
+ KEY_TYPE_inode_v2 => inode_v2(unsafe { transmute(self.v) }),
+ KEY_TYPE_alloc_v3 => inode_v3(unsafe { transmute(self.v) }),
+ KEY_TYPE_set => set,
+ KEY_TYPE_lru => lru(unsafe { transmute(self.v) }),
+ KEY_TYPE_alloc_v4 => alloc_v4(unsafe { transmute(self.v) }),
+ KEY_TYPE_backpointer => backpointer(unsafe { transmute(self.v) }),
+ KEY_TYPE_inode_v3 => inode_v3(unsafe { transmute(self.v) }),
+ KEY_TYPE_bucket_gens => bucket_gens(unsafe { transmute(self.v) }),
+ KEY_TYPE_snapshot_tree => snapshot_tree(unsafe { transmute(self.v) }),
+ KEY_TYPE_logged_op_truncate => logged_op_truncate(unsafe { transmute(self.v) }),
+ KEY_TYPE_logged_op_finsert => logged_op_finsert(unsafe { transmute(self.v) }),
+ KEY_TYPE_MAX => unreachable!(),
+ }
+ }
+}
+
+impl<'a> From<&'a c::bkey_i> for BkeySC<'a> {
+ fn from(k: &'a c::bkey_i) -> Self {
+ BkeySC { k: &k.k, v: &k.v, iter: PhantomData }
+ }
+}
+
+pub struct BkeySCToText<'a, 'b> {
+ k: &'a BkeySC<'a>,
+ fs: &'b Fs,
+}
+
+impl<'a, 'b> fmt::Display for BkeySCToText<'a, 'b> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ unsafe {
+ printbuf_to_formatter(f, |buf| c::bch2_bkey_val_to_text(buf, self.fs.raw, self.k.to_raw()))
+ }
+ }
+}
diff --git a/bch_bindgen/src/btree.rs b/bch_bindgen/src/btree.rs
new file mode 100644
index 00000000..09e86b34
--- /dev/null
+++ b/bch_bindgen/src/btree.rs
@@ -0,0 +1,197 @@
+use crate::SPOS_MAX;
+use crate::c;
+use crate::bkey::BkeySC;
+use crate::fs::Fs;
+use crate::errcode::{bch_errcode, errptr_to_result_c};
+use crate::printbuf_to_formatter;
+use std::fmt;
+use std::marker::PhantomData;
+use std::mem::MaybeUninit;
+use bitflags::bitflags;
+
+pub struct BtreeTrans<'f> {
+ raw: *mut c::btree_trans,
+ fs: PhantomData<&'f Fs>
+}
+
+impl<'f> BtreeTrans<'f> {
+ pub fn new(fs: &'f Fs) -> BtreeTrans {
+ unsafe {
+ BtreeTrans { raw: &mut *c::__bch2_trans_get(fs.raw, 0), fs: PhantomData }
+ }
+ }
+}
+
+impl<'f> Drop for BtreeTrans<'f> {
+ fn drop(&mut self) {
+ unsafe { c::bch2_trans_put(&mut *self.raw) }
+ }
+}
+
+bitflags! {
+ pub struct BtreeIterFlags: u16 {
+ const SLOTS = c::BTREE_ITER_SLOTS as u16;
+ const INTENT = c::BTREE_ITER_INTENT as u16;
+ const PREFETCH = c::BTREE_ITER_PREFETCH as u16;
+ const IS_EXTENTS = c::BTREE_ITER_IS_EXTENTS as u16;
+ const NOT_EXTENTS = c::BTREE_ITER_NOT_EXTENTS as u16;
+ const CACHED = c::BTREE_ITER_CACHED as u16;
+ const KEY_CACHED = c::BTREE_ITER_WITH_KEY_CACHE as u16;
+ const WITH_UPDATES = c::BTREE_ITER_WITH_UPDATES as u16;
+ const WITH_JOURNAL = c::BTREE_ITER_WITH_JOURNAL as u16;
+ const __ALL_SNAPSHOTS = c::__BTREE_ITER_ALL_SNAPSHOTS as u16;
+ const ALL_SNAPSHOTS = c::BTREE_ITER_ALL_SNAPSHOTS as u16;
+ const FILTER_SNAPSHOTS = c::BTREE_ITER_FILTER_SNAPSHOTS as u16;
+ const NOPRESERVE = c::BTREE_ITER_NOPRESERVE as u16;
+ const CACHED_NOFILL = c::BTREE_ITER_CACHED_NOFILL as u16;
+ const KEY_CACHE_FILL = c::BTREE_ITER_KEY_CACHE_FILL as u16;
+ }
+}
+
+pub struct BtreeIter<'t> {
+ raw: c::btree_iter,
+ trans: PhantomData<&'t BtreeTrans<'t>>,
+}
+
+impl<'t> BtreeIter<'t> {
+ pub fn new(trans: &'t BtreeTrans<'t>, btree: c::btree_id, pos: c::bpos, flags: BtreeIterFlags) -> BtreeIter<'t> {
+ unsafe {
+ let mut iter: MaybeUninit<c::btree_iter> = MaybeUninit::uninit();
+
+ c::bch2_trans_iter_init_outlined(
+ trans.raw,
+ iter.as_mut_ptr(),
+ btree,
+ pos,
+ flags.bits as u32);
+
+ BtreeIter { raw: iter.assume_init(), trans: PhantomData }
+ }
+ }
+
+ pub fn peek_upto<'i>(&'i mut self, end: c::bpos) -> Result<Option<BkeySC>, bch_errcode> {
+ unsafe {
+ let k = c::bch2_btree_iter_peek_upto(&mut self.raw, end);
+ errptr_to_result_c(k.k)
+ .map(|_| if !k.k.is_null() { Some(BkeySC { k: &*k.k, v: &*k.v, iter: PhantomData }) } else { None } )
+ }
+ }
+
+ pub fn peek(&mut self) -> Result<Option<BkeySC>, bch_errcode> {
+ self.peek_upto(SPOS_MAX)
+ }
+
+ pub fn peek_and_restart(&mut self) -> Result<Option<BkeySC>, bch_errcode> {
+ unsafe {
+ let k = c::bch2_btree_iter_peek_and_restart_outlined(&mut self.raw);
+
+ errptr_to_result_c(k.k)
+ .map(|_| if !k.k.is_null() { Some(BkeySC{ k: &*k.k, v: &*k.v, iter: PhantomData }) } else { None } )
+ }
+ }
+
+ pub fn advance(&mut self) {
+ unsafe {
+ c::bch2_btree_iter_advance(&mut self.raw);
+ }
+ }
+}
+
+impl<'t> Drop for BtreeIter<'t> {
+ fn drop(&mut self) {
+ unsafe { c::bch2_trans_iter_exit(self.raw.trans, &mut self.raw) }
+ }
+}
+
+pub struct BtreeNodeIter<'t> {
+ raw: c::btree_iter,
+ trans: PhantomData<&'t BtreeTrans<'t>>,
+}
+
+impl<'t> BtreeNodeIter<'t> {
+ pub fn new(trans: &'t BtreeTrans<'t>,
+ btree: c::btree_id,
+ pos: c::bpos,
+ locks_want: u32,
+ depth: u32,
+ flags: BtreeIterFlags) -> BtreeNodeIter {
+ unsafe {
+ let mut iter: MaybeUninit<c::btree_iter> = MaybeUninit::uninit();
+ c::bch2_trans_node_iter_init(
+ trans.raw,
+ iter.as_mut_ptr(),
+ btree,
+ pos,
+ locks_want,
+ depth,
+ flags.bits as u32);
+
+ BtreeNodeIter { raw: iter.assume_init(), trans: PhantomData }
+ }
+ }
+
+ pub fn peek<'i>(&'i mut self) -> Result<Option<&'i c::btree>, bch_errcode> {
+ unsafe {
+ let b = c::bch2_btree_iter_peek_node(&mut self.raw);
+ errptr_to_result_c(b).map(|b| if !b.is_null() { Some(&*b) } else { None })
+ }
+ }
+
+ pub fn peek_and_restart<'i>(&'i mut self) -> Result<Option<&'i c::btree>, bch_errcode> {
+ unsafe {
+ let b = c::bch2_btree_iter_peek_node_and_restart(&mut self.raw);
+ errptr_to_result_c(b).map(|b| if !b.is_null() { Some(&*b) } else { None })
+ }
+ }
+
+ pub fn advance<'i>(&'i mut self) {
+ unsafe {
+ c::bch2_btree_iter_next_node(&mut self.raw);
+ }
+ }
+
+ pub fn next<'i>(&'i mut self) -> Result<Option<&'i c::btree>, bch_errcode> {
+ unsafe {
+ let b = c::bch2_btree_iter_next_node(&mut self.raw);
+ errptr_to_result_c(b).map(|b| if !b.is_null() { Some(&*b) } else { None })
+ }
+ }
+}
+
+impl<'t> Drop for BtreeNodeIter<'t> {
+ fn drop(&mut self) {
+ unsafe { c::bch2_trans_iter_exit(self.raw.trans, &mut self.raw) }
+ }
+}
+
+impl<'b, 'f> c::btree {
+ pub fn to_text(&'b self, fs: &'f Fs) -> BtreeNodeToText<'b, 'f> {
+ BtreeNodeToText { b: &self, fs }
+ }
+
+ pub fn ondisk_to_text(&'b self, fs: &'f Fs) -> BtreeNodeOndiskToText<'b, 'f> {
+ BtreeNodeOndiskToText { b: &self, fs }
+ }
+}
+
+pub struct BtreeNodeToText<'b, 'f> {
+ b: &'b c::btree,
+ fs: &'f Fs,
+}
+
+impl<'b, 'f> fmt::Display for BtreeNodeToText<'b, 'f> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ printbuf_to_formatter(f, |buf| unsafe { c::bch2_btree_node_to_text(buf, self.fs.raw, self.b) })
+ }
+}
+
+pub struct BtreeNodeOndiskToText<'b, 'f> {
+ b: &'b c::btree,
+ fs: &'f Fs,
+}
+
+impl<'b, 'f> fmt::Display for BtreeNodeOndiskToText<'b, 'f> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ printbuf_to_formatter(f, |buf| unsafe { c::bch2_btree_node_ondisk_to_text(buf, self.fs.raw, self.b) })
+ }
+}
diff --git a/bch_bindgen/src/errcode.rs b/bch_bindgen/src/errcode.rs
new file mode 100644
index 00000000..4d75f1d2
--- /dev/null
+++ b/bch_bindgen/src/errcode.rs
@@ -0,0 +1,40 @@
+use crate::bcachefs;
+use std::ffi::CStr;
+use std::fmt;
+
+pub use crate::c::bch_errcode;
+
+impl fmt::Display for bch_errcode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let s = unsafe { CStr::from_ptr(bcachefs::bch2_err_str(*self as i32)) };
+ write!(f, "{:?}", s)
+ }
+}
+
+/* Can we make a function generic over ptr constness? */
+
+pub fn errptr_to_result<T>(p: *mut T) -> Result<*mut T, bch_errcode> {
+ let addr = p as usize;
+ let max_err: isize = -4096;
+ if addr > max_err as usize {
+ let addr = addr as i32;
+ let err: bch_errcode = unsafe { std::mem::transmute(-addr) };
+ Err(err)
+ } else {
+ Ok(p)
+ }
+}
+
+pub fn errptr_to_result_c<T>(p: *const T) -> Result<*const T, bch_errcode> {
+ let addr = p as usize;
+ let max_err: isize = -4096;
+ if addr > max_err as usize {
+ let addr = addr as i32;
+ let err: bch_errcode = unsafe { std::mem::transmute(-addr) };
+ Err(err)
+ } else {
+ Ok(p)
+ }
+}
+
+impl std::error::Error for bch_errcode {}
diff --git a/bch_bindgen/src/fs.rs b/bch_bindgen/src/fs.rs
new file mode 100644
index 00000000..b26c51b6
--- /dev/null
+++ b/bch_bindgen/src/fs.rs
@@ -0,0 +1,27 @@
+use std::ffi::CString;
+use std::os::unix::ffi::OsStrExt;
+use std::path::PathBuf;
+use crate::c;
+use crate::errcode::{bch_errcode, errptr_to_result};
+
+pub struct Fs {
+ pub raw: *mut c::bch_fs,
+}
+
+impl Fs {
+ pub fn open(devs: &Vec<PathBuf>, opts: c::bch_opts) -> Result<Fs, bch_errcode> {
+ let devs: Vec<_> = devs.iter()
+ .map(|i| CString::new(i.as_os_str().as_bytes()).unwrap().into_raw())
+ .collect();
+
+ let ret = unsafe { c::bch2_fs_open(devs[..].as_ptr(), devs.len() as u32, opts) };
+
+ errptr_to_result(ret).map(|fs| Fs { raw: fs})
+ }
+}
+
+impl Drop for Fs {
+ fn drop(&mut self) {
+ unsafe { c::bch2_fs_stop(self.raw) }
+ }
+}
diff --git a/bch_bindgen/src/keyutils.rs b/bch_bindgen/src/keyutils.rs
new file mode 100644
index 00000000..30fc56f9
--- /dev/null
+++ b/bch_bindgen/src/keyutils.rs
@@ -0,0 +1,6 @@
+#![allow(non_upper_case_globals)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(unused)]
+
+include!(concat!(env!("OUT_DIR"), "/keyutils.rs"));
diff --git a/bch_bindgen/src/keyutils_wrapper.h b/bch_bindgen/src/keyutils_wrapper.h
new file mode 100644
index 00000000..857cee2e
--- /dev/null
+++ b/bch_bindgen/src/keyutils_wrapper.h
@@ -0,0 +1 @@
+#include <keyutils.h>
diff --git a/bch_bindgen/src/lib.rs b/bch_bindgen/src/lib.rs
new file mode 100644
index 00000000..4c549442
--- /dev/null
+++ b/bch_bindgen/src/lib.rs
@@ -0,0 +1,168 @@
+pub mod bcachefs;
+pub mod btree;
+pub mod bkey;
+pub mod errcode;
+pub mod keyutils;
+pub mod rs;
+pub mod fs;
+pub mod opts;
+pub use paste::paste;
+
+pub mod c {
+ pub use crate::bcachefs::*;
+}
+
+use c::bpos as Bpos;
+
+pub const fn spos(inode: u64, offset: u64, snapshot: u32) -> Bpos {
+ Bpos { inode, offset, snapshot }
+}
+
+pub const fn pos(inode: u64, offset: u64) -> Bpos {
+ spos(inode, offset, 0)
+}
+
+pub const POS_MIN: Bpos = spos(0, 0, 0);
+pub const POS_MAX: Bpos = spos(u64::MAX, u64::MAX, 0);
+pub const SPOS_MAX: Bpos = spos(u64::MAX, u64::MAX, u32::MAX);
+
+use std::cmp::Ordering;
+
+impl PartialEq for Bpos {
+ fn eq(&self, other: &Self) -> bool {
+ self.cmp(other) == Ordering::Equal
+ }
+}
+
+impl Eq for Bpos {}
+
+impl PartialOrd for Bpos {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Bpos {
+ fn cmp(&self, other: &Self) -> Ordering {
+ let l_inode = self.inode;
+ let r_inode = other.inode;
+ let l_offset = self.offset;
+ let r_offset = other.offset;
+ let l_snapshot = self.snapshot;
+ let r_snapshot = other.snapshot;
+
+ l_inode.cmp(&r_inode)
+ .then(l_offset.cmp(&r_offset))
+ .then(l_snapshot.cmp(&r_snapshot))
+ }
+}
+
+use std::ffi::CStr;
+use std::fmt;
+
+impl fmt::Display for c::btree_id {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let s = unsafe { CStr::from_ptr(c::bch2_btree_id_str(*self)) };
+ let s = s.to_str().unwrap();
+ write!(f, "{}", s)
+ }
+}
+
+use std::str::FromStr;
+use std::ffi::CString;
+
+use std::error::Error;
+
+#[derive(Debug)]
+pub struct InvalidBtreeId;
+
+impl fmt::Display for InvalidBtreeId {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "invalid btree id")
+ }
+}
+
+impl Error for InvalidBtreeId {
+}
+
+impl FromStr for c::btree_id {
+ type Err = InvalidBtreeId;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let s = CString::new(s).unwrap();
+ let p = s.as_ptr();
+
+ let v = unsafe {c::match_string(c::__bch2_btree_ids[..].as_ptr(), (-(1 as isize)) as usize, p)};
+ if v >= 0 {
+ Ok(unsafe { std::mem::transmute(v) })
+ } else {
+ Err(InvalidBtreeId)
+ }
+ }
+}
+
+impl c::printbuf {
+ fn new() -> c::printbuf {
+ let mut buf: c::printbuf = Default::default();
+
+ buf.set_heap_allocated(true);
+ buf
+ }
+}
+
+impl Drop for c::printbuf {
+ fn drop(&mut self) {
+ unsafe { c::bch2_printbuf_exit(self) }
+ }
+}
+
+impl fmt::Display for Bpos {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut buf = c::printbuf::new();
+
+ unsafe { c::bch2_bpos_to_text(&mut buf, *self) };
+
+ let s = unsafe { CStr::from_ptr(buf.buf) };
+ let s = s.to_str().unwrap();
+ write!(f, "{}", s)
+ }
+}
+
+impl FromStr for c::bpos {
+ type Err = InvalidBtreeId;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ if s == "POS_MIN" {
+ return Ok(POS_MIN);
+ }
+
+ if s == "POS_MAX" {
+ return Ok(POS_MAX);
+ }
+
+ if s == "SPOS_MAX" {
+ return Ok(SPOS_MAX);
+ }
+
+ let mut fields = s.split(':');
+ let ino_str = fields.next().ok_or(InvalidBtreeId)?;
+ let off_str = fields.next().ok_or(InvalidBtreeId)?;
+ let snp_str = fields.next();
+
+ let ino: u64 = ino_str.parse().map_err(|_| InvalidBtreeId)?;
+ let off: u64 = off_str.parse().map_err(|_| InvalidBtreeId)?;
+ let snp: u32 = snp_str.map(|s| s.parse().ok()).flatten().unwrap_or(0);
+
+ Ok(c::bpos { inode: ino, offset: off, snapshot: snp })
+ }
+}
+
+pub fn printbuf_to_formatter<F>(f: &mut fmt::Formatter<'_>, func: F) -> fmt::Result
+ where F: Fn(*mut c::printbuf) {
+ let mut buf = c::printbuf::new();
+
+ func(&mut buf);
+
+ let s = unsafe { CStr::from_ptr(buf.buf) };
+ f.write_str(s.to_str().unwrap())
+}
diff --git a/bch_bindgen/src/libbcachefs_wrapper.h b/bch_bindgen/src/libbcachefs_wrapper.h
new file mode 100644
index 00000000..141b0835
--- /dev/null
+++ b/bch_bindgen/src/libbcachefs_wrapper.h
@@ -0,0 +1,22 @@
+#include "libbcachefs/super-io.h"
+#include "libbcachefs/checksum.h"
+#include "libbcachefs/bcachefs_format.h"
+#include "libbcachefs/btree_cache.h"
+#include "libbcachefs/btree_iter.h"
+#include "libbcachefs/debug.h"
+#include "libbcachefs/errcode.h"
+#include "libbcachefs/error.h"
+#include "libbcachefs/opts.h"
+#include "libbcachefs.h"
+#include "crypto.h"
+#include "include/linux/bio.h"
+#include "include/linux/blkdev.h"
+#include "cmds.h"
+#include "raid/raid.h"
+
+
+#define MARK_FIX_753(req_name) const blk_mode_t Fix753_##req_name = req_name;
+
+MARK_FIX_753(BLK_OPEN_READ);
+MARK_FIX_753(BLK_OPEN_WRITE);
+MARK_FIX_753(BLK_OPEN_EXCL);
diff --git a/bch_bindgen/src/opts.rs b/bch_bindgen/src/opts.rs
new file mode 100644
index 00000000..d38d469c
--- /dev/null
+++ b/bch_bindgen/src/opts.rs
@@ -0,0 +1,35 @@
+#[macro_export]
+macro_rules! opt_set {
+ ($opts:ident, $n:ident, $v:expr) => {
+ bch_bindgen::paste! {
+ $opts.$n = $v;
+ $opts.[<set_ $n _defined>](1)
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! opt_defined {
+ ($opts:ident, $n:ident) => {
+ bch_bindgen::paste! {
+ $opts.[< $n _defined>]()
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! opt_get {
+ ($opts:ident, $n:ident) => {
+ if bch_bindgen::opt_defined!($opts, $n) == 0 {
+ bch_bindgen::paste! {
+ unsafe {
+ bch_bindgen::bcachefs::bch2_opts_default.$n
+ }
+ }
+ } else {
+ bch_bindgen::paste! {
+ $opts.$n
+ }
+ }
+ };
+}
diff --git a/bch_bindgen/src/rs.rs b/bch_bindgen/src/rs.rs
new file mode 100644
index 00000000..24594ae1
--- /dev/null
+++ b/bch_bindgen/src/rs.rs
@@ -0,0 +1,29 @@
+use anyhow::anyhow;
+use crate::bcachefs;
+use crate::bcachefs::*;
+use crate::errcode::bch_errcode;
+
+pub fn read_super_opts(
+ path: &std::path::Path,
+ mut opts: bch_opts,
+) -> anyhow::Result<bch_sb_handle> {
+ use std::os::unix::ffi::OsStrExt;
+ let path = std::ffi::CString::new(path.as_os_str().as_bytes()).unwrap();
+
+ let mut sb = std::mem::MaybeUninit::zeroed();
+
+ let ret =
+ unsafe { crate::bcachefs::bch2_read_super(path.as_ptr(), &mut opts, sb.as_mut_ptr()) };
+
+ if ret != 0 {
+ let err: bch_errcode = unsafe { ::std::mem::transmute(ret) };
+ Err(anyhow!(err))
+ } else {
+ Ok(unsafe { sb.assume_init() })
+ }
+}
+
+pub fn read_super(path: &std::path::Path) -> anyhow::Result<bch_sb_handle> {
+ let opts = bcachefs::bch_opts::default();
+ read_super_opts(path, opts)
+}