Nothing to see here, move along
at main 449 lines 13 kB view raw
1use crate::block_io::BlockIo; 2use crate::cache::BlockCache; 3use crate::cap::{FsCap, FsRights}; 4use crate::dir; 5use crate::error::FsError; 6use crate::file; 7use crate::freemap::FreemapAllocator; 8use crate::pool::NodePool; 9use lancer_core::fs::{INODE_INLINE_MAX, Inode, InodeFlags, InodeType, MAX_SYMLINK_DEPTH}; 10 11const MAX_PATH_COMPONENTS: usize = 32; 12const MAX_NAME_LEN: usize = 680; 13 14#[allow(clippy::too_many_arguments)] 15pub fn file_create( 16 pool: &mut NodePool, 17 cache: &mut BlockCache, 18 bio: &mut BlockIo, 19 freemap: &mut FreemapAllocator, 20 parent_inode: &Inode, 21 name: &[u8], 22 inode_type: InodeType, 23 txn: u64, 24 next_object_id: u64, 25) -> Result<(Inode, Inode, u64), FsError> { 26 if name.is_empty() || name.len() > MAX_NAME_LEN { 27 return Err(FsError::NameTooLong); 28 } 29 30 let inode_block = freemap.alloc_blocks(cache, bio, 1)?; 31 32 let new_inode = match inode_type { 33 InodeType::File => { 34 let mut inode = Inode::new_file(next_object_id, 1, txn, 0, 0); 35 inode.flags |= InodeFlags::INLINE; 36 inode 37 } 38 InodeType::Directory => Inode::new_directory(next_object_id, 1, txn, 0, 0), 39 InodeType::Symlink => Inode::new_symlink(next_object_id, 1, txn, 0, 0, &[]), 40 }; 41 42 let inode_with_name = match name.len() > 34 { 43 true => { 44 let mut inode = new_inode; 45 inode.extended_attrs[..name.len()].copy_from_slice(name); 46 inode 47 } 48 false => new_inode, 49 }; 50 51 let inode_phys = crate::block_num_to_phys(inode_block); 52 file::write_inode_to_cache(cache, bio, inode_block, &inode_with_name)?; 53 54 let updated_parent = dir::dir_insert(pool, cache, bio, parent_inode, name, inode_phys, txn)?; 55 56 Ok((updated_parent, inode_with_name, inode_block)) 57} 58 59pub fn file_delete( 60 pool: &mut NodePool, 61 cache: &mut BlockCache, 62 bio: &mut BlockIo, 63 parent_inode: &Inode, 64 name: &[u8], 65 txn: u64, 66) -> Result<Inode, FsError> { 67 dir::dir_remove(pool, cache, bio, parent_inode, name, txn) 68} 69 70#[allow(clippy::too_many_arguments)] 71pub fn mkdir( 72 pool: &mut NodePool, 73 cache: &mut BlockCache, 74 bio: &mut BlockIo, 75 freemap: &mut FreemapAllocator, 76 parent_inode: &Inode, 77 name: &[u8], 78 txn: u64, 79 next_object_id: u64, 80) -> Result<(Inode, Inode, u64), FsError> { 81 file_create( 82 pool, 83 cache, 84 bio, 85 freemap, 86 parent_inode, 87 name, 88 InodeType::Directory, 89 txn, 90 next_object_id, 91 ) 92} 93 94pub fn rmdir( 95 pool: &mut NodePool, 96 cache: &mut BlockCache, 97 bio: &mut BlockIo, 98 parent_inode: &Inode, 99 name: &[u8], 100 txn: u64, 101) -> Result<Inode, FsError> { 102 let entry = 103 dir::dir_lookup(pool, cache, bio, parent_inode, name, None)?.ok_or(FsError::NotFound)?; 104 105 let child_block = crate::blockref_block_num(&entry); 106 let child_inode = file::read_inode(cache, bio, child_block)?; 107 108 match child_inode.inode_type_enum() { 109 Some(InodeType::Directory) => {} 110 _ => return Err(FsError::NotADirectory), 111 } 112 113 match dir::dir_is_empty(pool, cache, bio, &child_inode)? { 114 true => {} 115 false => return Err(FsError::DirectoryNotEmpty), 116 } 117 118 file_delete(pool, cache, bio, parent_inode, name, txn) 119} 120 121#[allow(clippy::too_many_arguments)] 122pub fn create_symlink( 123 pool: &mut NodePool, 124 cache: &mut BlockCache, 125 bio: &mut BlockIo, 126 freemap: &mut FreemapAllocator, 127 parent_inode: &Inode, 128 name: &[u8], 129 target: &[u8], 130 txn: u64, 131 next_object_id: u64, 132) -> Result<(Inode, Inode, u64), FsError> { 133 if target.is_empty() || target.len() > INODE_INLINE_MAX { 134 return Err(FsError::InvalidName); 135 } 136 137 if name.is_empty() || name.len() > MAX_NAME_LEN { 138 return Err(FsError::NameTooLong); 139 } 140 141 let inode_block = freemap.alloc_blocks(cache, bio, 1)?; 142 143 let symlink_inode = Inode::new_symlink(next_object_id, 1, txn, 0, 0, target); 144 145 let inode_with_name = match name.len() > 34 { 146 true => { 147 let mut inode = symlink_inode; 148 inode.extended_attrs[..name.len()].copy_from_slice(name); 149 inode 150 } 151 false => symlink_inode, 152 }; 153 154 let inode_phys = crate::block_num_to_phys(inode_block); 155 file::write_inode_to_cache(cache, bio, inode_block, &inode_with_name)?; 156 157 let updated_parent = dir::dir_insert(pool, cache, bio, parent_inode, name, inode_phys, txn)?; 158 159 Ok((updated_parent, inode_with_name, inode_block)) 160} 161 162pub fn readlink(inode: &Inode) -> Option<&[u8]> { 163 match inode.inode_type_enum() { 164 Some(InodeType::Symlink) => inode.inline_data(), 165 _ => None, 166 } 167} 168 169#[allow(clippy::too_many_arguments)] 170pub fn resolve_path( 171 pool: &mut NodePool, 172 cache: &mut BlockCache, 173 bio: &mut BlockIo, 174 root_inode: &Inode, 175 root_block_num: u64, 176 start_inode: &Inode, 177 start_block_num: u64, 178 path: &[u8], 179 txn_filter: Option<u64>, 180 symlink_depth: u8, 181) -> Result<(Inode, u64), FsError> { 182 if symlink_depth >= MAX_SYMLINK_DEPTH { 183 return Err(FsError::SymlinkDepthExceeded); 184 } 185 186 let (base_inode, base_block) = match path.first() { 187 Some(b'/') => (root_inode.clone(), root_block_num), 188 _ => (start_inode.clone(), start_block_num), 189 }; 190 191 let components = PathComponents::new(path); 192 193 components.fold_bounded( 194 (base_inode, base_block), 195 MAX_PATH_COMPONENTS, 196 |state, component| { 197 walk_component( 198 pool, 199 cache, 200 bio, 201 root_inode, 202 root_block_num, 203 state, 204 component, 205 txn_filter, 206 symlink_depth, 207 ) 208 }, 209 ) 210} 211 212#[allow(clippy::too_many_arguments)] 213fn walk_component( 214 pool: &mut NodePool, 215 cache: &mut BlockCache, 216 bio: &mut BlockIo, 217 root_inode: &Inode, 218 root_block_num: u64, 219 (current_inode, current_block): (Inode, u64), 220 component: &[u8], 221 txn_filter: Option<u64>, 222 symlink_depth: u8, 223) -> Result<(Inode, u64), FsError> { 224 match component { 225 b".." => Err(FsError::InvalidName), 226 b"." => Ok((current_inode, current_block)), 227 name => { 228 match current_inode.inode_type_enum() { 229 Some(InodeType::Directory) => {} 230 _ => return Err(FsError::NotADirectory), 231 } 232 233 let entry = dir::dir_lookup(pool, cache, bio, &current_inode, name, txn_filter)? 234 .ok_or(FsError::NotFound)?; 235 let child_block = crate::blockref_block_num(&entry); 236 let child_inode = file::read_inode(cache, bio, child_block)?; 237 238 match child_inode.inode_type_enum() { 239 Some(InodeType::Symlink) => { 240 let target = child_inode.inline_data().ok_or(FsError::InvalidBlock)?; 241 resolve_path( 242 pool, 243 cache, 244 bio, 245 root_inode, 246 root_block_num, 247 &current_inode, 248 current_block, 249 target, 250 txn_filter, 251 symlink_depth + 1, 252 ) 253 } 254 _ => Ok((child_inode, child_block)), 255 } 256 } 257 } 258} 259 260struct PathComponents<'a> { 261 remaining: &'a [u8], 262} 263 264impl<'a> PathComponents<'a> { 265 fn new(path: &'a [u8]) -> Self { 266 Self { remaining: path } 267 } 268 269 fn fold_bounded<S, F>(self, init: S, max_components: usize, mut f: F) -> Result<S, FsError> 270 where 271 F: FnMut(S, &[u8]) -> Result<S, FsError>, 272 { 273 self.enumerate() 274 .try_fold(init, |state, (i, component)| match i >= max_components { 275 true => Err(FsError::PathTooDeep), 276 false => f(state, component), 277 }) 278 } 279} 280 281impl<'a> Iterator for PathComponents<'a> { 282 type Item = &'a [u8]; 283 284 fn next(&mut self) -> Option<Self::Item> { 285 skip_slashes(&mut self.remaining); 286 match self.remaining.is_empty() { 287 true => None, 288 false => { 289 let end = self 290 .remaining 291 .iter() 292 .position(|&b| b == b'/') 293 .unwrap_or(self.remaining.len()); 294 let component = &self.remaining[..end]; 295 self.remaining = &self.remaining[end..]; 296 Some(component) 297 } 298 } 299 } 300} 301 302fn skip_slashes(remaining: &mut &[u8]) { 303 let skip = remaining 304 .iter() 305 .position(|&b| b != b'/') 306 .unwrap_or(remaining.len()); 307 *remaining = &remaining[skip..]; 308} 309 310#[allow(clippy::too_many_arguments)] 311pub fn resolve_path_cap( 312 pool: &mut NodePool, 313 cache: &mut BlockCache, 314 bio: &mut BlockIo, 315 dir_cap: &FsCap, 316 dir_inode: &Inode, 317 dir_block: u64, 318 path: &[u8], 319 txn_filter: Option<u64>, 320) -> Result<(FsCap, Inode, u64), FsError> { 321 dir_cap.check_rights(FsRights::TRAVERSE)?; 322 323 let components = PathComponents::new(path); 324 325 let (cap, inode, block, _depth) = components.fold_bounded( 326 (*dir_cap, dir_inode.clone(), dir_block, 0u8), 327 MAX_PATH_COMPONENTS, 328 |(cap, inode, block, depth), component| { 329 walk_component_cap( 330 pool, 331 cache, 332 bio, 333 dir_cap, 334 dir_inode, 335 dir_block, 336 (cap, inode, block), 337 component, 338 txn_filter, 339 depth, 340 ) 341 }, 342 )?; 343 Ok((cap, inode, block)) 344} 345 346#[allow(clippy::too_many_arguments)] 347fn walk_component_cap( 348 pool: &mut NodePool, 349 cache: &mut BlockCache, 350 bio: &mut BlockIo, 351 root_cap: &FsCap, 352 root_inode: &Inode, 353 root_block: u64, 354 (current_cap, current_inode, current_block): (FsCap, Inode, u64), 355 component: &[u8], 356 txn_filter: Option<u64>, 357 symlink_depth: u8, 358) -> Result<(FsCap, Inode, u64, u8), FsError> { 359 match component { 360 b".." => Err(FsError::InsufficientRights), 361 b"." => Ok((current_cap, current_inode, current_block, symlink_depth)), 362 name => { 363 current_cap.check_rights(FsRights::TRAVERSE)?; 364 365 match current_inode.inode_type_enum() { 366 Some(InodeType::Directory) => {} 367 _ => return Err(FsError::NotADirectory), 368 } 369 370 let entry = dir::dir_lookup(pool, cache, bio, &current_inode, name, txn_filter)? 371 .ok_or(FsError::NotFound)?; 372 let child_block = crate::blockref_block_num(&entry); 373 let child_inode = file::read_inode(cache, bio, child_block)?; 374 375 match child_inode.inode_type_enum() { 376 Some(InodeType::Symlink) => match symlink_depth >= MAX_SYMLINK_DEPTH { 377 true => Err(FsError::SymlinkDepthExceeded), 378 false => { 379 let target = child_inode.inline_data().ok_or(FsError::InvalidBlock)?; 380 let (cap, inode, block) = resolve_path_cap_recursive( 381 pool, 382 cache, 383 bio, 384 root_cap, 385 root_inode, 386 root_block, 387 &current_cap, 388 &current_inode, 389 current_block, 390 target, 391 txn_filter, 392 symlink_depth + 1, 393 )?; 394 Ok((cap, inode, block, symlink_depth + 1)) 395 } 396 }, 397 _ => { 398 let child_cap = 399 current_cap.derive(child_inode.object_id, child_inode.generation); 400 Ok((child_cap, child_inode, child_block, symlink_depth)) 401 } 402 } 403 } 404 } 405} 406 407#[allow(clippy::too_many_arguments)] 408fn resolve_path_cap_recursive( 409 pool: &mut NodePool, 410 cache: &mut BlockCache, 411 bio: &mut BlockIo, 412 root_cap: &FsCap, 413 root_inode: &Inode, 414 root_block: u64, 415 current_cap: &FsCap, 416 current_inode: &Inode, 417 current_block: u64, 418 path: &[u8], 419 txn_filter: Option<u64>, 420 symlink_depth: u8, 421) -> Result<(FsCap, Inode, u64), FsError> { 422 let (base_cap, base_inode, base_block) = match path.first() { 423 Some(b'/') => (*root_cap, root_inode.clone(), root_block), 424 _ => (*current_cap, current_inode.clone(), current_block), 425 }; 426 427 let components = PathComponents::new(path); 428 429 components 430 .fold_bounded( 431 (base_cap, base_inode, base_block, symlink_depth), 432 MAX_PATH_COMPONENTS, 433 |(cap, inode, block, depth), component| { 434 walk_component_cap( 435 pool, 436 cache, 437 bio, 438 root_cap, 439 root_inode, 440 root_block, 441 (cap, inode, block), 442 component, 443 txn_filter, 444 depth, 445 ) 446 }, 447 ) 448 .map(|(cap, inode, block, _depth)| (cap, inode, block)) 449}