Skip to content

Commit c2d767d

Browse files
committed
Added Metadata and timestamp support
1 parent 4e75301 commit c2d767d

4 files changed

Lines changed: 315 additions & 56 deletions

File tree

‎client/src/main.rs‎

Lines changed: 89 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use common::{
77
CompressionLevel, FileEntryData, RawEntryData, SourceFileEntryData, HEADER,
88
},
99
object_body::Object as OtherObject,
10+
primitives::{FileMetadata, Timestamp, RWX},
1011
read_header_and_body, read_header_from_file, read_header_from_slice,
1112
read_object_into_headers_sync, Hash, Header, Mode, ObjectType, BLOB_KEY, INDEX_KEY, TREE_KEY,
1213
};
@@ -37,23 +38,6 @@ impl<T: Object> Deref for Hashed<T> {
3738
}
3839

3940
impl<T: Object> Hashed<T> {
40-
// fn from_hash(cache: &PathBuf, hash: Hash) -> Self {
41-
// let (dir, file) = hash.get_parts();
42-
43-
// let file_path = cache.join(dir).join(file);
44-
45-
// assert!(file_path.exists());
46-
47-
// let reader = T::read_file_and_verify_type(&file_path);
48-
49-
// drop(reader);
50-
51-
// Self {
52-
// inner: T::from_file(cache, &file_path),
53-
// hash,
54-
// }
55-
// }
56-
5741
fn from_object(value: T) -> Self {
5842
Self {
5943
hash: value.get_hash(),
@@ -67,30 +51,6 @@ trait Object {
6751
fn get_hash(&self) -> Hash;
6852
fn get_prefix(&self) -> String;
6953
fn write_to(&self, path: &Path);
70-
71-
// fn from_file(cache: &PathBuf, file: &PathBuf) -> Self;
72-
73-
// fn read_file_and_verify_type(path: &PathBuf) -> BufReader<File> {
74-
// let f = File::open(file_path).unwrap();
75-
// let mut reader = BufReader::new(f);
76-
77-
// let mut data = Vec::new();
78-
// reader.read_until(0, &mut data);
79-
80-
// if data.last() == Some(&0) {
81-
// data.pop();
82-
// }
83-
84-
// let name = String::from_utf8(data).unwrap();
85-
86-
// let (typ, size) = name.split_once(' ').unwrap();
87-
88-
// let object_type = ObjectType::from_str(typ);
89-
90-
// assert!(object_type == T::get_object_type());
91-
92-
// reader
93-
// }
9454
}
9555

9656
struct CacheObject<'a> {
@@ -202,6 +162,17 @@ impl<'a> CacheObject<'a> {
202162

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

165+
let mut timestamp: [u8; 8] = [0; 8];
166+
file.read_exact(&mut timestamp)
167+
.expect("file to contain timestamp");
168+
169+
let timestamp = Timestamp::from(timestamp);
170+
171+
let mut metadata = [0u8; 1];
172+
file.read_exact(&mut metadata)
173+
.expect("file to contain metadata");
174+
let metadata = FileMetadata::from_u8(metadata[0]);
175+
205176
let mut hash: [u8; 32] = [0; 32];
206177
file.read_exact(&mut hash).expect("file to contain hash");
207178

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

214185
vec.push(match cache_object.object_type {
215-
ObjectType::Blob => TreeObject::Blob(cache_object.to_blob(mode, name)),
186+
ObjectType::Blob => {
187+
TreeObject::Blob(cache_object.to_blob(mode, timestamp, metadata, name))
188+
}
216189
ObjectType::Tree => TreeObject::Tree(cache_object.to_tree(mode, name)),
217190
ObjectType::Index => panic!("Invalid ObjectType in tree"),
218191
})
@@ -228,7 +201,13 @@ impl<'a> CacheObject<'a> {
228201
}
229202
}
230203

231-
fn to_blob(&self, mode: Mode, path: &str) -> Hashed<Blob> {
204+
fn to_blob(
205+
&self,
206+
mode: Mode,
207+
timestamp: Timestamp,
208+
metadata: FileMetadata,
209+
path: &str,
210+
) -> Hashed<Blob> {
232211
assert!(self.object_type == ObjectType::Blob);
233212

234213
let file = File::open(&self.file).unwrap();
@@ -246,6 +225,8 @@ impl<'a> CacheObject<'a> {
246225
path: path.to_string(),
247226
file: self.file.clone(),
248227
size,
228+
timestamp,
229+
metadata,
249230
},
250231
}
251232
}
@@ -346,6 +327,8 @@ impl Object for Index {
346327

347328
trait WithPath {
348329
fn get_path_component(&self) -> &String;
330+
fn get_metadata(&self) -> &FileMetadata;
331+
fn get_timestamp(&self) -> &Timestamp;
349332
fn get_mode(&self) -> &Mode;
350333
}
351334

@@ -408,6 +391,20 @@ impl Tree {
408391
}
409392
}
410393

394+
const ZERO_TIMESTAMP: Timestamp = Timestamp::from_nanos(0);
395+
const ZERO_METADATA: FileMetadata = FileMetadata {
396+
user_permissions: RWX {
397+
read: false,
398+
write: false,
399+
execute: false,
400+
},
401+
other_permissions: RWX {
402+
read: false,
403+
write: false,
404+
execute: false,
405+
},
406+
hidden_flag: false,
407+
};
411408
impl WithPath for Tree {
412409
fn get_path_component(&self) -> &String {
413410
&self.path
@@ -416,6 +413,15 @@ impl WithPath for Tree {
416413
fn get_mode(&self) -> &Mode {
417414
&self.mode
418415
}
416+
417+
fn get_metadata(&self) -> &FileMetadata {
418+
// Trees don't have metadata but we need to return something so we can encode the hidden flag
419+
&ZERO_METADATA
420+
}
421+
422+
fn get_timestamp(&self) -> &Timestamp {
423+
&ZERO_TIMESTAMP
424+
}
419425
}
420426

421427
impl Object for Tree {
@@ -459,6 +465,8 @@ impl Object for Tree {
459465
#[derive(Debug)]
460466
struct Blob {
461467
mode: Mode,
468+
timestamp: Timestamp,
469+
metadata: FileMetadata,
462470
path: String,
463471
file: PathBuf,
464472
size: u64,
@@ -474,6 +482,9 @@ impl Blob {
474482
path: path.file_name().unwrap().to_string_lossy().to_string(),
475483
size: path.metadata().unwrap().len(),
476484
file: path.to_path_buf(),
485+
timestamp: Timestamp::from_system_time(path.metadata().unwrap().modified().unwrap())
486+
.unwrap(),
487+
metadata: FileMetadata::from(path.metadata().unwrap()),
477488
}
478489
}
479490

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

487-
let size = src.metadata().unwrap().len();
498+
let metadata = src.metadata().unwrap();
499+
let size = metadata.len();
500+
let timestamp: Timestamp = metadata.modified().unwrap().try_into().unwrap();
501+
let metadata = FileMetadata::from(metadata);
488502
let prefix = format!("{} {}\0", BLOB_KEY, size);
489503

490504
let mut hasher = Sha256::new();
@@ -521,6 +535,8 @@ impl Blob {
521535
mode: Mode::Normal,
522536
path: src.file_name().unwrap().to_string_lossy().to_string(),
523537
file: src.to_path_buf(),
538+
timestamp,
539+
metadata,
524540
size,
525541
},
526542
}
@@ -535,6 +551,14 @@ impl WithPath for Blob {
535551
fn get_mode(&self) -> &Mode {
536552
&self.mode
537553
}
554+
555+
fn get_metadata(&self) -> &FileMetadata {
556+
&self.metadata
557+
}
558+
559+
fn get_timestamp(&self) -> &Timestamp {
560+
&self.timestamp
561+
}
538562
}
539563

540564
impl Object for Blob {
@@ -607,6 +631,8 @@ fn get_bytes_from_thing<T: WithPath>(object: &T, hash: &Hash) -> Vec<u8> {
607631
path.push(b' ');
608632
path.extend_from_slice(object.get_path_component().as_bytes());
609633
path.push(0);
634+
path.extend_from_slice(&object.get_timestamp().as_bytes());
635+
path.push(object.get_metadata().to_u8());
610636
path.extend_from_slice(&hash.hash);
611637

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

730756
let file = File::create(blob_path).expect("File to be created");
731-
let mut writer = BufWriter::new(file);
757+
{
758+
let mut writer = BufWriter::new(file.try_clone().unwrap());
732759

733-
let cache_file = File::open(&blob.file).unwrap();
734-
let mut reader = BufReader::new(cache_file);
760+
let cache_file = File::open(&blob.file).unwrap();
761+
let mut reader = BufReader::new(cache_file);
735762

736-
let _ = read_header_from_file(&mut reader);
763+
let _ = read_header_from_file(&mut reader);
737764

738-
let mut data: [u8; 1024] = [0; 1024];
739-
while let Ok(num) = reader.read(&mut data) {
740-
if num == 0 {
741-
break;
765+
let mut data: [u8; 1024] = [0; 1024];
766+
while let Ok(num) = reader.read(&mut data) {
767+
if num == 0 {
768+
break;
769+
}
770+
writer.write_all(&data[..num]).unwrap();
742771
}
743-
writer.write_all(&data[..num]).unwrap();
744772
}
773+
774+
let mut permissions = file.metadata().unwrap().permissions();
775+
776+
blob.metadata.modify_permissions(&mut permissions);
777+
778+
file.set_permissions(permissions).unwrap();
779+
780+
file.set_modified(blob.timestamp.into()).unwrap();
745781
}
746782
}
747783

‎common/src/archive.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ impl CompressionLevel {
8585
&self,
8686
algorithm: CompressionAlgorithm,
8787
) -> Result<i32, anyhow::Error> {
88-
// matrix of compression levels for each algorithm. The first dimension is the algorithm, the second dimension is the level (0-3)
88+
// matrix of compression levels for each algorithm. The first dimension is the algorithm, the second dimension is the level
8989
const LEVELS: [[i32; 3]; 4] = [
9090
[0, 0, 0], // None
9191
[3, 6, 15], // Zstd

‎common/src/object_body.rs‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use std::{collections::HashMap, io::Write, str::from_utf8};
22

33
use chrono::{DateTime, Utc};
44

5-
use crate::{Hash, Mode};
5+
use crate::{
6+
primitives::{FileMetadata, Timestamp},
7+
Hash, Mode,
8+
};
69

710
pub trait Object {
811
fn from_data(data: &[u8]) -> Self;
@@ -107,13 +110,16 @@ impl Object for Index {
107110
pub struct TreeEntry {
108111
pub mode: Mode,
109112
pub path: String,
113+
pub timestamp: Timestamp,
114+
pub metadata: FileMetadata,
110115
pub hash: Hash,
111116
}
112117

113118
#[derive(Debug)]
114119
pub struct Tree {
115120
pub contents: Vec<TreeEntry>,
116121
}
122+
117123
impl Object for Tree {
118124
fn from_data(data: &[u8]) -> Self {
119125
let mut contents = Vec::new();
@@ -138,11 +144,23 @@ impl Object for Tree {
138144
.expect("mode and filename to be seperated by space");
139145
let mode = Mode::from_str(mode).expect("valid mode");
140146

147+
let timestamp: [u8; 8] = data[position..position + 8]
148+
.try_into()
149+
.expect("Slice with incorrect length");
150+
151+
let metadata_byte = data[position + 8];
152+
let metadata = FileMetadata::from_u8(metadata_byte);
153+
154+
let position = position + 9;
155+
141156
let hash =
142157
Hash::try_from(&remaining[position..position + 64]).expect("Hash to be valid");
158+
143159
contents.push(TreeEntry {
144160
hash,
145161
mode,
162+
timestamp: timestamp.into(),
163+
metadata,
146164
path: name.to_string(),
147165
});
148166

@@ -160,6 +178,8 @@ impl Object for Tree {
160178
data.push(b' ');
161179
data.write_all(entry.path.as_bytes())?;
162180
data.push(0);
181+
data.write_all(&entry.timestamp.as_bytes())?;
182+
data.push(entry.metadata.to_u8());
163183
data.write_all(&entry.hash.hash)?;
164184

165185
Ok(())

0 commit comments

Comments
 (0)