feat(android): implement private Rust storage runtime (#90)
Dependency security audit / rustsec (push) Failing after 23s
Dependency security audit / rustsec (push) Failing after 23s
This commit is contained in:
1 parent
180851087b
commit
874c8139a4
36 files changed
+2848
-39
No files matched your search
@@ -4,7 +4,7 @@ use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
error::Error,
|
||||
fmt, fs,
|
||||
io::Write as _,
|
||||
io::{Read as _, Write as _},
|
||||
path::Path,
|
||||
sync::{
|
||||
Mutex,
|
||||
@@ -12,8 +12,8 @@ use std::{
|
||||
},
|
||||
};
|
||||
|
||||
use crate::atomic_file::TempFile;
|
||||
use cap_std::{ambient_authority, fs::Dir};
|
||||
use cap_tempfile::TempFile;
|
||||
use data_encoding::HEXLOWER;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest as _, Sha256};
|
||||
@@ -27,6 +27,7 @@ use crate::{
|
||||
};
|
||||
|
||||
const CACHE_VERSION: u32 = 1;
|
||||
const MAX_CATALOG_BYTES: u64 = 32 * 1024 * 1024;
|
||||
// ponytail: cache writes are rare; use per-cache locks only if profiling shows contention.
|
||||
static CACHE_WRITE_LOCK: Mutex<()> = Mutex::new(());
|
||||
|
||||
@@ -613,7 +614,9 @@ fn inspect_entry(
|
||||
|
||||
fn load_cache(path: &Path, store: &str) -> (Option<CachedCatalog>, Option<String>) {
|
||||
if let Ok(metadata) = fs::symlink_metadata(path)
|
||||
&& (metadata.file_type().is_symlink() || !metadata.is_file())
|
||||
&& (metadata.file_type().is_symlink()
|
||||
|| !metadata.is_file()
|
||||
|| metadata.len() > MAX_CATALOG_BYTES)
|
||||
{
|
||||
return (
|
||||
None,
|
||||
@@ -642,6 +645,59 @@ fn load_cache(path: &Path, store: &str) -> (Option<CachedCatalog>, Option<String
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn validate_private_catalog(
|
||||
root: &Path,
|
||||
path: &Path,
|
||||
) -> Result<bool, crate::mobile_private_storage::PrivateStorageError> {
|
||||
use crate::mobile_private_storage::PrivateStorageError;
|
||||
let _lock = CACHE_WRITE_LOCK
|
||||
.lock()
|
||||
.map_err(|_| PrivateStorageError::Io)?;
|
||||
let metadata = match fs::symlink_metadata(path) {
|
||||
Ok(metadata) => metadata,
|
||||
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(false),
|
||||
Err(error) => return Err(error.into()),
|
||||
};
|
||||
if metadata.file_type().is_symlink() || !metadata.is_file() {
|
||||
return Err(PrivateStorageError::UnsafePath);
|
||||
}
|
||||
let store = store_identity(root, path);
|
||||
let parent = Dir::open_ambient_dir(
|
||||
path.parent().ok_or(PrivateStorageError::UnsafePath)?,
|
||||
ambient_authority(),
|
||||
)?;
|
||||
let name = path.file_name().ok_or(PrivateStorageError::UnsafePath)?;
|
||||
let file = parent.open(name)?;
|
||||
crate::mobile_private_storage::private_permissions(&file, false)?;
|
||||
let mut bytes = Vec::new();
|
||||
if metadata.len() <= MAX_CATALOG_BYTES {
|
||||
file.take(MAX_CATALOG_BYTES + 1).read_to_end(&mut bytes)?;
|
||||
}
|
||||
let source = std::str::from_utf8(&bytes).ok();
|
||||
let valid = metadata.len() <= MAX_CATALOG_BYTES
|
||||
&& bytes.len() as u64 <= MAX_CATALOG_BYTES
|
||||
&& source
|
||||
.and_then(|source| toml::from_str::<CachedCatalog>(source).ok())
|
||||
.is_some_and(|catalog| {
|
||||
catalog.version == CACHE_VERSION
|
||||
&& catalog.store == store
|
||||
&& catalog.entries.iter().all(|(entry, record)| {
|
||||
EntryPath::parse(entry).is_ok()
|
||||
&& record.ciphertext_hash.len() == 64
|
||||
&& record
|
||||
.ciphertext_hash
|
||||
.bytes()
|
||||
.all(|byte| byte.is_ascii_hexdigit())
|
||||
})
|
||||
});
|
||||
if valid {
|
||||
return Ok(false);
|
||||
}
|
||||
parent.remove_file(name)?;
|
||||
parent.open(".")?.sync_all()?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn upsert_cache_record(
|
||||
path: &Path,
|
||||
store: &str,
|
||||
|
||||
Reference in new issue
Block a user