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}