iceoryx2_bb_posix/file_descriptor.rs
1// Copyright (c) 2023 Contributors to the Eclipse Foundation
2//
3// See the NOTICE file(s) distributed with this work for additional
4// information regarding copyright ownership.
5//
6// This program and the accompanying materials are made available under the
7// terms of the Apache Software License 2.0 which is available at
8// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
9// which is available at https://opensource.org/licenses/MIT.
10//
11// SPDX-License-Identifier: Apache-2.0 OR MIT
12
13//! Provides a [`FileDescriptor`] abstraction which takes the ownership of low-level POSIX
14//! file descriptors and the [`FileDescriptorBased`] & [`FileDescriptorManagement`] traits
15//! which provide advanced functionalities to all [`FileDescriptorBased`] constructs.
16//!
17//! # Examples
18//! ## Use [`FileDescriptorManagement`] to extend a type
19//!
20//! ```
21//! use iceoryx2_bb_posix::file_descriptor::*;
22//!
23//! // required for FileDescriptorManagement
24//! #[derive(Debug)]
25//! pub struct SomeConstructBasedOnFileDescriptor {
26//! fd: FileDescriptor
27//! }
28//!
29//! // implement FileDescriptorBased trait
30//! impl FileDescriptorBased for SomeConstructBasedOnFileDescriptor {
31//! fn file_descriptor(&self) -> &FileDescriptor {
32//! &self.fd
33//! }
34//! }
35//!
36//!
37//! // auto implement the FileDescriptorManagement trait to gain more file descriptor management
38//! // features
39//! impl FileDescriptorManagement for SomeConstructBasedOnFileDescriptor {}
40//! ```
41//!
42//! ## Work with [`FileDescriptorManagement`]
43//!
44//! ```no_run
45//! use iceoryx2_bb_system_types::file_path::FilePath;
46//! use iceoryx2_bb_container::semantic_string::SemanticString;
47//! use iceoryx2_bb_posix::file_descriptor::*;
48//! use iceoryx2_bb_posix::file::*;
49//! use iceoryx2_bb_posix::ownership::*;
50//! use iceoryx2_bb_posix::user::UserExt;
51//! use iceoryx2_bb_posix::group::GroupExt;
52//!
53//! let file_name = FilePath::new(b"/tmp/someFile").unwrap();
54//! let mut file = FileBuilder::new(&file_name).creation_mode(CreationMode::PurgeAndCreate)
55//! .create().expect("failed to create file");
56//!
57//! println!("owner: {:?}", file.ownership().unwrap());
58//! println!("permission: {}", file.permission().unwrap());
59//! println!("metadata: {:?}", file.metadata().unwrap());
60//!
61//! // set new owner
62//! file.set_ownership(OwnershipBuilder::new()
63//! .uid("testuser1".as_user().unwrap().uid())
64//! .gid("testgroup1".as_group().unwrap().gid()).create());
65//!
66//! // set new permissions
67//! file.set_permission(Permission::ALL);
68//! ```
69
70use core::fmt::Debug;
71
72use crate::config::EINTR_REPETITIONS;
73use crate::file::*;
74use crate::group::Gid;
75use crate::metadata::Metadata;
76use crate::ownership::*;
77use crate::permission::{Permission, PermissionExt};
78use crate::user::Uid;
79use iceoryx2_bb_log::{error, fail, fatal_panic};
80use iceoryx2_pal_posix::posix::errno::Errno;
81use iceoryx2_pal_posix::*;
82
83/// Represents a FileDescriptor in a POSIX system. Contains always a value greater or equal zero,
84/// a valid file descriptor. It takes the ownership of the provided file descriptor and calls
85/// [`posix::close`] on destruction.
86///
87/// # Example
88///
89/// ```ignore
90/// use iceoryx2_bb_posix::file_descriptor::*;
91///
92/// let valid_fd = FileDescriptor::new(2);
93/// let invalid_fd = FileDescriptor::new(-4);
94///
95/// println!("Created FD: {:?}", valid_fd.unwrap());
96/// ```
97#[repr(C)]
98#[derive(Debug, Eq, PartialEq)]
99pub struct FileDescriptor {
100 value: i32,
101 is_owned: bool,
102}
103
104impl Clone for FileDescriptor {
105 fn clone_from(&mut self, source: &Self) {
106 self.close();
107 // TODO: [#223] START: rewrite lines to: let *self = source.clone()
108 let temp = source.clone();
109 *self = temp;
110 // TODO: [#223] END
111 }
112
113 fn clone(&self) -> Self {
114 let fd_clone = unsafe { posix::dup(self.value) };
115 if fd_clone < 0 {
116 let msg = "Unable to clone file descriptor";
117 match Errno::get() {
118 Errno::EMFILE => {
119 fatal_panic!(from self, "{} since the maximum amount of open file descriptors for the process is reached.", msg)
120 }
121 v => fatal_panic!(from self, "{} since an unknown error occurred ({}).", msg, v),
122 }
123 }
124
125 Self {
126 value: fd_clone,
127 is_owned: true,
128 }
129 }
130}
131
132impl FileDescriptor {
133 /// Creates a FileDescriptor which does not hold the ownership of the file descriptor and will
134 /// not call [`posix::close`] on destruction.
135 pub fn non_owning_new(value: i32) -> Option<FileDescriptor> {
136 if value < 0 {
137 return None;
138 }
139
140 Self::new(value).map(|mut fd| {
141 fd.is_owned = false;
142 fd
143 })
144 }
145
146 /// Creates a new FileDescriptor. If the value is smaller than zero or it does not contain a
147 /// valid file descriptor value it returns [`None`].
148 pub fn new(value: i32) -> Option<FileDescriptor> {
149 if value < 0 {
150 return None;
151 }
152
153 if unsafe { posix::fcntl2(value, posix::F_GETFD) } < 0 {
154 return None;
155 }
156
157 Some(FileDescriptor {
158 value,
159 is_owned: true,
160 })
161 }
162
163 /// Creates a new FileDescriptor.
164 ///
165 /// # Safety
166 ///
167 /// * it must be a valid file descriptor
168 ///
169 pub unsafe fn new_unchecked(value: i32) -> FileDescriptor {
170 FileDescriptor {
171 value,
172 is_owned: true,
173 }
174 }
175
176 /// Returns the underlying value of the FileDescriptor
177 ///
178 /// # Safety
179 ///
180 /// * the user shall not store the value in a variable otherwise lifetime issues may be
181 /// encountered
182 /// * do not manually close the file descriptor with a sys call
183 ///
184 pub unsafe fn native_handle(&self) -> i32 {
185 self.value
186 }
187
188 fn close(&mut self) {
189 let mut counter = 0;
190 loop {
191 if unsafe { posix::close(self.value) } == 0 {
192 break;
193 }
194
195 match Errno::get() {
196 Errno::EBADF => {
197 fatal_panic!(from self, "This should never happen! Unable to close file due to an invalid file-descriptor.");
198 }
199 Errno::EINTR => {
200 counter += 1;
201 if counter > EINTR_REPETITIONS {
202 error!(from self, "Unable to close file since too many interrupt signals were received.");
203 }
204 }
205 Errno::EIO => {
206 error!(from self, "Unable to close file due to an I/O error.");
207 counter += 1;
208 }
209 v => {
210 fatal_panic!(from self, "This should never happen! Unable to close file since an unknown error occurred ({}).", v);
211 }
212 }
213
214 if counter > EINTR_REPETITIONS {
215 error!(from self, "Tried {} times to close the file but failed.", counter);
216 }
217 }
218 }
219}
220
221impl Drop for FileDescriptor {
222 fn drop(&mut self) {
223 if self.is_owned {
224 self.close()
225 }
226 }
227}
228
229/// Every construct which is based on some [`FileDescriptor`] can implement this trait to gain
230/// extended [`FileDescriptorManagement`] features.
231pub trait FileDescriptorBased {
232 /// Returns the file descriptor of the underlying construct
233 fn file_descriptor(&self) -> &FileDescriptor;
234}
235
236impl FileDescriptorBased for FileDescriptor {
237 fn file_descriptor(&self) -> &FileDescriptor {
238 self
239 }
240}
241
242impl FileDescriptorManagement for FileDescriptor {}
243
244/// Provides additional feature for every file descriptor based construct like
245/// * ownership handling, [`ownership`](FileDescriptorManagement::ownership()),
246/// [`set_ownership`](FileDescriptorManagement::set_ownership())
247/// * permission handling, [`permission`](FileDescriptorManagement::permission()),
248/// [`set_permission`](FileDescriptorManagement::set_permission())
249/// * truncate size, [`truncate`](FileDescriptorManagement::truncate())
250/// * accessing extended stats via [`Metadata`], [`metadata`](FileDescriptorManagement::metadata())
251///
252pub trait FileDescriptorManagement: FileDescriptorBased + Debug + Sized {
253 /// Returns the current user and group owner of the file descriptor
254 fn ownership(&self) -> Result<Ownership, FileStatError> {
255 let attr =
256 fail!(from self, when File::acquire_attributes(self), "Unable to read file owner.");
257 Ok(OwnershipBuilder::new()
258 .uid(Uid::new_from_native(attr.st_uid))
259 .gid(Gid::new_from_native(attr.st_gid))
260 .create())
261 }
262
263 /// Sets a new user and group owner
264 fn set_ownership(&mut self, ownership: Ownership) -> Result<(), FileSetOwnerError> {
265 fail!(from self, when File::set_ownership(self, ownership.uid(), ownership.gid()),
266 "Unable to set owner of the file.");
267 Ok(())
268 }
269
270 /// Returns the current permission of the file descriptor
271 fn permission(&self) -> Result<Permission, FileStatError> {
272 Ok(
273 fail!(from self, when File::acquire_attributes(self), "Unable to read permissions.")
274 .st_mode
275 .as_permission(),
276 )
277 }
278
279 /// Sets new permissions
280 fn set_permission(&mut self, permission: Permission) -> Result<(), FileSetPermissionError> {
281 fail!(from self, when File::set_permission(self, permission),
282 "Unable to update permission.");
283 Ok(())
284 }
285
286 /// Truncates to the file descriptor corresponding construct
287 fn truncate(&mut self, size: usize) -> Result<(), FileTruncateError> {
288 fail!(from self, when File::truncate(self, size),
289 "Unable to truncate to {}.", size);
290 Ok(())
291 }
292
293 /// Requires all available [`Metadata`] for the file descriptor
294 fn metadata(&self) -> Result<Metadata, FileStatError> {
295 Ok(Metadata::create(
296 &fail!(from self, when File::acquire_attributes(self),
297 "Unable to acquire attributes to create Metadata."),
298 ))
299 }
300}