kernel/object/handle/
mod.rs

1use alloc::{boxed::Box, sync::Arc, vec, vec::Vec};
2use spin::RwLock;
3
4use crate::object::{KernelObject, introspection};
5
6pub mod syscall;
7
8#[cfg(test)]
9mod tests;
10
11/// Handle type for referencing kernel objects
12pub type Handle = u32;
13
14/// Internal structure containing the actual handle table data.
15/// This is wrapped in Arc<RwLock<...>> to enable sharing between tasks.
16struct HandleTableInner {
17    /// Fixed-size handle table allocated on heap to avoid stack overflow
18    handles: Box<[Option<KernelObject>; HandleTable::MAX_HANDLES]>,
19    /// Metadata for each handle allocated on heap to avoid stack overflow
20    metadata: Box<[Option<HandleMetadata>; HandleTable::MAX_HANDLES]>,
21    /// Stack of available handle numbers for O(1) allocation
22    free_handles: Vec<Handle>,
23}
24
25/// Handle table for managing kernel objects with support for sharing between tasks.
26///
27/// This structure uses interior mutability via `Arc<RwLock<...>>` to enable
28/// sharing between parent and child tasks when using CLONE_FILES flag.
29/// The `Clone` implementation creates a shallow copy (Arc clone) that shares
30/// the same underlying data. Use `deep_clone()` for an independent copy.
31pub struct HandleTable {
32    inner: Arc<RwLock<HandleTableInner>>,
33}
34
35impl HandleTable {
36    /// Maximum number of handles per table (POSIX standard limit for fd)
37    pub const MAX_HANDLES: usize = 1024;
38
39    /// Create a new empty handle table.
40    pub fn new() -> Self {
41        // Initialize free handle stack in forward order (0 will be allocated first)
42        let mut free_handles = Vec::new();
43        for handle in (0..Self::MAX_HANDLES as Handle).rev() {
44            free_handles.push(handle);
45        }
46
47        // Allocate handles and metadata as boxed slices to avoid stack overflow
48        let handles = vec![None; Self::MAX_HANDLES]
49            .try_into()
50            .unwrap_or_else(|_| panic!("Failed to create boxed slice for handles"));
51
52        let metadata = vec![None; Self::MAX_HANDLES]
53            .try_into()
54            .unwrap_or_else(|_| panic!("Failed to create boxed slice for metadata"));
55
56        Self {
57            inner: Arc::new(RwLock::new(HandleTableInner {
58                handles,
59                metadata,
60                free_handles,
61            })),
62        }
63    }
64
65    /// Create a deep clone of this handle table (independent copy).
66    ///
67    /// This method creates a completely independent copy of the handle table,
68    /// including all handles and metadata. Use this when you need separate
69    /// handle tables for parent and child tasks (non-CLONE_FILES behavior).
70    pub fn deep_clone(&self) -> Self {
71        let inner = self.inner.read();
72
73        let handles_clone = {
74            let vec: Vec<Option<KernelObject>> = inner.handles.to_vec();
75            vec.try_into()
76                .unwrap_or_else(|_| panic!("slice with incorrect length"))
77        };
78
79        let metadata_clone = {
80            let vec: Vec<Option<HandleMetadata>> = inner.metadata.to_vec();
81            vec.try_into()
82                .unwrap_or_else(|_| panic!("slice with incorrect length"))
83        };
84
85        Self {
86            inner: Arc::new(RwLock::new(HandleTableInner {
87                handles: handles_clone,
88                metadata: metadata_clone,
89                free_handles: inner.free_handles.clone(),
90            })),
91        }
92    }
93
94    /// O(1) allocation with automatic metadata inference
95    pub fn insert(&self, obj: KernelObject) -> Result<Handle, &'static str> {
96        let metadata = Self::infer_metadata_from_object(&obj);
97        self.insert_with_metadata(obj, metadata)
98    }
99
100    /// O(1) allocation with explicit metadata
101    pub fn insert_with_metadata(
102        &self,
103        obj: KernelObject,
104        metadata: HandleMetadata,
105    ) -> Result<Handle, &'static str> {
106        let mut inner = self.inner.write();
107        if let Some(handle) = inner.free_handles.pop() {
108            inner.handles[handle as usize] = Some(obj);
109            inner.metadata[handle as usize] = Some(metadata);
110            Ok(handle)
111        } else {
112            Err("Too many open KernelObjects, limit reached")
113        }
114    }
115
116    /// Infer metadata from KernelObject type and usage context
117    ///
118    /// This function provides reasonable defaults for handle roles based on the KernelObject type.
119    /// Applications can override this by using insert_with_metadata() to specify exact roles.
120    fn infer_metadata_from_object(object: &KernelObject) -> HandleMetadata {
121        let handle_type = match object {
122            KernelObject::Pipe(_) => {
123                // Pipes are typically used for IPC, but could also be used for
124                // logging, temp storage, etc. We default to IPC as the most common case.
125                HandleType::IpcChannel
126            }
127            KernelObject::File(_file_obj) => {
128                // Files can serve many roles. Without additional context,
129                // we default to Regular usage. Applications should use
130                // insert_with_metadata() to specify specific roles like
131                // ConfigFile, LogOutput, etc.
132                HandleType::Regular
133            }
134            #[cfg(feature = "network")]
135            KernelObject::Socket(_) => {
136                // Sockets are used for network and IPC communication
137                HandleType::IpcChannel
138            }
139            KernelObject::EventChannel(_) => {
140                // Event channels are used for pub/sub communication
141                HandleType::EventChannel
142            }
143            KernelObject::EventSubscription(_) => {
144                // Event subscriptions are used for receiving events
145                HandleType::EventSubscription
146            }
147            KernelObject::SharedMemory(_) => {
148                // Shared memory is used for IPC and data sharing
149                HandleType::IpcChannel
150            }
151            KernelObject::Counter(_) => {
152                // Counter is used for event notification (IPC)
153                HandleType::IpcChannel
154            }
155        };
156
157        HandleMetadata {
158            handle_type,
159            access_mode: AccessMode::ReadWrite, // Default value
160            special_semantics: None,            // Normal behavior (inherit on exec, etc.)
161        }
162    }
163
164    /// O(1) access - executes a closure with a reference to the KernelObject
165    ///
166    /// Since the internal data is protected by RwLock, we cannot return a direct
167    /// reference. Instead, use this method to access the object within a closure.
168    pub fn with_object<F, R>(&self, handle: Handle, f: F) -> Option<R>
169    where
170        F: FnOnce(&KernelObject) -> R,
171    {
172        if handle as usize >= Self::MAX_HANDLES {
173            return None;
174        }
175        let inner = self.inner.read();
176        inner.handles[handle as usize].as_ref().map(f)
177    }
178
179    /// O(1) access - returns an Arc-level clone of the KernelObject if it exists
180    ///
181    /// This method returns an Arc-level clone of the KernelObject. Unlike the Clone
182    /// trait which may have side effects (e.g., incrementing Pipe reader/writer counts),
183    /// this performs a simple Arc reference count increment without modifying object state.
184    ///
185    /// For cases where you need dup() semantics (creating a new logical file descriptor
186    /// with proper reference counting), use `clone_for_dup()` instead.
187    pub fn get(&self, handle: Handle) -> Option<KernelObject> {
188        if handle as usize >= Self::MAX_HANDLES {
189            return None;
190        }
191        let inner = self.inner.read();
192        inner.handles[handle as usize]
193            .as_ref()
194            .map(|obj| obj.arc_clone())
195    }
196
197    /// O(1) access - returns a full clone with dup() semantics
198    ///
199    /// This method uses the KernelObject's Clone trait which may invoke custom_clone()
200    /// for objects like Pipes. This is appropriate when duplicating a file descriptor
201    /// (dup/dup2 syscalls) where the new descriptor should be tracked separately.
202    pub fn clone_for_dup(&self, handle: Handle) -> Option<KernelObject> {
203        if handle as usize >= Self::MAX_HANDLES {
204            return None;
205        }
206        let inner = self.inner.read();
207        inner.handles[handle as usize].clone()
208    }
209
210    /// O(1) removal
211    pub fn remove(&self, handle: Handle) -> Option<KernelObject> {
212        if handle as usize >= Self::MAX_HANDLES {
213            return None;
214        }
215
216        let mut inner = self.inner.write();
217        if let Some(obj) = inner.handles[handle as usize].take() {
218            inner.metadata[handle as usize] = None; // Clear metadata too
219            inner.free_handles.push(handle); // Return to free pool
220            Some(obj)
221        } else {
222            None
223        }
224    }
225
226    /// Update metadata for an existing handle
227    pub fn update_metadata(
228        &self,
229        handle: Handle,
230        new_metadata: HandleMetadata,
231    ) -> Result<(), &'static str> {
232        if handle as usize >= Self::MAX_HANDLES {
233            return Err("Invalid handle");
234        }
235
236        let mut inner = self.inner.write();
237        if inner.handles[handle as usize].is_some() {
238            inner.metadata[handle as usize] = Some(new_metadata);
239            Ok(())
240        } else {
241            Err("Handle does not exist")
242        }
243    }
244
245    /// Get the number of open handles
246    pub fn open_count(&self) -> usize {
247        let inner = self.inner.read();
248        Self::MAX_HANDLES - inner.free_handles.len()
249    }
250
251    /// Get all active handles
252    pub fn active_handles(&self) -> Vec<Handle> {
253        let inner = self.inner.read();
254        inner
255            .handles
256            .iter()
257            .enumerate()
258            .filter_map(|(i, handle)| {
259                if handle.is_some() {
260                    Some(i as Handle)
261                } else {
262                    None
263                }
264            })
265            .collect()
266    }
267
268    /// Check if this is the sole owner of the underlying handle table.
269    ///
270    /// Returns `true` if no other `HandleTable` shares the same inner data
271    /// (i.e., the `Arc` strong reference count is 1).
272    /// This is used to decide whether `close_all` should run during task exit:
273    /// when the handle table is shared via `CLONE_FILES` (threads), only the
274    /// last task holding the table should close all handles.
275    pub fn is_sole_owner(&self) -> bool {
276        Arc::strong_count(&self.inner) == 1
277    }
278
279    /// Close all handles (for process termination)
280    pub fn close_all(&self) {
281        let mut inner = self.inner.write();
282        for i in 0..Self::MAX_HANDLES {
283            if let Some(_obj) = inner.handles[i].take() {
284                // obj is automatically dropped, calling its Drop implementation
285                inner.metadata[i] = None; // Clear metadata too
286                inner.free_handles.push(i as Handle);
287            }
288        }
289    }
290
291    /// Check if a handle is valid
292    pub fn is_valid_handle(&self, handle: Handle) -> bool {
293        if handle as usize >= Self::MAX_HANDLES {
294            return false;
295        }
296        let inner = self.inner.read();
297        inner.handles[handle as usize].is_some()
298    }
299
300    /// Get metadata for a handle - returns a clone since we can't return a reference
301    pub fn get_metadata(&self, handle: Handle) -> Option<HandleMetadata> {
302        if handle as usize >= Self::MAX_HANDLES {
303            return None;
304        }
305        let inner = self.inner.read();
306        inner.metadata[handle as usize].clone()
307    }
308
309    /// Execute a closure with access to metadata
310    pub fn with_metadata<F, R>(&self, handle: Handle, f: F) -> Option<R>
311    where
312        F: FnOnce(&HandleMetadata) -> R,
313    {
314        if handle as usize >= Self::MAX_HANDLES {
315            return None;
316        }
317        let inner = self.inner.read();
318        inner.metadata[handle as usize].as_ref().map(f)
319    }
320
321    /// Iterate over handles with their objects and metadata, executing a closure for each
322    pub fn for_each_with_metadata<F>(&self, mut f: F)
323    where
324        F: FnMut(Handle, &KernelObject, &HandleMetadata),
325    {
326        let inner = self.inner.read();
327        for (i, obj) in inner.handles.iter().enumerate() {
328            if let Some(o) = obj.as_ref() {
329                if let Some(m) = inner.metadata[i].as_ref() {
330                    f(i as Handle, o, m);
331                }
332            }
333        }
334    }
335
336    /// Get detailed information about a KernelObject for user space introspection
337    pub fn get_object_info(&self, handle: Handle) -> Option<introspection::KernelObjectInfo> {
338        let inner = self.inner.read();
339
340        if handle as usize >= Self::MAX_HANDLES {
341            return None;
342        }
343
344        let kernel_obj = inner.handles[handle as usize].as_ref()?;
345        let metadata = inner.metadata[handle as usize].as_ref()?;
346        let handle_role = introspection::HandleRole::from(metadata.handle_type.clone());
347        let (readable, writable) = metadata.access_mode.into();
348
349        match kernel_obj {
350            KernelObject::File(_) => Some(introspection::KernelObjectInfo::for_file(
351                handle_role,
352                readable,
353                writable,
354            )),
355            KernelObject::Pipe(_) => Some(introspection::KernelObjectInfo::for_pipe(
356                handle_role,
357                readable,
358                writable,
359            )),
360            #[cfg(feature = "network")]
361            KernelObject::Socket(_) => Some(introspection::KernelObjectInfo::for_socket(
362                handle_role,
363                readable,
364                writable,
365            )),
366            KernelObject::EventChannel(_) => Some(
367                introspection::KernelObjectInfo::for_event_channel(handle_role),
368            ),
369            KernelObject::EventSubscription(_) => Some(
370                introspection::KernelObjectInfo::for_event_subscription(handle_role),
371            ),
372            KernelObject::SharedMemory(_) => Some(
373                introspection::KernelObjectInfo::for_shared_memory(handle_role, readable, writable),
374            ),
375            KernelObject::Counter(_) => Some(introspection::KernelObjectInfo::for_counter(
376                handle_role,
377                readable,
378                writable,
379            )),
380        }
381    }
382
383    /// Get access to the free_handles vector length for testing purposes
384    #[cfg(test)]
385    pub fn free_handles_len(&self) -> usize {
386        self.inner.read().free_handles.len()
387    }
388}
389
390impl Default for HandleTable {
391    fn default() -> Self {
392        Self::new()
393    }
394}
395
396/// Handle metadata for managing special semantics and ABI conversion
397///
398/// This metadata describes HOW a handle is being used, not WHAT the underlying KernelObject is.
399/// This enables proper ABI conversion, security policies, and resource management.
400///
401/// ## Examples of Role-based Usage
402///
403/// ```rust
404/// // Same file object used in different roles
405/// let config_file = file_obj.clone();
406/// let log_file = file_obj.clone();
407///
408/// // Handle for reading configuration
409/// let config_handle = task.handle_table.insert_with_metadata(
410///     KernelObject::File(config_file),
411///     HandleMetadata {
412///         handle_type: HandleType::ConfigFile,
413///         access_mode: AccessMode::ReadOnly,
414///         special_semantics: Some(SpecialSemantics::CloseOnExec),
415///     }
416/// )?;
417///
418/// // Handle for writing logs
419/// let log_handle = task.handle_table.insert_with_metadata(
420///     KernelObject::File(log_file),
421///     HandleMetadata {
422///         handle_type: HandleType::LogOutput,
423///         access_mode: AccessMode::WriteOnly,
424///         special_semantics: Some(SpecialSemantics::Append),
425///     }
426/// )?;
427/// ```
428///
429/// Clone implementation creates a shallow copy (Arc clone).
430/// This means the cloned HandleTable shares the same underlying data.
431/// Use `deep_clone()` to create an independent copy.
432impl Clone for HandleTable {
433    fn clone(&self) -> Self {
434        Self {
435            inner: Arc::clone(&self.inner),
436        }
437    }
438}
439
440#[derive(Clone, Debug)]
441pub struct HandleMetadata {
442    pub handle_type: HandleType,
443    pub access_mode: AccessMode,
444    pub special_semantics: Option<SpecialSemantics>,
445}
446
447/// Role-based handle classification
448///
449/// This enum describes HOW a handle is being used, not WHAT the underlying KernelObject is.
450/// The same KernelObject (e.g., a File) could be used in different roles by different handles.
451#[derive(Clone, Debug, PartialEq)]
452pub enum HandleType {
453    /// Standard input/output/error streams
454    StandardInputOutput(StandardInputOutput),
455    /// Inter-process communication channel
456    IpcChannel,
457    /// Event channel for pub/sub communication
458    EventChannel,
459    /// Event subscription for receiving events
460    EventSubscription,
461    /// Default/generic usage
462    Regular,
463}
464
465#[derive(Clone, Debug, PartialEq)]
466pub enum StandardInputOutput {
467    Stdin,
468    Stdout,
469    Stderr,
470}
471
472#[derive(Clone, Debug, PartialEq, Copy)]
473pub enum AccessMode {
474    ReadOnly,
475    WriteOnly,
476    ReadWrite,
477}
478
479/// Special behaviors that differ from default Unix semantics
480#[derive(Clone, Debug, PartialEq)]
481pub enum SpecialSemantics {
482    CloseOnExec, // Close on exec (O_CLOEXEC)
483    NonBlocking, // Non-blocking mode (O_NONBLOCK)
484    Append,      // Append mode (O_APPEND)
485    Sync,        // Synchronous writes (O_SYNC)
486}
487
488impl Default for HandleMetadata {
489    fn default() -> Self {
490        Self {
491            handle_type: HandleType::Regular,
492            access_mode: AccessMode::ReadWrite,
493            special_semantics: None,
494        }
495    }
496}