Usage Guide
Relevant source files
This guide provides practical examples and patterns for using the cap_access
library in real applications. It covers the essential usage patterns, access methods, and best practices for implementing capability-based access control in your code.
For architectural details about the underlying capability system, see Core Architecture. For information about integrating with ArceOS, see ArceOS Integration.
Basic Usage Patterns
The fundamental workflow with cap_access
involves creating protected objects with WithCap::new()
and accessing them through capability-checked methods.
Creating Protected Objects
flowchart TD subgraph subGraph0["Cap Constants"] READ["Cap::READ"] WRITE["Cap::WRITE"] EXECUTE["Cap::EXECUTE"] end Object["Raw Object(T)"] WithCap_new["WithCap::new()"] Capability["Cap Permission(READ | WRITE | EXECUTE)"] Protected["WithCap<T>Protected Object"] Capability --> WithCap_new EXECUTE --> Capability Object --> WithCap_new READ --> Capability WRITE --> Capability WithCap_new --> Protected
The most common pattern is to wrap objects at creation time with appropriate capabilities:
Object Type | Typical Capabilities | Use Case |
---|---|---|
Configuration Data | Cap::READ | Read-only system settings |
User Files | Cap::READ | Cap::WRITE | Editable user content |
Executable Code | Cap::READ | Cap::EXECUTE | Program binaries |
System Resources | Cap::READ | Cap::WRITE | Cap::EXECUTE | Full access resources |
Sources: src/lib.rs(L4 - L15) src/lib.rs(L23 - L27)
Capability Composition
Capabilities can be combined using bitwise operations to create complex permission sets:
flowchart TD subgraph subGraph1["Combined Capabilities"] RW["Cap::READ | Cap::WRITERead-Write Access"] RX["Cap::READ | Cap::EXECUTERead-Execute Access"] RWX["Cap::READ | Cap::WRITE | Cap::EXECUTEFull Access"] end subgraph subGraph0["Individual Capabilities"] READ_CAP["Cap::READ(1 << 0)"] WRITE_CAP["Cap::WRITE(1 << 1)"] EXEC_CAP["Cap::EXECUTE(1 << 2)"] end EXEC_CAP --> RWX EXEC_CAP --> RX READ_CAP --> RW READ_CAP --> RWX READ_CAP --> RX WRITE_CAP --> RW WRITE_CAP --> RWX
Sources: src/lib.rs(L4 - L15) README.md(L16 - L29)
Access Method Comparison
The WithCap<T>
struct provides three distinct access patterns, each with different safety and error handling characteristics:
flowchart TD subgraph subGraph1["Return Types"] Bool["Boolean CheckValidation only"] Option["Option PatternNone on failure"] Result["Result PatternCustom error on failure"] Direct["Direct ReferenceNo safety checks"] end subgraph subGraph0["Access Methods"] can_access["can_access(cap)→ bool"] access["access(cap)→ Option<&T>"] access_or_err["access_or_err(cap, err)→ Result<&T, E>"] access_unchecked["access_unchecked()→ &T (UNSAFE)"] end WithCap["WithCap<T>Protected Object"] WithCap --> access WithCap --> access_or_err WithCap --> access_unchecked WithCap --> can_access access --> Option access_or_err --> Result access_unchecked --> Direct can_access --> Bool
Method Selection Guidelines
Method | When to Use | Safety Level | Performance |
---|---|---|---|
can_access() | Pre-flight validation checks | Safe | Fastest |
access() | Optional access patterns | Safe | Fast |
access_or_err() | Error handling with context | Safe | Fast |
access_unchecked() | Performance-critical paths | Unsafe | Fastest |
Sources: src/lib.rs(L34 - L48) src/lib.rs(L59 - L78) src/lib.rs(L80 - L99) src/lib.rs(L50 - L57)
Practical Access Patterns
Safe Access with Option Handling
The access()
method returns Option<&T>
and is ideal for scenarios where access failure is expected and should be handled gracefully:
sequenceDiagram participant Client as Client participant WithCapaccess as "WithCap::access()" participant WithCapcan_access as "WithCap::can_access()" participant innerT as "inner: T" Client ->> WithCapaccess: access(requested_cap) WithCapaccess ->> WithCapcan_access: self.cap.contains(requested_cap) alt Capability Match WithCapcan_access -->> WithCapaccess: true WithCapaccess ->> innerT: &self.inner innerT -->> WithCapaccess: &T WithCapaccess -->> Client: Some(&T) else Capability Mismatch WithCapcan_access -->> WithCapaccess: false WithCapaccess -->> Client: None end
This pattern is demonstrated in the README examples where checking for Cap::EXECUTE
on a read-write object returns None
.
Sources: src/lib.rs(L72 - L78) README.md(L22 - L28)
Error-Based Access with Context
The access_or_err()
method provides custom error messages for better debugging and user feedback:
flowchart TD subgraph Output["Output"] Result_Ok["Result::Ok(&T)"] Result_Err["Result::Err(E)"] end subgraph subGraph1["access_or_err Process"] Check["can_access(cap)"] Success["Ok(&inner)"] Failure["Err(custom_error)"] end subgraph Input["Input"] Cap_Request["Requested Capability"] Error_Value["Custom Error Value"] end Cap_Request --> Check Check --> Failure Check --> Success Error_Value --> Check Failure --> Result_Err Success --> Result_Ok
This method is particularly useful in system programming contexts where specific error codes or messages are required for debugging.
Sources: src/lib.rs(L93 - L99)
Unsafe High-Performance Access
The access_unchecked()
method bypasses capability validation for performance-critical code paths:
Safety Requirements:
- Caller must manually verify capability compliance
- Should only be used in trusted, performance-critical contexts
- Requires
unsafe
block, making the safety contract explicit
Sources: src/lib.rs(L55 - L57)
Best Practices
Capability Design Patterns
- Principle of Least Privilege: Grant minimal necessary capabilities
- Capability Composition: Use bitwise OR to combine permissions
- Validation at Boundaries: Check capabilities at system boundaries
- Consistent Error Handling: Choose one access method per subsystem
Integration Patterns
flowchart TD subgraph subGraph2["Protected Resources"] FileSystem["File System Objects"] Memory["Memory Regions"] DeviceDrivers["Device Drivers"] end subgraph subGraph1["cap_access Integration"] WithCap_Factory["WithCap CreationWithCap::new(obj, cap)"] Access_Layer["Access Controlaccess() / access_or_err()"] Validation["Capability Validationcan_access()"] end subgraph subGraph0["Application Layer"] UserCode["User Application Code"] ServiceLayer["Service Layer"] end Access_Layer --> Validation ServiceLayer --> Access_Layer ServiceLayer --> WithCap_Factory UserCode --> Access_Layer UserCode --> WithCap_Factory WithCap_Factory --> DeviceDrivers WithCap_Factory --> FileSystem WithCap_Factory --> Memory
Common Usage Scenarios
Scenario | Recommended Pattern | Example |
---|---|---|
File Access Control | WithCap::new(file, Cap::READ | Cap::WRITE) | User file permissions |
Memory Protection | WithCap::new(region, Cap::READ | Cap::EXECUTE) | Code segment protection |
Device Driver Access | WithCap::new(device, Cap::WRITE) | Hardware write-only access |
Configuration Data | WithCap::new(config, Cap::READ) | Read-only system settings |
Sources: src/lib.rs(L23 - L27) README.md(L19 - L24)
Advanced Usage Patterns
Dynamic Capability Checking
The can_access()
method enables sophisticated access control logic:
This pattern is essential for security-sensitive applications that need comprehensive access logging and audit trails.
Sources: src/lib.rs(L46 - L48)
Multi-Level Access Control
Complex systems often require nested capability checks across multiple abstraction layers:
flowchart TD subgraph subGraph1["WithCap Instances"] AppData["WithCap<UserData>"] KernelData["WithCap<SystemCall>"] HWData["WithCap<Device>"] end subgraph subGraph0["System Architecture"] App["Application LayerCap::READ | Cap::WRITE"] Kernel["Kernel LayerCap::EXECUTE"] Hardware["Hardware LayerCap::READ | Cap::WRITE | Cap::EXECUTE"] end UserAccess["User Read Access"] SyscallAccess["System Call Execution"] DirectHW["Direct Hardware Access"] App --> AppData AppData --> UserAccess HWData --> DirectHW Hardware --> HWData Kernel --> KernelData KernelData --> SyscallAccess
This layered approach ensures that capability validation occurs at appropriate system boundaries while maintaining performance where needed.
Sources: src/lib.rs(L1 - L101)