1 #![no_std] 2 3 #![feature(array_chunks, map_first_last)] 4 5 extern crate alloc; 6 7 use core::mem::size_of; 8 9 use alloc::{ 10 boxed::Box, 11 collections::BTreeMap, 12 vec, 13 }; 14 15 //TODO: allow use of either 32-bit or 64-bit programs 16 #[cfg(target_pointer_width = "32")] 17 use goblin::elf32::{header::Header, program_header::program_header32::{ProgramHeader, PT_LOAD, PT_INTERP, PF_W, PF_X}}; 18 #[cfg(target_pointer_width = "64")] 19 use goblin::elf64::{header::Header, program_header::program_header64::{ProgramHeader, PT_LOAD, PT_INTERP, PF_W, PF_X}}; 20 21 22 use syscall::{ 23 PAGE_SIZE, 24 error::*, 25 flag::{MapFlags, SEEK_SET}, 26 }; 27 28 pub use self::arch::*; 29 mod arch; 30 31 pub enum FexecResult { 32 Normal { addrspace_handle: FdGuard }, 33 Interp { path: Box<[u8]>, image_file: FdGuard, open_via_dup: FdGuard, interp_override: InterpOverride }, 34 } 35 pub struct InterpOverride { 36 phs: Box<[u8]>, 37 at_entry: usize, 38 at_phnum: usize, 39 at_phent: usize, 40 name: Box<[u8]>, 41 tree: BTreeMap<usize, usize>, 42 } 43 44 pub struct ExtraInfo<'a> { 45 pub cwd: Option<&'a [u8]>, 46 } 47 48 pub fn fexec_impl<A, E>(image_file: FdGuard, open_via_dup: FdGuard, memory_scheme_fd: &FdGuard, path: &[u8], args: A, envs: E, total_args_envs_size: usize, extrainfo: &ExtraInfo, mut interp_override: Option<InterpOverride>) -> Result<FexecResult> 49 where 50 A: IntoIterator, 51 E: IntoIterator, 52 A::Item: AsRef<[u8]>, 53 E::Item: AsRef<[u8]>, 54 { 55 // Here, we do the minimum part of loading an application, which is what the kernel used to do. 56 // We load the executable into memory (albeit at different offsets in this executable), fix 57 // some misalignments, and then execute the SYS_EXEC syscall to replace the program memory 58 // entirely. 59 60 let mut header_bytes = [0_u8; size_of::<Header>()]; 61 read_all(*image_file, Some(0), &mut header_bytes)?; 62 let header = Header::from_bytes(&header_bytes); 63 64 let grants_fd = { 65 let current_addrspace_fd = FdGuard::new(syscall::dup(*open_via_dup, b"addrspace")?); 66 FdGuard::new(syscall::dup(*current_addrspace_fd, b"empty")?) 67 }; 68 let memory_fd = FdGuard::new(syscall::dup(*grants_fd, b"mem")?); 69 70 // Never allow more than 1 MiB of program headers. 71 const MAX_PH_SIZE: usize = 1024 * 1024; 72 let phentsize = u64::from(header.e_phentsize) as usize; 73 let phnum = u64::from(header.e_phnum) as usize; 74 let pheaders_size = phentsize.saturating_mul(phnum).saturating_add(size_of::<Header>()); 75 76 if pheaders_size > MAX_PH_SIZE { 77 return Err(Error::new(E2BIG)); 78 } 79 let mut phs_raw = vec! [0_u8; pheaders_size]; 80 phs_raw[..size_of::<Header>()].copy_from_slice(&header_bytes); 81 let phs = &mut phs_raw[size_of::<Header>()..]; 82 83 // TODO: Remove clone, but this would require more as_refs and as_muts 84 let mut tree = interp_override.as_mut().map_or_else(|| { 85 core::iter::once((0, PAGE_SIZE)).collect::<BTreeMap<_, _>>() 86 }, |o| core::mem::take(&mut o.tree)); 87 88 const BUFSZ: usize = 1024 * 256; 89 let mut buf = vec! [0_u8; BUFSZ]; 90 91 read_all(*image_file as usize, Some(header.e_phoff as u64), phs).map_err(|_| Error::new(EIO))?; 92 93 for ph_idx in 0..phnum { 94 let ph_bytes = &phs[ph_idx * phentsize..(ph_idx + 1) * phentsize]; 95 let segment: &ProgramHeader = plain::from_bytes(ph_bytes).map_err(|_| Error::new(EINVAL))?; 96 let mut flags = syscall::PROT_READ; 97 98 // W ^ X. If it is executable, do not allow it to be writable, even if requested 99 if segment.p_flags & PF_X == PF_X { 100 flags |= syscall::PROT_EXEC; 101 } else if segment.p_flags & PF_W == PF_W { 102 flags |= syscall::PROT_WRITE; 103 } 104 105 let voff = segment.p_vaddr as usize % PAGE_SIZE; 106 let vaddr = segment.p_vaddr as usize - voff; 107 let size = 108 (segment.p_memsz as usize + voff + PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE; 109 110 if segment.p_filesz > segment.p_memsz { 111 return Err(Error::new(ENOEXEC)); 112 } 113 #[forbid(unreachable_patterns)] 114 match segment.p_type { 115 // PT_INTERP must come before any PT_LOAD, so we don't have to iterate twice. 116 PT_INTERP => { 117 let mut interp = vec! [0_u8; segment.p_filesz as usize]; 118 read_all(*image_file as usize, Some(segment.p_offset as u64), &mut interp)?; 119 120 return Ok(FexecResult::Interp { 121 path: interp.into_boxed_slice(), 122 image_file, 123 open_via_dup, 124 interp_override: InterpOverride { 125 at_entry: header.e_entry as usize, 126 at_phnum: phnum, 127 at_phent: phentsize, 128 phs: phs_raw.into_boxed_slice(), 129 name: path.into(), 130 tree, 131 } 132 }); 133 } 134 PT_LOAD => { 135 allocate_remote(&grants_fd, memory_scheme_fd, vaddr, size, syscall::PROT_READ | syscall::PROT_WRITE)?; 136 syscall::lseek(*image_file as usize, segment.p_offset as isize, SEEK_SET).map_err(|_| Error::new(EIO))?; 137 syscall::lseek(*memory_fd, segment.p_vaddr as isize, SEEK_SET).map_err(|_| Error::new(EIO))?; 138 139 for size in core::iter::repeat(buf.len()).take((segment.p_filesz as usize) / buf.len()).chain(Some((segment.p_filesz as usize) % buf.len())) { 140 read_all(*image_file as usize, None, &mut buf[..size]).map_err(|_| Error::new(EIO))?; 141 let _ = syscall::write(*memory_fd, &buf[..size]).map_err(|_| Error::new(EIO))?; 142 } 143 mprotect_remote(&grants_fd, vaddr, size, flags)?; 144 145 if !tree.range(..=vaddr).next_back().filter(|(start, size)| **start + **size > vaddr).is_some() { 146 tree.insert(vaddr, size); 147 } 148 } 149 _ => continue, 150 } 151 } 152 153 allocate_remote(&grants_fd, memory_scheme_fd, STACK_TOP - STACK_SIZE, STACK_SIZE, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?; 154 tree.insert(STACK_TOP - STACK_SIZE, STACK_SIZE); 155 156 let mut sp = STACK_TOP - 256; 157 158 let mut push = |word: usize| { 159 sp -= size_of::<usize>(); 160 write_all(*memory_fd, Some(sp as u64), &usize::to_ne_bytes(word)) 161 }; 162 163 let pheaders_to_convey = if let Some(ref r#override) = interp_override { 164 &*r#override.phs 165 } else { 166 &*phs_raw 167 }; 168 let pheaders_size_aligned = (pheaders_to_convey.len()+PAGE_SIZE-1)/PAGE_SIZE*PAGE_SIZE; 169 let pheaders = find_free_target_addr(&tree, pheaders_size_aligned).ok_or(Error::new(ENOMEM))?; 170 tree.insert(pheaders, pheaders_size_aligned); 171 allocate_remote(&grants_fd, memory_scheme_fd, pheaders, pheaders_size_aligned, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?; 172 write_all(*memory_fd, Some(pheaders as u64), &pheaders_to_convey)?; 173 mprotect_remote(&grants_fd, pheaders, pheaders_size_aligned, MapFlags::PROT_READ)?; 174 175 push(0)?; 176 push(AT_NULL)?; 177 push(header.e_entry as usize)?; 178 if let Some(ref r#override) = interp_override { 179 push(AT_BASE)?; 180 push(r#override.at_entry)?; 181 } 182 push(AT_ENTRY)?; 183 push(pheaders + size_of::<Header>())?; 184 push(AT_PHDR)?; 185 push(interp_override.as_ref().map_or(header.e_phnum as usize, |o| o.at_phnum))?; 186 push(AT_PHNUM)?; 187 push(interp_override.as_ref().map_or(header.e_phentsize as usize, |o| o.at_phent))?; 188 push(AT_PHENT)?; 189 190 let total_args_envs_auxvpointee_size = total_args_envs_size + extrainfo.cwd.map_or(0, |s| s.len() + 1); 191 let args_envs_size_aligned = (total_args_envs_auxvpointee_size+PAGE_SIZE-1)/PAGE_SIZE*PAGE_SIZE; 192 let target_args_env_address = find_free_target_addr(&tree, args_envs_size_aligned).ok_or(Error::new(ENOMEM))?; 193 allocate_remote(&grants_fd, memory_scheme_fd, target_args_env_address, args_envs_size_aligned, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?; 194 tree.insert(target_args_env_address, args_envs_size_aligned); 195 196 let mut offset = 0; 197 198 let mut argc = 0; 199 200 { 201 let mut append = |source_slice: &[u8]| { 202 let address = target_args_env_address + offset; 203 write_all(*memory_fd, Some(address as u64), source_slice)?; 204 offset += source_slice.len() + 1; 205 Ok(address) 206 }; 207 208 if let Some(cwd) = extrainfo.cwd { 209 push(append(cwd)?)?; 210 push(AT_REDOX_INITIALCWD_PTR)?; 211 push(cwd.len())?; 212 push(AT_REDOX_INITIALCWD_LEN)?; 213 } 214 215 push(0)?; 216 217 for env in envs { 218 push(append(env.as_ref())?)?; 219 } 220 221 push(0)?; 222 223 for arg in args { 224 push(append(arg.as_ref())?)?; 225 argc += 1; 226 } 227 } 228 229 push(argc)?; 230 231 unsafe { deactivate_tcb(*open_via_dup)?; } 232 233 { 234 let current_sigaction_fd = FdGuard::new(syscall::dup(*open_via_dup, b"sigactions")?); 235 let empty_sigaction_fd = FdGuard::new(syscall::dup(*current_sigaction_fd, b"empty")?); 236 let sigaction_selection_fd = FdGuard::new(syscall::dup(*open_via_dup, b"current-sigactions")?); 237 238 let _ = syscall::write(*sigaction_selection_fd, &usize::to_ne_bytes(*empty_sigaction_fd))?; 239 } 240 241 // TODO: Restore old name if exec failed? 242 if let Ok(name_fd) = syscall::dup(*open_via_dup, b"name").map(FdGuard::new) { 243 let _ = syscall::write(*name_fd, interp_override.as_ref().map_or(path, |o| &o.name)); 244 } 245 if interp_override.is_some() { 246 let mmap_min_fd = FdGuard::new(syscall::dup(*grants_fd, b"mmap-min-addr")?); 247 let last_addr = tree.iter().rev().nth(1).map_or(0, |(off, len)| *off + *len); 248 let aligned_last_addr = (last_addr + PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE; 249 let _ = syscall::write(*mmap_min_fd, &usize::to_ne_bytes(aligned_last_addr)); 250 } 251 252 let addrspace_selection_fd = FdGuard::new(syscall::dup(*open_via_dup, b"current-addrspace")?); 253 254 let _ = syscall::write(*addrspace_selection_fd, &create_set_addr_space_buf(*grants_fd, header.e_entry as usize, sp)); 255 256 Ok(FexecResult::Normal { addrspace_handle: addrspace_selection_fd }) 257 } 258 fn write_usizes<const N: usize>(fd: &FdGuard, usizes: [usize; N]) -> Result<()> { 259 let _ = syscall::write(**fd, unsafe { plain::as_bytes(&usizes) }); 260 Ok(()) 261 } 262 fn allocate_remote(addrspace_fd: &FdGuard, memory_scheme_fd: &FdGuard, dst_addr: usize, len: usize, flags: MapFlags) -> Result<()> { 263 mmap_remote(addrspace_fd, memory_scheme_fd, 0, dst_addr, len, flags) 264 } 265 pub fn mmap_remote(addrspace_fd: &FdGuard, fd: &FdGuard, offset: usize, dst_addr: usize, len: usize, flags: MapFlags) -> Result<()> { 266 write_usizes(addrspace_fd, [ 267 // op 268 syscall::flag::ADDRSPACE_OP_MMAP, 269 // fd 270 **fd, 271 // "offset" 272 offset, 273 // address 274 dst_addr, 275 // size 276 len, 277 // flags 278 (flags | MapFlags::MAP_FIXED_NOREPLACE).bits(), 279 ]) 280 } 281 pub fn mprotect_remote(addrspace_fd: &FdGuard, addr: usize, len: usize, flags: MapFlags) -> Result<()> { 282 write_usizes(addrspace_fd, [ 283 // op 284 syscall::flag::ADDRSPACE_OP_MPROTECT, 285 // address 286 addr, 287 // size 288 len, 289 // flags 290 flags.bits(), 291 ]) 292 } 293 pub fn munmap_remote(addrspace_fd: &FdGuard, addr: usize, len: usize) -> Result<()> { 294 write_usizes(addrspace_fd, [ 295 // op 296 syscall::flag::ADDRSPACE_OP_MUNMAP, 297 // address 298 addr, 299 // size 300 len, 301 ]) 302 } 303 pub fn munmap_transfer(src: &FdGuard, dst: &FdGuard, src_addr: usize, dst_addr: usize, len: usize, flags: MapFlags) -> Result<()> { 304 write_usizes(dst, [ 305 // op 306 syscall::flag::ADDRSPACE_OP_TRANSFER, 307 // fd 308 **src, 309 // "offset" (source address) 310 src_addr, 311 // address 312 dst_addr, 313 // size 314 len, 315 // flags 316 (flags | MapFlags::MAP_FIXED_NOREPLACE).bits(), 317 ]) 318 } 319 fn read_all(fd: usize, offset: Option<u64>, buf: &mut [u8]) -> Result<()> { 320 if let Some(offset) = offset { 321 syscall::lseek(fd, offset as isize, SEEK_SET)?; 322 } 323 324 let mut total_bytes_read = 0; 325 326 while total_bytes_read < buf.len() { 327 total_bytes_read += match syscall::read(fd, &mut buf[total_bytes_read..])? { 328 0 => return Err(Error::new(ENOEXEC)), 329 bytes_read => bytes_read, 330 } 331 } 332 Ok(()) 333 } 334 fn write_all(fd: usize, offset: Option<u64>, buf: &[u8]) -> Result<()> { 335 if let Some(offset) = offset { 336 syscall::lseek(fd, offset as isize, SEEK_SET)?; 337 } 338 339 let mut total_bytes_written = 0; 340 341 while total_bytes_written < buf.len() { 342 total_bytes_written += match syscall::write(fd, &buf[total_bytes_written..])? { 343 0 => return Err(Error::new(EIO)), 344 bytes_written => bytes_written, 345 } 346 } 347 Ok(()) 348 } 349 350 // TODO: With the introduction of remote mmaps, remove this and let the kernel handle address 351 // allocation. 352 fn find_free_target_addr(tree: &BTreeMap<usize, usize>, size: usize) -> Option<usize> { 353 let mut iterator = tree.iter().peekable(); 354 355 // Ignore the space between zero and the first region, to avoid null pointers. 356 while let Some((cur_address, entry_size)) = iterator.next() { 357 let end = *cur_address + entry_size; 358 359 if let Some((next_address, _)) = iterator.peek() { 360 if **next_address - end > size { 361 return Some(end); 362 } 363 } 364 // No need to check last entry, since the stack will always be put at the highest 365 // possible address. 366 } 367 368 None 369 } 370 371 pub struct FdGuard { 372 fd: usize, 373 taken: bool, 374 } 375 impl FdGuard { 376 pub fn new(fd: usize) -> Self { 377 Self { 378 fd, taken: false, 379 } 380 } 381 pub fn take(&mut self) -> usize { 382 self.taken = true; 383 self.fd 384 } 385 } 386 impl core::ops::Deref for FdGuard { 387 type Target = usize; 388 389 fn deref(&self) -> &Self::Target { 390 &self.fd 391 } 392 } 393 394 impl Drop for FdGuard { 395 fn drop(&mut self) { 396 if !self.taken { 397 let _ = syscall::close(self.fd); 398 } 399 } 400 } 401 pub fn create_set_addr_space_buf(space: usize, ip: usize, sp: usize) -> [u8; size_of::<usize>() * 3] { 402 let mut buf = [0_u8; 3 * size_of::<usize>()]; 403 let mut chunks = buf.array_chunks_mut::<{size_of::<usize>()}>(); 404 *chunks.next().unwrap() = usize::to_ne_bytes(space); 405 *chunks.next().unwrap() = usize::to_ne_bytes(sp); 406 *chunks.next().unwrap() = usize::to_ne_bytes(ip); 407 buf 408 } 409 410 #[path = "../../../auxv_defs.rs"] 411 pub mod auxv_defs; 412 413 use auxv_defs::*; 414 415 /// Spawns a new context which will not share the same address space as the current one. File 416 /// descriptors from other schemes are reobtained with `dup`, and grants referencing such file 417 /// descriptors are reobtained through `fmap`. Other mappings are kept but duplicated using CoW. 418 pub fn fork_impl() -> Result<usize> { 419 unsafe { 420 Error::demux(__relibc_internal_fork_wrapper()) 421 } 422 } 423 424 fn fork_inner(initial_rsp: *mut usize) -> Result<usize> { 425 let (cur_filetable_fd, new_pid_fd, new_pid); 426 427 { 428 let cur_pid_fd = FdGuard::new(syscall::open("thisproc:current/open_via_dup", syscall::O_CLOEXEC)?); 429 (new_pid_fd, new_pid) = new_context()?; 430 431 // Do not allocate new signal stack, but copy existing address (all memory will be re-mapped 432 // CoW later). 433 { 434 let cur_sigstack_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sigstack")?); 435 let new_sigstack_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sigstack")?); 436 437 let mut sigstack_buf = usize::to_ne_bytes(0); 438 439 let _ = syscall::read(*cur_sigstack_fd, &mut sigstack_buf); 440 let _ = syscall::write(*new_sigstack_fd, &sigstack_buf); 441 } 442 443 copy_str(*cur_pid_fd, *new_pid_fd, "name")?; 444 445 { 446 let cur_sigaction_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sigactions")?); 447 let new_sigaction_fd = FdGuard::new(syscall::dup(*cur_sigaction_fd, b"copy")?); 448 let new_sigaction_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-sigactions")?); 449 450 let _ = syscall::write(*new_sigaction_sel_fd, &usize::to_ne_bytes(*new_sigaction_fd))?; 451 } 452 453 // Copy existing files into new file table, but do not reuse the same file table (i.e. new 454 // parent FDs will not show up for the child). 455 { 456 cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?); 457 458 // This must be done before the address space is copied. 459 unsafe { 460 initial_rsp.write(*cur_filetable_fd); 461 initial_rsp.add(1).write(*new_pid_fd); 462 } 463 } 464 465 // CoW-duplicate address space. 466 { 467 let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?); 468 469 // FIXME: Find mappings which use external file descriptors 470 471 let new_addr_space_fd = FdGuard::new(syscall::dup(*cur_addr_space_fd, b"exclusive")?); 472 473 let mut buf = vec! [0_u8; 4096]; 474 let mut bytes_read = 0; 475 476 loop { 477 let new_bytes_read = syscall::read(*cur_addr_space_fd, &mut buf[bytes_read..])?; 478 479 if new_bytes_read == 0 { break } 480 481 bytes_read += new_bytes_read; 482 } 483 let bytes = &buf[..bytes_read]; 484 485 for struct_bytes in bytes.array_chunks::<{size_of::<usize>() * 4}>() { 486 let mut words = struct_bytes.array_chunks::<{size_of::<usize>()}>().copied().map(usize::from_ne_bytes); 487 488 let addr = words.next().unwrap(); 489 let size = words.next().unwrap(); 490 let flags = words.next().unwrap(); 491 let offset = words.next().unwrap(); 492 493 if flags & 0x8000_0000 == 0 { 494 continue; 495 } 496 let map_flags = MapFlags::from_bits_truncate(flags); 497 498 let grant_fd = FdGuard::new(syscall::dup(*cur_addr_space_fd, alloc::format!("grant-{:x}", addr).as_bytes())?); 499 mmap_remote(&new_addr_space_fd, &grant_fd, offset, addr, size, map_flags)?; 500 } 501 let new_addr_space_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?); 502 503 let buf = create_set_addr_space_buf(*new_addr_space_fd, __relibc_internal_fork_ret as usize, initial_rsp as usize); 504 let _ = syscall::write(*new_addr_space_sel_fd, &buf)?; 505 } 506 copy_env_regs(*cur_pid_fd, *new_pid_fd)?; 507 } 508 // Copy the file table. We do this last to ensure that all previously used file descriptors are 509 // closed. The only exception -- the filetable selection fd and the current filetable fd -- 510 // will be closed by the child process. 511 { 512 // TODO: Use cross_scheme_links or something similar to avoid copying the file table in the 513 // kernel. 514 let new_filetable_fd = FdGuard::new(syscall::dup(*cur_filetable_fd, b"copy")?); 515 let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?); 516 let _ = syscall::write(*new_filetable_sel_fd, &usize::to_ne_bytes(*new_filetable_fd)); 517 } 518 519 // Unblock context. 520 syscall::kill(new_pid, syscall::SIGCONT)?; 521 522 // XXX: Killing with SIGCONT will put (pid, 65536) at key (pid, pgid) into the waitpid of this 523 // context. This means that if pgid is changed (as it is in ion for example), the pgid message 524 // in syscall::exit() will not be inserted as the key comparator thinks they're equal as their 525 // PIDs are. So, we have to call this to clear the waitpid queue to prevent deadlocks. 526 let _ = syscall::waitpid(new_pid, &mut 0, syscall::WUNTRACED | syscall::WCONTINUED); 527 528 Ok(new_pid) 529 } 530 531 pub fn new_context() -> Result<(FdGuard, usize)> { 532 // Create a new context (fields such as uid/gid will be inherited from the current context). 533 let fd = FdGuard::new(syscall::open("thisproc:new/open_via_dup", syscall::O_CLOEXEC)?); 534 535 // Extract pid. 536 let mut buffer = [0_u8; 64]; 537 let len = syscall::fpath(*fd, &mut buffer)?; 538 let buffer = buffer.get(..len).ok_or(Error::new(ENAMETOOLONG))?; 539 540 let colon_idx = buffer.iter().position(|c| *c == b':').ok_or(Error::new(EINVAL))?; 541 let slash_idx = buffer.iter().skip(colon_idx).position(|c| *c == b'/').ok_or(Error::new(EINVAL))? + colon_idx; 542 let pid_bytes = buffer.get(colon_idx + 1..slash_idx).ok_or(Error::new(EINVAL))?; 543 let pid_str = core::str::from_utf8(pid_bytes).map_err(|_| Error::new(EINVAL))?; 544 let pid = pid_str.parse::<usize>().map_err(|_| Error::new(EINVAL))?; 545 546 Ok((fd, pid)) 547 } 548 549 pub fn copy_str(cur_pid_fd: usize, new_pid_fd: usize, key: &str) -> Result<()> { 550 let cur_name_fd = FdGuard::new(syscall::dup(cur_pid_fd, key.as_bytes())?); 551 let new_name_fd = FdGuard::new(syscall::dup(new_pid_fd, key.as_bytes())?); 552 553 // TODO: Max path size? 554 let mut buf = [0_u8; 256]; 555 let len = syscall::read(*cur_name_fd, &mut buf)?; 556 let buf = buf.get(..len).ok_or(Error::new(ENAMETOOLONG))?; 557 558 syscall::write(*new_name_fd, &buf)?; 559 560 Ok(()) 561 } 562