Nothing to see here, move along meow
0

Configure Feed

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

at main 33 kB View raw
1use crate::block_io::BlockIo; 2use crate::btree; 3use crate::cache::BlockCache; 4use crate::cap::FsRights; 5use crate::commit::SuperblockPair; 6use crate::cow::FreemapReserved; 7use crate::ditto::DittoRegion; 8use crate::error::FsError; 9use crate::file; 10use crate::freemap::FreemapAllocator; 11use crate::handle::HandleTableSet; 12use crate::ipc_proto::{FsOpcode, FsRequest, FsResponse}; 13use crate::ops; 14use crate::pool::NodePool; 15use crate::scrub::ScrubState; 16use crate::snapshot; 17use crate::transaction; 18use lancer_core::fs::{ 19 BlockRef, Compression, DEDUP_SHARDS, INODE_DIRECT_REFS, InodeFlags, InodeType, 20}; 21use lancer_vfs_proto::{READDIR_ENTRY_SIZE, READDIR_MAX_NAME, ReadDirEntry}; 22 23const MAX_NAME_LEN: usize = 680; 24const LZ4_HASH_SIZE: usize = 1 << 12; 25 26pub struct FsState { 27 pub sb_pair: SuperblockPair, 28 pub freemap: FreemapAllocator, 29 pub reserved: FreemapReserved, 30 pub ditto: DittoRegion, 31 pub scrub: ScrubState, 32 pub handles: HandleTableSet, 33 pub next_object_id: u64, 34 pub dedup_roots: [BlockRef; DEDUP_SHARDS], 35 pub root_inode_block: u64, 36 pub root_object_id: u64, 37 pub root_generation: u64, 38} 39 40impl FsState { 41 pub fn from_superblock_pair( 42 sb_pair: SuperblockPair, 43 root_inode_block: u64, 44 root_object_id: u64, 45 root_generation: u64, 46 ) -> Self { 47 let freemap = FreemapAllocator::init(sb_pair.active()); 48 let reserved = FreemapReserved::init(sb_pair.active().total_blocks); 49 let ditto = DittoRegion::init(freemap.data_region_start(), freemap.total_data_blocks()); 50 let saved_cursor = sb_pair.active().scrub_cursor; 51 let mut scrub = ScrubState::new(); 52 scrub.start(saved_cursor); 53 let dedup_roots = sb_pair.active().dedup_roots; 54 let next_object_id = sb_pair.active().next_object_id.max(2); 55 56 Self { 57 sb_pair, 58 freemap, 59 reserved, 60 ditto, 61 scrub, 62 handles: HandleTableSet::new(), 63 next_object_id, 64 dedup_roots, 65 root_inode_block, 66 root_object_id, 67 root_generation, 68 } 69 } 70} 71 72#[allow(clippy::too_many_arguments)] 73pub fn dispatch_request( 74 req: &FsRequest, 75 pool: &mut NodePool, 76 cache: &mut BlockCache, 77 bio: &mut BlockIo, 78 state: &mut FsState, 79 data_area: *mut u8, 80 data_area_size: usize, 81 client_pid: u16, 82 scratch: &mut [u8], 83 hash_table: &mut [u16; LZ4_HASH_SIZE], 84) -> FsResponse { 85 let opcode_raw = req.opcode; 86 let tag = req.tag; 87 88 let response = match FsOpcode::from_u8(opcode_raw) { 89 None => FsResponse::error(opcode_raw, tag, FsError::InvalidBlock), 90 Some(opcode) => match opcode { 91 FsOpcode::Open => handle_open( 92 req, 93 pool, 94 cache, 95 bio, 96 state, 97 data_area, 98 data_area_size, 99 client_pid, 100 ), 101 FsOpcode::Read => handle_read( 102 req, 103 pool, 104 cache, 105 bio, 106 state, 107 data_area, 108 data_area_size, 109 client_pid, 110 ), 111 FsOpcode::Write => handle_write( 112 req, 113 pool, 114 cache, 115 bio, 116 state, 117 data_area, 118 data_area_size, 119 client_pid, 120 scratch, 121 hash_table, 122 ), 123 FsOpcode::Close => handle_close(req, state, client_pid), 124 FsOpcode::Stat => handle_stat(req, cache, bio, state, client_pid), 125 FsOpcode::Mkdir => handle_mkdir( 126 req, 127 pool, 128 cache, 129 bio, 130 state, 131 data_area, 132 data_area_size, 133 client_pid, 134 ), 135 FsOpcode::Unlink => handle_unlink( 136 req, 137 pool, 138 cache, 139 bio, 140 state, 141 data_area, 142 data_area_size, 143 client_pid, 144 ), 145 FsOpcode::ReadDir => handle_readdir( 146 req, 147 pool, 148 cache, 149 bio, 150 state, 151 data_area, 152 data_area_size, 153 client_pid, 154 ), 155 FsOpcode::CreateSnapshot => handle_create_snapshot( 156 req, 157 pool, 158 cache, 159 bio, 160 state, 161 data_area, 162 data_area_size, 163 client_pid, 164 ), 165 FsOpcode::DeleteSnapshot => handle_delete_snapshot( 166 req, 167 pool, 168 cache, 169 bio, 170 state, 171 data_area, 172 data_area_size, 173 client_pid, 174 ), 175 FsOpcode::Sync => handle_sync(req, pool, cache, bio, state, scratch, hash_table), 176 FsOpcode::StatFs => handle_statfs(req, cache, bio, state), 177 FsOpcode::Truncate => handle_truncate(req, pool, cache, bio, state, client_pid), 178 FsOpcode::Rename => handle_rename( 179 req, 180 pool, 181 cache, 182 bio, 183 state, 184 data_area, 185 data_area_size, 186 client_pid, 187 ), 188 }, 189 }; 190 191 let needs_commit = matches!( 192 FsOpcode::from_u8(opcode_raw), 193 Some( 194 FsOpcode::Open 195 | FsOpcode::Write 196 | FsOpcode::Mkdir 197 | FsOpcode::Unlink 198 | FsOpcode::Truncate 199 | FsOpcode::Rename 200 ) 201 ); 202 203 match needs_commit && response.status == 0 { 204 true => { 205 let tree_root = state.sb_pair.active().tree_root; 206 let dedup_roots = state.dedup_roots; 207 let snapshot_root = state.sb_pair.active().snapshot_root; 208 let scrub_cursor = state.scrub.cursor(); 209 210 match transaction::transaction_commit( 211 pool, 212 cache, 213 bio, 214 &mut state.freemap, 215 &mut state.reserved, 216 &mut state.sb_pair, 217 &tree_root, 218 &dedup_roots, 219 &snapshot_root, 220 Some(&state.ditto), 221 scrub_cursor, 222 state.next_object_id, 223 ) { 224 Ok(()) => { 225 state.dedup_roots = state.sb_pair.active().dedup_roots; 226 response 227 } 228 Err(e) => FsResponse::error(opcode_raw, tag, e), 229 } 230 } 231 false => response, 232 } 233} 234 235unsafe fn read_name_from_data<'a>( 236 data_area: *const u8, 237 data_area_size: usize, 238 offset: u64, 239 len: u64, 240) -> Option<&'a [u8]> { 241 let off = offset as usize; 242 let length = len as usize; 243 match off.checked_add(length) { 244 Some(end) if end <= data_area_size && length <= MAX_NAME_LEN && length > 0 => { 245 Some(unsafe { core::slice::from_raw_parts(data_area.add(off), length) }) 246 } 247 _ => None, 248 } 249} 250 251#[allow(clippy::too_many_arguments)] 252fn handle_open( 253 req: &FsRequest, 254 pool: &mut NodePool, 255 cache: &mut BlockCache, 256 bio: &mut BlockIo, 257 state: &mut FsState, 258 data_area: *mut u8, 259 data_area_size: usize, 260 client_pid: u16, 261) -> FsResponse { 262 let tag = req.tag; 263 let dir_handle = req.handle; 264 let mode = FsRights::from_raw(req.flags as u16); 265 let name_offset = req.arg0; 266 let name_len = req.arg1; 267 268 let result = (|| -> Result<(u8, u64, u8), FsError> { 269 let (dir_cap, dir_inode_block) = { 270 let client = state 271 .handles 272 .client(client_pid) 273 .ok_or(FsError::InvalidHandle)?; 274 let dir_entry = client.validate(dir_handle, FsRights::TRAVERSE)?; 275 (dir_entry.to_cap(), dir_entry.inode_block) 276 }; 277 let dir_inode = file::read_inode(cache, bio, dir_inode_block)?; 278 279 let name = unsafe { read_name_from_data(data_area, data_area_size, name_offset, name_len) } 280 .ok_or(FsError::InvalidName)?; 281 282 let resolve_result = ops::resolve_path_cap( 283 pool, 284 cache, 285 bio, 286 &dir_cap, 287 &dir_inode, 288 dir_inode_block, 289 name, 290 None, 291 ); 292 293 let (child_inode, child_block, open_rights) = match resolve_result { 294 Ok((child_cap, child_inode, child_block)) => { 295 (child_inode, child_block, child_cap.rights.restrict(mode)) 296 } 297 Err(FsError::NotFound) if mode.contains(FsRights::CREATE) => { 298 dir_cap 299 .check_rights(FsRights::CREATE) 300 .map_err(FsError::from)?; 301 302 let txn = state.sb_pair.active().transaction_id.wrapping_add(1); 303 let obj_id = state.next_object_id; 304 state.next_object_id += 1; 305 306 let (updated_parent, child_inode, child_block) = ops::file_create( 307 pool, 308 cache, 309 bio, 310 &mut state.freemap, 311 &dir_inode, 312 name, 313 InodeType::File, 314 txn, 315 obj_id, 316 )?; 317 318 file::write_inode_to_cache(cache, bio, dir_inode_block, &updated_parent)?; 319 320 let inheritable = dir_cap.rights.restrict(FsRights::TRAVERSE_INHERITABLE); 321 (child_inode, child_block, inheritable.restrict(mode)) 322 } 323 Err(e) => return Err(e), 324 }; 325 326 let client_mut = state 327 .handles 328 .client_mut(client_pid) 329 .ok_or(FsError::InvalidHandle)?; 330 let handle = client_mut.allocate( 331 child_inode.object_id, 332 child_inode.generation, 333 open_rights, 334 child_block, 335 )?; 336 337 Ok((handle, child_inode.size, child_inode.inode_type)) 338 })(); 339 340 match result { 341 Ok((handle, size, itype)) => FsResponse::success( 342 FsOpcode::Open as u8, 343 tag, 344 handle as u64, 345 size | ((itype as u64) << 56), 346 ), 347 Err(e) => FsResponse::error(FsOpcode::Open as u8, tag, e), 348 } 349} 350 351#[allow(clippy::too_many_arguments)] 352fn handle_read( 353 req: &FsRequest, 354 pool: &mut NodePool, 355 cache: &mut BlockCache, 356 bio: &mut BlockIo, 357 state: &mut FsState, 358 data_area: *mut u8, 359 data_area_size: usize, 360 client_pid: u16, 361) -> FsResponse { 362 let tag = req.tag; 363 let handle = req.handle; 364 let offset = req.arg0; 365 let len = req.arg1 as u32; 366 let buf_offset = req.arg2 as u32; 367 368 let result = (|| -> Result<usize, FsError> { 369 let (inode_block, generation) = { 370 let client = state 371 .handles 372 .client(client_pid) 373 .ok_or(FsError::InvalidHandle)?; 374 let entry = client.validate(handle, FsRights::READ)?; 375 (entry.inode_block, entry.generation) 376 }; 377 let inode = file::read_inode(cache, bio, inode_block)?; 378 379 match inode.generation == generation { 380 true => {} 381 false => return Err(FsError::StaleGeneration), 382 } 383 384 let write_end = (buf_offset as usize) 385 .checked_add(len as usize) 386 .ok_or(FsError::InvalidBlock)?; 387 match write_end <= data_area_size { 388 true => {} 389 false => return Err(FsError::InvalidBlock), 390 } 391 392 let buf = unsafe { 393 core::slice::from_raw_parts_mut(data_area.add(buf_offset as usize), len as usize) 394 }; 395 file::file_read(pool, cache, bio, &inode, offset, buf) 396 })(); 397 398 match result { 399 Ok(bytes_read) => FsResponse::success(FsOpcode::Read as u8, tag, bytes_read as u64, 0), 400 Err(e) => FsResponse::error(FsOpcode::Read as u8, tag, e), 401 } 402} 403 404#[allow(clippy::too_many_arguments)] 405fn handle_write( 406 req: &FsRequest, 407 pool: &mut NodePool, 408 cache: &mut BlockCache, 409 bio: &mut BlockIo, 410 state: &mut FsState, 411 data_area: *mut u8, 412 data_area_size: usize, 413 client_pid: u16, 414 scratch: &mut [u8], 415 hash_table: &mut [u16; LZ4_HASH_SIZE], 416) -> FsResponse { 417 let tag = req.tag; 418 let handle = req.handle; 419 let offset = req.arg0; 420 let len = req.arg1 as u32; 421 let buf_offset = req.arg2 as u32; 422 423 let result = (|| -> Result<usize, FsError> { 424 let (inode_block, inode) = { 425 let client = state 426 .handles 427 .client(client_pid) 428 .ok_or(FsError::InvalidHandle)?; 429 let entry = client.validate(handle, FsRights::WRITE)?; 430 let inode = file::read_inode(cache, bio, entry.inode_block)?; 431 match inode.generation == entry.generation { 432 true => (entry.inode_block, inode), 433 false => return Err(FsError::StaleGeneration), 434 } 435 }; 436 437 let read_end = (buf_offset as usize) 438 .checked_add(len as usize) 439 .ok_or(FsError::InvalidBlock)?; 440 match read_end <= data_area_size { 441 true => {} 442 false => return Err(FsError::InvalidBlock), 443 } 444 445 let data = unsafe { 446 core::slice::from_raw_parts( 447 data_area.add(buf_offset as usize) as *const u8, 448 len as usize, 449 ) 450 }; 451 452 let txn = state.sb_pair.active().transaction_id.wrapping_add(1); 453 let dedup_roots = state.dedup_roots; 454 455 let (updated_inode, written, new_dedups) = file::file_write( 456 pool, 457 cache, 458 bio, 459 &mut state.freemap, 460 &dedup_roots, 461 &inode, 462 offset, 463 data, 464 txn, 465 Compression::None, 466 scratch, 467 hash_table, 468 )?; 469 470 state.dedup_roots = new_dedups; 471 472 file::write_inode_to_cache(cache, bio, inode_block, &updated_inode)?; 473 474 Ok(written) 475 })(); 476 477 match result { 478 Ok(bytes_written) => { 479 FsResponse::success(FsOpcode::Write as u8, tag, bytes_written as u64, 0) 480 } 481 Err(e) => FsResponse::error(FsOpcode::Write as u8, tag, e), 482 } 483} 484 485fn handle_close(req: &FsRequest, state: &mut FsState, client_pid: u16) -> FsResponse { 486 let tag = req.tag; 487 let handle = req.handle; 488 489 let result = state 490 .handles 491 .client_mut(client_pid) 492 .ok_or(FsError::InvalidHandle) 493 .and_then(|client| client.release(handle).map_err(FsError::from)); 494 495 match result { 496 Ok(()) => FsResponse::success(FsOpcode::Close as u8, tag, 0, 0), 497 Err(e) => FsResponse::error(FsOpcode::Close as u8, tag, e), 498 } 499} 500 501fn handle_stat( 502 req: &FsRequest, 503 cache: &mut BlockCache, 504 bio: &mut BlockIo, 505 state: &FsState, 506 client_pid: u16, 507) -> FsResponse { 508 let tag = req.tag; 509 let handle = req.handle; 510 511 let result = (|| -> Result<(u64, u64), FsError> { 512 let client = state 513 .handles 514 .client(client_pid) 515 .ok_or(FsError::InvalidHandle)?; 516 let entry = client.validate(handle, FsRights::READ)?; 517 let inode = file::read_inode(cache, bio, entry.inode_block)?; 518 519 let type_and_blocks = 520 (inode.inode_type as u64) << 48 | (inode.block_count & 0xFFFF_FFFF_FFFF); 521 522 Ok((inode.size, type_and_blocks)) 523 })(); 524 525 match result { 526 Ok((size, type_blocks)) => { 527 FsResponse::success(FsOpcode::Stat as u8, tag, size, type_blocks) 528 } 529 Err(e) => FsResponse::error(FsOpcode::Stat as u8, tag, e), 530 } 531} 532 533#[allow(clippy::too_many_arguments)] 534fn handle_mkdir( 535 req: &FsRequest, 536 pool: &mut NodePool, 537 cache: &mut BlockCache, 538 bio: &mut BlockIo, 539 state: &mut FsState, 540 data_area: *mut u8, 541 data_area_size: usize, 542 client_pid: u16, 543) -> FsResponse { 544 let tag = req.tag; 545 let dir_handle = req.handle; 546 let name_offset = req.arg0; 547 let name_len = req.arg1; 548 549 let result = (|| -> Result<u8, FsError> { 550 let (inode_block, parent_rights) = { 551 let client = state 552 .handles 553 .client(client_pid) 554 .ok_or(FsError::InvalidHandle)?; 555 let entry = client.validate(dir_handle, FsRights::CREATE)?; 556 (entry.inode_block, entry.rights) 557 }; 558 let dir_inode = file::read_inode(cache, bio, inode_block)?; 559 560 let name = unsafe { read_name_from_data(data_area, data_area_size, name_offset, name_len) } 561 .ok_or(FsError::InvalidName)?; 562 563 let txn = state.sb_pair.active().transaction_id.wrapping_add(1); 564 let obj_id = state.next_object_id; 565 state.next_object_id += 1; 566 567 let (updated_parent, child_inode, child_block) = ops::mkdir( 568 pool, 569 cache, 570 bio, 571 &mut state.freemap, 572 &dir_inode, 573 name, 574 txn, 575 obj_id, 576 )?; 577 578 file::write_inode_to_cache(cache, bio, inode_block, &updated_parent)?; 579 580 let child_rights = parent_rights.restrict(FsRights::TRAVERSE_INHERITABLE); 581 let client_mut = state 582 .handles 583 .client_mut(client_pid) 584 .ok_or(FsError::InvalidHandle)?; 585 let new_handle = client_mut.allocate( 586 child_inode.object_id, 587 child_inode.generation, 588 child_rights, 589 child_block, 590 )?; 591 592 Ok(new_handle) 593 })(); 594 595 match result { 596 Ok(handle) => FsResponse::success(FsOpcode::Mkdir as u8, tag, handle as u64, 0), 597 Err(e) => FsResponse::error(FsOpcode::Mkdir as u8, tag, e), 598 } 599} 600 601#[allow(clippy::too_many_arguments)] 602fn handle_unlink( 603 req: &FsRequest, 604 pool: &mut NodePool, 605 cache: &mut BlockCache, 606 bio: &mut BlockIo, 607 state: &mut FsState, 608 data_area: *mut u8, 609 data_area_size: usize, 610 client_pid: u16, 611) -> FsResponse { 612 let tag = req.tag; 613 let dir_handle = req.handle; 614 let name_offset = req.arg0; 615 let name_len = req.arg1; 616 617 let result = (|| -> Result<(), FsError> { 618 let inode_block = { 619 let client = state 620 .handles 621 .client(client_pid) 622 .ok_or(FsError::InvalidHandle)?; 623 let entry = client.validate(dir_handle, FsRights::DELETE)?; 624 entry.inode_block 625 }; 626 let dir_inode = file::read_inode(cache, bio, inode_block)?; 627 628 let name = unsafe { read_name_from_data(data_area, data_area_size, name_offset, name_len) } 629 .ok_or(FsError::InvalidName)?; 630 631 let txn = state.sb_pair.active().transaction_id.wrapping_add(1); 632 let updated_parent = ops::file_delete(pool, cache, bio, &dir_inode, name, txn)?; 633 634 file::write_inode_to_cache(cache, bio, inode_block, &updated_parent)?; 635 636 Ok(()) 637 })(); 638 639 match result { 640 Ok(()) => FsResponse::success(FsOpcode::Unlink as u8, tag, 0, 0), 641 Err(e) => FsResponse::error(FsOpcode::Unlink as u8, tag, e), 642 } 643} 644 645#[allow(clippy::too_many_arguments)] 646fn handle_readdir( 647 req: &FsRequest, 648 pool: &mut NodePool, 649 cache: &mut BlockCache, 650 bio: &mut BlockIo, 651 state: &mut FsState, 652 data_area: *mut u8, 653 data_area_size: usize, 654 client_pid: u16, 655) -> FsResponse { 656 let tag = req.tag; 657 let dir_handle = req.handle; 658 let cursor = req.arg0; 659 let buf_offset = req.arg1 as usize; 660 let buf_len = req.arg2 as usize; 661 662 let result = (|| -> Result<(u64, u64), FsError> { 663 let inode_block = { 664 let client = state 665 .handles 666 .client(client_pid) 667 .ok_or(FsError::InvalidHandle)?; 668 let entry = client.validate(dir_handle, FsRights::LIST)?; 669 entry.inode_block 670 }; 671 let dir_inode = file::read_inode(cache, bio, inode_block)?; 672 673 let write_end = buf_offset 674 .checked_add(buf_len) 675 .ok_or(FsError::InvalidBlock)?; 676 match write_end <= data_area_size { 677 true => {} 678 false => return Err(FsError::InvalidBlock), 679 } 680 681 let max_entries = buf_len / READDIR_ENTRY_SIZE; 682 let mut iter = btree::btree_range(&dir_inode.direct[0], cursor, u64::MAX, None); 683 let mut entries_written: u64 = 0; 684 let mut next_cursor: u64 = cursor; 685 686 let mut done = false; 687 (0..max_entries).try_for_each(|_| -> Result<(), FsError> { 688 match done { 689 true => Ok(()), 690 false => match iter.next(pool, cache, bio)? { 691 None => { 692 done = true; 693 Ok(()) 694 } 695 Some(entry) => { 696 let name_len = entry.logical_size as u16; 697 let child_block = crate::blockref_block_num(&entry); 698 let child_inode = file::read_inode(cache, bio, child_block)?; 699 700 let mut name_buf = [0u8; READDIR_MAX_NAME]; 701 let inline_len = (name_len as usize).min(READDIR_MAX_NAME); 702 if entry.flags & 0x01 != 0 { 703 let mut full_name = [0u8; 34]; 704 crate::dir::unpack_inline_name(&entry, &mut full_name); 705 name_buf[..inline_len].copy_from_slice(&full_name[..inline_len]); 706 } else if name_len > 0 { 707 let inode_data = child_inode.extended_attrs; 708 let copy_len = inline_len.min(inode_data.len()); 709 name_buf[..copy_len].copy_from_slice(&inode_data[..copy_len]); 710 } 711 712 let dir_entry = ReadDirEntry { 713 object_id: child_inode.object_id, 714 size: child_inode.size, 715 name_len, 716 inode_type: child_inode.inode_type, 717 _pad: 0, 718 name: name_buf, 719 _pad2: [0; 4], 720 }; 721 722 let write_pos = buf_offset + entries_written as usize * READDIR_ENTRY_SIZE; 723 let entry_bytes = dir_entry.as_bytes(); 724 unsafe { 725 core::ptr::copy_nonoverlapping( 726 entry_bytes.as_ptr(), 727 data_area.add(write_pos), 728 READDIR_ENTRY_SIZE, 729 ); 730 } 731 732 entries_written += 1; 733 next_cursor = entry.key.wrapping_add(1); 734 Ok(()) 735 } 736 }, 737 } 738 })?; 739 740 Ok((entries_written, next_cursor)) 741 })(); 742 743 match result { 744 Ok((count, next)) => FsResponse::success(FsOpcode::ReadDir as u8, tag, count, next), 745 Err(e) => FsResponse::error(FsOpcode::ReadDir as u8, tag, e), 746 } 747} 748 749#[allow(clippy::too_many_arguments)] 750fn handle_create_snapshot( 751 req: &FsRequest, 752 pool: &mut NodePool, 753 cache: &mut BlockCache, 754 bio: &mut BlockIo, 755 state: &mut FsState, 756 data_area: *mut u8, 757 data_area_size: usize, 758 client_pid: u16, 759) -> FsResponse { 760 let tag = req.tag; 761 let name_offset = req.arg0; 762 let name_len = req.arg1; 763 764 let result = (|| -> Result<(), FsError> { 765 let client = state 766 .handles 767 .client(client_pid) 768 .ok_or(FsError::InvalidHandle)?; 769 client.validate(req.handle, FsRights::SNAPSHOT)?; 770 771 let name = unsafe { read_name_from_data(data_area, data_area_size, name_offset, name_len) } 772 .ok_or(FsError::InvalidName)?; 773 774 let tree_root = state.sb_pair.active().tree_root; 775 let dedup_roots = state.dedup_roots; 776 let snapshot_root = state.sb_pair.active().snapshot_root; 777 let scrub_cursor = state.scrub.cursor(); 778 779 snapshot::create_snapshot( 780 pool, 781 cache, 782 bio, 783 &mut state.freemap, 784 &mut state.reserved, 785 &mut state.sb_pair, 786 &tree_root, 787 &dedup_roots, 788 &snapshot_root, 789 name, 790 Some(&state.ditto), 791 scrub_cursor, 792 state.next_object_id, 793 )?; 794 795 state.dedup_roots = state.sb_pair.active().dedup_roots; 796 Ok(()) 797 })(); 798 799 match result { 800 Ok(()) => FsResponse::success(FsOpcode::CreateSnapshot as u8, tag, 0, 0), 801 Err(e) => FsResponse::error(FsOpcode::CreateSnapshot as u8, tag, e), 802 } 803} 804 805#[allow(clippy::too_many_arguments)] 806fn handle_delete_snapshot( 807 req: &FsRequest, 808 pool: &mut NodePool, 809 cache: &mut BlockCache, 810 bio: &mut BlockIo, 811 state: &mut FsState, 812 data_area: *mut u8, 813 data_area_size: usize, 814 client_pid: u16, 815) -> FsResponse { 816 let tag = req.tag; 817 let name_offset = req.arg0; 818 let name_len = req.arg1; 819 820 let result = (|| -> Result<(), FsError> { 821 let client = state 822 .handles 823 .client(client_pid) 824 .ok_or(FsError::InvalidHandle)?; 825 client.validate(req.handle, FsRights::SNAPSHOT)?; 826 827 let name = unsafe { read_name_from_data(data_area, data_area_size, name_offset, name_len) } 828 .ok_or(FsError::InvalidName)?; 829 830 let snapshot_root = state.sb_pair.active().snapshot_root; 831 let scrub_cursor = state.scrub.cursor(); 832 833 snapshot::delete_snapshot( 834 pool, 835 cache, 836 bio, 837 &mut state.freemap, 838 &mut state.reserved, 839 &mut state.sb_pair, 840 &snapshot_root, 841 name, 842 Some(&state.ditto), 843 scrub_cursor, 844 state.next_object_id, 845 )?; 846 847 state.dedup_roots = state.sb_pair.active().dedup_roots; 848 Ok(()) 849 })(); 850 851 match result { 852 Ok(()) => FsResponse::success(FsOpcode::DeleteSnapshot as u8, tag, 0, 0), 853 Err(e) => FsResponse::error(FsOpcode::DeleteSnapshot as u8, tag, e), 854 } 855} 856 857#[allow(clippy::too_many_arguments)] 858fn handle_sync( 859 req: &FsRequest, 860 pool: &mut NodePool, 861 cache: &mut BlockCache, 862 bio: &mut BlockIo, 863 state: &mut FsState, 864 _scratch: &mut [u8], 865 _hash_table: &mut [u16; LZ4_HASH_SIZE], 866) -> FsResponse { 867 let tag = req.tag; 868 869 let result = (|| -> Result<(), FsError> { 870 let tree_root = state.sb_pair.active().tree_root; 871 let dedup_roots = state.dedup_roots; 872 let snapshot_root = state.sb_pair.active().snapshot_root; 873 let scrub_cursor = state.scrub.cursor(); 874 875 transaction::transaction_commit( 876 pool, 877 cache, 878 bio, 879 &mut state.freemap, 880 &mut state.reserved, 881 &mut state.sb_pair, 882 &tree_root, 883 &dedup_roots, 884 &snapshot_root, 885 Some(&state.ditto), 886 scrub_cursor, 887 state.next_object_id, 888 )?; 889 890 state.dedup_roots = state.sb_pair.active().dedup_roots; 891 Ok(()) 892 })(); 893 894 match result { 895 Ok(()) => FsResponse::success(FsOpcode::Sync as u8, tag, 0, 0), 896 Err(e) => FsResponse::error(FsOpcode::Sync as u8, tag, e), 897 } 898} 899 900fn handle_statfs( 901 req: &FsRequest, 902 cache: &mut BlockCache, 903 bio: &mut BlockIo, 904 state: &mut FsState, 905) -> FsResponse { 906 let tag = req.tag; 907 let total = state.freemap.total_data_blocks(); 908 let free = match state.freemap.count_allocated_blocks(cache, bio) { 909 Ok(allocated) => total.saturating_sub(allocated), 910 Err(_) => 0, 911 }; 912 FsResponse::success(FsOpcode::StatFs as u8, tag, total, free) 913} 914 915fn handle_truncate( 916 req: &FsRequest, 917 _pool: &mut NodePool, 918 cache: &mut BlockCache, 919 bio: &mut BlockIo, 920 state: &mut FsState, 921 client_pid: u16, 922) -> FsResponse { 923 let tag = req.tag; 924 let handle = req.handle; 925 let new_size = req.arg0; 926 927 let result = (|| -> Result<(), FsError> { 928 let (inode_block, inode) = { 929 let client = state 930 .handles 931 .client(client_pid) 932 .ok_or(FsError::InvalidHandle)?; 933 let entry = client.validate(handle, FsRights::WRITE)?; 934 let inode = file::read_inode(cache, bio, entry.inode_block)?; 935 match inode.generation == entry.generation { 936 true => (entry.inode_block, inode), 937 false => return Err(FsError::StaleGeneration), 938 } 939 }; 940 941 match inode.inode_type { 942 0 => {} 943 1 => return Err(FsError::IsADirectory), 944 _ => return Err(FsError::InvalidHandle), 945 } 946 947 match new_size == 0 { 948 true => { 949 let mut updated = inode.clone(); 950 updated.size = 0; 951 updated.block_count = 0; 952 updated.flags |= InodeFlags::INLINE; 953 updated.direct = [BlockRef::ZERO; INODE_DIRECT_REFS]; 954 let txn = state.sb_pair.active().transaction_id.wrapping_add(1); 955 updated.transaction_id = txn; 956 file::write_inode_to_cache(cache, bio, inode_block, &updated)?; 957 Ok(()) 958 } 959 false => match new_size >= inode.size { 960 true => Ok(()), 961 false => Err(FsError::InvalidBlock), 962 }, 963 } 964 })(); 965 966 match result { 967 Ok(()) => FsResponse::success(FsOpcode::Truncate as u8, tag, new_size, 0), 968 Err(e) => FsResponse::error(FsOpcode::Truncate as u8, tag, e), 969 } 970} 971 972#[allow(clippy::too_many_arguments)] 973fn handle_rename( 974 req: &FsRequest, 975 pool: &mut NodePool, 976 cache: &mut BlockCache, 977 bio: &mut BlockIo, 978 state: &mut FsState, 979 data_area: *mut u8, 980 data_area_size: usize, 981 client_pid: u16, 982) -> FsResponse { 983 let tag = req.tag; 984 let src_dir_handle = req.handle; 985 let dst_dir_handle = req.flags; 986 let src_name_offset = req.arg0 as u32 as u64; 987 let src_name_len = (req.arg0 >> 32) as u32 as u64; 988 let dst_name_offset = req.arg1 as u32 as u64; 989 let dst_name_len = (req.arg1 >> 32) as u32 as u64; 990 991 let result = (|| -> Result<(), FsError> { 992 let (src_dir_block, dst_dir_block) = { 993 let client = state 994 .handles 995 .client(client_pid) 996 .ok_or(FsError::InvalidHandle)?; 997 let src_entry = client.validate(src_dir_handle, FsRights::DELETE)?; 998 let dst_entry = client.validate(dst_dir_handle, FsRights::CREATE)?; 999 (src_entry.inode_block, dst_entry.inode_block) 1000 }; 1001 1002 let src_name = unsafe { 1003 read_name_from_data(data_area, data_area_size, src_name_offset, src_name_len) 1004 } 1005 .ok_or(FsError::InvalidName)?; 1006 let dst_name = unsafe { 1007 read_name_from_data(data_area, data_area_size, dst_name_offset, dst_name_len) 1008 } 1009 .ok_or(FsError::InvalidName)?; 1010 1011 let mut src_name_buf = [0u8; MAX_NAME_LEN]; 1012 src_name_buf[..src_name.len()].copy_from_slice(src_name); 1013 let src_len = src_name.len(); 1014 1015 let mut dst_name_buf = [0u8; MAX_NAME_LEN]; 1016 dst_name_buf[..dst_name.len()].copy_from_slice(dst_name); 1017 let dst_len = dst_name.len(); 1018 1019 let src_dir_inode = file::read_inode(cache, bio, src_dir_block)?; 1020 let child_block = { 1021 let child_cap = ops::resolve_path_cap( 1022 pool, 1023 cache, 1024 bio, 1025 &state 1026 .handles 1027 .client(client_pid) 1028 .ok_or(FsError::InvalidHandle)? 1029 .validate(src_dir_handle, FsRights::TRAVERSE)? 1030 .to_cap(), 1031 &src_dir_inode, 1032 src_dir_block, 1033 &src_name_buf[..src_len], 1034 None, 1035 )?; 1036 child_cap.2 1037 }; 1038 let child_phys = crate::block_num_to_phys(child_block); 1039 1040 let txn = state.sb_pair.active().transaction_id.wrapping_add(1); 1041 1042 let updated_src = ops::file_delete( 1043 pool, 1044 cache, 1045 bio, 1046 &src_dir_inode, 1047 &src_name_buf[..src_len], 1048 txn, 1049 )?; 1050 file::write_inode_to_cache(cache, bio, src_dir_block, &updated_src)?; 1051 1052 let dst_dir_inode = match src_dir_block == dst_dir_block { 1053 true => updated_src, 1054 false => file::read_inode(cache, bio, dst_dir_block)?, 1055 }; 1056 1057 let updated_dst = crate::dir::dir_insert( 1058 pool, 1059 cache, 1060 bio, 1061 &dst_dir_inode, 1062 &dst_name_buf[..dst_len], 1063 child_phys, 1064 txn, 1065 )?; 1066 file::write_inode_to_cache(cache, bio, dst_dir_block, &updated_dst)?; 1067 1068 Ok(()) 1069 })(); 1070 1071 match result { 1072 Ok(()) => FsResponse::success(FsOpcode::Rename as u8, tag, 0, 0), 1073 Err(e) => FsResponse::error(FsOpcode::Rename as u8, tag, e), 1074 } 1075}