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:
parent
f0cbe4d5a2
commit
f7edbf70d0
@ -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" }
|
||||
|
15
himmelblaud/tdb/Cargo.toml
Normal file
15
himmelblaud/tdb/Cargo.toml
Normal 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
31
himmelblaud/tdb/build.rs
Normal 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
249
himmelblaud/tdb/src/lib.rs
Normal 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 {}
|
Loading…
x
Reference in New Issue
Block a user