1use std::{
2 io,
3 mem,
4 os::unix::io::RawFd,
5 path::Path,
6};
7
8use inotify_sys as ffi;
9use libc::{
10 c_void,
11 size_t,
12};
13
14const INOTIFY_EVENT_SIZE: usize = mem::size_of::<ffi::inotify_event>() + 257;
15
16pub fn read_into_buffer(fd: RawFd, buffer: &mut [u8]) -> isize {
17 unsafe {
18 // Discard the unaligned portion, if any, of the supplied buffer
19 let buffer: &mut [u8] = align_buffer_mut(buffer);
20
21 ffi::read(
22 fd,
23 buf:buffer.as_mut_ptr() as *mut c_void,
24 count:buffer.len() as size_t
25 )
26 }
27}
28
29pub fn align_buffer(buffer: &[u8]) -> &[u8] {
30 if buffer.len() >= mem::align_of::<ffi::inotify_event>() {
31 let ptr: *const u8 = buffer.as_ptr();
32 let offset: usize = ptr.align_offset(align:mem::align_of::<ffi::inotify_event>());
33 &buffer[offset..]
34 } else {
35 &buffer[0..0]
36 }
37}
38
39pub fn align_buffer_mut(buffer: &mut [u8]) -> &mut [u8] {
40 if buffer.len() >= mem::align_of::<ffi::inotify_event>() {
41 let ptr: *mut u8 = buffer.as_mut_ptr();
42 let offset: usize = ptr.align_offset(align:mem::align_of::<ffi::inotify_event>());
43 &mut buffer[offset..]
44 } else {
45 &mut buffer[0..0]
46 }
47}
48
49/// Get the inotify event buffer size
50///
51/// The maximum size of an inotify event and thus the buffer size to hold it
52/// can be calculated using this formula:
53/// `sizeof(struct inotify_event) + NAME_MAX + 1`
54///
55/// See: [https://man7.org/linux/man-pages/man7/inotify.7.html](https://man7.org/linux/man-pages/man7/inotify.7.html)
56///
57/// The NAME_MAX size formula is:
58/// `ABSOLUTE_PARENT_PATH_LEN + 1 + 255`
59///
60/// - `ABSOLUTE_PARENT_PATH_LEN` will be calculated at runtime.
61/// - Add 1 to account for a `/`, either in between the parent path and a filename
62/// or for the root directory.
63/// - Add the maximum number of chars in a filename, 255.
64///
65/// See: [https://github.com/torvalds/linux/blob/master/include/uapi/linux/limits.h](https://github.com/torvalds/linux/blob/master/include/uapi/linux/limits.h)
66///
67/// Unfortunately, we can't just do the same with max path length itself.
68///
69/// See: [https://eklitzke.org/path-max-is-tricky](https://eklitzke.org/path-max-is-tricky)
70///
71/// This function is really just a fallible wrapper around `get_absolute_path_buffer_size()`.
72///
73/// path: A relative or absolute path for the inotify events.
74pub fn get_buffer_size(path: &Path) -> io::Result<usize> {
75 Ok(get_absolute_path_buffer_size(&path.canonicalize()?))
76}
77
78/// Get the inotify event buffer size for an absolute path
79///
80/// For relative paths, consider using `get_buffer_size()` which provides a fallible wrapper
81/// for this function.
82///
83/// path: An absolute path for the inotify events.
84pub fn get_absolute_path_buffer_size(path: &Path) -> usize {
85 INOTIFY_EVENT_SIZE
86 // Get the length of the absolute parent path, if the path is not the root directory.
87 // Because we canonicalize the path, we do not need to worry about prefixes.
88 + if let Some(parent_path: &Path) = path.parent() {
89 parent_path.as_os_str().len()
90 } else {
91 0
92 }
93}
94