Nothing to see here, move along meow
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 24 kB View raw
1use crate::block_io::BlockIo; 2use crate::btree; 3use crate::cache::BlockCache; 4use crate::compression; 5use crate::dedup; 6use crate::error::FsError; 7use crate::freemap::FreemapAllocator; 8use crate::integrity; 9use crate::pool::NodePool; 10use lancer_core::fs::{ 11 BLOCK_SIZE_MIN, BLOCK_SIZE_MIN_LOG2, BlockRef, BlockType, Compression, DEDUP_SHARDS, 12 INODE_DIRECT_REFS, INODE_INLINE_MAX, INODE_SIZE, Inode, InodeFlags, 13}; 14use zerocopy::IntoBytes; 15 16const BLOCK_SIZE: usize = BLOCK_SIZE_MIN as usize; 17const LZ4_HASH_SIZE: usize = 1 << 12; 18 19pub fn cow_inode( 20 cache: &mut BlockCache, 21 bio: &mut BlockIo, 22 freemap: &mut FreemapAllocator, 23 inode: &Inode, 24) -> Result<u64, FsError> { 25 let new_block = freemap.alloc_blocks(cache, bio, 1)?; 26 write_inode_to_cache(cache, bio, new_block, inode)?; 27 Ok(new_block) 28} 29const LZ4_LEN_PREFIX: usize = 4; 30 31pub fn read_inode( 32 cache: &mut BlockCache, 33 bio: &mut BlockIo, 34 block_num: u64, 35) -> Result<Inode, FsError> { 36 let data = cache.cache_read(bio, block_num)?; 37 Ok(unsafe { core::ptr::read_unaligned(data.as_ptr() as *const Inode) }) 38} 39 40pub fn write_inode_to_cache( 41 cache: &mut BlockCache, 42 bio: &mut BlockIo, 43 block_num: u64, 44 inode: &Inode, 45) -> Result<(), FsError> { 46 let mut block = [0u8; BLOCK_SIZE]; 47 block[..INODE_SIZE].copy_from_slice(inode.as_bytes()); 48 cache.cache_write(bio, block_num, &block)?; 49 cache.mark_inode(block_num); 50 Ok(()) 51} 52 53pub fn read_inline(inode: &Inode, offset: u64, buf: &mut [u8]) -> usize { 54 match inode.inline_data() { 55 None => 0, 56 Some(data) => { 57 let start = (offset as usize).min(data.len()); 58 let available = data.len().saturating_sub(start); 59 let n = available.min(buf.len()); 60 buf[..n].copy_from_slice(&data[start..start + n]); 61 n 62 } 63 } 64} 65 66pub fn write_inline(inode: &Inode, offset: u64, data: &[u8]) -> Result<Inode, FsError> { 67 let end = offset as usize + data.len(); 68 match end <= INODE_INLINE_MAX { 69 false => Err(FsError::InvalidBlock), 70 true => { 71 let mut updated = inode.clone(); 72 updated.direct.as_mut_bytes()[offset as usize..end].copy_from_slice(data); 73 let new_size = (offset + data.len() as u64).max(updated.size); 74 updated.size = new_size; 75 updated.flags |= InodeFlags::INLINE; 76 Ok(updated) 77 } 78 } 79} 80 81#[allow(clippy::too_many_arguments)] 82pub fn migrate_to_blocks( 83 pool: &mut NodePool, 84 cache: &mut BlockCache, 85 bio: &mut BlockIo, 86 freemap: &mut FreemapAllocator, 87 dedup_roots: &mut [BlockRef; DEDUP_SHARDS], 88 inode: &Inode, 89 txn: u64, 90 eff_compression: Compression, 91 scratch: &mut [u8], 92 hash_table: &mut [u16; LZ4_HASH_SIZE], 93) -> Result<Inode, FsError> { 94 let inline_len = inode.size.min(INODE_INLINE_MAX as u64) as usize; 95 96 let mut block_buf = [0u8; BLOCK_SIZE]; 97 match inline_len { 98 0 => {} 99 n => { 100 let inline = inode.direct.as_bytes(); 101 block_buf[..n].copy_from_slice(&inline[..n]); 102 } 103 } 104 105 let data_ref = write_data_block( 106 pool, 107 cache, 108 bio, 109 freemap, 110 dedup_roots, 111 &block_buf, 112 txn, 113 eff_compression, 114 scratch, 115 hash_table, 116 )?; 117 118 let mut updated = inode.clone(); 119 updated.flags &= !InodeFlags::INLINE; 120 updated.direct = [BlockRef::ZERO; INODE_DIRECT_REFS]; 121 updated.direct[0] = data_ref; 122 updated.block_count = 1; 123 updated.transaction_id = txn; 124 125 Ok(updated) 126} 127 128fn get_data_block_ref( 129 pool: &mut NodePool, 130 cache: &mut BlockCache, 131 bio: &mut BlockIo, 132 inode: &Inode, 133 block_idx: u64, 134) -> Result<Option<BlockRef>, FsError> { 135 match inode.flags & InodeFlags::INDIRECT != 0 { 136 false => match block_idx < INODE_DIRECT_REFS as u64 { 137 true => Ok(Some(inode.direct[block_idx as usize])), 138 false => Ok(None), 139 }, 140 true => { 141 let key = block_idx * BLOCK_SIZE_MIN as u64; 142 btree::btree_lookup(pool, cache, bio, &inode.direct[0], key, None) 143 } 144 } 145} 146 147fn set_data_block_ref( 148 pool: &mut NodePool, 149 cache: &mut BlockCache, 150 bio: &mut BlockIo, 151 inode: &Inode, 152 block_idx: u64, 153 data_ref: BlockRef, 154 txn: u64, 155) -> Result<(Inode, bool), FsError> { 156 match inode.flags & InodeFlags::INDIRECT != 0 { 157 false => match block_idx < INODE_DIRECT_REFS as u64 { 158 true => { 159 let was_empty = inode.direct[block_idx as usize].is_null(); 160 let mut updated = inode.clone(); 161 updated.direct[block_idx as usize] = data_ref; 162 Ok((updated, was_empty)) 163 } 164 false => { 165 let migrated = migrate_direct_to_indirect(pool, cache, bio, inode, txn)?; 166 let key = block_idx * BLOCK_SIZE_MIN as u64; 167 let new_root = 168 btree::btree_insert(pool, cache, bio, &migrated.direct[0], key, data_ref, txn)?; 169 let mut result = migrated; 170 result.direct[0] = new_root; 171 Ok((result, true)) 172 } 173 }, 174 true => { 175 let key = block_idx * BLOCK_SIZE_MIN as u64; 176 let mut updated = inode.clone(); 177 match btree::btree_insert(pool, cache, bio, &updated.direct[0], key, data_ref, txn) { 178 Ok(new_root) => { 179 updated.direct[0] = new_root; 180 Ok((updated, true)) 181 } 182 Err(FsError::DuplicateKey) => { 183 let after_delete = 184 btree::btree_delete(pool, cache, bio, &updated.direct[0], key, txn)?; 185 let root_after = after_delete.unwrap_or(updated.direct[0]); 186 let new_root = 187 btree::btree_insert(pool, cache, bio, &root_after, key, data_ref, txn)?; 188 updated.direct[0] = new_root; 189 Ok((updated, false)) 190 } 191 Err(e) => Err(e), 192 } 193 } 194 } 195} 196 197fn migrate_direct_to_indirect( 198 pool: &mut NodePool, 199 cache: &mut BlockCache, 200 bio: &mut BlockIo, 201 inode: &Inode, 202 txn: u64, 203) -> Result<Inode, FsError> { 204 let root = (0..INODE_DIRECT_REFS as u64).try_fold(BlockRef::ZERO, |cur_root, i| { 205 let dref = inode.direct[i as usize]; 206 match dref.is_null() { 207 true => Ok(cur_root), 208 false => { 209 let key = i * BLOCK_SIZE_MIN as u64; 210 btree::btree_insert(pool, cache, bio, &cur_root, key, dref, txn) 211 } 212 } 213 })?; 214 215 let mut updated = inode.clone(); 216 updated.direct = [BlockRef::ZERO; INODE_DIRECT_REFS]; 217 updated.direct[0] = root; 218 updated.flags |= InodeFlags::INDIRECT; 219 Ok(updated) 220} 221 222pub fn file_read( 223 pool: &mut NodePool, 224 cache: &mut BlockCache, 225 bio: &mut BlockIo, 226 inode: &Inode, 227 offset: u64, 228 buf: &mut [u8], 229) -> Result<usize, FsError> { 230 match offset >= inode.size { 231 true => Ok(0), 232 false => { 233 let readable = (inode.size - offset).min(buf.len() as u64) as usize; 234 235 match inode.is_inline() { 236 true => Ok(read_inline(inode, offset, &mut buf[..readable])), 237 false => read_blocks(pool, cache, bio, inode, offset, &mut buf[..readable]), 238 } 239 } 240 } 241} 242 243fn read_blocks( 244 pool: &mut NodePool, 245 cache: &mut BlockCache, 246 bio: &mut BlockIo, 247 inode: &Inode, 248 offset: u64, 249 buf: &mut [u8], 250) -> Result<usize, FsError> { 251 let block_size = BLOCK_SIZE_MIN as u64; 252 let first_block = offset / block_size; 253 let end_byte = offset + buf.len() as u64; 254 let last_block = match end_byte { 255 0 => return Ok(0), 256 n => (n - 1) / block_size, 257 }; 258 let num_blocks = (last_block - first_block + 1) as usize; 259 260 (0..num_blocks).try_fold(0usize, |written, chunk_idx| { 261 let block_idx = first_block + chunk_idx as u64; 262 let block_start = block_idx * block_size; 263 let in_block_off = match block_idx == first_block { 264 true => (offset - block_start) as usize, 265 false => 0, 266 }; 267 let in_block_end = match block_idx == last_block { 268 true => (end_byte - block_start) as usize, 269 false => BLOCK_SIZE, 270 }; 271 let copy_len = in_block_end - in_block_off; 272 273 let block_ref = get_data_block_ref(pool, cache, bio, inode, block_idx)?; 274 match block_ref { 275 Some(r) if !r.is_null() => { 276 let block_num = crate::blockref_block_num(&r); 277 let raw = cache.cache_read(bio, block_num)?; 278 279 if r.integrity_crc != 0 && !integrity::verify_block_crc(raw, r.integrity_crc) { 280 return Err(FsError::IntegrityFailure); 281 } 282 283 let comp = r.compression_enum().unwrap_or(Compression::None); 284 285 match comp { 286 Compression::None => { 287 buf[written..written + copy_len] 288 .copy_from_slice(&raw[in_block_off..in_block_end]); 289 } 290 Compression::Lz4 => { 291 let clen = u32::from_le_bytes([raw[0], raw[1], raw[2], raw[3]]) as usize; 292 let compressed_end = (LZ4_LEN_PREFIX + clen).min(BLOCK_SIZE); 293 let mut decomp = [0u8; BLOCK_SIZE]; 294 let _len = compression::decompress_block( 295 &raw[LZ4_LEN_PREFIX..compressed_end], 296 comp, 297 r.logical_size, 298 &mut decomp, 299 )?; 300 buf[written..written + copy_len] 301 .copy_from_slice(&decomp[in_block_off..in_block_end]); 302 } 303 } 304 305 Ok(written + copy_len) 306 } 307 _ => { 308 buf[written..written + copy_len].fill(0); 309 Ok(written + copy_len) 310 } 311 } 312 }) 313} 314 315#[allow(clippy::too_many_arguments)] 316pub fn file_write( 317 pool: &mut NodePool, 318 cache: &mut BlockCache, 319 bio: &mut BlockIo, 320 freemap: &mut FreemapAllocator, 321 dedup_roots: &[BlockRef; DEDUP_SHARDS], 322 inode: &Inode, 323 offset: u64, 324 data: &[u8], 325 txn: u64, 326 eff_compression: Compression, 327 scratch: &mut [u8], 328 hash_table: &mut [u16; LZ4_HASH_SIZE], 329) -> Result<(Inode, usize, [BlockRef; DEDUP_SHARDS]), FsError> { 330 if data.is_empty() { 331 return Ok((inode.clone(), 0, *dedup_roots)); 332 } 333 334 if inode.is_inline() && offset + data.len() as u64 <= INODE_INLINE_MAX as u64 { 335 return write_inline(inode, offset, data).map(|i| (i, data.len(), *dedup_roots)); 336 } 337 338 let mut dedup_buf = *dedup_roots; 339 340 let cur_inode = match inode.is_inline() { 341 true => migrate_to_blocks( 342 pool, 343 cache, 344 bio, 345 freemap, 346 &mut dedup_buf, 347 inode, 348 txn, 349 eff_compression, 350 scratch, 351 hash_table, 352 )?, 353 false => inode.clone(), 354 }; 355 356 let block_size = BLOCK_SIZE_MIN as u64; 357 let first_block = offset / block_size; 358 let end_byte = offset + data.len() as u64; 359 let last_block = (end_byte - 1) / block_size; 360 let num_blocks = (last_block - first_block + 1) as usize; 361 362 let (final_inode, written, new_blocks) = (0..num_blocks).try_fold( 363 (cur_inode, 0usize, 0u64), 364 |(inode_acc, written_acc, added_blocks), chunk_idx| -> Result<_, FsError> { 365 let block_idx = first_block + chunk_idx as u64; 366 let block_start = block_idx * block_size; 367 let in_block_off = match block_idx == first_block { 368 true => (offset - block_start) as usize, 369 false => 0, 370 }; 371 let in_block_end = match block_idx == last_block { 372 true => (end_byte - block_start) as usize, 373 false => BLOCK_SIZE, 374 }; 375 let data_start = (block_start + in_block_off as u64 - offset) as usize; 376 let data_end = (data_start + in_block_end - in_block_off).min(data.len()); 377 let chunk_data = &data[data_start..data_end]; 378 379 let mut block_buf = [0u8; BLOCK_SIZE]; 380 let is_partial = in_block_off != 0 || in_block_end != BLOCK_SIZE; 381 382 match is_partial { 383 true => { 384 let existing = get_data_block_ref(pool, cache, bio, &inode_acc, block_idx)?; 385 match existing { 386 Some(r) if !r.is_null() => { 387 let raw = cache.cache_read(bio, crate::blockref_block_num(&r))?; 388 let existing_comp = r.compression_enum().unwrap_or(Compression::None); 389 match existing_comp { 390 Compression::None => block_buf.copy_from_slice(raw), 391 Compression::Lz4 => { 392 let clen = u32::from_le_bytes([raw[0], raw[1], raw[2], raw[3]]) 393 as usize; 394 let end = (LZ4_LEN_PREFIX + clen).min(BLOCK_SIZE); 395 compression::decompress_block( 396 &raw[LZ4_LEN_PREFIX..end], 397 existing_comp, 398 r.logical_size, 399 &mut block_buf, 400 )?; 401 } 402 } 403 } 404 _ => {} 405 } 406 block_buf[in_block_off..in_block_off + chunk_data.len()] 407 .copy_from_slice(chunk_data); 408 } 409 false => { 410 block_buf[..chunk_data.len()].copy_from_slice(chunk_data); 411 } 412 } 413 414 let data_ref = write_data_block( 415 pool, 416 cache, 417 bio, 418 freemap, 419 &mut dedup_buf, 420 &block_buf, 421 txn, 422 eff_compression, 423 scratch, 424 hash_table, 425 )?; 426 427 let (updated, is_new) = 428 set_data_block_ref(pool, cache, bio, &inode_acc, block_idx, data_ref, txn)?; 429 let added = added_blocks 430 + match is_new { 431 true => 1, 432 false => 0, 433 }; 434 Ok((updated, written_acc + chunk_data.len(), added)) 435 }, 436 )?; 437 438 let mut result = final_inode; 439 let new_end = offset + data.len() as u64; 440 if new_end > result.size { 441 result.size = new_end; 442 } 443 result.transaction_id = txn; 444 result.block_count += new_blocks; 445 446 Ok((result, written, dedup_buf)) 447} 448 449#[cfg(test)] 450mod tests { 451 use super::*; 452 453 fn make_inline_inode(data: &[u8]) -> Inode { 454 let mut inode = Inode::new_file(1, 1, 1, 0, 0); 455 inode.flags |= InodeFlags::INLINE; 456 inode.size = data.len() as u64; 457 inode.direct.as_mut_bytes()[..data.len()].copy_from_slice(data); 458 inode 459 } 460 461 #[test] 462 fn read_inline_from_start() { 463 let inode = make_inline_inode(b"hello world"); 464 let mut buf = [0u8; 32]; 465 let n = read_inline(&inode, 0, &mut buf); 466 assert_eq!(n, 11); 467 assert_eq!(&buf[..n], b"hello world"); 468 } 469 470 #[test] 471 fn read_inline_with_offset() { 472 let inode = make_inline_inode(b"hello world"); 473 let mut buf = [0u8; 32]; 474 let n = read_inline(&inode, 6, &mut buf); 475 assert_eq!(n, 5); 476 assert_eq!(&buf[..n], b"world"); 477 } 478 479 #[test] 480 fn read_inline_offset_past_end() { 481 let inode = make_inline_inode(b"short"); 482 let mut buf = [0u8; 32]; 483 let n = read_inline(&inode, 100, &mut buf); 484 assert_eq!(n, 0); 485 } 486 487 #[test] 488 fn read_inline_small_buffer() { 489 let inode = make_inline_inode(b"hello world"); 490 let mut buf = [0u8; 3]; 491 let n = read_inline(&inode, 0, &mut buf); 492 assert_eq!(n, 3); 493 assert_eq!(&buf[..n], b"hel"); 494 } 495 496 #[test] 497 fn read_inline_non_inline_inode_returns_zero() { 498 let inode = Inode::new_file(1, 1, 1, 0, 0); 499 let mut buf = [0u8; 32]; 500 assert_eq!(read_inline(&inode, 0, &mut buf), 0); 501 } 502 503 #[test] 504 fn write_inline_basic() { 505 let inode = Inode::new_file(1, 1, 1, 0, 0); 506 let updated = write_inline(&inode, 0, b"test data").unwrap(); 507 assert!(updated.is_inline()); 508 assert_eq!(updated.size, 9); 509 510 let mut buf = [0u8; 32]; 511 let n = read_inline(&updated, 0, &mut buf); 512 assert_eq!(&buf[..n], b"test data"); 513 } 514 515 #[test] 516 fn write_inline_at_offset() { 517 let inode = make_inline_inode(b"hello world"); 518 let updated = write_inline(&inode, 6, b"WORLD").unwrap(); 519 assert_eq!(updated.size, 11); 520 521 let mut buf = [0u8; 32]; 522 let n = read_inline(&updated, 0, &mut buf); 523 assert_eq!(&buf[..n], b"hello WORLD"); 524 } 525 526 #[test] 527 fn write_inline_extends_size() { 528 let inode = make_inline_inode(b"hi"); 529 let updated = write_inline(&inode, 2, b" there").unwrap(); 530 assert_eq!(updated.size, 8); 531 } 532 533 #[test] 534 fn write_inline_exceeds_max_returns_error() { 535 let inode = Inode::new_file(1, 1, 1, 0, 0); 536 let big_data = [0u8; INODE_INLINE_MAX + 1]; 537 assert!(write_inline(&inode, 0, &big_data).is_err()); 538 } 539 540 #[test] 541 fn write_inline_at_max_boundary() { 542 let inode = Inode::new_file(1, 1, 1, 0, 0); 543 let data = [0xAB_u8; INODE_INLINE_MAX]; 544 let updated = write_inline(&inode, 0, &data).unwrap(); 545 assert!(updated.is_inline()); 546 assert_eq!(updated.size, INODE_INLINE_MAX as u64); 547 } 548 549 #[test] 550 fn file_read_past_eof_returns_zero() { 551 let mut pool = crate::test_helpers::make_pool(); 552 let mut cache = crate::test_helpers::make_cache(); 553 let mut bio = crate::test_helpers::make_bio(32); 554 555 let inode = make_inline_inode(b"data"); 556 let mut buf = [0u8; 32]; 557 let n = file_read(&mut pool, &mut cache, &mut bio, &inode, 100, &mut buf).unwrap(); 558 assert_eq!(n, 0); 559 } 560 561 #[test] 562 fn file_read_inline_via_file_read() { 563 let mut pool = crate::test_helpers::make_pool(); 564 let mut cache = crate::test_helpers::make_cache(); 565 let mut bio = crate::test_helpers::make_bio(32); 566 567 let inode = make_inline_inode(b"inline content"); 568 let mut buf = [0u8; 64]; 569 let n = file_read(&mut pool, &mut cache, &mut bio, &inode, 0, &mut buf).unwrap(); 570 assert_eq!(n, 14); 571 assert_eq!(&buf[..n], b"inline content"); 572 } 573 574 #[test] 575 fn file_read_inline_partial() { 576 let mut pool = crate::test_helpers::make_pool(); 577 let mut cache = crate::test_helpers::make_cache(); 578 let mut bio = crate::test_helpers::make_bio(32); 579 580 let inode = make_inline_inode(b"hello"); 581 let mut buf = [0u8; 3]; 582 let n = file_read(&mut pool, &mut cache, &mut bio, &inode, 0, &mut buf).unwrap(); 583 assert_eq!(n, 3); 584 assert_eq!(&buf[..n], b"hel"); 585 } 586 587 #[test] 588 fn file_write_inline_via_file_write() { 589 let mut pool = crate::test_helpers::make_pool(); 590 let mut cache = crate::test_helpers::make_cache(); 591 let mut bio = crate::test_helpers::make_bio(256); 592 593 let inode = make_inline_inode(b""); 594 let mut scratch = [0u8; 8192]; 595 let mut hash_table = [0u16; 1 << 12]; 596 let dedup_roots = [BlockRef::ZERO; DEDUP_SHARDS]; 597 598 let (updated, written, _) = file_write( 599 &mut pool, 600 &mut cache, 601 &mut bio, 602 &mut crate::freemap::FreemapAllocator::init_empty(), 603 &dedup_roots, 604 &inode, 605 0, 606 b"small data", 607 1, 608 Compression::None, 609 &mut scratch, 610 &mut hash_table, 611 ) 612 .unwrap(); 613 614 assert_eq!(written, 10); 615 assert!(updated.is_inline()); 616 assert_eq!(updated.size, 10); 617 618 let mut buf = [0u8; 32]; 619 let n = read_inline(&updated, 0, &mut buf); 620 assert_eq!(&buf[..n], b"small data"); 621 } 622 623 #[test] 624 fn file_write_empty_data_noop() { 625 let mut pool = crate::test_helpers::make_pool(); 626 let mut cache = crate::test_helpers::make_cache(); 627 let mut bio = crate::test_helpers::make_bio(32); 628 629 let inode = Inode::new_file(1, 1, 1, 0, 0); 630 let mut scratch = [0u8; 8192]; 631 let mut hash_table = [0u16; 1 << 12]; 632 let dedup_roots = [BlockRef::ZERO; DEDUP_SHARDS]; 633 634 let (updated, written, _) = file_write( 635 &mut pool, 636 &mut cache, 637 &mut bio, 638 &mut crate::freemap::FreemapAllocator::init_empty(), 639 &dedup_roots, 640 &inode, 641 0, 642 &[], 643 1, 644 Compression::None, 645 &mut scratch, 646 &mut hash_table, 647 ) 648 .unwrap(); 649 650 assert_eq!(written, 0); 651 assert_eq!(updated.size, 0); 652 } 653} 654 655#[allow(clippy::too_many_arguments)] 656fn write_data_block( 657 pool: &mut NodePool, 658 cache: &mut BlockCache, 659 bio: &mut BlockIo, 660 freemap: &mut FreemapAllocator, 661 dedup_roots: &mut [BlockRef; DEDUP_SHARDS], 662 block_data: &[u8; BLOCK_SIZE], 663 txn: u64, 664 eff_compression: Compression, 665 scratch: &mut [u8], 666 hash_table: &mut [u16; LZ4_HASH_SIZE], 667) -> Result<BlockRef, FsError> { 668 let content_hash = integrity::xxhash128(block_data); 669 let shard = dedup::dedup_shard(content_hash); 670 671 match dedup::dedup_check(pool, cache, bio, &dedup_roots[shard], content_hash)? { 672 dedup::DedupResult::Reused(existing) => Ok(existing), 673 dedup::DedupResult::Unique => { 674 let comp = 675 compression::compress_block(block_data, eff_compression, scratch, hash_table); 676 677 let block_num = freemap.alloc_blocks(cache, bio, 1)?; 678 679 let mut write_buf = [0u8; BLOCK_SIZE]; 680 match comp.algorithm { 681 Compression::None => write_buf.copy_from_slice(block_data), 682 Compression::Lz4 => { 683 let len_bytes = (comp.compressed_len as u32).to_le_bytes(); 684 write_buf[..LZ4_LEN_PREFIX].copy_from_slice(&len_bytes); 685 write_buf[LZ4_LEN_PREFIX..LZ4_LEN_PREFIX + comp.compressed_len] 686 .copy_from_slice(&scratch[..comp.compressed_len]); 687 } 688 } 689 690 let on_disk_crc = integrity::crc32c(&write_buf); 691 cache.cache_write(bio, block_num, &write_buf)?; 692 693 let phys_addr = crate::block_num_to_phys(block_num); 694 let data_ref = BlockRef::new( 695 phys_addr, 696 BLOCK_SIZE_MIN_LOG2, 697 0, 698 txn, 699 content_hash, 700 on_disk_crc, 701 BlockType::Data, 702 comp.algorithm, 703 comp.logical_size, 704 0, 705 ); 706 707 let new_shard_root = dedup::dedup_insert( 708 pool, 709 cache, 710 bio, 711 &dedup_roots[shard], 712 content_hash, 713 &data_ref, 714 txn, 715 )?; 716 dedup_roots[shard] = new_shard_root; 717 718 Ok(data_ref) 719 } 720 } 721}