Skip to content

Commit 04c71e6

Browse files
authored
feat: calculate digest of the persistent cache piece to check the integrity of the metadata (#980)
Signed-off-by: Gaius <[email protected]>
1 parent 32c99b7 commit 04c71e6

File tree

6 files changed

+55
-36
lines changed

6 files changed

+55
-36
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ dragonfly-client-backend = { path = "dragonfly-client-backend", version = "0.2.1
3030
dragonfly-client-util = { path = "dragonfly-client-util", version = "0.2.11" }
3131
dragonfly-client-init = { path = "dragonfly-client-init", version = "0.2.11" }
3232
thiserror = "1.0"
33-
dragonfly-api = "=2.1.25"
33+
dragonfly-api = "=2.1.27"
3434
reqwest = { version = "0.12.4", features = [
3535
"stream",
3636
"native-tls",

dragonfly-client-storage/src/metadata.rs

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -303,25 +303,19 @@ impl Piece {
303303
}
304304
}
305305

306-
/// calculate_digest return the digest of the piece metadata,
307-
/// which is different from the digest of the piece content.
306+
/// calculate_digest return the digest of the piece metadata, including the piece number,
307+
/// offset, length and content digest. The digest is used to check the integrity of the
308+
/// piece metadata.
308309
pub fn calculate_digest(&self) -> String {
309310
let crc = Crc::<u32, Table<16>>::new(&CRC_32_ISCSI);
310-
let mut crc_digest = crc.digest();
311+
let mut digest = crc.digest();
312+
digest.update(&self.number.to_be_bytes());
313+
digest.update(&self.offset.to_be_bytes());
314+
digest.update(&self.length.to_be_bytes());
315+
digest.update(self.digest.as_bytes());
311316

312-
crc_digest.update(&self.number.to_be_bytes());
313-
crc_digest.update(&self.offset.to_be_bytes());
314-
crc_digest.update(&self.length.to_be_bytes());
315-
crc_digest.update(self.digest.as_bytes());
316-
317-
if let Some(parent_id) = &self.parent_id {
318-
crc_digest.update(parent_id.as_bytes());
319-
}
320-
321-
let digest =
322-
digest::Digest::new(digest::Algorithm::Crc32, crc_digest.finalize().to_string());
323-
324-
digest.to_string()
317+
let encoded = digest.finalize().to_string();
318+
digest::Digest::new(digest::Algorithm::Crc32, encoded).to_string()
325319
}
326320
}
327321

@@ -957,12 +951,11 @@ mod tests {
957951
offset: 0,
958952
length: 1024,
959953
digest: "crc32:3810626145".to_string(),
960-
parent_id: Some("d3c4e940ad06c47fc36ac67801e6f8e36cb4".to_string()),
961954
..Default::default()
962955
};
963956

964957
let digest = piece.calculate_digest();
965-
assert_eq!(digest, "crc32:523852508");
958+
assert_eq!(digest, "crc32:3874114958");
966959
}
967960

968961
#[test]

dragonfly-client-util/src/digest/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ impl FromStr for Digest {
129129
}
130130
}
131131

132-
/// calculate_file_hash calculates the hash of a file.
132+
/// calculate_file_digest calculates the digest of a file.
133133
#[instrument(skip_all)]
134134
pub fn calculate_file_digest(algorithm: Algorithm, path: &Path) -> ClientResult<Digest> {
135135
let f = std::fs::File::open(path)?;

dragonfly-client/src/grpc/dfdaemon_upload.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,8 @@ impl DfdaemonUpload for DfdaemonUploadServerHandler {
882882
cost: None,
883883
created_at: None,
884884
}),
885+
// Calculate the digest of the piece metadata, including the number, offset, length and
886+
// content digest. The digest is used to verify the integrity of the piece metadata.
885887
digest: Some(piece.calculate_digest()),
886888
}))
887889
}
@@ -1409,15 +1411,18 @@ impl DfdaemonUpload for DfdaemonUploadServerHandler {
14091411
Ok(Response::new(DownloadPersistentCachePieceResponse {
14101412
piece: Some(Piece {
14111413
number: piece.number,
1412-
parent_id: piece.parent_id,
1414+
parent_id: piece.parent_id.clone(),
14131415
offset: piece.offset,
14141416
length: piece.length,
1415-
digest: piece.digest,
1417+
digest: piece.digest.clone(),
14161418
content: Some(content),
14171419
traffic_type: None,
14181420
cost: None,
14191421
created_at: None,
14201422
}),
1423+
// Calculate the digest of the piece metadata, including the number, offset, length and
1424+
// content digest. The digest is used to verify the integrity of the piece metadata.
1425+
digest: Some(piece.calculate_digest()),
14211426
}))
14221427
}
14231428
}

dragonfly-client/src/resource/piece_downloader.rs

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -121,29 +121,30 @@ impl Downloader for GRPCDownloader {
121121
return Err(Error::InvalidParameter);
122122
};
123123

124+
let Some(content) = piece.content else {
125+
return Err(Error::InvalidParameter);
126+
};
127+
128+
// Calculate the digest of the piece metadata and compare it with the expected digest,
129+
// it verifies the integrity of the piece metadata.
124130
let piece_metadata = metadata::Piece {
125131
number,
126132
length: piece.length,
127133
offset: piece.offset,
128134
digest: piece.digest.clone(),
129-
parent_id: piece.parent_id.clone(),
130135
..Default::default()
131136
};
132137

133-
if let Some(expected_digest) = &response.digest {
134-
let actual_digest = piece_metadata.calculate_digest();
135-
if expected_digest != &actual_digest {
136-
return Err(Error::ValidationError(format!(
137-
"checksum mismatch, expected: {}, actual: {}",
138-
expected_digest, actual_digest
139-
)));
138+
if let Some(expected_digest) = response.digest {
139+
let digest = piece_metadata.calculate_digest();
140+
if expected_digest != digest {
141+
return Err(Error::DigestMismatch(
142+
expected_digest.to_string(),
143+
digest.to_string(),
144+
));
140145
}
141146
}
142147

143-
let Some(content) = piece.content else {
144-
return Err(Error::InvalidParameter);
145-
};
146-
147148
Ok((content, piece.offset, piece.digest))
148149
}
149150

@@ -179,6 +180,26 @@ impl Downloader for GRPCDownloader {
179180
return Err(Error::InvalidParameter);
180181
};
181182

183+
// Calculate the digest of the piece metadata and compare it with the expected digest,
184+
// it verifies the integrity of the piece metadata.
185+
let piece_metadata = metadata::Piece {
186+
number,
187+
length: piece.length,
188+
offset: piece.offset,
189+
digest: piece.digest.clone(),
190+
..Default::default()
191+
};
192+
193+
if let Some(expected_digest) = response.digest {
194+
let digest = piece_metadata.calculate_digest();
195+
if expected_digest != digest {
196+
return Err(Error::DigestMismatch(
197+
expected_digest.to_string(),
198+
digest.to_string(),
199+
));
200+
}
201+
}
202+
182203
Ok((content, piece.offset, piece.digest))
183204
}
184205
}

0 commit comments

Comments
 (0)