Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 89 additions & 55 deletions client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use common::{
CompressionLevel, FileEntryData, RawEntryData, SourceFileEntryData, HEADER,
},
object_body::Object as OtherObject,
primitives::{FileMetadata, Timestamp, RWX},
read_header_and_body, read_header_from_file, read_header_from_slice,
read_object_into_headers_sync, Hash, Header, Mode, ObjectType, BLOB_KEY, INDEX_KEY, TREE_KEY,
};
Expand Down Expand Up @@ -37,23 +38,6 @@ impl<T: Object> Deref for Hashed<T> {
}

impl<T: Object> Hashed<T> {
// fn from_hash(cache: &PathBuf, hash: Hash) -> Self {
// let (dir, file) = hash.get_parts();

// let file_path = cache.join(dir).join(file);

// assert!(file_path.exists());

// let reader = T::read_file_and_verify_type(&file_path);

// drop(reader);

// Self {
// inner: T::from_file(cache, &file_path),
// hash,
// }
// }

fn from_object(value: T) -> Self {
Self {
hash: value.get_hash(),
Expand All @@ -67,30 +51,6 @@ trait Object {
fn get_hash(&self) -> Hash;
fn get_prefix(&self) -> String;
fn write_to(&self, path: &Path);

// fn from_file(cache: &PathBuf, file: &PathBuf) -> Self;

// fn read_file_and_verify_type(path: &PathBuf) -> BufReader<File> {
// let f = File::open(file_path).unwrap();
// let mut reader = BufReader::new(f);

// let mut data = Vec::new();
// reader.read_until(0, &mut data);

// if data.last() == Some(&0) {
// data.pop();
// }

// let name = String::from_utf8(data).unwrap();

// let (typ, size) = name.split_once(' ').unwrap();

// let object_type = ObjectType::from_str(typ);

// assert!(object_type == T::get_object_type());

// reader
// }
}

struct CacheObject<'a> {
Expand Down Expand Up @@ -202,6 +162,17 @@ impl<'a> CacheObject<'a> {

let mode = Mode::from_str(mode).expect("valid mode");

let mut timestamp: [u8; 8] = [0; 8];
file.read_exact(&mut timestamp)
.expect("file to contain timestamp");

let timestamp = Timestamp::from(timestamp);

let mut metadata = [0u8; 1];
file.read_exact(&mut metadata)
.expect("file to contain metadata");
let metadata = FileMetadata::from_u8(metadata[0]);

let mut hash: [u8; 32] = [0; 32];
file.read_exact(&mut hash).expect("file to contain hash");

Expand All @@ -212,7 +183,9 @@ impl<'a> CacheObject<'a> {
let cache_object = CacheObject::from_file(self.cache, &object_file);

vec.push(match cache_object.object_type {
ObjectType::Blob => TreeObject::Blob(cache_object.to_blob(mode, name)),
ObjectType::Blob => {
TreeObject::Blob(cache_object.to_blob(mode, timestamp, metadata, name))
}
ObjectType::Tree => TreeObject::Tree(cache_object.to_tree(mode, name)),
ObjectType::Index => panic!("Invalid ObjectType in tree"),
})
Expand All @@ -228,7 +201,13 @@ impl<'a> CacheObject<'a> {
}
}

fn to_blob(&self, mode: Mode, path: &str) -> Hashed<Blob> {
fn to_blob(
&self,
mode: Mode,
timestamp: Timestamp,
metadata: FileMetadata,
path: &str,
) -> Hashed<Blob> {
assert!(self.object_type == ObjectType::Blob);

let file = File::open(&self.file).unwrap();
Expand All @@ -246,6 +225,8 @@ impl<'a> CacheObject<'a> {
path: path.to_string(),
file: self.file.clone(),
size,
timestamp,
metadata,
},
}
}
Expand Down Expand Up @@ -346,6 +327,8 @@ impl Object for Index {

trait WithPath {
fn get_path_component(&self) -> &String;
fn get_metadata(&self) -> &FileMetadata;
fn get_timestamp(&self) -> &Timestamp;
fn get_mode(&self) -> &Mode;
}

Expand Down Expand Up @@ -408,6 +391,20 @@ impl Tree {
}
}

const ZERO_TIMESTAMP: Timestamp = Timestamp::from_nanos(0);
const ZERO_METADATA: FileMetadata = FileMetadata {
user_permissions: RWX {
read: false,
write: false,
execute: false,
},
other_permissions: RWX {
read: false,
write: false,
execute: false,
},
hidden_flag: false,
};
impl WithPath for Tree {
fn get_path_component(&self) -> &String {
&self.path
Expand All @@ -416,6 +413,15 @@ impl WithPath for Tree {
fn get_mode(&self) -> &Mode {
&self.mode
}

fn get_metadata(&self) -> &FileMetadata {
// Trees don't have metadata but we need to return something so we can encode the hidden flag
&ZERO_METADATA
}

fn get_timestamp(&self) -> &Timestamp {
&ZERO_TIMESTAMP
}
}

impl Object for Tree {
Expand Down Expand Up @@ -459,6 +465,8 @@ impl Object for Tree {
#[derive(Debug)]
struct Blob {
mode: Mode,
timestamp: Timestamp,
metadata: FileMetadata,
path: String,
file: PathBuf,
size: u64,
Expand All @@ -474,6 +482,9 @@ impl Blob {
path: path.file_name().unwrap().to_string_lossy().to_string(),
size: path.metadata().unwrap().len(),
file: path.to_path_buf(),
timestamp: Timestamp::from_system_time(path.metadata().unwrap().modified().unwrap())
.unwrap(),
metadata: FileMetadata::from(path.metadata().unwrap()),
}
}

Expand All @@ -484,7 +495,10 @@ impl Blob {
fn hash_and_write(src: &Path, cache: Option<&Path>) -> Hashed<Self> {
assert!(src.is_file());

let size = src.metadata().unwrap().len();
let metadata = src.metadata().unwrap();
let size = metadata.len();
let timestamp: Timestamp = metadata.modified().unwrap().try_into().unwrap();
let metadata = FileMetadata::from(metadata);
let prefix = format!("{} {}\0", BLOB_KEY, size);

let mut hasher = Sha256::new();
Expand Down Expand Up @@ -521,6 +535,8 @@ impl Blob {
mode: Mode::Normal,
path: src.file_name().unwrap().to_string_lossy().to_string(),
file: src.to_path_buf(),
timestamp,
metadata,
size,
},
}
Expand All @@ -535,6 +551,14 @@ impl WithPath for Blob {
fn get_mode(&self) -> &Mode {
&self.mode
}

fn get_metadata(&self) -> &FileMetadata {
&self.metadata
}

fn get_timestamp(&self) -> &Timestamp {
&self.timestamp
}
}

impl Object for Blob {
Expand Down Expand Up @@ -607,6 +631,8 @@ fn get_bytes_from_thing<T: WithPath>(object: &T, hash: &Hash) -> Vec<u8> {
path.push(b' ');
path.extend_from_slice(object.get_path_component().as_bytes());
path.push(0);
path.extend_from_slice(&object.get_timestamp().as_bytes());
path.push(object.get_metadata().to_u8());
path.extend_from_slice(&hash.hash);

path
Expand Down Expand Up @@ -728,20 +754,30 @@ fn write_tree(tree: &Tree, path: &Path) {
let blob_path = path.join(&blob.path);

let file = File::create(blob_path).expect("File to be created");
let mut writer = BufWriter::new(file);
{
let mut writer = BufWriter::new(file.try_clone().unwrap());

let cache_file = File::open(&blob.file).unwrap();
let mut reader = BufReader::new(cache_file);
let cache_file = File::open(&blob.file).unwrap();
let mut reader = BufReader::new(cache_file);

let _ = read_header_from_file(&mut reader);
let _ = read_header_from_file(&mut reader);

let mut data: [u8; 1024] = [0; 1024];
while let Ok(num) = reader.read(&mut data) {
if num == 0 {
break;
let mut data: [u8; 1024] = [0; 1024];
while let Ok(num) = reader.read(&mut data) {
if num == 0 {
break;
}
writer.write_all(&data[..num]).unwrap();
}
writer.write_all(&data[..num]).unwrap();
}

let mut permissions = file.metadata().unwrap().permissions();

blob.metadata.modify_permissions(&mut permissions);

file.set_permissions(permissions).unwrap();

file.set_modified(blob.timestamp.into()).unwrap();
}
}

Expand Down Expand Up @@ -1228,7 +1264,6 @@ enum Commands {
},

Restore {
#[arg(short, long)]
directory: PathBuf,
#[arg(short, long)]
index: Hash,
Expand All @@ -1237,7 +1272,6 @@ enum Commands {
},

Cat {
#[arg(long)]
hash: Hash,
},

Expand Down
2 changes: 1 addition & 1 deletion common/src/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ impl CompressionLevel {
&self,
algorithm: CompressionAlgorithm,
) -> Result<i32, anyhow::Error> {
// matrix of compression levels for each algorithm. The first dimension is the algorithm, the second dimension is the level (0-3)
// matrix of compression levels for each algorithm. The first dimension is the algorithm, the second dimension is the level
const LEVELS: [[i32; 3]; 4] = [
[0, 0, 0], // None
[3, 6, 15], // Zstd
Expand Down
4 changes: 4 additions & 0 deletions common/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ impl Hash {
&self.hash_string
}

pub fn as_path(&self) -> String {
format!("{}/{}", &self.hash_string[..2], &self.hash_string[2..])
}

pub fn from_string(value: &str) -> Option<Self> {
if value.len() != 64 {
return None;
Expand Down
22 changes: 21 additions & 1 deletion common/src/object_body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use std::{collections::HashMap, io::Write, str::from_utf8};

use chrono::{DateTime, Utc};

use crate::{Hash, Mode};
use crate::{
primitives::{FileMetadata, Timestamp},
Hash, Mode,
};

pub trait Object {
fn from_data(data: &[u8]) -> Self;
Expand Down Expand Up @@ -107,13 +110,16 @@ impl Object for Index {
pub struct TreeEntry {
pub mode: Mode,
pub path: String,
pub timestamp: Timestamp,
pub metadata: FileMetadata,
pub hash: Hash,
}

#[derive(Debug)]
pub struct Tree {
pub contents: Vec<TreeEntry>,
}

impl Object for Tree {
fn from_data(data: &[u8]) -> Self {
let mut contents = Vec::new();
Expand All @@ -138,11 +144,23 @@ impl Object for Tree {
.expect("mode and filename to be seperated by space");
let mode = Mode::from_str(mode).expect("valid mode");

let timestamp: [u8; 8] = data[position..position + 8]
.try_into()
.expect("Slice with incorrect length");

let metadata_byte = data[position + 8];
let metadata = FileMetadata::from_u8(metadata_byte);

let position = position + 9;

let hash =
Hash::try_from(&remaining[position..position + 64]).expect("Hash to be valid");

contents.push(TreeEntry {
hash,
mode,
timestamp: timestamp.into(),
metadata,
path: name.to_string(),
});

Expand All @@ -160,6 +178,8 @@ impl Object for Tree {
data.push(b' ');
data.write_all(entry.path.as_bytes())?;
data.push(0);
data.write_all(&entry.timestamp.as_bytes())?;
data.push(entry.metadata.to_u8());
data.write_all(&entry.hash.hash)?;

Ok(())
Expand Down
Loading
Loading