iceoryx2_bb_posix/
file.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//! Read, create, write or modify files based on a POSIX api. It provides also advanced features
14//! like [`Permission`] setting and to be created from a [`FileDescriptor`].
15//!
16//! # Examples
17//! ```no_run
18//! # extern crate iceoryx2_loggers;
19//!
20//! use iceoryx2_bb_posix::file::*;
21//! use iceoryx2_bb_system_types::file_path::FilePath;
22//! use iceoryx2_bb_container::semantic_string::SemanticString;
23//!
24//! let file_name = FilePath::new(b"ooh_makes_the_file.md").unwrap();
25//! let mut file = FileBuilder::new(&file_name)
26//!                                  .creation_mode(CreationMode::CreateExclusive)
27//!                                  .permission(Permission::OWNER_ALL | Permission::GROUP_READ)
28//!                                  .truncate_size(1024)
29//!                                  .create().expect("Failed to create file");
30//!
31//! let mut content: Vec<u8> = vec![];
32//! file.read_to_vector(&mut content).expect("Failed to read file");
33//! file.write(content.as_slice()).expect("Failed to write file");
34//!
35//! if File::does_exist(&file_name).expect("Failed to check for existance") {
36//!   println!("Woohoo file exists");
37//! }
38//!
39//! match File::remove(&file_name).expect("Failed to remove") {
40//!   true => println!("removed file"),
41//!   false => println!("file did not exist"),
42//! }
43//! ```
44
45use core::fmt::Debug;
46
47use alloc::string::String;
48use alloc::vec::Vec;
49
50use iceoryx2_bb_concurrency::atomic::AtomicBool;
51use iceoryx2_bb_concurrency::atomic::Ordering;
52use iceoryx2_bb_container::semantic_string::SemanticString;
53use iceoryx2_bb_elementary::enum_gen;
54use iceoryx2_bb_system_types::file_path::FilePath;
55use iceoryx2_log::{fail, trace, warn};
56use iceoryx2_pal_posix::posix::errno::Errno;
57use iceoryx2_pal_posix::posix::MemZeroedStruct;
58use iceoryx2_pal_posix::*;
59
60pub use crate::creation_mode::CreationMode;
61use crate::file_descriptor::{FileDescriptor, FileDescriptorBased, FileDescriptorManagement};
62use crate::group::Gid;
63use crate::group::GroupError;
64use crate::ownership::OwnershipBuilder;
65use crate::user::{Uid, UserError};
66pub use crate::{access_mode::AccessMode, permission::*};
67
68enum_gen! { FileRemoveError
69  entry:
70    InsufficientPermissions,
71    CurrentlyInUse,
72    LoopInSymbolicLinks,
73    MaxSupportedPathLengthExceeded,
74    PartOfReadOnlyFileSystem,
75    UnknownError(i32)
76}
77
78impl core::fmt::Display for FileRemoveError {
79    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
80        write!(f, "FileRemoveError::{self:?}")
81    }
82}
83
84impl core::error::Error for FileRemoveError {}
85
86enum_gen! { FileAccessError
87  entry:
88    LoopInSymbolicLinks,
89    MaxSupportedPathLengthExceeded,
90    InsufficientPermissions,
91    UnknownError(i32)
92}
93
94impl core::fmt::Display for FileAccessError {
95    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96        write!(f, "FileAccessError::{self:?}")
97    }
98}
99
100impl core::error::Error for FileAccessError {}
101
102enum_gen! { FileCreationError
103  entry:
104    InsufficientPermissions,
105    InsufficientMemory,
106    FileAlreadyExists,
107    NoSpaceLeft,
108    FileTooBig,
109    Interrupt,
110    IsDirectory,
111    LoopInSymbolicLinks,
112    FilesytemIsReadOnly,
113    DirectoryDoesNotExist,
114    PerProcessFileHandleLimitReached,
115    SystemWideFileHandleLimitReached,
116    MaxFilePathLengthExceeded,
117    UnknownError(i32)
118
119  mapping:
120    FileStatError,
121    UserError,
122    GroupError,
123    FileSetOwnerError,
124    FileTruncateError,
125    FileSetPermissionError,
126    FileAccessError,
127    FileRemoveError
128}
129
130impl core::fmt::Display for FileCreationError {
131    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
132        write!(f, "FileCreationError::{self:?}")
133    }
134}
135
136impl core::error::Error for FileCreationError {}
137
138enum_gen! { FileOpenError
139  entry:
140    InsufficientPermissions,
141    InsufficientMemory,
142    FileDoesNotExist,
143    FileTooBig,
144    Interrupt,
145    IsDirectory,
146    LoopInSymbolicLinks,
147    FilesytemIsReadOnly,
148    PerProcessFileHandleLimitReached,
149    SystemWideFileHandleLimitReached,
150    MaxFilePathLengthExceeded,
151    UnknownError(i32)
152}
153
154impl core::fmt::Display for FileOpenError {
155    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
156        write!(f, "FileOpenError::{self:?}")
157    }
158}
159
160impl core::error::Error for FileOpenError {}
161
162#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
163pub enum FileTruncateError {
164    Interrupt,
165    SizeTooBig,
166    IOerror,
167    FileNotOpenedForWriting,
168    ReadOnlyFilesystem,
169    UnknownError(i32),
170}
171
172impl core::fmt::Display for FileTruncateError {
173    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
174        write!(f, "FileTruncateError::{self:?}")
175    }
176}
177
178impl core::error::Error for FileTruncateError {}
179
180#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
181pub enum FileStatError {
182    InvalidFileDescriptor,
183    IOerror,
184    FileTooBig,
185    UnknownFileType,
186    UnknownError(i32),
187}
188
189impl core::fmt::Display for FileStatError {
190    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
191        write!(f, "FileStatError::{self:?}")
192    }
193}
194
195impl core::error::Error for FileStatError {}
196
197#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
198pub enum FileSetPermissionError {
199    InvalidFileDescriptor,
200    InsufficientPermissions,
201    ReadOnlyFilesystem,
202    UnknownError(i32),
203}
204
205impl core::fmt::Display for FileSetPermissionError {
206    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
207        write!(f, "FileSetPermissionError::{self:?}")
208    }
209}
210
211impl core::error::Error for FileSetPermissionError {}
212
213#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
214pub enum FileSetOwnerError {
215    InvalidFileDescriptor,
216    InsufficientPermissions,
217    ReadOnlyFilesystem,
218    InvalidId,
219    IOerror,
220    Interrupt,
221    UnknownError(i32),
222}
223
224impl core::fmt::Display for FileSetOwnerError {
225    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
226        write!(f, "FileSetOwnerError::{self:?}")
227    }
228}
229
230impl core::error::Error for FileSetOwnerError {}
231
232enum_gen! { FileReadError
233  entry:
234    Interrupt,
235    IOerror,
236    IsDirectory,
237    FileTooBig,
238    InsufficientResources,
239    InsufficientMemory,
240    NonExistingOrIncapableDevice,
241    UnknownError(i32)
242
243  mapping:
244    FileOffsetError,
245    FileStatError
246}
247
248impl core::fmt::Display for FileReadError {
249    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
250        write!(f, "FileReadError::{self:?}")
251    }
252}
253
254impl core::error::Error for FileReadError {}
255
256#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
257pub enum FileOffsetError {
258    InvalidFileDescriptor,
259    FileTooBig,
260    DoesNotSupportSeeking,
261    UnknownError(i32),
262}
263
264impl core::fmt::Display for FileOffsetError {
265    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
266        write!(f, "FileOffsetError::{self:?}")
267    }
268}
269
270impl core::error::Error for FileOffsetError {}
271
272enum_gen! { FileWriteError
273  entry:
274    Interrupt,
275    WriteBufferTooBig,
276    IOerror,
277    NoSpaceLeft,
278    InsufficientResources,
279    InsufficientPermissions,
280    NonExistingOrIncapableDevice,
281    UnknownError(i32)
282  mapping:
283    FileOffsetError
284}
285
286impl core::fmt::Display for FileWriteError {
287    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
288        write!(f, "FileWriteError::{self:?}")
289    }
290}
291
292impl core::error::Error for FileWriteError {}
293
294#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
295pub enum FileSyncError {
296    Interrupt,
297    NotSupported,
298    IOerror,
299    UnknownError(i32),
300}
301
302impl core::fmt::Display for FileSyncError {
303    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
304        write!(f, "FileSyncError::{self:?}")
305    }
306}
307
308impl core::error::Error for FileSyncError {}
309
310enum_gen! {
311    /// The FileError enum is a generalization when one doesn't require the fine-grained error
312    /// handling enums. One can forward FileError as more generic return value when a method
313    /// returns a File***Error.
314    /// On a higher level it is again convertable to [`crate::Error`].
315    FileError
316  generalization:
317    Create <= FileCreationError,
318    Write <= FileSyncError; FileWriteError; FileTruncateError; FileRemoveError,
319    Read <= FileOffsetError; FileReadError; FileOpenError; FileAccessError,
320    Credentials <= FileSetOwnerError; FileSetPermissionError,
321    Stat <= FileStatError
322}
323
324impl core::fmt::Display for FileError {
325    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
326        write!(f, "FileError::{self:?}")
327    }
328}
329
330impl core::error::Error for FileError {}
331
332impl From<()> for FileStatError {
333    fn from(_: ()) -> Self {
334        FileStatError::UnknownFileType
335    }
336}
337
338#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
339pub enum FileReadLineState {
340    LineLen(usize),
341    EndOfFile(usize),
342}
343
344/// Opens or creates a new [`File`]. When calling [`FileBuilder::creation_mode`] the
345/// [`FileCreationBuilder`] is returned which provides additional settings only available
346/// for newly created files.
347///
348/// # Details
349/// The default [`AccessMode`] is [`AccessMode::Read`].
350///
351/// # Examples
352/// ## Open existing file for reading
353/// ```
354/// # extern crate iceoryx2_loggers;
355///
356/// use iceoryx2_bb_posix::file::*;
357/// use iceoryx2_bb_system_types::file_path::FilePath;
358/// use iceoryx2_bb_container::semantic_string::SemanticString;
359///
360/// let file_name = FilePath::new(b"/some/where/over/the/rainbow.md").unwrap();
361/// let file = FileBuilder::new(&file_name)
362///                              .open_existing(AccessMode::Read);
363/// ```
364///
365/// ## Create new file for reading and writing with extras
366/// ```ignore
367/// use iceoryx2_bb_posix::file::*;
368/// use iceoryx2_bb_posix::user::UserExt;
369/// use iceoryx2_bb_posix::group::GroupExt;
370/// use iceoryx2_bb_system_types::file_path::FilePath;
371/// use iceoryx2_bb_container::semantic_string::SemanticString;
372///
373/// let file_name = FilePath::new(b"/below/the/surface.md").unwrap();
374/// let file = FileBuilder::new(&file_name)
375///                              .creation_mode(CreationMode::CreateExclusive)
376///                              // BEGIN: optional settings
377///                              .permission(Permission::OWNER_ALL | Permission::GROUP_READ)
378///                              .owner("testuser1".as_user().unwrap().uid())
379///                              .group("testgroup2".as_group().unwrap().gid())
380///                              .truncate_size(1024)
381///                              // END: optional settings
382///                              .create();
383/// ```
384#[derive(Debug)]
385pub struct FileBuilder {
386    file_path: FilePath,
387    access_mode: AccessMode,
388    permission: Permission,
389    has_ownership: bool,
390    owner: Option<Uid>,
391    group: Option<Gid>,
392    truncate_size: Option<usize>,
393    creation_mode: Option<CreationMode>,
394}
395
396impl FileBuilder {
397    /// Creates a new FileBuilder and sets the path of the file which should be opened.
398    pub fn new(file_path: &FilePath) -> Self {
399        FileBuilder {
400            file_path: *file_path,
401            access_mode: AccessMode::Read,
402            permission: Permission::OWNER_ALL,
403            has_ownership: false,
404            owner: None,
405            group: None,
406            truncate_size: None,
407            creation_mode: None,
408        }
409    }
410
411    /// Defines if the created or opened file is owned by the [`File`] object. If it is owned, the
412    /// [`File`] object will remove the underlying file when it goes out of scope.
413    pub fn has_ownership(mut self, value: bool) -> Self {
414        self.has_ownership = value;
415        self
416    }
417
418    /// Returns a [`FileCreationBuilder`] object to define further settings exclusively
419    /// for newly created files. Sets the [`AccessMode`] of the file to [`AccessMode::ReadWrite`].
420    pub fn creation_mode(mut self, value: CreationMode) -> FileCreationBuilder {
421        self.creation_mode = Some(value);
422        self.access_mode = AccessMode::ReadWrite;
423        FileCreationBuilder { config: self }
424    }
425
426    /// Opens an existing file at the given file_path and defines how the files [`AccessMode`],
427    /// for reading, writing or both. Is independent of
428    /// the current permissions of the file. But writing to a read-only file can result
429    /// in some kind of error.
430    pub fn open_existing(mut self, value: AccessMode) -> Result<File, FileOpenError> {
431        self.access_mode = value;
432        File::open(self)
433    }
434}
435
436/// Sets additional settings for files which are being newly created. Is returned when
437/// [`FileBuilder::creation_mode()`] is called in [`FileBuilder`].
438pub struct FileCreationBuilder {
439    config: FileBuilder,
440}
441
442impl FileCreationBuilder {
443    /// Sets the permission of the new file. [`Permission`] behave like a bitset and can
444    /// be used accordingly.
445    ///
446    /// # Examples
447    /// ```no_run
448    /// # extern crate iceoryx2_loggers;
449    ///
450    /// use iceoryx2_bb_posix::file::*;
451    /// use iceoryx2_bb_system_types::file_path::FilePath;
452    /// use iceoryx2_bb_container::semantic_string::SemanticString;
453    ///
454    /// let file_name = FilePath::new(b"someFileName.txt").unwrap();
455    /// let file = FileBuilder::new(&file_name)
456    ///                              .creation_mode(CreationMode::CreateExclusive)
457    ///                              .permission(Permission::OWNER_ALL | Permission::GROUP_READ)
458    ///                              .create().expect("failed to create file");
459    /// ```
460    pub fn permission(mut self, value: Permission) -> Self {
461        self.config.permission = value;
462        self
463    }
464
465    /// Sets the user from a string. If only a user id is available one can acquire the user
466    /// name via [`crate::user::User`] and the trait [`crate::user::UserExt`].
467    ///
468    /// # Examples
469    /// ```no_run
470    /// # extern crate iceoryx2_loggers;
471    ///
472    /// use iceoryx2_bb_posix::file::*;
473    /// use iceoryx2_bb_posix::user::UserExt;
474    /// use iceoryx2_bb_system_types::file_path::FilePath;
475    /// use iceoryx2_bb_container::semantic_string::SemanticString;
476    ///
477    /// let file_name = FilePath::new(b"anotherFile.md").unwrap();
478    /// let file = FileBuilder::new(&file_name)
479    ///                              .creation_mode(CreationMode::CreateExclusive)
480    ///                              .owner("testuser1".as_user().expect("user invalid").uid())
481    ///                              .create().expect("failed to create file");
482    /// ```
483    pub fn owner(mut self, value: Uid) -> Self {
484        self.config.owner = Some(value);
485        self
486    }
487
488    /// Sets the group from a string. If only a group id is available one can acquire the group
489    /// name via [`crate::group::Group`] and the trait [`crate::group::GroupExt`].
490    ///
491    /// # Examples
492    /// ```no_run
493    /// # extern crate iceoryx2_loggers;
494    ///
495    /// use iceoryx2_bb_posix::file::*;
496    /// use iceoryx2_bb_posix::group::*;
497    /// use iceoryx2_bb_system_types::file_path::FilePath;
498    /// use iceoryx2_bb_container::semantic_string::SemanticString;
499    ///
500    /// let file_name = FilePath::new(b"fileName.md").unwrap();
501    /// let file = FileBuilder::new(&file_name)
502    ///                              .creation_mode(CreationMode::CreateExclusive)
503    ///                              .group("testgroup1".as_group().expect("group invalid").gid())
504    ///                              .create().expect("failed to create file");
505    /// ```
506    pub fn group(mut self, value: Gid) -> Self {
507        self.config.group = Some(value);
508        self
509    }
510
511    /// Sets the size of the newly created file.
512    pub fn truncate_size(mut self, value: usize) -> Self {
513        self.config.truncate_size = Some(value);
514        self
515    }
516
517    /// Creates a new file
518    pub fn create(self) -> Result<File, FileCreationError> {
519        let mut file = File::create(&self.config)?;
520        fail!(from self.config, when file.set_permission(self.config.permission), "Failed to set permissions.");
521
522        if self.config.truncate_size.is_some() {
523            fail!(from self.config, when File::truncate(&file, self.config.truncate_size.unwrap()), "Failed to truncate file size.");
524        }
525
526        if self.config.owner.is_some() || self.config.group.is_some() {
527            let owner =
528                fail!(from self.config, when file.ownership(), "Failed to acquire current owners.");
529
530            let owner_id = match self.config.owner.as_ref() {
531                Some(v) => *v,
532                None => owner.uid(),
533            };
534
535            let group_id = match self.config.group.as_ref() {
536                Some(v) => *v,
537                None => owner.gid(),
538            };
539
540            fail!(from self.config, when file.set_ownership(OwnershipBuilder::new().uid(owner_id).gid(group_id).create()),
541                "Failed to set ownership.");
542        }
543
544        trace!(from self.config, "created");
545        Ok(file)
546    }
547}
548
549/// Opens, creates or modifies files. Can be created by the [`FileBuilder`].
550#[derive(Debug)]
551pub struct File {
552    path: Option<FilePath>,
553    file_descriptor: FileDescriptor,
554    has_ownership: AtomicBool,
555}
556
557impl Drop for File {
558    fn drop(&mut self) {
559        if self.has_ownership.load(Ordering::Relaxed) {
560            if let Err(e) = self.set_permission(Permission::ALL) {
561                warn!(from self,
562                    "Unable to adjust the files permission as preparation to remove the file ({e:?}).");
563            }
564
565            match &self.path {
566                None => {
567                    warn!(from self, "Files created from file descriptors cannot remove themselves.")
568                }
569                Some(p) => match File::remove(p) {
570                    Ok(false) | Err(_) => {
571                        warn!(from self, "Failed to remove owned file");
572                    }
573                    Ok(true) => (),
574                },
575            };
576        }
577    }
578}
579
580impl File {
581    fn create(config: &FileBuilder) -> Result<File, FileCreationError> {
582        let msg = "Unable to create file";
583        let create_file = || -> Result<Option<FileDescriptor>, FileCreationError> {
584            Ok(FileDescriptor::new(unsafe {
585                posix::open_with_mode(
586                    config.file_path.as_c_str(),
587                    config
588                        .creation_mode
589                        .expect("CreationMode required when creating new file.")
590                        .as_oflag()
591                        | config.access_mode.as_oflag(),
592                    config.permission.as_mode(),
593                )
594            }))
595        };
596
597        let file_descriptor = match config
598            .creation_mode
599            .expect("The creation mode must always be defined when creating a file.")
600        {
601            CreationMode::CreateExclusive => create_file(),
602            CreationMode::PurgeAndCreate => {
603                if fail!(from config, when File::does_exist(&config.file_path), "{} since the file existance verification failed.", msg)
604                {
605                    fail!(from config, when File::remove(&config.file_path), "{} since the removal of the already existing file failed.", msg);
606                }
607
608                create_file()
609            }
610            CreationMode::OpenOrCreate => {
611                match fail!(from config, when File::does_exist(&config.file_path), "{} since the file existance verification failed.", msg)
612                {
613                    true => Ok(FileDescriptor::new(unsafe {
614                        posix::open(config.file_path.as_c_str(), config.access_mode.as_oflag())
615                    })),
616                    false => create_file(),
617                }
618            }
619        }?;
620
621        if let Some(v) = file_descriptor {
622            return Ok(File {
623                path: Some(config.file_path),
624                file_descriptor: v,
625                has_ownership: AtomicBool::new(config.has_ownership),
626            });
627        }
628
629        handle_errno!(FileCreationError, from config,
630            Errno::EACCES => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
631            Errno::EEXIST => (FileAlreadyExists, "{} since the file already exists.", msg),
632            Errno::EINTR => (Interrupt, "{} since an interrupt signal was received.", msg),
633            Errno::ENOENT => (DirectoryDoesNotExist, "{} since the path points to a directory that does not exist.", msg),
634            Errno::EISDIR => (IsDirectory, "{} since the path is a directory.",msg),
635            Errno::ELOOP => (LoopInSymbolicLinks, "{} since a loop in the symbolic links was detected.", msg),
636            Errno::EMFILE => (PerProcessFileHandleLimitReached, "{} since the current process already holds the maximum amount of file descriptors.", msg),
637            Errno::ENAMETOOLONG => (MaxFilePathLengthExceeded, "{} since the file path length exceeds the maximum supported file path length.", msg),
638            Errno::ENFILE => (SystemWideFileHandleLimitReached, "{} since the system-wide maximum of filedescriptors is reached.", msg),
639            Errno::EOVERFLOW => (FileTooBig, "{} since it is too large to be represented with 'off_t'.", msg),
640            Errno::ENOSPC => (NoSpaceLeft, "{} since there is no space left on the target file-system.", msg),
641            Errno::EROFS => (FilesytemIsReadOnly, "{} with write access on an read-only file system.", msg),
642            Errno::ENOMEM => (InsufficientMemory, "{} due to insufficient memory.", msg),
643            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).",msg, v)
644        );
645    }
646
647    fn open(config: FileBuilder) -> Result<File, FileOpenError> {
648        let msg = "Unable to open file";
649        let file_descriptor = FileDescriptor::new(unsafe {
650            posix::open(config.file_path.as_c_str(), config.access_mode.as_oflag())
651        });
652
653        if let Some(v) = file_descriptor {
654            trace!(from config, "opened");
655            return Ok(File {
656                path: Some(config.file_path),
657                file_descriptor: v,
658                has_ownership: AtomicBool::new(config.has_ownership),
659            });
660        }
661
662        handle_errno!(FileOpenError, from config,
663            Errno::EACCES => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
664            Errno::EINTR => (Interrupt, "{} since an interrupt signal was received.", msg),
665            Errno::EISDIR => (IsDirectory, "{} since the path is a directory.",msg),
666            Errno::ELOOP => (LoopInSymbolicLinks, "{} since a loop in the symbolic links was detected.", msg),
667            Errno::EMFILE => (PerProcessFileHandleLimitReached, "{} since the current process already holds the maximum amount of file descriptors.", msg),
668            Errno::ENAMETOOLONG => (MaxFilePathLengthExceeded, "{} since the file path length exceeds the maximum supported file path length.", msg),
669            Errno::ENFILE => (SystemWideFileHandleLimitReached, "{} since the system-wide maximum of filedescriptors is reached.", msg),
670            Errno::ENOENT => (FileDoesNotExist, "{} since it does not exist.", msg),
671            Errno::EOVERFLOW => (FileTooBig, "{} since it is too large to be represented with 'off_t'.", msg),
672            Errno::EROFS => (FilesytemIsReadOnly, "{} with write access on an read-only file system.", msg),
673            Errno::ENOMEM => (InsufficientMemory, "{} due to insufficient memory.", msg),
674            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).",msg, v)
675        );
676    }
677
678    /// Returns `true` if the [`File`] is owned by the construct and automatically
679    /// removed when it goes out-of-scope, otherwise it returns `false`.
680    pub fn has_ownership(&self) -> bool {
681        self.has_ownership.load(Ordering::Relaxed)
682    }
683
684    /// Takes the ownership to the underlying file, meaning when [`File`] goes out of scope the
685    /// file is removed from the file system.
686    pub fn acquire_ownership(&self) {
687        self.has_ownership.store(true, Ordering::Relaxed);
688    }
689
690    /// Releases the ownership to the underlying file, meaning when [`File`] goes out of scope, the
691    /// file will not be removed from the file system.
692    pub fn release_ownership(&self) {
693        self.has_ownership.store(false, Ordering::Relaxed);
694    }
695
696    /// Takes ownership of a [`FileDescriptor`]. When [`File`] goes out of scope the file
697    /// descriptor is closed.
698    pub fn from_file_descriptor(file_descriptor: FileDescriptor) -> Self {
699        trace!(from "File::from_file_descriptor", "opened {:?}", file_descriptor);
700        Self {
701            path: None,
702            file_descriptor,
703            has_ownership: AtomicBool::new(false),
704        }
705    }
706
707    /// Reads the current line into a provided vector.
708    pub fn read_line_to_vector(
709        &self,
710        buf: &mut Vec<u8>,
711    ) -> Result<FileReadLineState, FileReadError> {
712        let mut buffer = [0u8; 1];
713
714        let mut counter = 0;
715        loop {
716            if self.read(&mut buffer)? == 0 {
717                return Ok(FileReadLineState::EndOfFile(counter));
718            }
719
720            if buffer[0] == b'\n' {
721                return Ok(FileReadLineState::LineLen(counter));
722            }
723
724            buf.push(buffer[0]);
725            counter += 1;
726        }
727    }
728
729    /// Reads the current line into a provided string.
730    pub fn read_line_to_string(
731        &self,
732        buf: &mut String,
733    ) -> Result<FileReadLineState, FileReadError> {
734        self.read_line_to_vector(unsafe { buf.as_mut_vec() })
735    }
736
737    /// Reads the content of a file into a slice and returns the number of bytes read but at most
738    /// `buf.len()` bytes.
739    pub fn read(&self, buf: &mut [u8]) -> Result<u64, FileReadError> {
740        let bytes_read = unsafe {
741            posix::read(
742                self.file_descriptor.native_handle(),
743                buf.as_mut_ptr() as *mut posix::void,
744                buf.len(),
745            )
746        };
747
748        if bytes_read >= 0 {
749            return Ok(bytes_read as u64);
750        }
751
752        let msg = "Unable to read file";
753        handle_errno!(FileReadError, from self,
754            Errno::EINTR => (Interrupt, "{} since an interrupt signal was received.", msg),
755            Errno::EIO => (IOerror, "{} since an I/O error occurred.", msg),
756            Errno::EISDIR => (IsDirectory, "{} since it is actually a directory.", msg),
757            Errno::EOVERFLOW => (FileTooBig, "{} since the file is too big and would cause an overflow in an internal structure.", msg),
758            Errno::ENOBUFS => (InsufficientResources, "{} due to insufficient resources to perform the operation.", msg),
759            Errno::ENOMEM => (InsufficientMemory, "{} due to insufficient memory to perform the operation.", msg),
760            Errno::ENXIO => (NonExistingOrIncapableDevice, "{} since the device either does not exist or is not capable of that operation.", msg),
761            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
762        )
763    }
764
765    /// Reads and appending the content of a file into a vector and returns the number of bytes read.
766    pub fn read_to_vector(&self, buf: &mut Vec<u8>) -> Result<u64, FileReadError> {
767        let attr = fail!(from self, when File::acquire_attributes(self), "Unable to acquire file length to read contents of file.");
768
769        let start = buf.len();
770        buf.resize(attr.st_size as usize + start, 0u8);
771        self.read(&mut buf[start..])
772    }
773
774    /// Reads and appending the content of a file into a string and returns the number of bytes read.
775    pub fn read_to_string(&self, buf: &mut String) -> Result<u64, FileReadError> {
776        self.read_to_vector(unsafe { buf.as_mut_vec() })
777    }
778
779    /// Reads a range of a file beginning from `start`. The range length is determined by
780    /// to length of the slice `buf`. Returns the bytes read.
781    pub fn read_range(&self, start: u64, buf: &mut [u8]) -> Result<u64, FileReadError> {
782        let offset = fail!(from self, when self.seek(start), "Unable to set offset to read a range from the file.");
783
784        if offset != start {
785            return Ok(0);
786        }
787
788        self.read(buf)
789    }
790
791    /// Reads a range of a file beginning from `start` until `end` and returns the bytes read.
792    pub fn read_range_to_vector(
793        &self,
794        start: u64,
795        end: u64,
796        buf: &mut Vec<u8>,
797    ) -> Result<u64, FileReadError> {
798        if start >= end {
799            return Ok(0);
800        }
801
802        let start_of_vec = buf.len();
803        buf.resize((end - start) as usize + start_of_vec, 0u8);
804        self.read_range(start, &mut buf[start_of_vec..])
805    }
806
807    /// Reads a range of a file beginning from `start` until `end` and returns the bytes read.
808    pub fn read_range_to_string(
809        &self,
810        start: u64,
811        end: u64,
812        buf: &mut String,
813    ) -> Result<u64, FileReadError> {
814        self.read_range_to_vector(start, end, unsafe { buf.as_mut_vec() })
815    }
816
817    /// Writes a slice into a file and returns the number of bytes which were written.
818    pub fn write(&mut self, buf: &[u8]) -> Result<u64, FileWriteError> {
819        let bytes_written = unsafe {
820            posix::write(
821                self.file_descriptor.native_handle(),
822                buf.as_ptr() as *const posix::void,
823                buf.len(),
824            )
825        };
826
827        if bytes_written >= 0 {
828            return Ok(bytes_written as u64);
829        }
830
831        let msg = "Unable to write content";
832        handle_errno!(FileWriteError, from self,
833            Errno::EFBIG => (WriteBufferTooBig, "{} since the file size would then exceed the internal maximum file size limit.", msg),
834            Errno::EINTR => (Interrupt, "{} since an interrupt signal was received.", msg),
835            Errno::EIO => (IOerror, "{} due to an I/O error.", msg),
836            Errno::ENOSPC => (NoSpaceLeft, "{} since there is no space left on the device containing the file.", msg),
837            Errno::ENOBUFS => (InsufficientResources, "{} due to insufficient resources.", msg),
838            Errno::ENXIO => (NonExistingOrIncapableDevice, "{} since the operation is outside of the capabilities of the device or the device does not exists.", msg),
839            Errno::EACCES => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
840            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).",msg, v)
841        );
842    }
843
844    /// Writes a slice into a file beginning from `start` and returns the number of bytes which were written.
845    pub fn write_at(&mut self, start: u64, buf: &[u8]) -> Result<u64, FileWriteError> {
846        let offset = fail!(from self, when self.seek(start), "Unable to set offset to write content at a specific position.");
847
848        if offset != start {
849            return Ok(0);
850        }
851
852        self.write(buf)
853    }
854
855    /// Syncs all file modification with the file system.
856    pub fn flush(&mut self) -> Result<(), FileSyncError> {
857        if unsafe { posix::fsync(self.file_descriptor.native_handle()) } == 0 {
858            return Ok(());
859        }
860
861        let msg = "Unable to sync file to device";
862        handle_errno!(FileSyncError, from self,
863            Errno::EINTR => (Interrupt, "{} since an interrupt signal was received.", msg),
864            Errno::EINVAL => (NotSupported, "{} since this operation is not supported by the file.", msg),
865            Errno::EIO => (IOerror, "{} due to an I/O error.", msg),
866            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg,v)
867        );
868    }
869
870    /// Returns true if `path` exists, otherwise false.
871    pub fn does_exist(path: &FilePath) -> Result<bool, FileAccessError> {
872        let msg = "Unable to determine if file";
873        if unsafe { posix::access(path.as_c_str(), posix::F_OK) } == 0 {
874            return Ok(true);
875        }
876
877        handle_errno!(FileAccessError, from "File::does_exist",
878            success Errno::ENOENT => false,
879            Errno::ELOOP => (LoopInSymbolicLinks, "{} \"{}\" exists since a loop exists in the symbolic links.", msg, path),
880            Errno::ENAMETOOLONG => (MaxSupportedPathLengthExceeded, "{} \"{}\" exists since it is longer than the maximum path name length", msg, path),
881            Errno::EACCES => (InsufficientPermissions, "{} \"{}\" due to insufficient permissions.", msg, path),
882            Errno::EPERM => (InsufficientPermissions, "{} \"{}\" due to insufficient permissions.", msg, path),
883            v => (UnknownError(v as i32), "{} \"{}\" exists caused by an unknown error ({}).", msg, path, v)
884        );
885    }
886
887    /// Deletes the file managed by self
888    pub fn remove_self(self) -> Result<bool, FileRemoveError> {
889        match &self.path {
890            None => {
891                warn!(from self, "Files created from file descriptors cannot remove themselves.");
892                Ok(false)
893            }
894            Some(p) => File::remove(p),
895        }
896    }
897
898    /// Returns [`Some`] when the file was created via path. If it was created via a
899    /// [`FileDescriptor`] it returns [`None`].
900    pub fn path(&self) -> Option<&FilePath> {
901        match self.path {
902            None => None,
903            Some(_) => self.path.as_ref(),
904        }
905    }
906
907    /// Deletes a file. Returns true if the file existed and was removed and false if the
908    /// file did not exist.
909    pub fn remove(path: &FilePath) -> Result<bool, FileRemoveError> {
910        let msg = "Unable to remove file";
911        if unsafe { posix::remove(path.as_c_str()) } >= 0 {
912            trace!(from "File::remove", "\"{}\"", path);
913            return Ok(true);
914        }
915
916        handle_errno!(FileRemoveError, from "File::remove",
917            success Errno::ENOENT => false,
918            Errno::EACCES => (InsufficientPermissions, "{} \"{}\" due to insufficient permissions.", msg, path),
919            Errno::EPERM => (InsufficientPermissions, "{} \"{}\" due to insufficient permissions.", msg, path),
920            Errno::EBUSY => (CurrentlyInUse, "{} \"{}\" since it is currently in use.", msg, path),
921            Errno::ELOOP => (LoopInSymbolicLinks, "{} \"{}\" since a loop exists in the symbolic links.", msg, path),
922            Errno::ENAMETOOLONG => (MaxSupportedPathLengthExceeded, "{} \"{}\" since it is longer than the maximum path name length.", msg, path),
923            Errno::EROFS => (PartOfReadOnlyFileSystem, "{} \"{}\" since it is part of a read-only filesystem.", msg, path),
924            v => (UnknownError(v as i32), "{} \"{}\" since an unkown error occurred ({}).", msg, path, v)
925        );
926    }
927
928    /// Seek to an absolute position in the file.
929    pub fn seek(&self, offset: u64) -> Result<u64, FileOffsetError> {
930        Self::set_offset(self, offset)
931    }
932
933    pub(crate) fn set_offset<T: FileDescriptorBased + Debug>(
934        this: &T,
935        offset: u64,
936    ) -> Result<u64, FileOffsetError> {
937        let new_offset = unsafe {
938            posix::lseek(
939                this.file_descriptor().native_handle(),
940                offset as posix::off_t,
941                posix::SEEK_SET,
942            )
943        };
944
945        if new_offset >= 0 {
946            return Ok(new_offset as u64);
947        }
948
949        let msg = "Unable to change read/write position to";
950        handle_errno!(FileOffsetError, from this,
951            Errno::EBADF => (InvalidFileDescriptor, "{} {} since the provide file-descriptor was not valid.", msg, offset),
952            Errno::EOVERFLOW => (FileTooBig, "{} {} since the file size is so large that ic cannot be represented by an internal structure.", msg, offset),
953            Errno::ESPIPE => (DoesNotSupportSeeking, "{} {} since the file type does not support seeking.", msg, offset),
954            v => (UnknownError(v as i32), "{} {} due to an unknown error ({}).", msg, offset, v)
955        );
956    }
957
958    pub(crate) fn truncate<T: FileDescriptorBased + Debug>(
959        this: &T,
960        size: usize,
961    ) -> Result<(), FileTruncateError> {
962        if unsafe { posix::ftruncate(this.file_descriptor().native_handle(), size as posix::off_t) }
963            == 0
964        {
965            return Ok(());
966        }
967
968        let msg = "Unable to resize file to";
969        handle_errno!(FileTruncateError, from this,
970            Errno::EINTR => (Interrupt, "{} {} since an interrupt signal was received.", msg, size),
971            Errno::EFBIG => (SizeTooBig, "{} {} since the size is too big. Maybe the file can only shrink?", msg, size),
972            Errno::EIO => (IOerror, "{} {} due to an I/O error while writing to the file system.", msg, size),
973            Errno::EBADF => (FileNotOpenedForWriting, "{} {} file is not opened for writing.", msg, size),
974            Errno::EROFS => (ReadOnlyFilesystem, "{} {} since the file resides on a read-only file system.", msg, size),
975            v => (UnknownError(v as i32), "{} {} due to an unknown error ({}).", msg, size, v)
976        );
977    }
978
979    pub(crate) fn acquire_attributes<T: FileDescriptorBased + Debug>(
980        this: &T,
981    ) -> Result<posix::stat_t, FileStatError> {
982        let mut attr = posix::stat_t::new_zeroed();
983        if unsafe { posix::fstat(this.file_descriptor().native_handle(), &mut attr) } == -1 {
984            let msg = "Unable to acquire file stats";
985            handle_errno!(FileStatError, from this,
986                Errno::EBADF => (InvalidFileDescriptor, "{} since an invalid file-descriptor was provided.", msg),
987                Errno::EIO => (IOerror, "{} due to an I/O error while reading from the file system.", msg),
988                Errno::EOVERFLOW => (FileTooBig, "{} since the file size is so large that it cannot be represented by an internal structure.", msg),
989                v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
990            );
991        }
992
993        Ok(attr)
994    }
995
996    pub(crate) fn set_permission<T: FileDescriptorBased + Debug>(
997        this: &T,
998        permission: Permission,
999    ) -> Result<(), FileSetPermissionError> {
1000        if unsafe { posix::fchmod(this.file_descriptor().native_handle(), permission.as_mode()) }
1001            == 0
1002        {
1003            return Ok(());
1004        }
1005
1006        let msg = "Unable to update permission";
1007        handle_errno!(FileSetPermissionError, from this,
1008            Errno::EBADF => (InvalidFileDescriptor, "{} to {} since an invalid file-descriptor was provided.", msg,  permission),
1009            Errno::EPERM => (InsufficientPermissions, "{} {} due to insufficient permissions.", msg, permission),
1010            Errno::EROFS => (ReadOnlyFilesystem, "{} {} since the file resides on a read-only file system.", msg, permission),
1011            v => (UnknownError(v as i32), "{} {} due to an unknown error ({}).", msg, permission, v)
1012        );
1013    }
1014
1015    pub(crate) fn set_ownership<T: FileDescriptorBased + Debug>(
1016        this: &T,
1017        uid: Uid,
1018        gid: Gid,
1019    ) -> Result<(), FileSetOwnerError> {
1020        if unsafe {
1021            posix::fchown(
1022                this.file_descriptor().native_handle(),
1023                uid.to_native(),
1024                gid.to_native(),
1025            )
1026        } == 0
1027        {
1028            return Ok(());
1029        }
1030
1031        let msg = "Unable to update ownership";
1032        handle_errno!(FileSetOwnerError, from this,
1033            Errno::EBADF => (InvalidFileDescriptor, "{} to uid {}, gid {} since an invalid file-descriptor was provided.", msg, uid, gid),
1034            Errno::EPERM => (InsufficientPermissions, "{} to uid {}, gid {} due to insufficient permissions.", msg, uid, gid),
1035            Errno::EROFS => (ReadOnlyFilesystem, "{} to uid {}, gid {} since the file is located on an read-only filesystem.", msg, uid, gid),
1036            Errno::EINVAL => (InvalidId, "{} to uid {}, gid {} since the owner or group id is not a valid id.", msg, uid, gid),
1037            Errno::EIO => (IOerror, "{} to uid {}, gid {} due to an I/O error.", msg, uid, gid),
1038            Errno::EINTR => (Interrupt, "{} to uid {}, gid {} since an interrupt signal was received.", msg, uid, gid),
1039            v => (UnknownError(v as i32), "{} to uid {}, gid {} due to an unknown error ({}).", msg, uid, gid, v)
1040        );
1041    }
1042}
1043
1044impl FileDescriptorBased for File {
1045    fn file_descriptor(&self) -> &FileDescriptor {
1046        &self.file_descriptor
1047    }
1048}
1049
1050impl FileDescriptorManagement for File {}