wala-rust

Unnamed repository; edit this file 'description' to name the repository.
Info | Log | Files | Refs | README | LICENSE

commit 9fb1e7527dc594f9fba05ebcecb56805b8f92319
parent d4b456f26e2310f3f842e89451df23332e5e6361
Author: lash <dev@holbrook.no>
Date:   Tue, 21 Jun 2022 15:51:07 +0000

Complete path checks for immutable and mutable put

Diffstat:
Asrc/record.rs | 248+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 248 insertions(+), 0 deletions(-)

diff --git a/src/record.rs b/src/record.rs @@ -0,0 +1,248 @@ +use std::str::FromStr; +use std::io; +use std::convert::Infallible; +use std::fs::File; +use std::io::{ + Write, + Read, +}; +use std::os::unix::fs::symlink; +use std::path::{ + PathBuf, + Path, +}; +use std::fs::copy as fs_copy; +use std::error::Error; +use sha2::{Sha256, Digest}; +use std::fmt; + +use crate::auth::AuthResult; + +use tempfile::NamedTempFile; + +use log::{debug, info, error}; + +#[derive(Debug)] +pub enum RequestErrorType { + ReadError, + WriteError, + AuthError, + FormatError, +} + +#[derive(Debug)] +pub struct RequestError { + pub typ: RequestErrorType, + pub v: Option<String>, +} + +impl fmt::Display for RequestError { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(self.description()) + } +} + +impl Error for RequestError { + fn description(&self) -> &str { + match &self.v { + Some(v) => { + return v.as_str(); + }, + None => { + }, + } + "" + } +} + +pub struct Record { + pub digest: Vec<u8>, + pub path: PathBuf, +} + +pub struct ResourceKey { + v: Vec<u8>, +} + +impl FromStr for ResourceKey { + type Err = Infallible; + + fn from_str(s: &str) -> Result<ResourceKey, Infallible> { + let mut h = Sha256::new(); + h.update(&s[..]); + let k = ResourceKey{ + v: h.finalize().to_vec(), + }; + Ok(k) + } +} + +impl ResourceKey { + pub fn pointer_for(&self, subject: &AuthResult) -> Vec<u8> { + let mut h = Sha256::new(); + h.update(&self.v); + h.update(&subject.identity); + h.finalize().to_vec() + } +} + + +pub fn put_immutable(path: &Path, mut f: impl Read, expected_size: usize) -> Result<Record, RequestError> { + let z: Vec<u8>; + let hash: String; + let mut total_size: usize = 0; + let tempfile = match NamedTempFile::new() { + Ok(of) => { +// debug!("writing to tempfile {:?} expected size {}", of.path(), expected_size); + debug!("writing to tempfile {:?}", of.path()); + let mut buf: [u8; 65535] = [0; 65535]; + let mut h = Sha256::new(); + loop { + match f.read(&mut buf[..]) { + Ok(v) => { + if v == 0 { + break; + } + total_size += v; + let data = &buf[..v]; + h.update(data); + of.as_file().write(data); + }, + Err(e) => { + error!("cannot read from request body: {}", e); + let err = RequestError{ + typ: RequestErrorType::ReadError, + v: None, + }; + return Err(err); + }, + } + } + + if expected_size > 0 { + if expected_size != total_size { + let err = RequestError{ + typ: RequestErrorType::ReadError, + v: None, + }; + return Err(err); + } + } + + z = h.finalize().to_vec(); + hash = hex::encode(&z); + info!("have hash {} for content", hash); + of + }, + Err(e) => { + let err = RequestError{ + typ: RequestErrorType::WriteError, + v: None, + }; + return Err(err); + } + }; + + let final_path_buf = path.join(&hash); + let final_path = final_path_buf.as_path(); + fs_copy(tempfile.path(), final_path); + + let r = Record{ + digest: z, + path: final_path_buf, + }; + Ok(r) +} + +pub fn put_mutable(pointer: Vec<u8>, path: &Path, mut f: impl Read, expected_size: usize) -> Result<Record, RequestError> { + let record = put_immutable(path, f, expected_size); + + let mutable_ref = hex::encode(&pointer); + let link_path_buf = path.join(&mutable_ref); + match record { + Ok(v) => { + symlink(&v.path, &link_path_buf); + let r = Record{ + digest: pointer, + path: link_path_buf.clone(), + }; + return Ok(r); + }, + Err(e) => { + return Err(e); + } + } +} + +#[cfg(test)] +mod tests { + use super::ResourceKey; + use super::AuthResult; + use super::{ + put_immutable, + put_mutable, + }; + use std::fs::read; + use tempfile::tempdir; + use hex; + + use env_logger; + use log::{debug, info, error}; + + #[test] + fn test_pointer() { + let resource = ResourceKey{ + v: vec!(0x66, 0x6f, 0x6f), + }; + let subject = AuthResult{ + identity: vec!(0x62, 0x61, 0x72), + }; + let r = resource.pointer_for(&subject); + + let foobar_digest = hex::decode("c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2").unwrap(); + assert_eq!(r, foobar_digest); + } + + #[test] + fn test_immutable() { + let d = tempdir().unwrap(); + let b = b"foo"; + put_immutable(d.path(), &b[..], 3); + + let immutable_path_buf = d.path().join("2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"); + let immutable_path = immutable_path_buf.as_path(); + debug!(">>>>> checking immutable path {:?}", immutable_path); + assert!(immutable_path.is_file()); + + let mut r = read(immutable_path).unwrap(); + assert_eq!(r, b.to_vec()); + + } + + #[test] + fn test_mutable() { + env_logger::init(); + + let d = tempdir().unwrap(); + let b = b"foo"; + let ptr = b"foobar"; + put_mutable(ptr.to_vec(), d.path(), &b[..], 3); + + let foobar_hex = hex::encode(ptr); + let mutable_path_buf = d.path().join(foobar_hex); + let mutable_path = mutable_path_buf.as_path(); + debug!(">>>>> checking mutable path {:?}", mutable_path); + assert!(mutable_path.is_symlink()); + + let mut r = read(mutable_path).unwrap(); + assert_eq!(r, b.to_vec()); + + let immutable_path_buf = d.path().join("2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"); + let immutable_path = immutable_path_buf.as_path(); + debug!(">>>>> checking immutable path {:?}", immutable_path); + assert!(immutable_path.is_file()); + + let mut r = read(immutable_path).unwrap(); + assert_eq!(r, b.to_vec()); + } +}