xref: /relibc/src/platform/redox/redox-exec/src/lib.rs (revision be35961d82cd98f2a2e61c4f1869271b9f4af571)
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