diff options
author | edef <edef@unfathomable.blue> | 2021-08-14 21:28:14 +0000 |
---|---|---|
committer | edef <edef@unfathomable.blue> | 2021-08-14 21:28:14 +0000 |
commit | db7c54f92f386a94db8af7a12626d2657b4dd640 (patch) | |
tree | 4baba57bac54c68823a834c0f8aa97b24cfec7a2 | |
parent | dcae0f9c8a94f05bf55cf9b6fbc773502ab5784f (diff) | |
download | unf-legacy-db7c54f92f386a94db8af7a12626d2657b4dd640.tar.zst |
ripple/fossil: a basic content-addressable store
Fossil stores content-addressed blobs of file contents and Protobuf-encoded directory listings, backed by Sled. Change-Id: I8b49de6342218ca00755cec980b1d0cfb18878a7
-rw-r--r-- | ripple/Cargo.lock | 640 | ||||
-rw-r--r-- | ripple/Cargo.lock.license | 1 | ||||
-rw-r--r-- | ripple/Cargo.toml | 3 | ||||
-rw-r--r-- | ripple/fossil/.gitignore | 4 | ||||
-rw-r--r-- | ripple/fossil/Cargo.toml | 17 | ||||
-rw-r--r-- | ripple/fossil/build.rs | 9 | ||||
-rw-r--r-- | ripple/fossil/src/bin/add.rs | 31 | ||||
-rw-r--r-- | ripple/fossil/src/bin/extract.rs | 57 | ||||
-rw-r--r-- | ripple/fossil/src/lib.rs | 206 | ||||
-rw-r--r-- | ripple/fossil/src/store.proto | 28 | ||||
-rw-r--r-- | ripple/shell.nix | 7 |
11 files changed, 1003 insertions, 0 deletions
diff --git a/ripple/Cargo.lock b/ripple/Cargo.lock index d280e4a..fdab2f2 100644 --- a/ripple/Cargo.lock +++ b/ripple/Cargo.lock @@ -3,5 +3,645 @@ version = 3 [[package]] +name = "anyhow" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da1976d75adbe5fbc88130ecd119529cf1cc6a93ae1546d8696ee66f0d21af1" + +[[package]] +name = "blake3" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b64485778c4f16a6a5a9d335e80d449ac6c70cdd6a06d2af18a6f6f775a125b3" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if 0.1.10", + "constant_time_eq", + "crypto-mac", + "digest", + "rayon", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "cc" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[package]] +name = "fossil" +version = "0.1.0" +dependencies = [ + "blake3", + "byteorder", + "bytes", + "prost", + "prost-build", + "sled", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itertools" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" +dependencies = [ + "either", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" + +[[package]] +name = "lock_api" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "prost" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603" +dependencies = [ + "bytes", + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prost", + "prost-types", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600d2f334aa05acb02a755e217ef1ab6dea4d51b58b7846588b747edec04efba" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b" +dependencies = [ + "bytes", + "prost", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] name = "ripple" version = "0.1.0" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sled" +version = "0.34.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d0132f3e393bcb7390c60bb45769498cf4550bcb7a21d7f95c02b69f6362cdc" +dependencies = [ + "crc32fast", + "crossbeam-epoch", + "crossbeam-utils", + "fs2", + "fxhash", + "libc", + "log", + "parking_lot", +] + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "typenum" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "which" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9" +dependencies = [ + "either", + "lazy_static", + "libc", +] + +[[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" diff --git a/ripple/Cargo.lock.license b/ripple/Cargo.lock.license index dddf7fd..26667bf 100644 --- a/ripple/Cargo.lock.license +++ b/ripple/Cargo.lock.license @@ -1,2 +1,3 @@ SPDX-FileCopyrightText: V <v@unfathomable.blue> +SPDX-FileCopyrightText: edef <edef@unfathomable.blue> SPDX-License-Identifier: CC0-1.0 diff --git a/ripple/Cargo.toml b/ripple/Cargo.toml index 654f89b..5255ca8 100644 --- a/ripple/Cargo.toml +++ b/ripple/Cargo.toml @@ -7,3 +7,6 @@ version = "0.1.0" edition = "2018" [dependencies] + +[workspace] +members = ["fossil"] diff --git a/ripple/fossil/.gitignore b/ripple/fossil/.gitignore new file mode 100644 index 0000000..be75022 --- /dev/null +++ b/ripple/fossil/.gitignore @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: edef <edef@unfathomable.blue> +# SPDX-License-Identifier: OSL-3.0 + +/target diff --git a/ripple/fossil/Cargo.toml b/ripple/fossil/Cargo.toml new file mode 100644 index 0000000..a88a5f8 --- /dev/null +++ b/ripple/fossil/Cargo.toml @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: edef <edef@unfathomable.blue> +# SPDX-License-Identifier: OSL-3.0 + +[package] +name = "fossil" +version = "0.1.0" +edition = "2018" + +[dependencies] +prost = "0.8.0" +bytes = "1.0.1" +blake3 = { version = "0.3.8", features = ["rayon"] } +sled = "0.34.6" +byteorder = "1.4.3" + +[build-dependencies] +prost-build = "0.8.0" diff --git a/ripple/fossil/build.rs b/ripple/fossil/build.rs new file mode 100644 index 0000000..412c2d2 --- /dev/null +++ b/ripple/fossil/build.rs @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: edef <edef@unfathomable.blue> +// SPDX-License-Identifier: OSL-3.0 + +use std::io::Result; + +fn main() -> Result<()> { + prost_build::compile_protos(&["src/store.proto"], &["src/"])?; + Ok(()) +} diff --git a/ripple/fossil/src/bin/add.rs b/ripple/fossil/src/bin/add.rs new file mode 100644 index 0000000..114f893 --- /dev/null +++ b/ripple/fossil/src/bin/add.rs @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: edef <edef@unfathomable.blue> +// SPDX-License-Identifier: OSL-3.0 + +use { + fossil::Directory, + prost::Message, + std::{ + env, + io::{self, Write}, + path::Path, + }, +}; + +fn main() { + let store = fossil::Store::open("fossil.db").unwrap(); + let mut root = Directory::new(); + + for name in env::args().skip(1) { + let path = Path::new(&name); + let name = path + .file_name() + .and_then(|s| s.to_str()) + .expect("invalid path") + .to_owned(); + + root.children.insert(name, store.add_path(path)); + } + + let mut stdout = io::stdout(); + stdout.write_all(&root.into_pb().encode_to_vec()).unwrap(); +} diff --git a/ripple/fossil/src/bin/extract.rs b/ripple/fossil/src/bin/extract.rs new file mode 100644 index 0000000..f83ce0e --- /dev/null +++ b/ripple/fossil/src/bin/extract.rs @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: edef <edef@unfathomable.blue> +// SPDX-License-Identifier: OSL-3.0 + +use { + fossil::{store, Directory}, + prost::Message, + std::{ + fs, + io::{self, Read, Write}, + os::unix::{fs::symlink, prelude::OpenOptionsExt}, + path::Path, + }, +}; + +fn main() { + let store = fossil::Store::open("fossil.db").unwrap(); + let root = { + let mut stdin = io::stdin(); + + let mut bytes = Vec::new(); + stdin.read_to_end(&mut bytes).unwrap(); + + let pb = store::Directory::decode(&*bytes).unwrap(); + Directory::from_pb(pb) + }; + + let root_path = Path::new("."); + extract(&store, root_path, &root); +} + +fn extract(store: &fossil::Store, path: &Path, dir: &Directory) { + for (name, node) in &dir.children { + let path = path.join(name); + match node.clone() { + fossil::Node::Directory { r#ref } => { + let blob = store.read_blob(r#ref); + let pb = store::Directory::decode(&*blob).unwrap(); + fs::create_dir(&path).unwrap(); + extract(store, &path, &Directory::from_pb(pb)); + } + fossil::Node::File { r#ref, executable } => { + let mode = if executable { 0o755 } else { 0o644 }; + let mut f = fs::OpenOptions::new() + .write(true) + .create_new(true) + .mode(mode) + .open(path) + .unwrap(); + let blob = store.read_blob(r#ref); + f.write_all(&blob).unwrap(); + } + fossil::Node::Link { target } => { + symlink(target, path).unwrap(); + } + } + } +} diff --git a/ripple/fossil/src/lib.rs b/ripple/fossil/src/lib.rs new file mode 100644 index 0000000..6fb5269 --- /dev/null +++ b/ripple/fossil/src/lib.rs @@ -0,0 +1,206 @@ +// SPDX-FileCopyrightText: edef <edef@unfathomable.blue> +// SPDX-License-Identifier: OSL-3.0 + +use { + byteorder::{BigEndian, ByteOrder}, + prost::Message, + std::{collections::BTreeMap, fs, io, os::unix::fs::PermissionsExt, path::Path}, +}; + +pub mod store { + include!(concat!(env!("OUT_DIR"), "/fossil.store.rs")); +} + +const DIGEST_BYTES: usize = blake3::OUT_LEN; +const OFFSET_BYTES: usize = 4; + +pub struct Store { + db: sled::Db, +} + +impl Store { + pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Store> { + let db = sled::open(path)?; + Ok(Store { db }) + } + + pub fn add_path<P: AsRef<Path>>(&self, path: P) -> Node { + let path = path.as_ref(); + let meta = fs::symlink_metadata(path).unwrap(); + + match meta.file_type() { + ty if ty.is_dir() => { + let mut d = Directory::new(); + + for entry in path.read_dir().unwrap() { + let entry = entry.unwrap(); + let name = entry.file_name().into_string().unwrap(); + d.children.insert(name, self.add_path(entry.path())); + } + + let blob = d.into_pb().encode_to_vec(); + + Node::Directory { + r#ref: self.write_blob(&blob), + } + } + ty if ty.is_file() => { + let executable = (meta.permissions().mode() & 0o100) != 0; + + let blob = fs::read(path).unwrap(); + Node::File { + executable, + r#ref: self.write_blob(&blob), + } + } + ty if ty.is_symlink() => { + let target = path + .read_link() + .unwrap() + .to_str() + .expect("symlink target is invalid UTF-8") + .to_owned(); + + Node::Link { target } + } + _ => panic!("not a symlink or a regular file"), + } + } + + fn write_blob(&self, data: &[u8]) -> Digest { + let digest = { + let mut h = blake3::Hasher::new(); + h.update_with_join::<blake3::join::RayonJoin>(&data); + *h.finalize().as_bytes() + }; + + // TODO(edef): maybe don't use the default tree? + // we should probably have a "blob" tree, + // and reserve the default tree for DB metadata + + self.db + .transaction::<_, _, sled::Error>(|db| { + for (n, chunk) in data.chunks(4096).enumerate() { + let mut key = [0u8; DIGEST_BYTES + OFFSET_BYTES]; + key[..DIGEST_BYTES].copy_from_slice(&digest); + BigEndian::write_u32(&mut key[DIGEST_BYTES..], n as u32); + db.insert(&key[..], chunk)?; + } + Ok(()) + }) + .unwrap(); + + digest.into() + } + + pub fn read_blob(&self, r#ref: Digest) -> Vec<u8> { + let mut buffer = Vec::new(); + let mut h = blake3::Hasher::new(); + for element in self.db.scan_prefix(r#ref.as_bytes()) { + let (_, chunk) = element.unwrap(); + h.update(&chunk); + buffer.extend_from_slice(&chunk); + } + + if buffer.len() == 0 { + panic!("blob not found"); + } + + if h.finalize() != r#ref { + panic!("hash mismatch"); + } + + buffer + } +} + +pub type Digest = blake3::Hash; + +pub struct Directory { + pub children: BTreeMap<String, Node>, +} + +#[derive(Clone)] +pub enum Node { + Directory { r#ref: Digest }, + File { r#ref: Digest, executable: bool }, + Link { target: String }, +} + +impl Directory { + pub fn new() -> Directory { + Directory { + children: BTreeMap::new(), + } + } + + pub fn into_pb(self) -> store::Directory { + let mut d = store::Directory::default(); + + for (name, node) in self.children.into_iter() { + match node { + Node::Directory { r#ref } => d.directories.push(store::DirectoryNode { + name, + r#ref: r#ref.as_bytes().to_vec(), + }), + Node::File { r#ref, executable } => d.files.push(store::FileNode { + name, + r#ref: r#ref.as_bytes().to_vec(), + executable, + }), + Node::Link { target } => d.links.push(store::LinkNode { name, target }), + } + } + + d + } + + pub fn from_pb(pb: store::Directory) -> Directory { + let mut children = BTreeMap::new(); + + for child in pb.directories { + children.insert( + child.name, + Node::Directory { + r#ref: digest_from_bytes(&child.r#ref), + }, + ); + } + + for child in pb.files { + children.insert( + child.name, + Node::File { + r#ref: digest_from_bytes(&child.r#ref), + executable: child.executable, + }, + ); + } + + for child in pb.links { + children.insert( + child.name, + Node::Link { + target: child.target, + }, + ); + } + + Directory { children } + } +} + +#[track_caller] +fn digest_from_bytes(bytes: &[u8]) -> Digest { + if bytes.len() != DIGEST_BYTES { + panic!( + "digest is {} bytes, expecting {} bytes", + bytes.len(), + DIGEST_BYTES + ); + } + + let mut buffer = [0; DIGEST_BYTES]; + buffer.copy_from_slice(bytes); + buffer.into() +} diff --git a/ripple/fossil/src/store.proto b/ripple/fossil/src/store.proto new file mode 100644 index 0000000..58832f0 --- /dev/null +++ b/ripple/fossil/src/store.proto @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: edef <edef@unfathomable.blue> +// SPDX-License-Identifier: OSL-3.0 + +syntax = "proto3"; + +package fossil.store; + +message Directory { + repeated DirectoryNode directories = 1; + repeated FileNode files = 2; + repeated LinkNode links = 3; +} + +message DirectoryNode { + string name = 1; + bytes ref = 2; +} + +message FileNode { + string name = 1; + bytes ref = 2; + bool executable = 3; +} + +message LinkNode { + string name = 1; + string target = 2; +} diff --git a/ripple/shell.nix b/ripple/shell.nix index 9282613..03804c7 100644 --- a/ripple/shell.nix +++ b/ripple/shell.nix @@ -1,4 +1,5 @@ # SPDX-FileCopyrightText: V <v@unfathomable.blue> +# SPDX-FileCopyrightText: edef <edef@unfathomable.blue> # SPDX-License-Identifier: OSL-3.0 with import ./nix; @@ -12,5 +13,11 @@ mkShell { # needed by rust-analyzer rustc # core crate code rustfmt # format-on-save + + # needed by prost-build + protobuf ]; + + # needed by prost-build + PROTOC = "protoc"; } |