mirror of
https://github.com/XTXMarkets/ternfs.git
synced 2025-12-20 10:10:17 -06:00
Initial commit of EggsFS
- the filesystem best enjoyed with Tea + Eggs
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
314
Cargo.lock
generated
Normal file
314
Cargo.lock
generated
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bincode"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bindgen"
|
||||||
|
version = "0.59.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cexpr",
|
||||||
|
"clang-sys",
|
||||||
|
"lazy_static",
|
||||||
|
"lazycell",
|
||||||
|
"peeking_take_while",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"rustc-hash",
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2-sys"
|
||||||
|
version = "0.1.11+1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.73"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cexpr"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clang-sys"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b"
|
||||||
|
dependencies = [
|
||||||
|
"glob",
|
||||||
|
"libc",
|
||||||
|
"libloading",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "eggsfs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bincode",
|
||||||
|
"rocksdb",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazycell"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.126"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libloading"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "librocksdb-sys"
|
||||||
|
version = "0.6.1+6.28.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81bc587013734dadb7cf23468e531aa120788b87243648be42e2d3a072186291"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen",
|
||||||
|
"bzip2-sys",
|
||||||
|
"cc",
|
||||||
|
"glob",
|
||||||
|
"libc",
|
||||||
|
"libz-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libz-sys"
|
||||||
|
version = "1.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "peeking_take_while"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
|
||||||
|
dependencies = [
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rocksdb"
|
||||||
|
version = "0.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "620f4129485ff1a7128d184bc687470c21c7951b64779ebc9cfdad3dcd920290"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"librocksdb-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.137"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.137"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.98"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "eggsfs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rocksdb = { version = "0.18.0", default-features = false }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
bincode = "1.3.3"
|
||||||
253
src/bin/basic_client.rs
Normal file
253
src/bin/basic_client.rs
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
use bincode::Options;
|
||||||
|
use std::vec::Vec;
|
||||||
|
use std::string::String;
|
||||||
|
use std::net::UdpSocket;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[path="../hex.rs"]
|
||||||
|
mod hex;
|
||||||
|
#[path="../metadata_msgs.rs"]
|
||||||
|
mod metadata_msgs;
|
||||||
|
#[path="../schema.rs"]
|
||||||
|
mod schema;
|
||||||
|
|
||||||
|
|
||||||
|
use metadata_msgs::*;
|
||||||
|
|
||||||
|
|
||||||
|
// this root id is ineffecient to encode
|
||||||
|
const ROOT_ID: u64 = !0u64;
|
||||||
|
|
||||||
|
const UDP_MTU: usize = 1472;
|
||||||
|
|
||||||
|
const LOCAL_HOST: std::net::IpAddr = std::net::IpAddr::V4(
|
||||||
|
std::net::Ipv4Addr::new(127, 0, 0, 1));
|
||||||
|
|
||||||
|
|
||||||
|
fn generate_request_id() -> u64 {
|
||||||
|
let now = std::time::SystemTime::now();
|
||||||
|
let now_ns = now.duration_since(std::time::SystemTime::UNIX_EPOCH
|
||||||
|
).expect("Now is less than UNIX_EPOCH!").as_nanos();
|
||||||
|
now_ns as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn resolve(parent_id: u64, subname: String, ts: u64
|
||||||
|
) -> MetadataResult<Option<ResolvedInode>> {
|
||||||
|
|
||||||
|
let request_id = generate_request_id();
|
||||||
|
|
||||||
|
let port = schema::shard(parent_id) as u16 + 22272;
|
||||||
|
let target = std::net::SocketAddr::new(LOCAL_HOST, port);
|
||||||
|
let msg = MetadataRequest {
|
||||||
|
request_id: request_id,
|
||||||
|
body: MetadataRequestBody::Resolve{
|
||||||
|
parent_id: parent_id, subname: subname, ts: ts,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let encoded = bincode_opt().serialize(&msg).expect("Failed to encode msg");
|
||||||
|
let sock = UdpSocket::bind("127.0.0.1:0").expect(
|
||||||
|
"Could not bind UDP 127.0.0.1:0");
|
||||||
|
sock.send_to(&encoded, &target).expect("Failed to send msg");
|
||||||
|
|
||||||
|
let mut buf = [0u8; UDP_MTU];
|
||||||
|
|
||||||
|
match sock.recv(&mut buf) {
|
||||||
|
Ok(len) => {
|
||||||
|
let res = bincode_opt().deserialize::<MetadataResponse>(
|
||||||
|
&buf[..len]);
|
||||||
|
match res {
|
||||||
|
Ok(response) => {
|
||||||
|
if response.request_id != request_id {
|
||||||
|
Err(MetadataError{
|
||||||
|
kind: MetadataErrorKind::LogicError,
|
||||||
|
text: format!("Request id expected: {} got: {}",
|
||||||
|
request_id, response.request_id)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let b = response.body?;
|
||||||
|
match b {
|
||||||
|
//todo: dis
|
||||||
|
MetadataResponseBody::Resolve(result) => {
|
||||||
|
Ok(result)
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
let s = format!(
|
||||||
|
"Received unexpected response body {:?}",
|
||||||
|
b);
|
||||||
|
Err(MetadataError{
|
||||||
|
kind: MetadataErrorKind::LogicError,
|
||||||
|
text: s,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
let s = format!("Error deserializing {}\n{}", e,
|
||||||
|
hex::hexdump(&buf[..len]));
|
||||||
|
Err(MetadataError{
|
||||||
|
kind: MetadataErrorKind::BincodeError,
|
||||||
|
text: s,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
let s = format!("Error receiving reply from {}: {}", target, e);
|
||||||
|
Err(MetadataError{
|
||||||
|
kind: MetadataErrorKind::NetworkError,
|
||||||
|
text: s,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn resolve_abs_path(path: &str, ts: u64) -> MetadataResult<ResolvedInode> {
|
||||||
|
|
||||||
|
let bits: Vec<&str> = path.split('/').collect();
|
||||||
|
|
||||||
|
if bits[0] != "" {
|
||||||
|
return Err(MetadataError{
|
||||||
|
kind: MetadataErrorKind::LogicError,
|
||||||
|
text: String::from("Path must start with /"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut parent_inode = ResolvedInode {
|
||||||
|
id: ROOT_ID,
|
||||||
|
creation_time: 0,
|
||||||
|
deletion_time: 0,
|
||||||
|
is_file: false,
|
||||||
|
};
|
||||||
|
for (i, bit) in bits[1..].iter().enumerate() {
|
||||||
|
if parent_inode.is_file {
|
||||||
|
let filename = bits[..i+1].join("/");
|
||||||
|
let s = format!("{} is a file", filename);
|
||||||
|
return Err(MetadataError{
|
||||||
|
kind: MetadataErrorKind::LogicError,
|
||||||
|
text: s,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = resolve(parent_inode.id, bit.to_string(), ts)?;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
None => {
|
||||||
|
let subdirname = bits[..i+2].join("/");
|
||||||
|
let s = format!("No such file or directory {}", subdirname);
|
||||||
|
return Err(MetadataError{
|
||||||
|
kind: MetadataErrorKind::LogicError,
|
||||||
|
text: s,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
Some(inode) => {
|
||||||
|
parent_inode = inode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(parent_inode)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
|
||||||
|
if args.len() < 3 {
|
||||||
|
eprintln!("Usage: {} (resolve|ls|mkdir|rmdir) path [creation_ts]",
|
||||||
|
args[0]);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let command = &args[1];
|
||||||
|
|
||||||
|
let path = &args[2];
|
||||||
|
let creation_time = if args.len() >= 4 {
|
||||||
|
Some(args[3].parse::<u64>().expect(
|
||||||
|
"Couldn't parse creation_time as int"))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
if command == "rmdir" && creation_time.is_none() {
|
||||||
|
panic!("rmdir command requires a creation time");
|
||||||
|
}
|
||||||
|
let final_slash = path.rfind('/').expect("Path must contain /");
|
||||||
|
let basename = &path[final_slash+1..];
|
||||||
|
|
||||||
|
let resolve_path = if command == "mkdir" || path == "/" {
|
||||||
|
// for mkdir need to resolve the parent
|
||||||
|
&path[..final_slash]
|
||||||
|
} else {
|
||||||
|
&path
|
||||||
|
};
|
||||||
|
|
||||||
|
let sock = UdpSocket::bind("127.0.0.1:0").expect(
|
||||||
|
"Could not bind UDP 127.0.0.1:0");
|
||||||
|
|
||||||
|
sock.set_read_timeout(Some(Duration::new(2, 0))).expect(
|
||||||
|
"Could not set read_timeout");
|
||||||
|
|
||||||
|
let result = resolve_abs_path(&resolve_path, !0u64);
|
||||||
|
let resolved_inode = match result {
|
||||||
|
Ok(resolved) => resolved,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to resolve {}: {:?}", resolve_path, e);
|
||||||
|
std::process::exit(2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let shard = schema::shard(resolved_inode.id);
|
||||||
|
|
||||||
|
let pretty_resolve_path = if resolve_path == "" {
|
||||||
|
"/"
|
||||||
|
} else {
|
||||||
|
resolve_path
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("'{}' resolved to:\n{:#?}", pretty_resolve_path,
|
||||||
|
resolved_inode);
|
||||||
|
|
||||||
|
let body = match command.as_str() {
|
||||||
|
"resolve" => {
|
||||||
|
// nothing else needs doing, we're good
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
"rmdir" => MetadataRequestBody::RmDir{
|
||||||
|
parent_id: resolved_inode.id,
|
||||||
|
subdirname: String::from(basename),
|
||||||
|
creation_time: creation_time.unwrap(),
|
||||||
|
},
|
||||||
|
"mkdir" => MetadataRequestBody::MkDir{
|
||||||
|
parent_id: resolved_inode.id,
|
||||||
|
subdirname: String::from(basename),
|
||||||
|
},
|
||||||
|
_ => panic!("Command {} not supported yet", command),
|
||||||
|
};
|
||||||
|
|
||||||
|
let msg = MetadataRequest{
|
||||||
|
request_id: generate_request_id(),
|
||||||
|
body: body,
|
||||||
|
};
|
||||||
|
|
||||||
|
let port = shard as u16 + 22272;
|
||||||
|
let target = std::net::SocketAddr::new(LOCAL_HOST, port);
|
||||||
|
|
||||||
|
let encoded = bincode_opt().serialize(&msg).expect("Failed to encode msg");
|
||||||
|
sock.send_to(&encoded, &target).expect("Failed to send msg");
|
||||||
|
|
||||||
|
let mut buf = [0u8; UDP_MTU];
|
||||||
|
match sock.recv_from(&mut buf) {
|
||||||
|
Ok((len, src_addr)) => {
|
||||||
|
let res = bincode_opt().deserialize::<MetadataResponse>(
|
||||||
|
&buf[..len]);
|
||||||
|
match res {
|
||||||
|
Err(e) => println!("Failed to decode reply, reason {} raw:\n{}",
|
||||||
|
e, hex::hexdump(&buf[..len])),
|
||||||
|
Ok(r) => println!("Got reply from {}:\n{:#?}", src_addr, r),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => println!("Error receiving reply from {}: {}", target, e),
|
||||||
|
}
|
||||||
|
}
|
||||||
249
src/bin/raftless_metadata.rs
Normal file
249
src/bin/raftless_metadata.rs
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
use bincode::Options;
|
||||||
|
use rocksdb;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[path="../hex.rs"]
|
||||||
|
mod hex;
|
||||||
|
#[path="../metadata_msgs.rs"]
|
||||||
|
mod metadata_msgs;
|
||||||
|
|
||||||
|
use metadata_msgs::*;
|
||||||
|
|
||||||
|
// TODO: support jumbo frames (~8192 bytes)
|
||||||
|
const UDP_MTU: usize = 1472;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct InodeTableValue {
|
||||||
|
id: u64,
|
||||||
|
deletion_time: u64,
|
||||||
|
is_file: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn make_key(parent_id: u64, subname: &str, ts: u64) -> Vec<u8> {
|
||||||
|
// key format: b'D' + parent_id + subname + ts
|
||||||
|
// subname is stored as a pascal string
|
||||||
|
// which gives us desirable sorting properties
|
||||||
|
// e.g. make_key(-1, "a", 0) < make_key(-1, "a", !0u64) < make_key(-1, "ab", 1234)
|
||||||
|
let key_len = 1 + 8 + 1 + subname.len() + 8;
|
||||||
|
let mut key = vec![0u8; key_len];
|
||||||
|
key[0] = b'D';
|
||||||
|
key[1..9].copy_from_slice(&parent_id.to_le_bytes());
|
||||||
|
key[9] = subname.len() as u8;
|
||||||
|
key[10..key_len-8].copy_from_slice(subname.as_bytes());
|
||||||
|
key[key_len-8..].copy_from_slice(&ts.to_le_bytes());
|
||||||
|
key
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// on success returns the creation time
|
||||||
|
fn do_mkdir(parent_id: u64, next_id: &mut u64, subname: &str, db: &rocksdb::DB
|
||||||
|
) -> MetadataResult<MetadataResponseBody> {
|
||||||
|
|
||||||
|
if subname.len() > 255 {
|
||||||
|
let text = format!("Subname len {} too long, max 255", subname.len());
|
||||||
|
return Err(MetadataError{
|
||||||
|
kind: MetadataErrorKind::NameTooLong,
|
||||||
|
text: text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let this_id = *next_id;
|
||||||
|
|
||||||
|
let now = std::time::SystemTime::now();
|
||||||
|
let creation_time = now.duration_since(std::time::SystemTime::UNIX_EPOCH
|
||||||
|
).expect("Now is less than UNIX_EPOCH!").as_secs();
|
||||||
|
|
||||||
|
let mut key = make_key(parent_id, subname, !0u64);
|
||||||
|
|
||||||
|
let key_lower_bound = make_key(parent_id, subname, 0);
|
||||||
|
|
||||||
|
let mut read_options = rocksdb::ReadOptions::default();
|
||||||
|
// N.B. "`iterate_lower_bound` is inclusive"
|
||||||
|
read_options.set_iterate_lower_bound(key_lower_bound);
|
||||||
|
|
||||||
|
let iter_mode = rocksdb::IteratorMode::From(&key,
|
||||||
|
rocksdb::Direction::Reverse);
|
||||||
|
|
||||||
|
let result = db.iterator_opt(iter_mode, read_options).next();
|
||||||
|
|
||||||
|
// if there are already an entries with the same subname
|
||||||
|
// ensure the latest one is deleted
|
||||||
|
if let Some((key, value)) = result {
|
||||||
|
let key_len = key.len();
|
||||||
|
let existing_creation_ts = u64::from_le_bytes(
|
||||||
|
key[key_len-8..].try_into().unwrap());
|
||||||
|
|
||||||
|
// disallow if the existing one was created recently
|
||||||
|
if existing_creation_ts + 10 > creation_time {
|
||||||
|
let text = format!("Name {} was created recently ({} seconds ago)",
|
||||||
|
subname, creation_time as i64 - existing_creation_ts as i64);
|
||||||
|
return Err(MetadataError{
|
||||||
|
kind: MetadataErrorKind::TooSoon,
|
||||||
|
text: text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let decoded_val: InodeTableValue = bincode_opt().deserialize(&value
|
||||||
|
).expect("Bad value in db");
|
||||||
|
|
||||||
|
if decoded_val.deletion_time == 0 {
|
||||||
|
let text = format!("Inode '{}' ts {} exists and is not deleted",
|
||||||
|
subname, existing_creation_ts);
|
||||||
|
return Err(MetadataError{
|
||||||
|
kind: MetadataErrorKind::InodeAlreadyExists,
|
||||||
|
text: text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let key_len = key.len();
|
||||||
|
key[key_len-8..].copy_from_slice(&creation_time.to_le_bytes());
|
||||||
|
|
||||||
|
let new_val = InodeTableValue {
|
||||||
|
id: this_id,
|
||||||
|
deletion_time: 0,
|
||||||
|
is_file: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let serialized_val = bincode_opt().serialize(&new_val).expect(
|
||||||
|
"Could not serialze value");
|
||||||
|
|
||||||
|
let mut batch = rocksdb::WriteBatch::default();
|
||||||
|
batch.put(key, serialized_val);
|
||||||
|
batch.put(b"M_LAST_INODE_ID", this_id.to_le_bytes());
|
||||||
|
if let Err(e) = db.write(batch) {
|
||||||
|
return Err(MetadataError{
|
||||||
|
kind: MetadataErrorKind::RocksDbError,
|
||||||
|
text: format!("Error writing to rocks: {:?}", e),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
*next_id += 1;
|
||||||
|
|
||||||
|
Ok(MetadataResponseBody::MkDir(ResolvedInode{
|
||||||
|
id: this_id,
|
||||||
|
creation_time: creation_time,
|
||||||
|
deletion_time: 0,
|
||||||
|
is_file: false,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// on success returns creation_ts and value bytes
|
||||||
|
fn do_resolve(parent_id: u64, subname: &str, snapshot_ts: u64, db: &rocksdb::DB
|
||||||
|
) -> MetadataResult<MetadataResponseBody> {
|
||||||
|
|
||||||
|
let key_lower_bound = make_key(parent_id, subname, 0);
|
||||||
|
let key_upper_bound = make_key(parent_id, subname, snapshot_ts);
|
||||||
|
|
||||||
|
let iter_mode = rocksdb::IteratorMode::From(&key_upper_bound,
|
||||||
|
rocksdb::Direction::Reverse);
|
||||||
|
let mut read_options = rocksdb::ReadOptions::default();
|
||||||
|
// N.B. "`iterate_lower_bound` is inclusive"
|
||||||
|
read_options.set_iterate_lower_bound(key_lower_bound);
|
||||||
|
|
||||||
|
let result = db.iterator_opt(iter_mode, read_options).next();
|
||||||
|
|
||||||
|
let maybe_resolved = match result {
|
||||||
|
None => {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
Some((key, raw_value)) => {
|
||||||
|
let key_len = key.len();
|
||||||
|
let creation_time = u64::from_le_bytes(
|
||||||
|
key[key_len-8..].try_into().unwrap());
|
||||||
|
let value: InodeTableValue = bincode_opt().deserialize(
|
||||||
|
&raw_value).expect("Bad value in db");
|
||||||
|
let response = ResolvedInode {
|
||||||
|
id: value.id,
|
||||||
|
creation_time: creation_time,
|
||||||
|
deletion_time: value.deletion_time,
|
||||||
|
is_file: value.is_file,
|
||||||
|
};
|
||||||
|
Some(response)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(MetadataResponseBody::Resolve(maybe_resolved))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
|
||||||
|
if args.len() < 2 {
|
||||||
|
println!("Usage: {} shard", args[0]);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let shard: u8 = args[1].parse().expect("Failed to parse shard");
|
||||||
|
|
||||||
|
let db = rocksdb::DB::open_default(
|
||||||
|
format!("/home/jchicke/playground/fs/{}", shard)).expect(
|
||||||
|
"Could not load db");
|
||||||
|
|
||||||
|
let mut next_inode_id = match db.get(b"M_LAST_INODE_ID").unwrap() {
|
||||||
|
None => (shard as u64) << 56,
|
||||||
|
Some(v) => u64::from_le_bytes(v.try_into().expect(
|
||||||
|
"Bad format for M_LAST_INODE_ID")) + 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Using {:#018X} as next inode id", next_inode_id);
|
||||||
|
|
||||||
|
let port: u16 = shard as u16 + 22272;
|
||||||
|
|
||||||
|
let addr = std::net::SocketAddr::new(
|
||||||
|
std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)), port);
|
||||||
|
|
||||||
|
let sock = std::net::UdpSocket::bind(addr).expect("Could not bind UDP");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut buf = [0u8; UDP_MTU];
|
||||||
|
let (len, origin) = sock.recv_from(&mut buf).expect("Recv failed");
|
||||||
|
match bincode_opt().deserialize::<MetadataRequest>(&buf[..len]) {
|
||||||
|
Err(e) => eprintln!("Failed to decode message with error {}\n{}", e,
|
||||||
|
hex::hexdump(&buf[..len])),
|
||||||
|
Ok(m) => {
|
||||||
|
println!("Got {:?} from {}", m, origin);
|
||||||
|
let maybe_response_body = match m.body {
|
||||||
|
MetadataRequestBody::Resolve { parent_id, subname, ts }
|
||||||
|
=> Some(do_resolve(parent_id, &subname, ts, &db)),
|
||||||
|
MetadataRequestBody::MkDir { parent_id, subdirname }
|
||||||
|
=> Some(do_mkdir(parent_id, &mut next_inode_id,
|
||||||
|
&subdirname, &db)),
|
||||||
|
_ => {
|
||||||
|
eprintln!("Command not supported yet, ignoring");
|
||||||
|
None
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if let Some(response_body) = maybe_response_body {
|
||||||
|
let response = MetadataResponse{
|
||||||
|
request_id: m.request_id,
|
||||||
|
body: response_body,
|
||||||
|
};
|
||||||
|
let response_sz = bincode_opt().serialized_size(&response
|
||||||
|
).unwrap() as usize;
|
||||||
|
if response_sz > UDP_MTU {
|
||||||
|
eprintln!("response {:?} is too big for UDP_MTU {}",
|
||||||
|
response, UDP_MTU);
|
||||||
|
}
|
||||||
|
let res0 = bincode_opt().serialize_into(&mut buf[..],
|
||||||
|
&response);
|
||||||
|
if let Err(e) = res0 {
|
||||||
|
eprintln!("Failed to Serialise\n{:#?}, reason: {}",
|
||||||
|
response, e);
|
||||||
|
}
|
||||||
|
let res1 = sock.send_to(&buf[..response_sz], origin);
|
||||||
|
if let Err(e) = res1 {
|
||||||
|
eprintln!("Failed to send to {}, reason: {}", origin,
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
94
src/hex.rs
Normal file
94
src/hex.rs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
// adapted from native/core/Hex.cpp
|
||||||
|
|
||||||
|
use std::string::String;
|
||||||
|
|
||||||
|
|
||||||
|
const HEX_CHARS: [u16; 256] = [
|
||||||
|
0x3030, 0x3130, 0x3230, 0x3330, 0x3430, 0x3530, 0x3630, 0x3730, 0x3830, 0x3930, 0x4130, 0x4230, 0x4330, 0x4430, 0x4530, 0x4630,
|
||||||
|
0x3031, 0x3131, 0x3231, 0x3331, 0x3431, 0x3531, 0x3631, 0x3731, 0x3831, 0x3931, 0x4131, 0x4231, 0x4331, 0x4431, 0x4531, 0x4631,
|
||||||
|
0x3032, 0x3132, 0x3232, 0x3332, 0x3432, 0x3532, 0x3632, 0x3732, 0x3832, 0x3932, 0x4132, 0x4232, 0x4332, 0x4432, 0x4532, 0x4632,
|
||||||
|
0x3033, 0x3133, 0x3233, 0x3333, 0x3433, 0x3533, 0x3633, 0x3733, 0x3833, 0x3933, 0x4133, 0x4233, 0x4333, 0x4433, 0x4533, 0x4633,
|
||||||
|
0x3034, 0x3134, 0x3234, 0x3334, 0x3434, 0x3534, 0x3634, 0x3734, 0x3834, 0x3934, 0x4134, 0x4234, 0x4334, 0x4434, 0x4534, 0x4634,
|
||||||
|
0x3035, 0x3135, 0x3235, 0x3335, 0x3435, 0x3535, 0x3635, 0x3735, 0x3835, 0x3935, 0x4135, 0x4235, 0x4335, 0x4435, 0x4535, 0x4635,
|
||||||
|
0x3036, 0x3136, 0x3236, 0x3336, 0x3436, 0x3536, 0x3636, 0x3736, 0x3836, 0x3936, 0x4136, 0x4236, 0x4336, 0x4436, 0x4536, 0x4636,
|
||||||
|
0x3037, 0x3137, 0x3237, 0x3337, 0x3437, 0x3537, 0x3637, 0x3737, 0x3837, 0x3937, 0x4137, 0x4237, 0x4337, 0x4437, 0x4537, 0x4637,
|
||||||
|
0x3038, 0x3138, 0x3238, 0x3338, 0x3438, 0x3538, 0x3638, 0x3738, 0x3838, 0x3938, 0x4138, 0x4238, 0x4338, 0x4438, 0x4538, 0x4638,
|
||||||
|
0x3039, 0x3139, 0x3239, 0x3339, 0x3439, 0x3539, 0x3639, 0x3739, 0x3839, 0x3939, 0x4139, 0x4239, 0x4339, 0x4439, 0x4539, 0x4639,
|
||||||
|
0x3041, 0x3141, 0x3241, 0x3341, 0x3441, 0x3541, 0x3641, 0x3741, 0x3841, 0x3941, 0x4141, 0x4241, 0x4341, 0x4441, 0x4541, 0x4641,
|
||||||
|
0x3042, 0x3142, 0x3242, 0x3342, 0x3442, 0x3542, 0x3642, 0x3742, 0x3842, 0x3942, 0x4142, 0x4242, 0x4342, 0x4442, 0x4542, 0x4642,
|
||||||
|
0x3043, 0x3143, 0x3243, 0x3343, 0x3443, 0x3543, 0x3643, 0x3743, 0x3843, 0x3943, 0x4143, 0x4243, 0x4343, 0x4443, 0x4543, 0x4643,
|
||||||
|
0x3044, 0x3144, 0x3244, 0x3344, 0x3444, 0x3544, 0x3644, 0x3744, 0x3844, 0x3944, 0x4144, 0x4244, 0x4344, 0x4444, 0x4544, 0x4644,
|
||||||
|
0x3045, 0x3145, 0x3245, 0x3345, 0x3445, 0x3545, 0x3645, 0x3745, 0x3845, 0x3945, 0x4145, 0x4245, 0x4345, 0x4445, 0x4545, 0x4645,
|
||||||
|
0x3046, 0x3146, 0x3246, 0x3346, 0x3446, 0x3546, 0x3646, 0x3746, 0x3846, 0x3946, 0x4146, 0x4246, 0x4346, 0x4446, 0x4546, 0x4646,
|
||||||
|
];
|
||||||
|
|
||||||
|
const ASCII_CHARS: [u8; 256] = [
|
||||||
|
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
|
||||||
|
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
|
||||||
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||||
|
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||||
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
||||||
|
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||||
|
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
||||||
|
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x2e,
|
||||||
|
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
|
||||||
|
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
|
||||||
|
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
|
||||||
|
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
|
||||||
|
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
|
||||||
|
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
|
||||||
|
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
|
||||||
|
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
pub fn hexdump(data: &[u8]) -> String {
|
||||||
|
let n_lines = (data.len() + 15) >> 4;
|
||||||
|
let mut retval = vec![0u8; 68 * n_lines];
|
||||||
|
|
||||||
|
for i in 0..n_lines {
|
||||||
|
// Write position
|
||||||
|
retval[i*68+0] = (HEX_CHARS[((i*16) >> 24) & 0xFF]) as u8;
|
||||||
|
retval[i*68+1] = (HEX_CHARS[((i*16) >> 24) & 0xFF] >> 8) as u8;
|
||||||
|
retval[i*68+2] = (HEX_CHARS[((i*16) >> 16) & 0xFF]) as u8;
|
||||||
|
retval[i*68+3] = (HEX_CHARS[((i*16) >> 16) & 0xFF] >> 8) as u8;
|
||||||
|
retval[i*68+4] = (HEX_CHARS[((i*16) >> 8) & 0xFF]) as u8;
|
||||||
|
retval[i*68+5] = (HEX_CHARS[((i*16) >> 8) & 0xFF] >> 8) as u8;
|
||||||
|
retval[i*68+6] = (HEX_CHARS[(i*16) & 0xFF]) as u8;
|
||||||
|
retval[i*68+7] = (HEX_CHARS[(i*16) & 0xFF] >> 8) as u8;
|
||||||
|
retval[i*68+8] = b':';
|
||||||
|
|
||||||
|
// Write hex values
|
||||||
|
for j in 0..8usize {
|
||||||
|
retval[i*68+9+j*5] = b' ';
|
||||||
|
if i*16+j*2 >= data.len() {
|
||||||
|
retval[i*68+9+j*5+1] = b' ';
|
||||||
|
retval[i*68+9+j*5+2] = b' ';
|
||||||
|
} else {
|
||||||
|
retval[i*68+9+j*5+1] = HEX_CHARS[data[i*16+j*2] as usize] as u8;
|
||||||
|
retval[i*68+9+j*5+2] = (HEX_CHARS[data[i*16+j*2] as usize] >> 8) as u8;
|
||||||
|
}
|
||||||
|
if i*16+j*2+1 >= data.len() {
|
||||||
|
retval[i*68+9+j*5+3] = b' ';
|
||||||
|
retval[i*68+9+j*5+4] = b' ';
|
||||||
|
} else {
|
||||||
|
retval[i*68+9+j*5+3] = HEX_CHARS[data[i*16+j*2+1] as usize] as u8;
|
||||||
|
retval[i*68+9+j*5+4] = (HEX_CHARS[data[i*16+j*2+1] as usize] >> 8) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval[i*68+49] = b' ';
|
||||||
|
retval[i*68+50] = b' ';
|
||||||
|
|
||||||
|
// Write ASCII values
|
||||||
|
for j in 0..16usize {
|
||||||
|
if i*16+j >= data.len() {
|
||||||
|
retval[i*68+j+51] = b' ';
|
||||||
|
} else {
|
||||||
|
retval[i*68+j+51] = ASCII_CHARS[data[i*16+j] as usize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval[i*68+67] = b'\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { String::from_utf8_unchecked(retval) }
|
||||||
|
}
|
||||||
83
src/metadata_msgs.rs
Normal file
83
src/metadata_msgs.rs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use std::string::String;
|
||||||
|
use bincode;
|
||||||
|
|
||||||
|
|
||||||
|
pub fn bincode_opt() -> bincode::DefaultOptions {
|
||||||
|
bincode::DefaultOptions::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub enum MetadataRequestBody {
|
||||||
|
CreateBlock,
|
||||||
|
DeleteBlockRequest,
|
||||||
|
DeleteBlockComplete,
|
||||||
|
ReadBlockMap,
|
||||||
|
CreateFile,
|
||||||
|
DeleteFile,
|
||||||
|
PurgeFileRequest,
|
||||||
|
PurgeFileComplete,
|
||||||
|
MvFile,
|
||||||
|
LsDir,
|
||||||
|
MkDir{ parent_id: u64, subdirname: String },
|
||||||
|
// creation_time on RmDir ensures it's idempotent
|
||||||
|
RmDir{ parent_id: u64, subdirname: String, creation_time: u64 },
|
||||||
|
MvDir,
|
||||||
|
// if you're looking at "the present" use !0u64 as ts
|
||||||
|
// otherwise populate it with snapshot ts
|
||||||
|
Resolve{ parent_id: u64, subname: String, ts: u64 },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct MetadataRequest {
|
||||||
|
pub request_id: u64, // echoed back to the client in response
|
||||||
|
// client's responsibiltity to keep unique
|
||||||
|
pub body: MetadataRequestBody,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct ResolvedInode {
|
||||||
|
pub id: u64,
|
||||||
|
pub creation_time: u64,
|
||||||
|
pub deletion_time: u64, // 0 => not deleted
|
||||||
|
pub is_file: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub enum MetadataErrorKind {
|
||||||
|
TooSoon,
|
||||||
|
InodeAlreadyExists,
|
||||||
|
NameTooLong,
|
||||||
|
RocksDbError,
|
||||||
|
NetworkError,
|
||||||
|
BincodeError,
|
||||||
|
LogicError,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct MetadataError {
|
||||||
|
pub kind: MetadataErrorKind,
|
||||||
|
pub text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub type MetadataResult<T> = Result<T, MetadataError>;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub enum MetadataResponseBody {
|
||||||
|
MkDir(ResolvedInode),
|
||||||
|
Resolve(Option<ResolvedInode>),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct MetadataResponse {
|
||||||
|
pub request_id: u64, // echoed back from request
|
||||||
|
pub body: MetadataResult<MetadataResponseBody>,
|
||||||
|
}
|
||||||
9
src/schema.rs
Normal file
9
src/schema.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
use core::arch::x86_64;
|
||||||
|
|
||||||
|
|
||||||
|
pub fn shard(inode_id: u64) -> u8 {
|
||||||
|
let ret = unsafe {
|
||||||
|
x86_64::_mm_crc32_u64(0, inode_id)
|
||||||
|
};
|
||||||
|
ret as u8
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user