blob: c41ab8d1b80f843d83d5f78b7933a92b4d7b0b0b [file] [log] [blame]
Andrew Walbran9487efd2024-08-20 18:37:59 +01001// Copyright (C) 2024 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Andrew Walbran0a3eb862024-09-18 13:49:04 +010015use std::{
16 ffi::c_int,
17 mem::forget,
18 os::fd::{BorrowedFd, FromRawFd, IntoRawFd, OwnedFd},
19 ptr::NonNull,
20};
Andrew Walbran9487efd2024-08-20 18:37:59 +010021
22/// Rust wrapper around `native_handle_t`.
23///
24/// This owns the `native_handle_t` and its file descriptors, and will close them and free it when
25/// it is dropped.
26#[derive(Debug)]
27pub struct NativeHandle(NonNull<ffi::native_handle_t>);
28
29impl NativeHandle {
Andrew Walbran0a3eb862024-09-18 13:49:04 +010030 /// Creates a new `NativeHandle` with the given file descriptors and integer values.
31 ///
32 /// The `NativeHandle` will take ownership of the file descriptors and close them when it is
33 /// dropped.
34 pub fn new(fds: Vec<OwnedFd>, ints: &[c_int]) -> Option<Self> {
35 let fd_count = fds.len();
36 // SAFETY: native_handle_create doesn't have any safety requirements.
37 let handle = unsafe {
38 ffi::native_handle_create(fd_count.try_into().unwrap(), ints.len().try_into().unwrap())
39 };
40 let handle = NonNull::new(handle)?;
41 for (i, fd) in fds.into_iter().enumerate() {
42 // SAFETY: `handle` must be valid because it was just created, and the array offset is
43 // within the bounds of what we allocated above.
44 unsafe {
45 *(*handle.as_ptr()).data.as_mut_ptr().add(i) = fd.into_raw_fd();
46 }
47 }
48 for (i, value) in ints.iter().enumerate() {
49 // SAFETY: `handle` must be valid because it was just created, and the array offset is
50 // within the bounds of what we allocated above. Note that `data` is uninitialized
51 // until after this so we can't use `slice::from_raw_parts_mut` or similar to create a
52 // reference to it so we use raw pointers arithmetic instead.
53 unsafe {
54 *(*handle.as_ptr()).data.as_mut_ptr().add(fd_count + i) = *value;
55 }
56 }
57 // SAFETY: `handle` must be valid because it was just created.
58 unsafe {
59 ffi::native_handle_set_fdsan_tag(handle.as_ptr());
60 }
61 Some(Self(handle))
62 }
63
64 /// Returns a borrowed view of all the file descriptors in this native handle.
65 pub fn fds(&self) -> Vec<BorrowedFd> {
66 self.data()[..self.fd_count()]
67 .iter()
68 .map(|fd| {
69 // SAFETY: The `native_handle_t` maintains ownership of the file descriptor so it
70 // won't be closed until this `NativeHandle` is destroyed. The `BorrowedFd` will
71 // have a lifetime constrained to that of `&self`, so it can't outlive it.
72 unsafe { BorrowedFd::borrow_raw(*fd) }
73 })
74 .collect()
75 }
76
77 /// Returns the integer values in this native handle.
78 pub fn ints(&self) -> &[c_int] {
79 &self.data()[self.fd_count()..]
80 }
81
82 /// Destroys the `NativeHandle`, taking ownership of the file descriptors it contained.
83 pub fn into_fds(self) -> Vec<OwnedFd> {
84 let fds = self.data()[..self.fd_count()]
85 .iter()
86 .map(|fd| {
87 // SAFETY: The `native_handle_t` has ownership of the file descriptor, and
88 // after this we destroy it without closing the file descriptor so we can take over
89 // ownership of it.
90 unsafe { OwnedFd::from_raw_fd(*fd) }
91 })
92 .collect();
93
94 // SAFETY: Our wrapped `native_handle_t` pointer is always valid, and it won't be accessed
95 // after this because we own it and forget it.
96 unsafe {
97 assert_eq!(ffi::native_handle_delete(self.0.as_ptr()), 0);
98 }
99 // Don't drop self, as that would cause `native_handle_close` to be called and close the
100 // file descriptors.
101 forget(self);
102 fds
103 }
104
105 /// Returns a reference to the underlying `native_handle_t`.
106 fn as_ref(&self) -> &ffi::native_handle_t {
107 // SAFETY: All the ways of creating a `NativeHandle` ensure that the `native_handle_t` is
108 // valid and initialised, and lives as long as the `NativeHandle`. We enforce Rust's
109 // aliasing rules by giving the reference a lifetime matching that of `&self`.
110 unsafe { self.0.as_ref() }
111 }
112
113 /// Returns the number of file descriptors included in the native handle.
114 fn fd_count(&self) -> usize {
115 self.as_ref().numFds.try_into().unwrap()
116 }
117
118 /// Returns the number of integer values included in the native handle.
119 fn int_count(&self) -> usize {
120 self.as_ref().numInts.try_into().unwrap()
121 }
122
123 /// Returns a slice reference for all the used `data` field of the native handle, including both
124 /// file descriptors and integers.
125 fn data(&self) -> &[c_int] {
126 let total_count = self.fd_count() + self.int_count();
127 // SAFETY: The data must have been initialised with this number of elements when the
128 // `NativeHandle` was created.
129 unsafe { self.as_ref().data.as_slice(total_count) }
130 }
131
Andrew Walbran9487efd2024-08-20 18:37:59 +0100132 /// Wraps a raw `native_handle_t` pointer, taking ownership of it.
133 ///
134 /// # Safety
135 ///
136 /// `native_handle` must be a valid pointer to a `native_handle_t`, and must not be used
137 /// anywhere else after calling this method.
138 pub unsafe fn from_raw(native_handle: NonNull<ffi::native_handle_t>) -> Self {
139 Self(native_handle)
140 }
141
142 /// Creates a new `NativeHandle` wrapping a clone of the given `native_handle_t` pointer.
143 ///
144 /// Unlike [`from_raw`](Self::from_raw) this doesn't take ownership of the pointer passed in, so
145 /// the caller remains responsible for closing and freeing it.
146 ///
147 /// # Safety
148 ///
149 /// `native_handle` must be a valid pointer to a `native_handle_t`.
150 pub unsafe fn clone_from_raw(native_handle: NonNull<ffi::native_handle_t>) -> Option<Self> {
151 // SAFETY: The caller promised that `native_handle` was valid.
152 let cloned = unsafe { ffi::native_handle_clone(native_handle.as_ptr()) };
153 NonNull::new(cloned).map(Self)
154 }
155
156 /// Returns a raw pointer to the wrapped `native_handle_t`.
157 ///
158 /// This is only valid as long as this `NativeHandle` exists, so shouldn't be stored. It mustn't
159 /// be closed or deleted.
160 pub fn as_raw(&self) -> NonNull<ffi::native_handle_t> {
161 self.0
162 }
163
164 /// Turns the `NativeHandle` into a raw `native_handle_t`.
165 ///
166 /// The caller takes ownership of the `native_handle_t` and its file descriptors, so is
167 /// responsible for closing and freeing it.
168 pub fn into_raw(self) -> NonNull<ffi::native_handle_t> {
169 let raw = self.0;
170 forget(self);
171 raw
172 }
173}
174
175impl Clone for NativeHandle {
176 fn clone(&self) -> Self {
177 // SAFETY: Our wrapped `native_handle_t` pointer is always valid.
178 unsafe { Self::clone_from_raw(self.0) }.expect("native_handle_clone returned null")
179 }
180}
181
182impl Drop for NativeHandle {
183 fn drop(&mut self) {
184 // SAFETY: Our wrapped `native_handle_t` pointer is always valid, and it won't be accessed
185 // after this because we own it and are being dropped.
186 unsafe {
187 assert_eq!(ffi::native_handle_close(self.0.as_ptr()), 0);
188 assert_eq!(ffi::native_handle_delete(self.0.as_ptr()), 0);
189 }
190 }
191}
192
193// SAFETY: `NativeHandle` owns the `native_handle_t`, which just contains some integers and file
194// descriptors, which aren't tied to any particular thread.
195unsafe impl Send for NativeHandle {}
196
197// SAFETY: A `NativeHandle` can be used from different threads simultaneously, as is is just
198// integers and file descriptors.
199unsafe impl Sync for NativeHandle {}
Andrew Walbran0a3eb862024-09-18 13:49:04 +0100200
201#[cfg(test)]
202mod test {
203 use super::*;
204 use std::fs::File;
205
206 #[test]
207 fn create_empty() {
208 let handle = NativeHandle::new(vec![], &[]).unwrap();
209 assert_eq!(handle.fds().len(), 0);
210 assert_eq!(handle.ints(), &[]);
211 }
212
213 #[test]
214 fn create_with_ints() {
215 let handle = NativeHandle::new(vec![], &[1, 2, 42]).unwrap();
216 assert_eq!(handle.fds().len(), 0);
217 assert_eq!(handle.ints(), &[1, 2, 42]);
218 }
219
220 #[test]
221 fn create_with_fd() {
222 let file = File::open("/dev/null").unwrap();
223 let handle = NativeHandle::new(vec![file.into()], &[]).unwrap();
224 assert_eq!(handle.fds().len(), 1);
225 assert_eq!(handle.ints(), &[]);
226 }
227
228 #[test]
229 fn clone() {
230 let file = File::open("/dev/null").unwrap();
231 let original = NativeHandle::new(vec![file.into()], &[42]).unwrap();
232 assert_eq!(original.ints(), &[42]);
233 assert_eq!(original.fds().len(), 1);
234
235 let cloned = original.clone();
236 drop(original);
237
238 assert_eq!(cloned.ints(), &[42]);
239 assert_eq!(cloned.fds().len(), 1);
240
241 drop(cloned);
242 }
243}