Repository: hannobraun/inotify-rs (language: rust)

Issue:
Consider increasing the buffer size for the stream example
Currently, the [buffer size of the stream example](https://github.com/hannobraun/inotify-rs/blob/master/examples/stream.rs#L31) is set to `let mut buffer = [0; 32];`. When I tried to run the example, I hit a rather cryptic OS error, `Invalid Argument`. This was easily fixed by increasing the buffer size.

The [unit test uses](https://github.com/hannobraun/inotify-rs/blob/master/tests/main.rs#L226) `let mut buffer = [0; 1024];`. Maybe the example should use `1024` as well?

It is unlikely that `tempfile` would produce a path in either case that would cause the inotify message to exceed `1024`. I've come up with a function though that given a filepath it will calculate the maximum inotify message size one could expect.

Please, let me know if either of these would be worth opening an MR for.

```rust
fn get_buffer_size(path: &Path) -> usize {
    // Get the length of the parent path if the filepath is not the root directory.
    // Because we canonicalized the filepath above, we do not need to worry about
    // prefixes.
    let name_max = if let Some(parent_path) = path.parent() {
        parent_path.as_os_str().len()
    } else {
        0
    }
    // Add one to account for a `/`, either in between the parent path and a filename
    // or for the root directory.
    + 1
    // Add the maximum number of chars in a filename, 255
    // https://github.com/torvalds/linux/blob/master/include/uapi/linux/limits.h
    // Unfortunetely, we can't just do the same with max path length itself.
    // https://eklitzke.org/path-max-is-tricky
    + 255;

    // inotify event max size formula:
    // sizeof(struct inotify_event) + NAME_MAX + 1
    // https://man7.org/linux/man-pages/man7/inotify.7.html
    std::mem::size_of::<inotify_sys::inotify_event>() + name_max + 1
}
```





Discussion / hints:
Thanks for bringing this up, @epompeii. I have no idea why the buffer size in that example is so small. Increasing to `1024` is reasonable.

Your buffer size function looks good. Maybe we can include it as a utility function in the library, or did you have something else in mind?

Fix patch:
diff --git a/examples/stream.rs b/examples/stream.rs
index e44bde6..f2e6b88 100644
--- a/examples/stream.rs
+++ b/examples/stream.rs
@@ -28,7 +28,7 @@ async fn main() -> Result<(), io::Error> {
         }
     });
 
-    let mut buffer = [0; 32];
+    let mut buffer = [0; 1024];
     let mut stream = inotify.event_stream(&mut buffer)?;
 
     while let Some(event_or_error) = stream.next().await {
diff --git a/src/lib.rs b/src/lib.rs
index 5a77bd2..7375ee5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -91,6 +91,10 @@ pub use crate::events::{
     Events,
 };
 pub use crate::inotify::Inotify;
+pub use crate::util::{
+    get_buffer_size,
+    get_absolute_path_buffer_size,
+};
 pub use crate::watches::{
     WatchDescriptor,
     WatchMask,
diff --git a/src/util.rs b/src/util.rs
index 1028832..25ddb03 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -1,6 +1,8 @@
 use std::{
+    io,
     mem,
-    os::unix::io::RawFd
+    os::unix::io::RawFd,
+    path::Path,
 };
 
 use inotify_sys as ffi;
@@ -9,6 +11,7 @@ use libc::{
     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 {
@@ -40,5 +43,51 @@ pub fn align_buffer_mut(buffer: &mut [u8]) -> &mut [u8] {
         &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
+    }
 }


Test patch: