Skip to content

Commit e430101

Browse files
author
Yang Kaiyong
committed
nydus-image(feat): add crc32 in external chunks
Signed-off-by: Yang Kaiyong <yangkaiyong.yky@antgroup.com>
1 parent 05bbd14 commit e430101

File tree

2 files changed

+133
-14
lines changed

2 files changed

+133
-14
lines changed

builder/src/attributes.rs

+44-11
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use gix_attributes::parse;
1111
use gix_attributes::parse::Kind;
1212

1313
const KEY_TYPE: &str = "type";
14+
const KEY_CRCS: &str = "crcs";
1415
const VAL_EXTERNAL: &str = "external";
1516

1617
pub struct Parser {}
@@ -24,6 +25,7 @@ pub struct Item {
2425
#[derive(Clone, Debug, Eq, PartialEq, Default)]
2526
pub struct Attributes {
2627
pub items: HashMap<PathBuf, HashMap<String, String>>,
28+
pub crcs: HashMap<PathBuf, Vec<u32>>,
2729
}
2830

2931
impl Attributes {
@@ -33,6 +35,7 @@ impl Attributes {
3335
let _items = parse(&content);
3436

3537
let mut items = HashMap::new();
38+
let mut crcs = HashMap::new();
3639
for _item in _items {
3740
let _item = _item?;
3841
if let Kind::Pattern(pattern) = _item.0 {
@@ -43,15 +46,32 @@ impl Attributes {
4346
let mut current_path = path.clone();
4447
let mut attributes = HashMap::new();
4548
let mut _type = String::new();
49+
let mut crc_arr = vec![];
4650
for line in _item.1 {
4751
let line = line?;
4852
let name = line.name.as_str();
4953
let state = line.state.as_bstr().unwrap_or_default();
5054
if name == KEY_TYPE {
5155
_type = state.to_string();
5256
}
57+
if name == KEY_CRCS {
58+
crc_arr = state
59+
.to_string()
60+
.split(',')
61+
.map(|s| {
62+
let trimmed = s.trim();
63+
let hex_str = if trimmed.starts_with("0x") {
64+
&trimmed[2..]
65+
} else {
66+
trimmed
67+
};
68+
u32::from_str_radix(hex_str, 16).map_err(|e| anyhow::anyhow!(e))
69+
})
70+
.collect::<Result<Vec<u32>, _>>()?;
71+
}
5372
attributes.insert(name.to_string(), state.to_string());
5473
}
74+
crcs.insert(path.clone(), crc_arr);
5575
items.insert(path, attributes);
5676

5777
// process parent directory
@@ -69,7 +89,7 @@ impl Attributes {
6989
}
7090
}
7191

72-
Ok(Attributes { items })
92+
Ok(Attributes { items, crcs })
7393
}
7494

7595
fn check_external(&self, attributes: &HashMap<String, String>) -> bool {
@@ -99,6 +119,10 @@ impl Attributes {
99119
pub fn get_values<P: AsRef<Path>>(&self, path: P) -> Option<&HashMap<String, String>> {
100120
self.items.get(path.as_ref())
101121
}
122+
123+
pub fn get_crcs<P: AsRef<Path>>(&self, path: P) -> Option<&Vec<u32>> {
124+
self.crcs.get(path.as_ref())
125+
}
102126
}
103127

104128
#[cfg(test)]
@@ -113,17 +137,25 @@ mod tests {
113137
let file = TempFile::new().unwrap();
114138
fs::write(
115139
file.as_path(),
116-
"/foo type=external
117-
/bar type=external
140+
"/foo type=external crcs=0x1234,0x5678
141+
/bar type=external crcs=0x1234,0x5678
118142
/models/foo/bar type=external",
119143
)
120144
.unwrap();
121145

122146
let attributes = Attributes::from(file.as_path()).unwrap();
123-
let _attributes: HashMap<String, String> = [("type".to_string(), "external".to_string())]
124-
.iter()
125-
.cloned()
126-
.collect();
147+
let _attributes_base: HashMap<String, String> =
148+
[("type".to_string(), "external".to_string())]
149+
.iter()
150+
.cloned()
151+
.collect();
152+
let _attributes: HashMap<String, String> = [
153+
("type".to_string(), "external".to_string()),
154+
("crcs".to_string(), "0x1234,0x5678".to_string()),
155+
]
156+
.iter()
157+
.cloned()
158+
.collect();
127159

128160
let items_map: HashMap<PathBuf, HashMap<String, String>> = vec![
129161
Item {
@@ -136,21 +168,22 @@ mod tests {
136168
},
137169
Item {
138170
pattern: PathBuf::from("/models"),
139-
attributes: _attributes.clone(),
171+
attributes: _attributes_base.clone(),
140172
},
141173
Item {
142174
pattern: PathBuf::from("/models/foo"),
143-
attributes: _attributes.clone(),
175+
attributes: _attributes_base.clone(),
144176
},
145177
Item {
146178
pattern: PathBuf::from("/models/foo/bar"),
147-
attributes: _attributes.clone(),
179+
attributes: _attributes_base.clone(),
148180
},
149181
]
150182
.into_iter()
151183
.map(|item| (item.pattern, item.attributes))
152184
.collect();
153185

154-
assert_eq!(attributes, Attributes { items: items_map });
186+
assert_eq!(attributes.items, items_map);
187+
assert_eq!(attributes.get_crcs("/foo"), Some(&vec![0x1234, 0x5678]))
155188
}
156189
}

builder/src/core/node.rs

+89-3
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ impl Node {
366366

367367
let chunk_data = &mut data_buf[0..uncompressed_size as usize];
368368
let (mut chunk, mut chunk_info) =
369-
self.read_file_chunk(ctx, reader, chunk_data, blob_mgr.external)?;
369+
self.read_file_chunk(ctx, reader, chunk_data, i, blob_mgr.external)?;
370370
if let Some(h) = inode_hasher.as_mut() {
371371
h.digest_update(chunk.id().as_ref());
372372
}
@@ -432,6 +432,7 @@ impl Node {
432432
ctx: &BuildContext,
433433
reader: &mut R,
434434
buf: &mut [u8],
435+
index: u32,
435436
external: bool,
436437
) -> Result<(ChunkWrapper, Option<BlobChunkInfoV2Ondisk>)> {
437438
let mut chunk = self.inode.create_chunk();
@@ -475,6 +476,22 @@ impl Node {
475476
}
476477
}
477478

479+
if ctx.crc_checker != crc::Algorithm::None && external {
480+
println!("crc checker is enabled for external blob");
481+
if let Some(crcs) = ctx.attributes.get_crcs(self.target()) {
482+
println!("matched crcs for file {}", self.target().display());
483+
if (index as usize) >= crcs.len() {
484+
return Err(anyhow!(
485+
"invalid crc index {} for file {}",
486+
index,
487+
self.target().display()
488+
));
489+
}
490+
chunk.set_has_crc(true);
491+
chunk.set_crc32(crcs[index as usize]);
492+
}
493+
}
494+
478495
if ctx.cipher != crypt::Algorithm::None && !external {
479496
chunk.set_encrypted(true);
480497
}
@@ -999,12 +1016,12 @@ impl Node {
9991016

10001017
#[cfg(test)]
10011018
mod tests {
1002-
use std::io::BufReader;
1019+
use std::{collections::HashMap, io::BufReader};
10031020

10041021
use nydus_utils::{digest, BufReaderInfo};
10051022
use vmm_sys_util::tempfile::TempFile;
10061023

1007-
use crate::{ArtifactWriter, BlobCacheGenerator, HashChunkDict};
1024+
use crate::{attributes::Attributes, ArtifactWriter, BlobCacheGenerator, HashChunkDict};
10081025

10091026
use super::*;
10101027

@@ -1212,4 +1229,73 @@ mod tests {
12121229
node.remove_xattr(OsStr::new("system.posix_acl_default.key"));
12131230
assert!(!node.inode.has_xattr());
12141231
}
1232+
1233+
#[test]
1234+
fn test_read_file_chunk_crc_valid_index() {
1235+
let mut ctx = BuildContext::default();
1236+
ctx.crc_checker = crc::Algorithm::Crc32Iscsi;
1237+
ctx.attributes = Attributes {
1238+
crcs: HashMap::new(),
1239+
..Default::default()
1240+
};
1241+
let target = PathBuf::from("/test_file");
1242+
ctx.attributes
1243+
.crcs
1244+
.insert(target.clone(), vec![0x12345678, 0x87654321]);
1245+
1246+
let node = Node::new(
1247+
InodeWrapper::new(RafsVersion::V5),
1248+
NodeInfo {
1249+
path: target.clone(),
1250+
target: target.clone(),
1251+
..Default::default()
1252+
},
1253+
1,
1254+
);
1255+
1256+
let mut reader = std::io::Cursor::new(vec![0u8; 1024]);
1257+
let mut buf = [0u8; 1024];
1258+
1259+
print!("target: {}", node.target().display());
1260+
let result = node.read_file_chunk(&ctx, &mut reader, &mut buf, 1, true);
1261+
assert!(result.is_ok());
1262+
let (chunk, _) = result.unwrap();
1263+
assert_eq!(chunk.crc32(), 0x87654321);
1264+
assert!(chunk.has_crc());
1265+
1266+
// test invalid crc index
1267+
let result = node.read_file_chunk(&ctx, &mut reader, &mut buf, 2, true);
1268+
assert!(result.is_err());
1269+
let err = result.unwrap_err().to_string();
1270+
assert!(err.contains("invalid crc index 2 for file /test_file"));
1271+
}
1272+
1273+
#[test]
1274+
fn test_read_file_chunk_crc_invalid_index() {
1275+
let mut ctx = BuildContext::default();
1276+
ctx.crc_checker = crc::Algorithm::Crc32Iscsi;
1277+
ctx.attributes = Attributes {
1278+
crcs: HashMap::new(),
1279+
..Default::default()
1280+
};
1281+
let target = PathBuf::from("/test_file");
1282+
ctx.attributes.crcs.insert(target.clone(), vec![0x12345678]);
1283+
1284+
let node = Node::new(
1285+
InodeWrapper::new(RafsVersion::V5),
1286+
NodeInfo {
1287+
path: target.clone(),
1288+
..Default::default()
1289+
},
1290+
1,
1291+
);
1292+
1293+
let mut reader = std::io::Cursor::new(vec![0u8; 1024]);
1294+
let mut buf = [0u8; 1024];
1295+
1296+
let result = node.read_file_chunk(&ctx, &mut reader, &mut buf, 1, true);
1297+
assert!(result.is_err());
1298+
let err = result.unwrap_err().to_string();
1299+
assert!(err.contains("invalid crc index 1 for file /test_file"));
1300+
}
12151301
}

0 commit comments

Comments
 (0)