1
0
mirror of https://github.com/samba-team/samba.git synced 2025-03-09 08:58:35 +03:00

Add rust tdb bindings

Signed-off-by: David Mulder <dmulder@samba.org>
Reviewed-by: Alexander Bokovoy <ab@samba.org>
This commit is contained in:
David Mulder 2024-07-30 09:49:22 -06:00
parent f0cbe4d5a2
commit f7edbf70d0
4 changed files with 297 additions and 1 deletions

View File

@ -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" }

View File

@ -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"

31
himmelblaud/tdb/build.rs Normal file
View File

@ -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()
);
}

249
himmelblaud/tdb/src/lib.rs Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<Mutex<*mut ffi::tdb_context>>,
}
impl Tdb {
pub fn open(
name: &str,
hash_size: Option<i32>,
tdb_flags: Option<i32>,
open_flags: Option<i32>,
mode: Option<u32>,
) -> Result<Self, Box<dyn Error + '_>> {
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<bool, Box<dyn Error + '_>> {
let tdb = self.tdb.lock()?;
unsafe { Ok(ffi::tdb_transaction_start(*tdb) == 0) }
}
pub fn transaction_commit(&mut self) -> Result<bool, Box<dyn Error + '_>> {
let tdb = self.tdb.lock()?;
unsafe { Ok(ffi::tdb_transaction_commit(*tdb) == 0) }
}
pub fn transaction_cancel(&mut self) -> Result<bool, Box<dyn Error + '_>> {
let tdb = self.tdb.lock()?;
unsafe { Ok(ffi::tdb_transaction_cancel(*tdb) == 0) }
}
pub fn exists(&self, key: &str) -> Result<bool, Box<dyn Error + '_>> {
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<String, Box<dyn Error + '_>> {
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<bool, Box<dyn Error + '_>> {
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<u32>,
) -> Result<bool, Box<dyn Error + '_>> {
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<Vec<String>, Box<dyn Error + '_>> {
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 {}