1use alloc::format;
40use alloc::vec;
41use alloc::vec::Vec;
42
43use iceoryx2_bb_container::semantic_string::SemanticString;
44use iceoryx2_bb_container::string::strnlen;
45use iceoryx2_bb_elementary::enum_gen;
46use iceoryx2_bb_elementary::scope_guard::ScopeGuardBuilder;
47use iceoryx2_bb_system_types::{file_name::FileName, file_path::FilePath, path::Path};
48use iceoryx2_log::{error, fail, fatal_panic, trace};
49use iceoryx2_pal_configuration::PATH_SEPARATOR;
50use iceoryx2_pal_posix::posix::MemZeroedStruct;
51use iceoryx2_pal_posix::*;
52use iceoryx2_pal_posix::{posix::errno::Errno, posix::S_IFDIR};
53
54use crate::file::{File, FileRemoveError};
55use crate::file_type::FileType;
56pub use crate::permission::Permission;
57use crate::{config::EINTR_REPETITIONS, file_descriptor::*, metadata::*};
58
59enum_gen! { DirectoryOpenError
60 entry:
61 LoopInSymbolicLinks,
62 InsufficientPermissions,
63 NotADirectory,
64 PerProcessFileHandleLimitReached,
65 SystemWideFileHandleLimitReached,
66 DoesNotExist,
67 UnknownError(i32)
68}
69
70#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
71pub enum DirectoryStatError {
72 InsufficientPermissions,
73 IOerror,
74 DoesNotExist,
75 PathPrefixIsNotADirectory,
76 DataOverflowInStatStruct,
77 LoopInSymbolicLinks,
78 UnknownError(i32),
79}
80
81enum_gen! { DirectoryReadError
82 entry:
83 InsufficientPermissions,
84 DirectoryDoesNoLongerExist,
85 InsufficientMemory,
86 PerProcessFileHandleLimitReached,
87 SystemWideFileHandleLimitReached,
88 UnknownError(i32)
89
90 mapping:
91 DirectoryStatError
92}
93
94enum_gen! { DirectoryCreateError
95 entry:
96 InsufficientPermissions,
97 DirectoryAlreadyExists,
98 LoopInSymbolicLinks,
99 ExceedsParentsLinkCount,
100 PartsOfThePathDoNotExist,
101 PartsOfThePathAreNotADirectory,
102 NoSpaceLeft,
103 ReadOnlyFilesystem,
104 UnableToApplyPermissions,
105 UnknownError(i32)
106 mapping:
107 DirectoryOpenError
108}
109
110enum_gen! { DirectoryRemoveError
111 entry:
112 InsufficientPermissions,
113 CurrentlyInUse,
114 NotEmptyOrHardLinksPointingToTheDirectory,
115 IOerror,
116 LastComponentIsDot,
117 LoopInSymbolicLinks,
118 DirectoryDoesNotExist,
119 NotADirectory,
120 ResidesOnReadOnlyFileSystem,
121 DanglingSymbolicLink,
122 UnknownError(i32)
123 mapping:
124 DirectoryOpenError,
125 DirectoryReadError,
126 FileRemoveError
127}
128
129#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
130pub enum DirectoryAccessError {
131 InsufficientPermissions,
132 IOerror,
133 PathPrefixIsNotADirectory,
134 DataOverflowInStatStruct,
135 LoopInSymbolicLinks,
136 UnknownError(i32),
137}
138
139enum_gen! {
140 DirectoryError
145 generalization:
146 Create <= DirectoryCreateError,
147 Open <= DirectoryOpenError,
148 Read <= DirectoryReadError; DirectoryStatError,
149 Remove <= DirectoryRemoveError
150}
151
152pub struct DirectoryEntry {
175 name: FileName,
176 metadata: Metadata,
177}
178
179impl DirectoryEntry {
180 pub fn name(&self) -> &FileName {
181 &self.name
182 }
183
184 pub fn metadata(&self) -> &Metadata {
185 &self.metadata
186 }
187}
188
189#[derive(Debug)]
193pub struct Directory {
194 path: Path,
195 directory_stream: *mut posix::DIR,
196 file_descriptor: FileDescriptor,
197}
198
199impl Drop for Directory {
200 fn drop(&mut self) {
201 let mut counter = 0;
202 loop {
203 if unsafe { posix::closedir(self.directory_stream) } == 0 {
204 break;
205 }
206
207 let msg = "Unable to close directory stream";
208 match Errno::get() {
209 Errno::EBADF => {
210 fatal_panic!(from self, "This should never happen! {} due to an invalid file-descriptor.", msg);
211 }
212 Errno::EINTR => {
213 counter += 1;
214 if counter > EINTR_REPETITIONS {
215 error!(from self, "{} since too many interrupt signals were received.", msg);
216 }
217 }
218 v => {
219 fatal_panic!(from self, "This should never happen! {} since an unknown error occurred ({}).", msg, v);
220 }
221 }
222
223 if counter > EINTR_REPETITIONS {
224 error!(from self, "Tried {} times to close the file but failed.", counter);
225 }
226 }
227 }
228}
229
230impl Directory {
231 pub fn new(path: &Path) -> Result<Self, DirectoryOpenError> {
232 let directory_stream = unsafe { posix::opendir(path.as_c_str()) };
233
234 let msg = format!("Unable to open directory \"{path}\"");
235 if directory_stream.is_null() {
236 handle_errno!(DirectoryOpenError, from "Directory::new",
237 Errno::EACCES => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
238 Errno::ELOOP => (LoopInSymbolicLinks, "{} due to a loop in the symbolic links.", msg),
239 Errno::ENOENT => (DoesNotExist, "{} since the path does not exist.", msg),
240 Errno::ENOTDIR => (NotADirectory, "{} since the path is not a directory.", msg),
241 Errno::EMFILE => (PerProcessFileHandleLimitReached, "{} since the file descriptor limit of the process was reached.", msg),
242 Errno::ENFILE => (SystemWideFileHandleLimitReached, "{} since the system-wide limit of file descriptors was reached.", msg),
243 v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
244 );
245 }
246
247 let file_descriptor =
248 FileDescriptor::non_owning_new(unsafe { posix::dirfd(directory_stream) });
249 if file_descriptor.is_none() {
250 fatal_panic!(from "Directory::new",
251 "This should never happen! {} since 'dirfd' states that the acquired directory stream is invalid.", msg);
252 }
253
254 Ok(Directory {
255 path: *path,
256 directory_stream,
257 file_descriptor: file_descriptor.unwrap(),
258 })
259 }
260
261 fn create_single_directory(
262 path: &Path,
263 permission: Permission,
264 ) -> Result<(), DirectoryCreateError> {
265 let origin = "Directory::create()";
266 let msg = format!("Unable to create directory \"{path}\"");
267
268 if unsafe { posix::mkdir(path.as_c_str(), permission.as_mode()) } == -1 {
269 handle_errno!(DirectoryCreateError, from origin,
270 Errno::EACCES => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
271 Errno::EEXIST => (DirectoryAlreadyExists, "{} since the directory already exists.", msg),
272 Errno::ELOOP => (LoopInSymbolicLinks, "{} due to a loop in the symbolic links.", msg),
273 Errno::EMLINK => (ExceedsParentsLinkCount, "{} since it would exceed the parents link count.", msg),
274 Errno::ENOENT => (PartsOfThePathDoNotExist, "{} since parts of the path either do not exist.", msg),
275 Errno::ENOSPC => (NoSpaceLeft, "{} since there is no space left on the target device.", msg),
276 Errno::ENOTDIR => (PartsOfThePathAreNotADirectory, "{} since parts of the path are not a directory.", msg),
277 Errno::EROFS => (ReadOnlyFilesystem, "{} since the parent directory resides on a read-only filesystem.", msg),
278 v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
279 );
280 }
281
282 let mut dir = match Directory::new(path) {
290 Ok(dir) => dir,
291 Err(e) => {
292 let _ = Directory::remove(path);
293 fail!(from origin, with DirectoryCreateError::UnableToApplyPermissions,
294 "{msg} since it could not be opened after creation to apply the permissions due to {e:?}.");
295 }
296 };
297
298 match dir.set_permission(permission) {
299 Ok(()) => Ok(()),
300 Err(e) => {
301 let _ = Directory::remove(path);
302 fail!(from origin, with DirectoryCreateError::UnableToApplyPermissions,
303 "{msg} since the permissions could not be applied due to {e:?}.");
304 }
305 }
306 }
307
308 pub fn create(path: &Path, permission: Permission) -> Result<Self, DirectoryCreateError> {
310 let origin = "Directory::create()";
311 let msg = format!("Unable to create directory \"{path}\"");
312 let entries = path.entries();
313
314 let mut inc_path = if path.is_absolute() {
315 Path::new_root_path()
316 } else {
317 Path::new_empty()
318 };
319
320 for entry in entries {
321 inc_path
322 .add_path_entry(&entry.into())
323 .expect("Always works since it recreates the provided path");
324
325 match Directory::does_exist(&inc_path) {
326 Ok(true) => (),
327 Ok(false) => match Directory::create_single_directory(&inc_path, permission) {
328 Ok(()) | Err(DirectoryCreateError::DirectoryAlreadyExists) => (),
329 Err(e) => {
330 fail!(from origin, with e,
331 "{} since the directory {} could not be created due to {:?}.",
332 msg, inc_path, e);
333 }
334 },
335 Err(DirectoryAccessError::InsufficientPermissions) => {
336 fail!(from origin, with DirectoryCreateError::InsufficientPermissions,
337 "{} since the path {} could not be accessed due to insufficient permissions.", msg, inc_path);
338 }
339 Err(DirectoryAccessError::PathPrefixIsNotADirectory) => {
340 fail!(from origin, with DirectoryCreateError::PartsOfThePathAreNotADirectory,
341 "{} since the path {} is not a directory.", msg, inc_path);
342 }
343 Err(v) => {
344 fail!(from origin, with DirectoryCreateError::UnknownError(0),
345 "{} due to a failure while accessing {} ({:?}).", msg, inc_path, v);
346 }
347 };
348 }
349
350 match Directory::new(path) {
351 Ok(d) => {
352 trace!(from d, "created with permissions \"{permission}\"");
353 Ok(d)
354 }
355 Err(e) => {
356 fail!(from origin, with e.into(),
357 "Failed to open newly created directory \"{}\".", path);
358 }
359 }
360 }
361
362 pub fn path(&self) -> &Path {
364 &self.path
365 }
366
367 pub fn remove_empty(path: &Path) -> Result<(), DirectoryRemoveError> {
369 if unsafe { posix::rmdir(path.as_c_str()) } == -1 {
370 let msg = format!("Unable to remove empty directory \"{path}\"");
371 handle_errno!(DirectoryRemoveError, from "Directory::remove",
372 Errno::EACCES => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
373 Errno::EPERM => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
374 Errno::EBUSY => (CurrentlyInUse, "{} since the directory is currently in use.", msg),
375 Errno::EINVAL => (LastComponentIsDot, "{} since the last path component is \".\".", msg),
376 Errno::ELOOP => (LoopInSymbolicLinks, "{} due to a loop in the symbolic links of the path \".\".", msg),
377 Errno::ENOENT => (DanglingSymbolicLink, "{} since the path contains a dangling symbolic link.", msg),
378 Errno::EEXIST => (DirectoryDoesNotExist, "{} since the directory does not exist.", msg),
379 Errno::ENOTDIR => (NotADirectory, "{} since it is not a directory.", msg),
380 Errno::EROFS => (ResidesOnReadOnlyFileSystem, "{} since the directory resides on a read only file system.", msg),
381 Errno::ENOTEMPTY => (NotEmptyOrHardLinksPointingToTheDirectory, "{} since the directory is not empty or there are hard links pointing to the directory.", msg),
382 Errno::EIO => (IOerror, "{} due to a physicial I/O error.", msg),
383 v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
384 );
385 }
386
387 trace!(from "Directory::remove", "removed \"{}\"", path);
388 Ok(())
389 }
390
391 pub fn remove(path: &Path) -> Result<(), DirectoryRemoveError> {
393 let msg = format!("Unable to remove directory \"{path}\"");
394 let origin = "Directory::remove()";
395
396 let dir = fail!(from origin, when Directory::new(path),
397 "{} since the directory {} could not be opened.", msg, path);
398 let contents = fail!(from origin, when dir.contents(),
399 "{} since the directory contents of {} could not be read.", msg, path);
400
401 for entry in contents {
402 let mut sub_path = *path;
403 sub_path
404 .add_path_entry(&entry.name().into())
405 .expect("always a valid path entry");
406 if entry.metadata().file_type() == FileType::Directory {
407 fail!(from origin, when Directory::remove(&sub_path),
408 "{} since the sub-path {} could not be removed.", msg, sub_path);
409 } else {
410 fail!(from origin, when File::remove(&unsafe{FilePath::new_unchecked(sub_path.as_bytes())}),
411 "{} since the file {} could not be removed.", msg, sub_path);
412 }
413 }
414
415 Self::remove_empty(path)
416 }
417
418 pub fn contents(&self) -> Result<Vec<DirectoryEntry>, DirectoryReadError> {
420 let mut namelist: *mut *mut posix::types::dirent =
421 core::ptr::null_mut::<*mut posix::types::dirent>();
422 let number_of_directory_entries =
423 unsafe { posix::scandir(self.path.as_c_str(), &mut namelist) };
424
425 let _memory_cleanup_guard = ScopeGuardBuilder::new(namelist)
426 .on_init(|_| {
427 if number_of_directory_entries < 0 {
428 let msg = "Unable to read directory contents";
429 handle_errno!(DirectoryReadError, from self,
430 Errno::EACCES => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
431 Errno::ENOENT => (DirectoryDoesNoLongerExist, "{} since the directory does not exist anymore.", msg),
432 Errno::ENOMEM => (InsufficientMemory, "{} due to insufficient memory.", msg),
433 Errno::EMFILE => (PerProcessFileHandleLimitReached, "{} since the file descriptor limit of the process was reached.", msg),
434 Errno::ENFILE => (SystemWideFileHandleLimitReached, "{} since the system-wide limit of file descriptors was reached.", msg),
435 v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
436 );
437 }
438
439 Ok(())
440 })
441 .on_drop(|v| {
442 for i in 0..number_of_directory_entries {
443 unsafe { posix::free(*(v.offset(i as isize)) as *mut posix::void) };
444 }
445 unsafe { posix::free(*v as *mut posix::void) };
446 }).create()?;
447
448 let mut contents: Vec<DirectoryEntry> = vec![];
449 for i in 0..number_of_directory_entries {
450 let raw_name =
451 unsafe { (*(*namelist.offset(i as isize))).d_name.as_ptr() as *mut posix::c_char };
452 let raw_name_length = unsafe { strnlen(raw_name, FileName::max_len()) };
453
454 if raw_name_length == 0 {
455 continue;
456 }
457
458 const DOT: posix::c_char = b'.' as _;
459 if raw_name_length == 1 && unsafe { *raw_name == DOT } {
461 continue;
462 }
463
464 if raw_name_length == 2
466 && unsafe { *raw_name == DOT }
467 && unsafe { *raw_name.offset(1) == DOT }
468 {
469 continue;
470 }
471
472 match unsafe { FileName::from_c_str(raw_name) } {
473 Ok(name) => {
474 let msg = format!(
475 "Failed to acquire stats \"{name}\" while reading directory content"
476 );
477 match Self::acquire_metadata(self, &name, &msg) {
478 Ok(metadata) => contents.push(DirectoryEntry { name, metadata }),
479 Err(DirectoryStatError::DoesNotExist)
480 | Err(DirectoryStatError::InsufficientPermissions) => (),
481 Err(e) => {
482 fail!(from self, with e.into(),
483 "{} due to an internal failure {:?}.", msg, e);
484 }
485 }
486 }
487 Err(v) => {
488 error!(from self, "Directory contains entries that are not representable with FileName struct ({:?}).", v);
489 }
490 }
491 }
492
493 Ok(contents)
494 }
495
496 pub fn does_exist(path: &Path) -> Result<bool, DirectoryAccessError> {
498 let mut buffer = posix::stat_t::new_zeroed();
499 let msg = format!("Unable to determine if \"{path}\" does exist");
500
501 if unsafe { posix::stat(path.as_c_str(), &mut buffer) } == -1 {
502 handle_errno!(DirectoryAccessError, from "Directory::does_exist",
503 success Errno::ENOENT => false,
504 Errno::EACCES => (InsufficientPermissions, "{} due to insufficient permissions to open path.", msg),
505 Errno::EIO => (IOerror, "{} due to an io error while reading directory stats.", msg),
506 Errno::ELOOP => (LoopInSymbolicLinks, "{} due to a symbolic link loop in the path.", msg),
507 Errno::ENOTDIR => (PathPrefixIsNotADirectory, "{} since the path prefix is not a directory.", msg),
508 Errno::EOVERFLOW => (DataOverflowInStatStruct, "{} since certain properties like size would cause an overflow in the underlying stat struct.", msg),
509 v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
510 );
511 }
512
513 Ok(buffer.st_mode & S_IFDIR != 0)
514 }
515
516 fn acquire_metadata(&self, file: &FileName, msg: &str) -> Result<Metadata, DirectoryStatError> {
517 let mut buffer = posix::stat_t::new_zeroed();
518 let mut path = *self.path();
519 path.push(PATH_SEPARATOR).unwrap();
520 path.push_bytes(file.as_bytes()).unwrap();
521
522 if unsafe { posix::stat(path.as_c_str(), &mut buffer) } == -1 {
523 handle_errno!(DirectoryStatError, from self,
524 Errno::EACCES => (InsufficientPermissions, "{} due to insufficient permissions to open path.", msg),
525 Errno::EIO => (IOerror, "{} due to an io error while reading directory stats.", msg),
526 Errno::ELOOP => (LoopInSymbolicLinks, "{} due to a symbolic link loop in the path.", msg),
527 Errno::ENOENT => (DoesNotExist, "{} since the path does not exist.", msg),
528 Errno::ENOTDIR => (PathPrefixIsNotADirectory, "{} since the path prefix is not a directory.", msg),
529 Errno::EOVERFLOW => (DataOverflowInStatStruct, "{} since certain properties like size would cause an overflow in the underlying stat struct.", msg),
530 v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
531 );
532 }
533
534 Ok(Metadata::create(&buffer))
535 }
536}
537
538impl FileDescriptorBased for Directory {
539 fn file_descriptor(&self) -> &FileDescriptor {
540 &self.file_descriptor
541 }
542}
543
544impl FileDescriptorManagement for Directory {}