wezterm-blob-leases-0.1.1/.cargo_vcs_info.json0000644000000001610000000000100147140ustar { "git": { "sha1": "034e2179661a7e8030cfbd6c3d3d37853a2ec276" }, "path_in_vcs": "wezterm-blob-leases" }wezterm-blob-leases-0.1.1/Cargo.lock0000644000000262150000000000100126770ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "atomic" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" dependencies = [ "bytemuck", ] [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bitflags" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bytemuck" version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "cpufeatures" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", ] [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", ] [[package]] name = "errno" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", "windows-sys", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] [[package]] name = "getrandom" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" dependencies = [ "cfg-if", "libc", "wasi", "windows-targets", ] [[package]] name = "libc" version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "linux-raw-sys" version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "mac_address" version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" dependencies = [ "nix", "winapi", ] [[package]] name = "memoffset" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "nix" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags", "cfg-if", "cfg_aliases", "libc", "memoffset", ] [[package]] name = "once_cell" version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "rustix" version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "serde" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "sha2" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "syn" version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" dependencies = [ "cfg-if", "fastrand", "getrandom", "once_cell", "rustix", "windows-sys", ] [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "uuid" version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" dependencies = [ "atomic", "getrandom", "serde", ] [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" version = "0.13.3+wasi-0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" dependencies = [ "wit-bindgen-rt", ] [[package]] name = "wezterm-blob-leases" version = "0.1.1" dependencies = [ "getrandom", "mac_address", "serde", "sha2", "tempfile", "thiserror", "uuid", ] [[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" [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "wit-bindgen-rt" version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ "bitflags", ] wezterm-blob-leases-0.1.1/Cargo.toml0000644000000024620000000000100127200ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "wezterm-blob-leases" version = "0.1.1" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Manage image blob caching/leasing for wezterm" readme = false license = "MIT" repository = "https://github.com/wezterm/wezterm" [lib] name = "wezterm_blob_leases" path = "src/lib.rs" [dependencies.getrandom] version = "0.3.1" [dependencies.mac_address] version = "1.1.8" [dependencies.serde] version = "1.0" features = ["derive"] optional = true [dependencies.sha2] version = "0.10" [dependencies.tempfile] version = "3.16" optional = true [dependencies.thiserror] version = "1.0" [dependencies.uuid] version = "1.13" features = [ "v1", "rng", ] [features] default = [] serde = [ "dep:serde", "uuid/serde", ] simple_tempdir = ["dep:tempfile"] wezterm-blob-leases-0.1.1/Cargo.toml.orig000064400000000000000000000011741046102023000164000ustar 00000000000000[package] name = "wezterm-blob-leases" version = "0.1.1" edition = "2021" repository = "https://github.com/wezterm/wezterm" description = "Manage image blob caching/leasing for wezterm" license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] getrandom = "0.3.1" mac_address = "1.1.8" serde = {version="1.0", features=["derive"], optional=true} sha2 = "0.10" tempfile = {version="3.16", optional=true} thiserror = "1.0" uuid = {version="1.13", features=["v1", "rng"]} [features] default = [] serde = ["dep:serde", "uuid/serde"] simple_tempdir = ["dep:tempfile"] wezterm-blob-leases-0.1.1/LICENSE.md000064400000000000000000000020641046102023000151140ustar 00000000000000MIT License Copyright (c) 2023-Present Wez Furlong Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. wezterm-blob-leases-0.1.1/src/content_id.rs000064400000000000000000000017051046102023000167740ustar 00000000000000#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use sha2::Digest; /// Identifies data within the store. /// This is an (unspecified) hash of the content #[derive(Clone, Copy, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ContentId([u8; 32]); impl ContentId { pub fn for_bytes(bytes: &[u8]) -> Self { let mut hasher = sha2::Sha256::new(); hasher.update(bytes); Self(hasher.finalize().into()) } pub fn as_hash_bytes(&self) -> [u8; 32] { self.0 } } impl std::fmt::Display for ContentId { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { write!(fmt, "sha256-")?; for byte in &self.0 { write!(fmt, "{byte:x}")?; } Ok(()) } } impl std::fmt::Debug for ContentId { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { write!(fmt, "ContentId({self})") } } wezterm-blob-leases-0.1.1/src/error.rs000064400000000000000000000011361046102023000157750ustar 00000000000000use crate::ContentId; use std::path::PathBuf; use thiserror::Error; #[derive(Error, Debug)] pub enum Error { #[error("Lease Expired, data is no longer accessible")] LeaseExpired, #[error("Content with id {0} not found")] ContentNotFound(ContentId), #[error("Io error in BlobLease: {0}")] Io(#[from] std::io::Error), #[error("Storage has already been initialized")] AlreadyInitializedStorage, #[error("Storage has not been initialized")] StorageNotInit, #[error("Storage location {0} may be corrupt: {1}")] StorageDirIoError(PathBuf, std::io::Error), } wezterm-blob-leases-0.1.1/src/lease.rs000064400000000000000000000071761046102023000157470ustar 00000000000000use crate::{get_storage, BoxedReader, ContentId, Error, LeaseId}; use std::sync::Arc; /// A lease represents a handle to data in the store. /// The lease will help to keep the data alive in the store. /// Depending on the policy configured for the store, it /// may guarantee to keep the data intact for its lifetime, /// or in some cases, it the store is being thrashed and at /// capacity, it may have been evicted. #[derive(Clone, Debug, PartialEq, Eq)] pub struct BlobLease { inner: Arc, } #[derive(Debug, PartialEq, Eq)] struct LeaseInner { pub content_id: ContentId, pub lease_id: LeaseId, } impl BlobLease { pub(crate) fn make_lease(content_id: ContentId, lease_id: LeaseId) -> Self { Self { inner: Arc::new(LeaseInner { content_id, lease_id, }), } } /// Returns a copy of the data, owned by the caller pub fn get_data(&self) -> Result, Error> { let storage = get_storage()?; storage.get_data(self.inner.content_id, self.inner.lease_id) } /// Returns a reader that can be used to stream/seek into /// the data pub fn get_reader(&self) -> Result { let storage = get_storage()?; storage.get_reader(self.inner.content_id, self.inner.lease_id) } pub fn content_id(&self) -> ContentId { self.inner.content_id } } impl Drop for LeaseInner { fn drop(&mut self) { if let Ok(storage) = get_storage() { storage .advise_lease_dropped(self.lease_id, self.content_id) .ok(); } } } /// Serialize a lease as the corresponding data bytes. /// This can fail during serialization if the lease is /// stale, but not during deserialization, as deserialiation /// will store the data implicitly. #[cfg(feature = "serde")] pub mod lease_bytes { use super::*; use crate::BlobManager; use serde::{de, ser, Deserialize, Serialize}; /// Serialize a lease as its bytes pub fn serialize(lease: &BlobLease, serializer: S) -> Result where S: ser::Serializer, { let data = lease .get_data() .map_err(|err| ser::Error::custom(format!("{err:#}")))?; data.serialize(serializer) } /// Deserialize a lease from bytes. pub fn deserialize<'de, D>(d: D) -> Result where D: de::Deserializer<'de>, { let data = as Deserialize>::deserialize(d)?; BlobManager::store(&data).map_err(|err| de::Error::custom(format!("{err:#}"))) } } /// Serialize a lease to/from its content id. /// This can fail in either direction if the lease is stale /// during serialization, or if the data for that content id /// is not available during deserialization. #[cfg(feature = "serde")] pub mod lease_content_id { use super::*; use crate::BlobManager; use serde::{de, ser, Deserialize, Serialize}; /// Serialize a lease as its content id pub fn serialize(lease: &BlobLease, serializer: S) -> Result where S: ser::Serializer, { lease.inner.content_id.serialize(serializer) } /// Deserialize a lease from a content id. /// Will fail unless the content id is already available /// to the local storage manager pub fn deserialize<'de, D>(d: D) -> Result where D: de::Deserializer<'de>, { let content_id = ::deserialize(d)?; BlobManager::get_by_content_id(content_id) .map_err(|err| de::Error::custom(format!("{err:#}"))) } } wezterm-blob-leases-0.1.1/src/lease_id.rs000064400000000000000000000015771046102023000164220ustar 00000000000000use std::sync::LazyLock; use uuid::Uuid; /// Represents an individual lease #[derive(Clone, Copy, Eq, PartialEq, Debug)] pub struct LeaseId { uuid: Uuid, pid: u32, } impl std::fmt::Display for LeaseId { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { write!(fmt, "lease:pid={},{}", self.pid, self.uuid.hyphenated()) } } fn get_mac_address() -> [u8; 6] { match mac_address::get_mac_address() { Ok(Some(addr)) => addr.bytes(), _ => { let mut mac = [0u8; 6]; getrandom::fill(&mut mac).ok(); mac } } } impl LeaseId { pub fn new() -> Self { static MAC: LazyLock<[u8; 6]> = LazyLock::new(get_mac_address); let uuid = Uuid::now_v1(&*MAC); let pid = std::process::id(); Self { uuid, pid } } pub fn pid(&self) -> u32 { self.pid } } wezterm-blob-leases-0.1.1/src/lib.rs000064400000000000000000000003401046102023000154060ustar 00000000000000mod content_id; mod error; mod lease; mod lease_id; mod manager; mod storage; pub mod simple_tempdir; pub use content_id::*; pub use error::*; pub use lease::*; pub use lease_id::*; pub use manager::*; pub use storage::*; wezterm-blob-leases-0.1.1/src/manager.rs000064400000000000000000000015521046102023000162600ustar 00000000000000use crate::{get_storage, BlobLease, ContentId, Error, LeaseId}; pub struct BlobManager {} impl BlobManager { /// Store data into the store, de-duplicating it and returning /// a BlobLease that can be used to reference and access it. pub fn store(data: &[u8]) -> Result { let storage = get_storage()?; let lease_id = LeaseId::new(); let content_id = ContentId::for_bytes(data); storage.store(content_id, data, lease_id)?; Ok(BlobLease::make_lease(content_id, lease_id)) } /// Attempt to resolve by content id pub fn get_by_content_id(content_id: ContentId) -> Result { let storage = get_storage()?; let lease_id = LeaseId::new(); storage.lease_by_content(content_id, lease_id)?; Ok(BlobLease::make_lease(content_id, lease_id)) } } wezterm-blob-leases-0.1.1/src/simple_tempdir.rs000064400000000000000000000120311046102023000176550ustar 00000000000000#![cfg(feature = "simple_tempdir")] use crate::{BlobStorage, BoxedReader, BufSeekRead, ContentId, Error, LeaseId}; use std::collections::HashMap; use std::fs::File; use std::io::{BufReader, Write}; use std::path::{Path, PathBuf}; use std::sync::Mutex; use tempfile::TempDir; pub struct SimpleTempDir { root: TempDir, refs: Mutex>, } impl SimpleTempDir { pub fn new() -> Result { let root = tempfile::Builder::new() .prefix("wezterm-blob-lease-") .rand_bytes(8) .tempdir()?; Ok(Self { root, refs: Mutex::new(HashMap::new()), }) } pub fn new_in>(path: P) -> Result { let path = path.as_ref(); std::fs::create_dir_all(path)?; let root = tempfile::Builder::new() .prefix("wezterm-blob-lease-") .rand_bytes(8) .tempdir_in(path)?; Ok(Self { root, refs: Mutex::new(HashMap::new()), }) } fn path_for_content(&self, content_id: ContentId) -> Result { let path = self.root.path().join(format!("{content_id}")); std::fs::create_dir_all(path.parent().unwrap()) .map_err(|err| Error::StorageDirIoError(path.clone(), err))?; Ok(path) } fn add_ref(&self, content_id: ContentId) { *self.refs.lock().unwrap().entry(content_id).or_insert(0) += 1; } fn del_ref(&self, content_id: ContentId) { let mut refs = self.refs.lock().unwrap(); match refs.get_mut(&content_id) { Some(count) if *count == 1 => { if let Ok(path) = self.path_for_content(content_id) { if let Err(err) = std::fs::remove_file(&path) { eprintln!("Failed to remove {}: {err:#}", path.display()); } } *count = 0; } Some(count) => { *count -= 1; } None => { // Shouldn't really happen... } } } } impl BlobStorage for SimpleTempDir { fn store(&self, content_id: ContentId, data: &[u8], _lease_id: LeaseId) -> Result<(), Error> { let mut refs = self.refs.lock().unwrap(); let path = self.path_for_content(content_id)?; let mut file = tempfile::Builder::new() .prefix("new-") .rand_bytes(5) .tempfile_in(&self.root.path())?; file.write_all(data)?; file.persist(&path) .map_err(|persist_err| persist_err.error)?; *refs.entry(content_id).or_insert(0) += 1; Ok(()) } fn lease_by_content(&self, content_id: ContentId, _lease_id: LeaseId) -> Result<(), Error> { let _refs = self.refs.lock().unwrap(); let path = self.path_for_content(content_id)?; if path.exists() { self.add_ref(content_id); Ok(()) } else { Err(Error::ContentNotFound(content_id)) } } fn get_data(&self, content_id: ContentId, _lease_id: LeaseId) -> Result, Error> { let _refs = self.refs.lock().unwrap(); let path = self.path_for_content(content_id)?; Ok(std::fs::read(&path).map_err(|err| Error::StorageDirIoError(path, err))?) } fn get_reader(&self, content_id: ContentId, lease_id: LeaseId) -> Result { struct Reader { file: BufReader, content_id: ContentId, lease_id: LeaseId, } impl BufSeekRead for Reader {} impl std::io::BufRead for Reader { fn fill_buf(&mut self) -> std::io::Result<&[u8]> { self.file.fill_buf() } fn consume(&mut self, amount: usize) { self.file.consume(amount) } } impl std::io::Read for Reader { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.file.read(buf) } } impl std::io::Seek for Reader { fn seek(&mut self, whence: std::io::SeekFrom) -> std::io::Result { self.file.seek(whence) } } impl Drop for Reader { fn drop(&mut self) { if let Ok(s) = crate::get_storage() { s.advise_lease_dropped(self.lease_id, self.content_id).ok(); } } } let path = self.path_for_content(content_id)?; let file = BufReader::new(std::fs::File::open(&path)?); self.add_ref(content_id); Ok(Box::new(Reader { file, content_id, lease_id, })) } fn advise_lease_dropped(&self, _lease_id: LeaseId, content_id: ContentId) -> Result<(), Error> { self.del_ref(content_id); Ok(()) } fn advise_of_pid(&self, _pid: u32) -> Result<(), Error> { Ok(()) } fn advise_pid_terminated(&self, _pid: u32) -> Result<(), Error> { Ok(()) } } wezterm-blob-leases-0.1.1/src/storage.rs000064400000000000000000000055651046102023000163220ustar 00000000000000use crate::{ContentId, Error, LeaseId}; use std::io::{BufRead, Seek}; use std::sync::{Arc, Mutex}; static STORAGE: Mutex>> = Mutex::new(None); pub trait BufSeekRead: BufRead + Seek {} pub type BoxedReader = Box; /// Implements the actual storage mechanism for blobs pub trait BlobStorage { /// Store data with the provided content_id. /// lease_id is provided by the caller to identify this store. /// The underlying store is expected to dedup storing data with the same /// content_id. fn store(&self, content_id: ContentId, data: &[u8], lease_id: LeaseId) -> Result<(), Error>; /// Resolve the data associated with content_id. /// If found, establish a lease with the given lease_id. /// If not found, returns Err(Error::ContentNotFound) fn lease_by_content(&self, content_id: ContentId, lease_id: LeaseId) -> Result<(), Error>; /// Retrieves the data identified by content_id. /// lease_id is provided in order to advise the storage system /// which lease fetched it, so that it can choose to record that /// information to track the liveness of a lease fn get_data(&self, content_id: ContentId, lease_id: LeaseId) -> Result, Error>; /// Retrieves the data identified by content_id as a readable+seekable /// buffered handle. /// /// lease_id is provided in order to advise the storage system /// which lease fetched it, so that it can choose to record that /// information to track the liveness of a lease. /// /// The returned handle serves to extend the lifetime of the lease. fn get_reader(&self, content_id: ContentId, lease_id: LeaseId) -> Result; /// Advises the storage manager that a particular lease has been dropped. fn advise_lease_dropped(&self, lease_id: LeaseId, content_id: ContentId) -> Result<(), Error>; /// Advises the storage manager that a given process id is now, or /// continues to be, alive and a valid consumer of the store. fn advise_of_pid(&self, pid: u32) -> Result<(), Error>; /// Advises the storage manager that a given process id is, or will /// very shortly, terminate and will cease to be a valid consumer /// of the store. /// It may choose to do something to invalidate all leases with /// a corresponding pid. fn advise_pid_terminated(&self, pid: u32) -> Result<(), Error>; } pub fn register_storage( storage: Arc, ) -> Result<(), Error> { STORAGE.lock().unwrap().replace(storage); Ok(()) } pub fn get_storage() -> Result, Error> { STORAGE .lock() .unwrap() .as_ref() .map(|s| s.clone()) .ok_or_else(|| Error::StorageNotInit) } pub fn clear_storage() { STORAGE.lock().unwrap().take(); }