From: David Mulder Date: Tue, 30 Jul 2024 15:49:22 +0000 (-0600) Subject: Add rust tdb bindings X-Git-Tag: tdb-1.4.13~924 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f7edbf70d06662084f48471d0e7f93d147e6ff14;p=thirdparty%2Fsamba.git Add rust tdb bindings Signed-off-by: David Mulder Reviewed-by: Alexander Bokovoy --- diff --git a/himmelblaud/Cargo.toml b/himmelblaud/Cargo.toml index bdf487d30da..8261b59dea5 100644 --- a/himmelblaud/Cargo.toml +++ b/himmelblaud/Cargo.toml @@ -17,7 +17,7 @@ dbg = { workspace = true } [workspace] members = [ "chelps", "dbg", "ntstatus_gen", - "param", + "param", "tdb", ] [workspace.dependencies] @@ -25,3 +25,4 @@ param = { path = "param" } dbg = { path = "dbg" } chelps = { path = "chelps" } ntstatus_gen = { path = "ntstatus_gen" } +tdb = { path = "tdb" } diff --git a/himmelblaud/tdb/Cargo.toml b/himmelblaud/tdb/Cargo.toml new file mode 100644 index 00000000000..da3d076abd2 --- /dev/null +++ b/himmelblaud/tdb/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "tdb" +edition.workspace = true +license.workspace = true +homepage.workspace = true +version.workspace = true + +[dependencies] +chelps = { workspace = true } +dbg.workspace = true +libc = "0.2.155" +ntstatus_gen.workspace = true + +[build-dependencies] +bindgen = "0.69.4" diff --git a/himmelblaud/tdb/build.rs b/himmelblaud/tdb/build.rs new file mode 100644 index 00000000000..7a9923cf786 --- /dev/null +++ b/himmelblaud/tdb/build.rs @@ -0,0 +1,31 @@ +use std::env; +use std::path::PathBuf; + +fn main() { + let bindings = bindgen::Builder::default() + .blocklist_function("qgcvt") + .blocklist_function("qgcvt_r") + .blocklist_function("qfcvt") + .blocklist_function("qfcvt_r") + .blocklist_function("qecvt") + .blocklist_function("qecvt_r") + .blocklist_function("strtold") + .clang_arg("-includesys/stat.h") + .header("../../lib/tdb/include/tdb.h") + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); + + let mut src_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + src_dir.push("../../bin/default/lib/tdb"); + println!("cargo:rustc-link-lib=tdb"); + println!( + "cargo:rustc-link-search=native={}", + src_dir.to_str().unwrap() + ); +} diff --git a/himmelblaud/tdb/src/lib.rs b/himmelblaud/tdb/src/lib.rs new file mode 100644 index 00000000000..9514e282453 --- /dev/null +++ b/himmelblaud/tdb/src/lib.rs @@ -0,0 +1,249 @@ +/* + Unix SMB/CIFS implementation. + + trivial database library FFI + + Copyright (C) David Mulder 2024 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +use chelps::wrap_string; +use dbg::DBG_ERR; +use libc::free; +use ntstatus_gen::NT_STATUS_UNSUCCESSFUL; +use std::error::Error; +use std::ffi::c_void; +use std::fmt; +use std::sync::{Arc, Mutex}; + +mod ffi { + #![allow(non_upper_case_globals)] + #![allow(non_camel_case_types)] + #![allow(non_snake_case)] + #![allow(dead_code)] + #![allow(clippy::upper_case_acronyms)] + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +} + +pub const TDB_SUCCESS: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_SUCCESS; +pub const TDB_ERR_CORRUPT: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_CORRUPT; +pub const TDB_ERR_IO: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_IO; +pub const TDB_ERR_LOCK: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_LOCK; +pub const TDB_ERR_OOM: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_OOM; +pub const TDB_ERR_EXISTS: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_EXISTS; +pub const TDB_ERR_NOLOCK: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_NOLOCK; +pub const TDB_ERR_LOCK_TIMEOUT: ffi::TDB_ERROR = + ffi::TDB_ERROR_TDB_ERR_LOCK_TIMEOUT; +pub const TDB_ERR_NOEXIST: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_NOEXIST; +pub const TDB_ERR_EINVAL: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_EINVAL; +pub const TDB_ERR_RDONLY: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_RDONLY; +pub const TDB_ERR_NESTING: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_NESTING; + +#[allow(non_camel_case_types)] +#[derive(PartialEq, Eq)] +pub struct TDB_ERROR(pub u32); + +impl TDB_ERROR { + fn description(&self) -> &str { + match self.0 { + ffi::TDB_ERROR_TDB_SUCCESS => "TDB_SUCCESS", + ffi::TDB_ERROR_TDB_ERR_CORRUPT => "TDB_ERR_CORRUPT", + ffi::TDB_ERROR_TDB_ERR_IO => "TDB_ERR_IO", + ffi::TDB_ERROR_TDB_ERR_LOCK => "TDB_ERR_LOCK", + ffi::TDB_ERROR_TDB_ERR_OOM => "TDB_ERR_OOM", + ffi::TDB_ERROR_TDB_ERR_EXISTS => "TDB_ERR_EXISTS", + ffi::TDB_ERROR_TDB_ERR_NOLOCK => "TDB_ERR_NOLOCK", + ffi::TDB_ERROR_TDB_ERR_LOCK_TIMEOUT => "TDB_ERR_LOCK_TIMEOUT", + ffi::TDB_ERROR_TDB_ERR_NOEXIST => "TDB_ERR_NOEXIST", + ffi::TDB_ERROR_TDB_ERR_EINVAL => "TDB_ERR_EINVAL", + ffi::TDB_ERROR_TDB_ERR_RDONLY => "TDB_ERR_RDONLY", + ffi::TDB_ERROR_TDB_ERR_NESTING => "TDB_ERR_NESTING", + _ => "Unknown TDB_ERROR error code", + } + } +} + +macro_rules! lock_check { + ($res:expr) => {{ + $res.map_err(|e| { + DBG_ERR!("Failed to obtain tdb lock: {:?}", e); + Box::new(TDB_ERROR(TDB_ERR_NOLOCK)) + })? + }}; +} + +impl fmt::Display for TDB_ERROR { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TDB_ERROR({:#x}): {}", self.0, self.description()) + } +} + +impl fmt::Debug for TDB_ERROR { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TDB_ERROR({:#x})", self.0) + } +} + +impl std::error::Error for TDB_ERROR {} + +#[derive(Clone)] +pub struct Tdb { + tdb: Arc>, +} + +impl Tdb { + pub fn open( + name: &str, + hash_size: Option, + tdb_flags: Option, + open_flags: Option, + mode: Option, + ) -> Result> { + let tdb = unsafe { + ffi::tdb_open( + wrap_string(name), + hash_size.unwrap_or(0), + tdb_flags.unwrap_or(ffi::TDB_DEFAULT as i32), + open_flags.unwrap_or(libc::O_RDWR), + mode.unwrap_or(0o600), + ) + }; + if tdb.is_null() { + return Err(Box::new(NT_STATUS_UNSUCCESSFUL)); + } + Ok(Tdb { + tdb: Arc::new(Mutex::new(tdb)), + }) + } + + pub fn transaction_start(&mut self) -> Result> { + let tdb = self.tdb.lock()?; + unsafe { Ok(ffi::tdb_transaction_start(*tdb) == 0) } + } + + pub fn transaction_commit(&mut self) -> Result> { + let tdb = self.tdb.lock()?; + unsafe { Ok(ffi::tdb_transaction_commit(*tdb) == 0) } + } + + pub fn transaction_cancel(&mut self) -> Result> { + let tdb = self.tdb.lock()?; + unsafe { Ok(ffi::tdb_transaction_cancel(*tdb) == 0) } + } + + pub fn exists(&self, key: &str) -> Result> { + let tdb = lock_check!(self.tdb.lock()); + let key = ffi::TDB_DATA { + dptr: wrap_string(key) as *mut u8, + dsize: key.len(), + }; + unsafe { Ok(ffi::tdb_exists(*tdb, key) == 1) } + } + + pub fn fetch(&self, key: &str) -> Result> { + let tdb = self.tdb.lock()?; + let key = ffi::TDB_DATA { + dptr: wrap_string(key) as *mut u8, + dsize: key.len(), + }; + let res = unsafe { ffi::tdb_fetch(*tdb, key) }; + if res.dptr.is_null() { + let err = unsafe { ffi::tdb_error(*tdb) }; + return Err(Box::new(TDB_ERROR(err))); + } + Ok(unsafe { + std::str::from_utf8_unchecked(std::slice::from_raw_parts( + res.dptr, res.dsize, + )) + } + .to_string()) + } + + pub fn delete(&mut self, key: &str) -> Result> { + let tdb = self.tdb.lock()?; + let key = ffi::TDB_DATA { + dptr: wrap_string(key) as *mut u8, + dsize: key.len(), + }; + unsafe { Ok(ffi::tdb_delete(*tdb, key) == 0) } + } + + pub fn store( + &mut self, + key: &str, + dbuf: &[u8], + flag: Option, + ) -> Result> { + let tdb = self.tdb.lock()?; + let flag = match flag { + Some(flag) => flag, + None => ffi::TDB_REPLACE, + }; + let key = ffi::TDB_DATA { + dptr: wrap_string(key) as *mut u8, + dsize: key.len(), + }; + let dbuf = ffi::TDB_DATA { + dptr: dbuf.as_ptr() as *mut u8, + dsize: dbuf.len(), + }; + unsafe { Ok(ffi::tdb_store(*tdb, key, dbuf, flag as i32) == 0) } + } + + pub fn keys(&self) -> Result, Box> { + let mut res = Vec::new(); + let tdb = self.tdb.lock()?; + let mut key = unsafe { ffi::tdb_firstkey(*tdb) }; + if key.dptr.is_null() { + return Ok(res); + } + let rkey = unsafe { + std::str::from_utf8_unchecked(std::slice::from_raw_parts( + key.dptr, key.dsize, + )) + } + .to_string(); + res.push(rkey); + + loop { + let next = unsafe { ffi::tdb_nextkey(*tdb, key) }; + unsafe { free(key.dptr as *mut c_void) }; + if next.dptr.is_null() { + break; + } else { + let rkey = unsafe { + std::str::from_utf8_unchecked(std::slice::from_raw_parts( + next.dptr, next.dsize, + )) + } + .to_string(); + res.push(rkey); + } + key = next; + } + + Ok(res) + } +} + +impl Drop for Tdb { + fn drop(&mut self) { + let tdb = self.tdb.lock().unwrap(); + unsafe { ffi::tdb_close(*tdb) }; + } +} + +unsafe impl Send for Tdb {} +unsafe impl Sync for Tdb {}