1 | use std::{ |
2 | io, |
3 | mem, |
4 | os::unix::io::RawFd, |
5 | path::Path, |
6 | }; |
7 | |
8 | use inotify_sys as ffi; |
9 | use libc::{ |
10 | c_void, |
11 | size_t, |
12 | }; |
13 | |
14 | const INOTIFY_EVENT_SIZE: usize = mem::size_of::<ffi::inotify_event>() + 257; |
15 | |
16 | pub 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 | |
29 | pub 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 | |
39 | pub 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. |
74 | pub 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. |
84 | pub 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 | |