starry_api/imp/task/
clone.rs1use alloc::sync::Arc;
2use axerrno::{LinuxError, LinuxResult};
3use axfs::{CURRENT_DIR, CURRENT_DIR_PATH};
4use axhal::arch::{TrapFrame, UspaceContext};
5use axprocess::Pid;
6use axsignal::Signo;
7use axsync::Mutex;
8use axtask::{TaskExtRef, current};
9use bitflags::bitflags;
10use linux_raw_sys::general::*;
11use starry_core::{
12 mm::copy_from_kernel,
13 task::{ProcessData, TaskExt, ThreadData, add_thread_to_table, new_user_task},
14};
15
16use crate::{file::FD_TABLE, ptr::UserPtr};
17
18bitflags! {
19 #[derive(Debug, Clone, Copy, Default)]
21 struct CloneFlags: u32 {
22 const VM = CLONE_VM;
25 const FS = CLONE_FS;
28 const FILES = CLONE_FILES;
31 const SIGHAND = CLONE_SIGHAND;
34 const PTRACE = CLONE_PTRACE;
37 const VFORK = CLONE_VFORK;
41 const PARENT = CLONE_PARENT;
44 const THREAD = CLONE_THREAD;
47 const NEWNS = CLONE_NEWNS;
49 const SYSVSEM = CLONE_SYSVSEM;
52 const SETTLS = CLONE_SETTLS;
54 const PARENT_SETTID = CLONE_PARENT_SETTID;
56 const CHILD_CLEARTID = CLONE_CHILD_CLEARTID;
59 const UNTRACED = CLONE_UNTRACED;
62 const CHILD_SETTID = CLONE_CHILD_SETTID;
64 const NEWCGROUP = CLONE_NEWCGROUP;
66 const NEWUTS = CLONE_NEWUTS;
68 const NEWIPC = CLONE_NEWIPC;
70 const NEWUSER = CLONE_NEWUSER;
72 const NEWPID = CLONE_NEWPID;
74 const NEWNET = CLONE_NEWNET;
76 const IO = CLONE_IO;
78 }
79}
80
81pub fn sys_clone(
82 tf: &TrapFrame,
83 flags: u32,
84 stack: usize,
85 parent_tid: usize,
86 #[cfg(any(target_arch = "x86_64", target_arch = "loongarch64"))] child_tid: usize,
87 tls: usize,
88 #[cfg(not(any(target_arch = "x86_64", target_arch = "loongarch64")))] child_tid: usize,
89) -> LinuxResult<isize> {
90 const FLAG_MASK: u32 = 0xff;
91 let exit_signal = flags & FLAG_MASK;
92 let flags = CloneFlags::from_bits_truncate(flags & !FLAG_MASK);
93
94 info!(
95 "sys_clone <= flags: {:?}, exit_signal: {}, stack: {:#x}, ptid: {:#x}, ctid: {:#x}, tls: {:#x}",
96 flags, exit_signal, stack, parent_tid, child_tid, tls
97 );
98
99 if exit_signal != 0 && flags.contains(CloneFlags::THREAD | CloneFlags::PARENT) {
100 return Err(LinuxError::EINVAL);
101 }
102 if flags.contains(CloneFlags::THREAD) && !flags.contains(CloneFlags::VM | CloneFlags::SIGHAND) {
103 return Err(LinuxError::EINVAL);
104 }
105 let exit_signal = Signo::from_repr(exit_signal as u8);
106
107 let mut new_uctx = UspaceContext::from(tf);
108 if stack != 0 {
109 new_uctx.set_sp(stack);
110 }
111 if flags.contains(CloneFlags::SETTLS) {
112 new_uctx.set_tls(tls);
113 }
114 new_uctx.set_retval(0);
115
116 let set_child_tid = if flags.contains(CloneFlags::CHILD_SETTID) {
117 Some(UserPtr::<u32>::from(child_tid).get_as_mut()?)
118 } else {
119 None
120 };
121
122 let curr = current();
123 let mut new_task = new_user_task(curr.name(), new_uctx, set_child_tid);
124
125 let tid = new_task.id().as_u64() as Pid;
126 if flags.contains(CloneFlags::PARENT_SETTID) {
127 *UserPtr::<Pid>::from(parent_tid).get_as_mut()? = tid;
128 }
129
130 let process = if flags.contains(CloneFlags::THREAD) {
131 new_task.ctx_mut().set_page_table_root(
132 curr.task_ext()
133 .process_data()
134 .aspace
135 .lock()
136 .page_table_root(),
137 );
138
139 curr.task_ext().thread.process()
140 } else {
141 let parent = if flags.contains(CloneFlags::PARENT) {
142 curr.task_ext()
143 .thread
144 .process()
145 .parent()
146 .ok_or(LinuxError::EINVAL)?
147 } else {
148 curr.task_ext().thread.process().clone()
149 };
150 let builder = parent.fork(tid);
151
152 let aspace = if flags.contains(CloneFlags::VM) {
153 curr.task_ext().process_data().aspace.clone()
154 } else {
155 let mut aspace = curr.task_ext().process_data().aspace.lock();
156 let mut aspace = aspace.try_clone()?;
157 copy_from_kernel(&mut aspace)?;
158 Arc::new(Mutex::new(aspace))
159 };
160 new_task
161 .ctx_mut()
162 .set_page_table_root(aspace.lock().page_table_root());
163
164 let signal_actions = if flags.contains(CloneFlags::SIGHAND) {
165 parent
166 .data::<ProcessData>()
167 .map_or_else(Arc::default, |it| it.signal.actions.clone())
168 } else {
169 Arc::default()
170 };
171 let process_data = ProcessData::new(
172 curr.task_ext().process_data().exe_path.read().clone(),
173 aspace,
174 signal_actions,
175 exit_signal,
176 );
177
178 if flags.contains(CloneFlags::FILES) {
179 FD_TABLE
180 .deref_from(&process_data.ns)
181 .init_shared(FD_TABLE.share());
182 } else {
183 FD_TABLE
184 .deref_from(&process_data.ns)
185 .init_new(FD_TABLE.copy_inner());
186 }
187
188 if flags.contains(CloneFlags::FS) {
189 CURRENT_DIR
190 .deref_from(&process_data.ns)
191 .init_shared(CURRENT_DIR.share());
192 CURRENT_DIR_PATH
193 .deref_from(&process_data.ns)
194 .init_shared(CURRENT_DIR_PATH.share());
195 } else {
196 CURRENT_DIR
197 .deref_from(&process_data.ns)
198 .init_new(CURRENT_DIR.copy_inner());
199 CURRENT_DIR_PATH
200 .deref_from(&process_data.ns)
201 .init_new(CURRENT_DIR_PATH.copy_inner());
202 }
203 &builder.data(process_data).build()
204 };
205
206 let thread_data = ThreadData::new(process.data().unwrap());
207 if flags.contains(CloneFlags::CHILD_CLEARTID) {
208 thread_data.set_clear_child_tid(child_tid);
209 }
210
211 let thread = process.new_thread(tid).data(thread_data).build();
212 add_thread_to_table(&thread);
213 new_task.init_task_ext(TaskExt::new(thread));
214 axtask::spawn_task(new_task);
215
216 Ok(tid as _)
217}
218
219pub fn sys_fork(tf: &TrapFrame) -> LinuxResult<isize> {
220 sys_clone(tf, SIGCHLD, 0, 0, 0, 0)
221}