starry_api/imp/task/
clone.rs

1use 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    /// Options for use with [`sys_clone`].
20    #[derive(Debug, Clone, Copy, Default)]
21    struct CloneFlags: u32 {
22        /// The calling process and the child process run in the same
23        /// memory space.
24        const VM = CLONE_VM;
25        /// The caller and the child process share the same  filesystem
26        /// information.
27        const FS = CLONE_FS;
28        /// The calling process and the child process share the same file
29        /// descriptor table.
30        const FILES = CLONE_FILES;
31        /// The calling process and the child process share the same table
32        /// of signal handlers.
33        const SIGHAND = CLONE_SIGHAND;
34        /// If the calling process is being traced, then trace the child
35        /// also.
36        const PTRACE = CLONE_PTRACE;
37        /// The execution of the calling process is suspended until the
38        /// child releases its virtual memory resources via a call to
39        /// execve(2) or _exit(2) (as with vfork(2)).
40        const VFORK = CLONE_VFORK;
41        /// The parent of the new child  (as returned by getppid(2))
42        /// will be the same as that of the calling process.
43        const PARENT = CLONE_PARENT;
44        /// The child is placed in the same thread group as the calling
45        /// process.
46        const THREAD = CLONE_THREAD;
47        /// The cloned child is started in a new mount namespace.
48        const NEWNS = CLONE_NEWNS;
49        /// The child and the calling process share a single list of System
50        /// V semaphore adjustment values
51        const SYSVSEM = CLONE_SYSVSEM;
52        /// The TLS (Thread Local Storage) descriptor is set to tls.
53        const SETTLS = CLONE_SETTLS;
54        /// Store the child thread ID in the parent's memory.
55        const PARENT_SETTID = CLONE_PARENT_SETTID;
56        /// Clear (zero) the child thread ID in child memory when the child
57        /// exits, and do a wakeup on the futex at that address.
58        const CHILD_CLEARTID = CLONE_CHILD_CLEARTID;
59        /// A tracing process cannot force `CLONE_PTRACE` on this child
60        /// process.
61        const UNTRACED = CLONE_UNTRACED;
62        /// Store the child thread ID in the child's memory.
63        const CHILD_SETTID = CLONE_CHILD_SETTID;
64        /// Create the process in a new cgroup namespace.
65        const NEWCGROUP = CLONE_NEWCGROUP;
66        /// Create the process in a new UTS namespace.
67        const NEWUTS = CLONE_NEWUTS;
68        /// Create the process in a new IPC namespace.
69        const NEWIPC = CLONE_NEWIPC;
70        /// Create the process in a new user namespace.
71        const NEWUSER = CLONE_NEWUSER;
72        /// Create the process in a new PID namespace.
73        const NEWPID = CLONE_NEWPID;
74        /// Create the process in a new network namespace.
75        const NEWNET = CLONE_NEWNET;
76        /// The new process shares an I/O context with the calling process.
77        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}