diff options
| -rw-r--r-- | fctdrive/src/main.rs | 33 |
1 files changed, 26 insertions, 7 deletions
diff --git a/fctdrive/src/main.rs b/fctdrive/src/main.rs index 78b3a28..12955bd 100644 --- a/fctdrive/src/main.rs +++ b/fctdrive/src/main.rs | |||
| @@ -3,6 +3,7 @@ use std::{ | |||
| 3 | fmt::Display, | 3 | fmt::Display, |
| 4 | fs::File, | 4 | fs::File, |
| 5 | io::{BufWriter, Read as _, Write}, | 5 | io::{BufWriter, Read as _, Write}, |
| 6 | os::unix::fs::MetadataExt, | ||
| 6 | path::{Path, PathBuf}, | 7 | path::{Path, PathBuf}, |
| 7 | str::FromStr, | 8 | str::FromStr, |
| 8 | sync::{Arc, Mutex}, | 9 | sync::{Arc, Mutex}, |
| @@ -122,6 +123,11 @@ pub fn blob_hash_file(file_path: &Path) -> std::io::Result<BlobId> { | |||
| 122 | Ok(BlobId(hasher.finalize().try_into().unwrap())) | 123 | Ok(BlobId(hasher.finalize().try_into().unwrap())) |
| 123 | } | 124 | } |
| 124 | 125 | ||
| 126 | pub fn blob_size(store: &BlobStore, blob_id: &BlobId) -> std::io::Result<u64> { | ||
| 127 | let blob_path = blob_path(store, blob_id); | ||
| 128 | Ok(blob_path.metadata()?.size()) | ||
| 129 | } | ||
| 130 | |||
| 125 | const OPERATION_KIND_CREATE_FILE: &'static str = "create_file"; | 131 | const OPERATION_KIND_CREATE_FILE: &'static str = "create_file"; |
| 126 | const OPERATION_KIND_CREATE_DIR: &'static str = "create_dir"; | 132 | const OPERATION_KIND_CREATE_DIR: &'static str = "create_dir"; |
| 127 | const OPERATION_KIND_REMOVE: &'static str = "remove"; | 133 | const OPERATION_KIND_REMOVE: &'static str = "remove"; |
| @@ -155,11 +161,13 @@ impl FromStr for Operation { | |||
| 155 | OPERATION_KIND_CREATE_FILE => { | 161 | OPERATION_KIND_CREATE_FILE => { |
| 156 | let path_str = iter.next().ok_or_else(err)?; | 162 | let path_str = iter.next().ok_or_else(err)?; |
| 157 | let blob_str = iter.next().ok_or_else(err)?; | 163 | let blob_str = iter.next().ok_or_else(err)?; |
| 164 | let size_str = iter.next().ok_or_else(err)?; | ||
| 158 | 165 | ||
| 159 | let path = path_str.parse().map_err(|_| err())?; | 166 | let path = path_str.parse().map_err(|_| err())?; |
| 160 | let blob = blob_str.parse().map_err(|_| err())?; | 167 | let blob = blob_str.parse().map_err(|_| err())?; |
| 168 | let size = size_str.parse().map_err(|_| err())?; | ||
| 161 | 169 | ||
| 162 | OperationData::CreateFile(OperationCreateFile { path, blob }) | 170 | OperationData::CreateFile(OperationCreateFile { path, blob, size }) |
| 163 | } | 171 | } |
| 164 | OPERATION_KIND_CREATE_DIR => { | 172 | OPERATION_KIND_CREATE_DIR => { |
| 165 | let path_str = iter.next().ok_or_else(err)?; | 173 | let path_str = iter.next().ok_or_else(err)?; |
| @@ -208,8 +216,8 @@ impl Display for Operation { | |||
| 208 | match &self.data { | 216 | match &self.data { |
| 209 | OperationData::CreateFile(data) => write!( | 217 | OperationData::CreateFile(data) => write!( |
| 210 | f, | 218 | f, |
| 211 | "{}\t{}\t{}", | 219 | "{}\t{}\t{}\t{}", |
| 212 | OPERATION_KIND_CREATE_FILE, data.path, data.blob | 220 | OPERATION_KIND_CREATE_FILE, data.path, data.blob, data.size |
| 213 | ), | 221 | ), |
| 214 | OperationData::CreateDir(data) => { | 222 | OperationData::CreateDir(data) => { |
| 215 | write!(f, "{}\t{}", OPERATION_KIND_CREATE_DIR, data.path) | 223 | write!(f, "{}\t{}", OPERATION_KIND_CREATE_DIR, data.path) |
| @@ -241,6 +249,7 @@ pub enum OperationData { | |||
| 241 | pub struct OperationCreateFile { | 249 | pub struct OperationCreateFile { |
| 242 | pub path: DrivePath, | 250 | pub path: DrivePath, |
| 243 | pub blob: BlobId, | 251 | pub blob: BlobId, |
| 252 | pub size: u64, | ||
| 244 | } | 253 | } |
| 245 | 254 | ||
| 246 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 255 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| @@ -439,6 +448,7 @@ pub struct FsNode { | |||
| 439 | pub lastmod: u64, | 448 | pub lastmod: u64, |
| 440 | pub children: HashMap<DrivePathComponent, FsNodeId>, | 449 | pub children: HashMap<DrivePathComponent, FsNodeId>, |
| 441 | pub blob: BlobId, | 450 | pub blob: BlobId, |
| 451 | pub size: u64, | ||
| 442 | } | 452 | } |
| 443 | 453 | ||
| 444 | slotmap::new_key_type! { pub struct FsNodeId; } | 454 | slotmap::new_key_type! { pub struct FsNodeId; } |
| @@ -457,6 +467,7 @@ impl Default for Fs { | |||
| 457 | lastmod: 0, | 467 | lastmod: 0, |
| 458 | children: Default::default(), | 468 | children: Default::default(), |
| 459 | blob: Default::default(), | 469 | blob: Default::default(), |
| 470 | size: 0, | ||
| 460 | }); | 471 | }); |
| 461 | Self { root, nodes } | 472 | Self { root, nodes } |
| 462 | } | 473 | } |
| @@ -495,6 +506,7 @@ pub fn apply_create_file( | |||
| 495 | lastmod: header.timestamp, | 506 | lastmod: header.timestamp, |
| 496 | children: Default::default(), | 507 | children: Default::default(), |
| 497 | blob: Default::default(), | 508 | blob: Default::default(), |
| 509 | size: 0, | ||
| 498 | }); | 510 | }); |
| 499 | fs.nodes[parent_id].children.insert(comp.clone(), id); | 511 | fs.nodes[parent_id].children.insert(comp.clone(), id); |
| 500 | id | 512 | id |
| @@ -528,6 +540,7 @@ pub fn apply_create_file( | |||
| 528 | lastmod: header.timestamp, | 540 | lastmod: header.timestamp, |
| 529 | children: Default::default(), | 541 | children: Default::default(), |
| 530 | blob: data.blob.clone(), | 542 | blob: data.blob.clone(), |
| 543 | size: data.size, | ||
| 531 | }); | 544 | }); |
| 532 | fs.nodes[parent_id].children.insert(name, id); | 545 | fs.nodes[parent_id].children.insert(name, id); |
| 533 | } | 546 | } |
| @@ -563,6 +576,7 @@ pub fn apply_create_dir( | |||
| 563 | lastmod: header.timestamp, | 576 | lastmod: header.timestamp, |
| 564 | children: Default::default(), | 577 | children: Default::default(), |
| 565 | blob: Default::default(), | 578 | blob: Default::default(), |
| 579 | size: 0, | ||
| 566 | }); | 580 | }); |
| 567 | fs.nodes[parent_id].children.insert(comp.clone(), id); | 581 | fs.nodes[parent_id].children.insert(comp.clone(), id); |
| 568 | id | 582 | id |
| @@ -587,6 +601,7 @@ pub fn apply_create_dir( | |||
| 587 | lastmod: header.timestamp, | 601 | lastmod: header.timestamp, |
| 588 | children: Default::default(), | 602 | children: Default::default(), |
| 589 | blob: Default::default(), | 603 | blob: Default::default(), |
| 604 | size: 0, | ||
| 590 | }); | 605 | }); |
| 591 | 606 | ||
| 592 | let parent = &mut fs.nodes[parent_id]; | 607 | let parent = &mut fs.nodes[parent_id]; |
| @@ -780,12 +795,13 @@ fn main() { | |||
| 780 | } | 795 | } |
| 781 | 796 | ||
| 782 | fn cmd_create_file(args: CreateFileArgs) { | 797 | fn cmd_create_file(args: CreateFileArgs) { |
| 798 | let store = common_create_blob_store(&args.common); | ||
| 783 | let file_blob_id = blob_hash_file(&args.file).unwrap(); | 799 | let file_blob_id = blob_hash_file(&args.file).unwrap(); |
| 800 | let file_blob_size = blob_size(&store, &file_blob_id).unwrap(); | ||
| 784 | 801 | ||
| 785 | let _lock = common_write_lock(&args.common); | 802 | let _lock = common_write_lock(&args.common); |
| 786 | let mut fs = Fs::default(); | 803 | let mut fs = Fs::default(); |
| 787 | let mut ops = common_read_log_file(&args.common); | 804 | let mut ops = common_read_log_file(&args.common); |
| 788 | let store = common_create_blob_store(&args.common); | ||
| 789 | ops.iter().for_each(|op| apply(&mut fs, op).unwrap()); | 805 | ops.iter().for_each(|op| apply(&mut fs, op).unwrap()); |
| 790 | 806 | ||
| 791 | let timestamp = args.timestamp.unwrap_or_else(get_timestamp); | 807 | let timestamp = args.timestamp.unwrap_or_else(get_timestamp); |
| @@ -801,6 +817,7 @@ fn cmd_create_file(args: CreateFileArgs) { | |||
| 801 | data: OperationData::CreateFile(OperationCreateFile { | 817 | data: OperationData::CreateFile(OperationCreateFile { |
| 802 | path: args.path, | 818 | path: args.path, |
| 803 | blob: file_blob_id, | 819 | blob: file_blob_id, |
| 820 | size: file_blob_size, | ||
| 804 | }), | 821 | }), |
| 805 | }; | 822 | }; |
| 806 | apply(&mut fs, &new_op).unwrap(); | 823 | apply(&mut fs, &new_op).unwrap(); |
| @@ -917,15 +934,15 @@ fn write_node( | |||
| 917 | FsNodeKind::File => { | 934 | FsNodeKind::File => { |
| 918 | writeln!( | 935 | writeln!( |
| 919 | writer, | 936 | writer, |
| 920 | "{}\tfile\t{}\t{}\t{}", | 937 | "{}\tfile\t{}\t{}\t{}\t{}", |
| 921 | path, node.lastmod, node.blob, node.author | 938 | path, node.lastmod, node.blob, node.size, node.author |
| 922 | ) | 939 | ) |
| 923 | .unwrap(); | 940 | .unwrap(); |
| 924 | } | 941 | } |
| 925 | FsNodeKind::Directory => { | 942 | FsNodeKind::Directory => { |
| 926 | writeln!( | 943 | writeln!( |
| 927 | writer, | 944 | writer, |
| 928 | "{}\tdir\t{}\t-\t{}", | 945 | "{}\tdir\t{}\t-\t-\t{}", |
| 929 | path, node.lastmod, node.author | 946 | path, node.lastmod, node.author |
| 930 | ) | 947 | ) |
| 931 | .unwrap(); | 948 | .unwrap(); |
| @@ -988,6 +1005,7 @@ fn cmd_import(args: ImportArgs) { | |||
| 988 | let drive_path = rel.to_str().unwrap().parse::<DrivePath>().unwrap(); | 1005 | let drive_path = rel.to_str().unwrap().parse::<DrivePath>().unwrap(); |
| 989 | let blob_id = blob_hash_file(&file).unwrap(); | 1006 | let blob_id = blob_hash_file(&file).unwrap(); |
| 990 | blob_import_file(&store, ImportMode::HardLink, &blob_id, &file).unwrap(); | 1007 | blob_import_file(&store, ImportMode::HardLink, &blob_id, &file).unwrap(); |
| 1008 | let blob_size = blob_size(&store, &blob_id).unwrap(); | ||
| 991 | let op = Operation { | 1009 | let op = Operation { |
| 992 | header: OperationHeader { | 1010 | header: OperationHeader { |
| 993 | timestamp, | 1011 | timestamp, |
| @@ -997,6 +1015,7 @@ fn cmd_import(args: ImportArgs) { | |||
| 997 | data: OperationData::CreateFile(OperationCreateFile { | 1015 | data: OperationData::CreateFile(OperationCreateFile { |
| 998 | path: drive_path, | 1016 | path: drive_path, |
| 999 | blob: blob_id, | 1017 | blob: blob_id, |
| 1018 | size: blob_size, | ||
| 1000 | }), | 1019 | }), |
| 1001 | }; | 1020 | }; |
| 1002 | ops.push(op); | 1021 | ops.push(op); |
