twizzler/object/
builder.rs

1use std::{marker::PhantomData, mem::MaybeUninit};
2
3use twizzler_abi::{
4    object::{ObjID, Protections},
5    syscall::{
6        BackingType, CreateTieSpec, LifetimeType, ObjectCreate, ObjectCreateFlags, ObjectSource,
7    },
8};
9use twizzler_rt_abi::{bindings::CREATE_KIND_NEW, object::MapFlags};
10
11use super::{Object, TxObject};
12use crate::{
13    marker::{BaseType, StoreCopy},
14    object::RawObject,
15    Result,
16};
17
18/// An object builder, for constructing objects using a builder API.
19#[derive(Clone)]
20pub struct ObjectBuilder<Base: BaseType> {
21    spec: ObjectCreate,
22    src_objs: Vec<ObjectSource>,
23    ties: Vec<CreateTieSpec>,
24    name: Option<String>,
25    _pd: PhantomData<Base>,
26}
27
28impl<Base: BaseType> ObjectBuilder<Base> {
29    /// Make a new object builder.
30    pub fn new(spec: ObjectCreate) -> Self {
31        Self {
32            spec,
33            _pd: PhantomData,
34            name: None,
35            src_objs: Vec::new(),
36            ties: Vec::new(),
37        }
38    }
39
40    /// Make the object persistent.
41    pub fn persist(mut self, persist: bool) -> Self {
42        if persist {
43            self.spec.lt = LifetimeType::Persistent;
44        } else {
45            self.spec.lt = LifetimeType::Volatile;
46        }
47        self
48    }
49
50    /// Cast the base type.
51    pub fn cast<U: BaseType>(self) -> ObjectBuilder<U> {
52        ObjectBuilder::<U>::new(self.spec)
53    }
54
55    /// Add a Source Object that this new object will copy from.
56    pub fn add_src(mut self, obj_src: ObjectSource) -> Self {
57        self.src_objs.push(obj_src);
58        self
59    }
60
61    /// Add a tie specification for this object creation.
62    pub fn add_tie(mut self, tie: CreateTieSpec) -> Self {
63        self.ties.push(tie);
64        self
65    }
66
67    pub fn named(mut self, name: impl ToString) -> Self {
68        self.name = Some(name.to_string());
69        self
70    }
71}
72
73fn bind_name(id: ObjID, name: &str) -> Result<()> {
74    let create = twizzler_rt_abi::bindings::create_options {
75        id: id.raw(),
76        kind: CREATE_KIND_NEW,
77    };
78    let fd = twizzler_rt_abi::fd::twz_rt_fd_open(name, create, 0)?;
79    twizzler_rt_abi::fd::twz_rt_fd_close(fd);
80    Ok(())
81}
82
83impl<Base: BaseType + StoreCopy> ObjectBuilder<Base> {
84    /// Build an object using the provided base value.
85    ///
86    /// # Panics
87    /// This function may panic if the default permissions dont contain READ and WRITE.
88    /// If your usecase requires that the default permissions for an object can't
89    /// contain those flags, please look at the `twizzler_security::SecureBuilderExt` trait.
90    /// # Example
91    /// ```
92    /// # use twizzler::object::ObjectBuilder;
93    /// let builder = ObjectBuilder::default();
94    /// let obj = builder.build(42u32).unwrap();
95    /// ```
96    pub fn build(&self, base: Base) -> Result<Object<Base>> {
97        if !self
98            .spec
99            .def_prot
100            .contains(Protections::READ | Protections::WRITE)
101        {
102            // this panic is more helpful than a memory protection fault
103            panic!(
104                "Unable to build object! Default permissions must contain READ | WRITE...\n
105                If you would like to build an object without those permissions, look at the
106                `twizzler_security::SecureBuilderExt` trait
107                "
108            )
109        }
110        self.build_inplace(|tx| tx.write(base))
111    }
112}
113
114impl<Base: BaseType> ObjectBuilder<Base> {
115    /// Build an object using the provided constructor function.
116    ///
117    /// The constructor should call the .write() method on the TxObject, and
118    /// return the result.
119    /// # Example
120    /// ```
121    /// # use twizzler::object::ObjectBuilder;
122    /// let builder = ObjectBuilder::default();
123    /// let obj = builder.build_inplace(|tx| tx.write(42u32)).unwrap();
124    /// ```
125    pub fn build_inplace<F>(&self, ctor: F) -> Result<Object<Base>>
126    where
127        F: FnOnce(TxObject<MaybeUninit<Base>>) -> Result<TxObject<Base>>,
128    {
129        tracing::trace!("object spec{:#?}", self.spec);
130
131        let id = twizzler_abi::syscall::sys_object_create(
132            self.spec,
133            self.src_objs.as_slice(),
134            self.ties.as_slice(),
135        )?;
136        let mut flags = MapFlags::READ | MapFlags::WRITE;
137        if self.spec.lt == LifetimeType::Persistent {
138            flags.insert(MapFlags::PERSIST);
139            if let Some(ref name) = self.name {
140                bind_name(id, name)?;
141            }
142        } else {
143            if let Some(ref name) = self.name {
144                tracing::warn!(
145                    "tried to name volatile object at creation time: {} {}",
146                    id,
147                    name
148                );
149            }
150        }
151
152        tracing::trace!("Creating object with id: {id:?}");
153        let mu_object = unsafe { Object::<MaybeUninit<Base>>::map_unchecked(id, flags) }?;
154        let metadata = mu_object.meta_ptr();
155
156        unsafe {
157            tracing::trace!("metadata:{:#?}", *metadata);
158        }
159
160        let object = ctor(mu_object.into_tx()?)?;
161        object.into_object()
162    }
163
164    /// Build an object using the provided constructor function.
165    ///
166    /// The constructor should call the .write() method on the TxObject or
167    /// otherwise ensure that it is safe to call .assume_init on the underlying
168    /// MaybeUninit.
169    ///
170    /// # Safety
171    /// The caller must ensure that the base is initialized, see MaybeUninit::assume_init.
172    ///
173    /// # Example
174    /// ```
175    /// # use twizzler::object::ObjectBuilder;
176    /// let builder = ObjectBuilder::default();
177    /// let obj = unsafe {
178    ///     builder
179    ///         .build_ctor(|tx| {
180    ///             tx.write(42u32);
181    ///         })
182    ///         .unwrap()
183    /// };
184    /// ```
185    pub unsafe fn build_ctor<F>(&self, ctor: F) -> Result<Object<Base>>
186    where
187        F: FnOnce(&mut TxObject<MaybeUninit<Base>>),
188    {
189        self.build_inplace(|mut tx| {
190            ctor(&mut tx);
191            Ok(tx.assume_init())
192        })
193    }
194}
195
196impl<Base: BaseType> Default for ObjectBuilder<Base> {
197    fn default() -> Self {
198        Self::new(ObjectCreate::new(
199            BackingType::Normal,
200            LifetimeType::Volatile,
201            None,
202            ObjectCreateFlags::empty(),
203            Protections::all(),
204        ))
205    }
206}
207
208#[cfg(test)]
209mod tests {
210    use super::ObjectBuilder;
211    use crate::{marker::BaseType, object::TypedObject, ptr::InvPtr};
212
213    #[test]
214    fn builder_simple() {
215        let builder = ObjectBuilder::default();
216        let obj = builder.build(42u32).unwrap();
217        let base = obj.base();
218        assert_eq!(*base, 42);
219    }
220
221    struct Foo {
222        ptr: InvPtr<u32>,
223    }
224    impl BaseType for Foo {}
225
226    #[test]
227    fn builder_complex() {
228        let builder = ObjectBuilder::default();
229        let obj_1 = builder.build(42u32).unwrap();
230        let base = obj_1.base_ref();
231        assert_eq!(*base, 42);
232
233        let builder = ObjectBuilder::<Foo>::default();
234        let obj = builder
235            .build_inplace(|tx| {
236                let foo = Foo {
237                    ptr: InvPtr::new(&tx, base)?,
238                };
239                tx.write(foo)
240            })
241            .unwrap();
242        let base_foo = obj.base();
243        let r = unsafe { base_foo.ptr.resolve() };
244        assert_eq!(*r, 42);
245    }
246}