1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use std::{
    io,
    mem,
    os::unix::io::RawFd,
    path::Path,
};

use inotify_sys as ffi;
use libc::{
    c_void,
    size_t,
};

const INOTIFY_EVENT_SIZE: usize = mem::size_of::<ffi::inotify_event>() + 257;

pub fn read_into_buffer(fd: RawFd, buffer: &mut [u8]) -> isize {
    unsafe {
        // Discard the unaligned portion, if any, of the supplied buffer
        let buffer = align_buffer_mut(buffer);

        ffi::read(
            fd,
            buffer.as_mut_ptr() as *mut c_void,
            buffer.len() as size_t
        )
    }
}

pub fn align_buffer(buffer: &[u8]) -> &[u8] {
    if buffer.len() >= mem::align_of::<ffi::inotify_event>() {
        let ptr = buffer.as_ptr();
        let offset = ptr.align_offset(mem::align_of::<ffi::inotify_event>());
        &buffer[offset..]
    } else {
        &buffer[0..0]
    }
}

pub fn align_buffer_mut(buffer: &mut [u8]) -> &mut [u8] {
   if buffer.len() >= mem::align_of::<ffi::inotify_event>() {
        let ptr = buffer.as_mut_ptr();
        let offset = ptr.align_offset(mem::align_of::<ffi::inotify_event>());
        &mut buffer[offset..]
   } else {
       &mut buffer[0..0]
   }
}

/// Get the inotify event buffer size
///
/// The maximum size of an inotify event and thus the buffer size to hold it
/// can be calculated using this formula:
/// `sizeof(struct inotify_event) + NAME_MAX + 1`
///
/// See: [https://man7.org/linux/man-pages/man7/inotify.7.html](https://man7.org/linux/man-pages/man7/inotify.7.html)
///
/// The NAME_MAX size formula is:
/// `ABSOLUTE_PARENT_PATH_LEN + 1 + 255`
///
/// - `ABSOLUTE_PARENT_PATH_LEN` will be calculated at runtime.
/// - Add 1 to account for a `/`, either in between the parent path and a filename
/// or for the root directory.
/// - Add the maximum number of chars in a filename, 255.
///
/// 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)
///
/// Unfortunately, we can't just do the same with max path length itself.
///
/// See: [https://eklitzke.org/path-max-is-tricky](https://eklitzke.org/path-max-is-tricky)
///
/// This function is really just a fallible wrapper around `get_absolute_path_buffer_size()`.
///
/// path: A relative or absolute path for the inotify events.
pub fn get_buffer_size(path: &Path) -> io::Result<usize> {
    Ok(get_absolute_path_buffer_size(&path.canonicalize()?))
}

/// Get the inotify event buffer size for an absolute path
///
/// For relative paths, consider using `get_buffer_size()` which provides a fallible wrapper
/// for this function.
///
/// path: An absolute path for the inotify events.
pub fn get_absolute_path_buffer_size(path: &Path) -> usize {
    INOTIFY_EVENT_SIZE
    // Get the length of the absolute parent path, if the path is not the root directory.
    // Because we canonicalize the path, we do not need to worry about prefixes.
    + if let Some(parent_path) = path.parent() {
        parent_path.as_os_str().len()
    } else {
        0
    }
}