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}