Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

419
vendor/inotify/src/events.rs vendored Normal file
View File

@@ -0,0 +1,419 @@
use std::{
ffi::{
OsStr,
OsString,
},
mem,
os::unix::ffi::OsStrExt,
sync::Weak,
};
use inotify_sys as ffi;
use crate::fd_guard::FdGuard;
use crate::watches::WatchDescriptor;
/// Iterator over inotify events
///
/// Allows for iteration over the events returned by
/// [`Inotify::read_events_blocking`] or [`Inotify::read_events`].
///
/// [`Inotify::read_events_blocking`]: crate::Inotify::read_events_blocking
/// [`Inotify::read_events`]: crate::Inotify::read_events
#[derive(Debug)]
pub struct Events<'a> {
fd : Weak<FdGuard>,
buffer : &'a [u8],
num_bytes: usize,
pos : usize,
}
impl<'a> Events<'a> {
pub(crate) fn new(fd: Weak<FdGuard>, buffer: &'a [u8], num_bytes: usize)
-> Self
{
Events {
fd,
buffer,
num_bytes,
pos: 0,
}
}
}
impl<'a> Iterator for Events<'a> {
type Item = Event<&'a OsStr>;
fn next(&mut self) -> Option<Self::Item> {
if self.pos < self.num_bytes {
let (step, event) = Event::from_buffer(self.fd.clone(), &self.buffer[self.pos..]);
self.pos += step;
Some(event)
}
else {
None
}
}
}
/// An inotify event
///
/// A file system event that describes a change that the user previously
/// registered interest in. To watch for events, call [`Watches::add`]. To
/// retrieve events, call [`Inotify::read_events_blocking`] or
/// [`Inotify::read_events`].
///
/// [`Watches::add`]: crate::Watches::add
/// [`Inotify::read_events_blocking`]: crate::Inotify::read_events_blocking
/// [`Inotify::read_events`]: crate::Inotify::read_events
#[derive(Clone, Debug)]
pub struct Event<S> {
/// Identifies the watch this event originates from
///
/// This [`WatchDescriptor`] is equal to the one that [`Watches::add`]
/// returned when interest for this event was registered. The
/// [`WatchDescriptor`] can be used to remove the watch using
/// [`Watches::remove`], thereby preventing future events of this type
/// from being created.
///
/// [`Watches::add`]: crate::Watches::add
/// [`Watches::remove`]: crate::Watches::remove
pub wd: WatchDescriptor,
/// Indicates what kind of event this is
pub mask: EventMask,
/// Connects related events to each other
///
/// When a file is renamed, this results two events: [`MOVED_FROM`] and
/// [`MOVED_TO`]. The `cookie` field will be the same for both of them,
/// thereby making is possible to connect the event pair.
///
/// [`MOVED_FROM`]: EventMask::MOVED_FROM
/// [`MOVED_TO`]: EventMask::MOVED_TO
pub cookie: u32,
/// The name of the file the event originates from
///
/// This field is set only if the subject of the event is a file or directory in a
/// watched directory. If the event concerns a file or directory that is
/// watched directly, `name` will be `None`.
pub name: Option<S>,
}
impl<'a> Event<&'a OsStr> {
fn new(fd: Weak<FdGuard>, event: &ffi::inotify_event, name: &'a OsStr)
-> Self
{
let mask = EventMask::from_bits(event.mask)
.expect("Failed to convert event mask. This indicates a bug.");
let wd = crate::WatchDescriptor {
id: event.wd,
fd,
};
let name = if name.is_empty() {
None
}
else {
Some(name)
};
Event {
wd,
mask,
cookie: event.cookie,
name,
}
}
/// Create an `Event` from a buffer
///
/// Assumes that a full `inotify_event` plus its name is located at the
/// beginning of `buffer`.
///
/// Returns the number of bytes used from the buffer, and the event.
///
/// # Panics
///
/// Panics if the buffer does not contain a full event, including its name.
pub(crate) fn from_buffer(
fd : Weak<FdGuard>,
buffer: &'a [u8],
)
-> (usize, Self)
{
let event_size = mem::size_of::<ffi::inotify_event>();
// Make sure that the buffer is big enough to contain an event, without
// the name. Otherwise we can't safely convert it to an `inotify_event`.
assert!(buffer.len() >= event_size);
let ffi_event_ptr = buffer.as_ptr() as *const ffi::inotify_event;
// We have a pointer to an `inotify_event`, pointing to the beginning of
// `buffer`. Since we know, as per the assertion above, that there are
// enough bytes in the buffer for at least one event, we can safely
// read that `inotify_event`.
// We call `read_unaligned()` since the byte buffer has alignment 1
// and `inotify_event` has a higher alignment, so `*` cannot be used to dereference
// the unaligned pointer (undefined behavior).
let ffi_event = unsafe { ffi_event_ptr.read_unaligned() };
// The name's length is given by `event.len`. There should always be
// enough bytes left in the buffer to fit the name. Let's make sure that
// is the case.
let bytes_left_in_buffer = buffer.len() - event_size;
assert!(bytes_left_in_buffer >= ffi_event.len as usize);
// Directly after the event struct should be a name, if there's one
// associated with the event. Let's make a new slice that starts with
// that name. If there's no name, this slice might have a length of `0`.
let bytes_consumed = event_size + ffi_event.len as usize;
let name = &buffer[event_size..bytes_consumed];
// Remove trailing '\0' bytes
//
// The events in the buffer are aligned, and `name` is filled up
// with '\0' up to the alignment boundary. Here we remove those
// additional bytes.
//
// The `unwrap` here is safe, because `splitn` always returns at
// least one result, even if the original slice contains no '\0'.
let name = name
.splitn(2, |b| b == &0u8)
.next()
.unwrap();
let event = Event::new(
fd,
&ffi_event,
OsStr::from_bytes(name),
);
(bytes_consumed, event)
}
/// Returns an owned copy of the event.
#[deprecated = "use `to_owned()` instead; methods named `into_owned()` usually take self by value"]
#[allow(clippy::wrong_self_convention)]
pub fn into_owned(&self) -> EventOwned {
self.to_owned()
}
/// Returns an owned copy of the event.
#[must_use = "cloning is often expensive and is not expected to have side effects"]
pub fn to_owned(&self) -> EventOwned {
Event {
wd: self.wd.clone(),
mask: self.mask,
cookie: self.cookie,
name: self.name.map(OsStr::to_os_string),
}
}
}
/// An owned version of `Event`
pub type EventOwned = Event<OsString>;
bitflags! {
/// Indicates the type of an event
///
/// This struct can be retrieved from an [`Event`] via its `mask` field.
/// You can determine the [`Event`]'s type by comparing the `EventMask` to
/// its associated constants.
///
/// Please refer to the documentation of [`Event`] for a usage example.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct EventMask: u32 {
/// File was accessed
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_ACCESS`].
const ACCESS = ffi::IN_ACCESS;
/// Metadata (permissions, timestamps, ...) changed
///
/// When watching a directory, this event can be triggered for the
/// directory itself, as well as objects inside the directory.
///
/// See [`inotify_sys::IN_ATTRIB`].
const ATTRIB = ffi::IN_ATTRIB;
/// File opened for writing was closed
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_CLOSE_WRITE`].
const CLOSE_WRITE = ffi::IN_CLOSE_WRITE;
/// File or directory not opened for writing was closed
///
/// When watching a directory, this event can be triggered for the
/// directory itself, as well as objects inside the directory.
///
/// See [`inotify_sys::IN_CLOSE_NOWRITE`].
const CLOSE_NOWRITE = ffi::IN_CLOSE_NOWRITE;
/// File/directory created in watched directory
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_CREATE`].
const CREATE = ffi::IN_CREATE;
/// File/directory deleted from watched directory
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
const DELETE = ffi::IN_DELETE;
/// Watched file/directory was deleted
///
/// See [`inotify_sys::IN_DELETE_SELF`].
const DELETE_SELF = ffi::IN_DELETE_SELF;
/// File was modified
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_MODIFY`].
const MODIFY = ffi::IN_MODIFY;
/// Watched file/directory was moved
///
/// See [`inotify_sys::IN_MOVE_SELF`].
const MOVE_SELF = ffi::IN_MOVE_SELF;
/// File was renamed/moved; watched directory contained old name
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_MOVED_FROM`].
const MOVED_FROM = ffi::IN_MOVED_FROM;
/// File was renamed/moved; watched directory contains new name
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_MOVED_TO`].
const MOVED_TO = ffi::IN_MOVED_TO;
/// File or directory was opened
///
/// When watching a directory, this event can be triggered for the
/// directory itself, as well as objects inside the directory.
///
/// See [`inotify_sys::IN_OPEN`].
const OPEN = ffi::IN_OPEN;
/// Watch was removed
///
/// This event will be generated, if the watch was removed explicitly
/// (via [`Watches::remove`]), or automatically (because the file was
/// deleted or the file system was unmounted).
///
/// See [`inotify_sys::IN_IGNORED`].
///
/// [`Watches::remove`]: crate::Watches::remove
const IGNORED = ffi::IN_IGNORED;
/// Event related to a directory
///
/// The subject of the event is a directory.
///
/// See [`inotify_sys::IN_ISDIR`].
const ISDIR = ffi::IN_ISDIR;
/// Event queue overflowed
///
/// The event queue has overflowed and events have presumably been lost.
///
/// See [`inotify_sys::IN_Q_OVERFLOW`].
const Q_OVERFLOW = ffi::IN_Q_OVERFLOW;
/// File system containing watched object was unmounted.
/// File system was unmounted
///
/// The file system that contained the watched object has been
/// unmounted. An event with [`EventMask::IGNORED`] will subsequently be
/// generated for the same watch descriptor.
///
/// See [`inotify_sys::IN_UNMOUNT`].
const UNMOUNT = ffi::IN_UNMOUNT;
}
}
impl EventMask {
/// Wrapper around [`Self::from_bits_retain`] for backwards compatibility
///
/// # Safety
///
/// This function is not actually unsafe. It is just a wrapper around the
/// safe [`Self::from_bits_retain`].
#[deprecated = "Use the safe `from_bits_retain` method instead"]
pub unsafe fn from_bits_unchecked(bits: u32) -> Self {
Self::from_bits_retain(bits)
}
}
#[cfg(test)]
mod tests {
use std::{
io::prelude::*,
mem,
slice,
sync,
};
use inotify_sys as ffi;
use super::Event;
#[test]
fn from_buffer_should_not_mistake_next_event_for_name_of_previous_event() {
let mut buffer = [0u8; 1024];
// First, put a normal event into the buffer
let event = ffi::inotify_event {
wd: 0,
mask: 0,
cookie: 0,
len: 0, // no name following after event
};
let event = unsafe {
slice::from_raw_parts(
&event as *const _ as *const u8,
mem::size_of_val(&event),
)
};
(&mut buffer[..]).write(event)
.expect("Failed to write into buffer");
// After that event, simulate an event that starts with a non-zero byte.
buffer[mem::size_of_val(&event)] = 1;
// Now create the event and verify that the name is actually `None`, as
// dictated by the value `len` above.
let (_, event) = Event::from_buffer(
sync::Weak::new(),
&buffer,
);
assert_eq!(event.name, None);
}
}

93
vendor/inotify/src/fd_guard.rs vendored Normal file
View File

@@ -0,0 +1,93 @@
use std::{
ops::Deref,
os::unix::io::{
AsFd,
AsRawFd,
BorrowedFd,
FromRawFd,
IntoRawFd,
RawFd,
},
sync::atomic::{
AtomicBool,
Ordering,
},
};
use inotify_sys as ffi;
/// A RAII guard around a `RawFd` that closes it automatically on drop.
#[derive(Debug)]
pub struct FdGuard {
pub(crate) fd : RawFd,
pub(crate) close_on_drop: AtomicBool,
}
impl FdGuard {
/// Indicate that the wrapped file descriptor should _not_ be closed
/// when the guard is dropped.
///
/// This should be called in cases where ownership of the wrapped file
/// descriptor has been "moved" out of the guard.
///
/// This is factored out into a separate function to ensure that it's
/// always used consistently.
#[inline]
pub fn should_not_close(&self) {
self.close_on_drop.store(false, Ordering::Release);
}
}
impl Deref for FdGuard {
type Target = RawFd;
#[inline]
fn deref(&self) -> &Self::Target {
&self.fd
}
}
impl Drop for FdGuard {
fn drop(&mut self) {
if self.close_on_drop.load(Ordering::Acquire) {
unsafe { ffi::close(self.fd); }
}
}
}
impl FromRawFd for FdGuard {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
FdGuard {
fd,
close_on_drop: AtomicBool::new(true),
}
}
}
impl IntoRawFd for FdGuard {
fn into_raw_fd(self) -> RawFd {
self.should_not_close();
self.fd
}
}
impl AsRawFd for FdGuard {
fn as_raw_fd(&self) -> RawFd {
self.fd
}
}
impl AsFd for FdGuard {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw(self.fd) }
}
}
impl PartialEq for FdGuard {
fn eq(&self, other: &FdGuard) -> bool {
self.fd == other.fd
}
}

386
vendor/inotify/src/inotify.rs vendored Normal file
View File

@@ -0,0 +1,386 @@
use std::{
io,
os::unix::io::{
AsFd,
AsRawFd,
BorrowedFd,
FromRawFd,
IntoRawFd,
OwnedFd,
RawFd,
},
path::Path,
sync::{
atomic::AtomicBool,
Arc,
}
};
use inotify_sys as ffi;
use libc::{
F_GETFL,
F_SETFL,
O_NONBLOCK,
fcntl,
};
use crate::events::Events;
use crate::fd_guard::FdGuard;
use crate::util::read_into_buffer;
use crate::watches::{
WatchDescriptor,
WatchMask,
Watches,
};
#[cfg(feature = "stream")]
use crate::stream::EventStream;
/// Idiomatic Rust wrapper around Linux's inotify API
///
/// `Inotify` is a wrapper around an inotify instance. It generally tries to
/// adhere to the underlying inotify API closely, while making access to it
/// safe and convenient.
///
/// Please refer to the [top-level documentation] for further details and a
/// usage example.
///
/// [top-level documentation]: crate
#[derive(Debug)]
pub struct Inotify {
fd: Arc<FdGuard>,
}
impl Inotify {
/// Creates an [`Inotify`] instance
///
/// Initializes an inotify instance by calling [`inotify_init1`].
///
/// This method passes both flags accepted by [`inotify_init1`], not giving
/// the user any choice in the matter, as not passing the flags would be
/// inappropriate in the context of this wrapper:
///
/// - [`IN_CLOEXEC`] prevents leaking file descriptors to other processes.
/// - [`IN_NONBLOCK`] controls the blocking behavior of the inotify API,
/// which is entirely managed by this wrapper.
///
/// # Errors
///
/// Directly returns the error from the call to [`inotify_init1`], without
/// adding any error conditions of its own.
///
/// # Examples
///
/// ```
/// use inotify::Inotify;
///
/// let inotify = Inotify::init()
/// .expect("Failed to initialize an inotify instance");
/// ```
///
/// [`inotify_init1`]: inotify_sys::inotify_init1
/// [`IN_CLOEXEC`]: inotify_sys::IN_CLOEXEC
/// [`IN_NONBLOCK`]: inotify_sys::IN_NONBLOCK
pub fn init() -> io::Result<Inotify> {
let fd = unsafe {
// Initialize inotify and pass both `IN_CLOEXEC` and `IN_NONBLOCK`.
//
// `IN_NONBLOCK` is needed, because `Inotify` manages blocking
// behavior for the API consumer, and the way we do that is to make
// everything non-blocking by default and later override that as
// required.
//
// Passing `IN_CLOEXEC` prevents leaking file descriptors to
// processes executed by this process and seems to be a best
// practice. I don't grasp this issue completely and failed to find
// any authoritative sources on the topic. There's some discussion in
// the open(2) and fcntl(2) man pages, but I didn't find that
// helpful in understanding the issue of leaked file descriptors.
// For what it's worth, there's a Rust issue about this:
// https://github.com/rust-lang/rust/issues/12148
ffi::inotify_init1(ffi::IN_CLOEXEC | ffi::IN_NONBLOCK)
};
if fd == -1 {
return Err(io::Error::last_os_error());
}
Ok(Inotify {
fd: Arc::new(FdGuard {
fd,
close_on_drop: AtomicBool::new(true),
}),
})
}
/// Gets an interface that allows adding and removing watches.
/// See [`Watches::add`] and [`Watches::remove`].
pub fn watches(&self) -> Watches {
Watches::new(self.fd.clone())
}
/// Deprecated: use `Inotify.watches().add()` instead
#[deprecated = "use `Inotify.watches().add()` instead"]
pub fn add_watch<P>(&mut self, path: P, mask: WatchMask)
-> io::Result<WatchDescriptor>
where P: AsRef<Path>
{
self.watches().add(path, mask)
}
/// Deprecated: use `Inotify.watches().remove()` instead
#[deprecated = "use `Inotify.watches().remove()` instead"]
pub fn rm_watch(&mut self, wd: WatchDescriptor) -> io::Result<()> {
self.watches().remove(wd)
}
/// Waits until events are available, then returns them
///
/// Blocks the current thread until at least one event is available. If this
/// is not desirable, please consider [`Inotify::read_events`].
///
/// This method calls [`Inotify::read_events`] internally and behaves
/// essentially the same, apart from the blocking behavior. Please refer to
/// the documentation of [`Inotify::read_events`] for more information.
pub fn read_events_blocking<'a>(&mut self, buffer: &'a mut [u8])
-> io::Result<Events<'a>>
{
unsafe {
let res = fcntl(**self.fd, F_GETFL);
if res == -1 {
return Err(io::Error::last_os_error());
}
if fcntl(**self.fd, F_SETFL, res & !O_NONBLOCK) == -1 {
return Err(io::Error::last_os_error());
}
};
let result = self.read_events(buffer);
unsafe {
let res = fcntl(**self.fd, F_GETFL);
if res == -1 {
return Err(io::Error::last_os_error());
}
if fcntl(**self.fd, F_SETFL, res | O_NONBLOCK) == -1 {
return Err(io::Error::last_os_error());
}
};
result
}
/// Returns one buffer's worth of available events
///
/// Reads as many events as possible into `buffer`, and returns an iterator
/// over them. If no events are available, an iterator is still returned. If
/// you need a method that will block until at least one event is available,
/// please consider [`read_events_blocking`].
///
/// Please note that inotify will merge identical successive unread events
/// into a single event. This means this method can not be used to count the
/// number of file system events.
///
/// The `buffer` argument, as the name indicates, is used as a buffer for
/// the inotify events. Its contents may be overwritten.
///
/// # Errors
///
/// This function directly returns all errors from the call to [`read`].
/// In addition, [`ErrorKind::UnexpectedEof`] is returned, if the call to
/// [`read`] returns `0`, signaling end-of-file.
///
/// If `buffer` is too small, this will result in an error with
/// [`ErrorKind::InvalidInput`]. On very old Linux kernels,
/// [`ErrorKind::UnexpectedEof`] will be returned instead.
///
/// # Examples
///
/// ```no_run
/// use inotify::Inotify;
/// use std::io::ErrorKind;
///
/// let mut inotify = Inotify::init()
/// .expect("Failed to initialize an inotify instance");
///
/// let mut buffer = [0; 1024];
/// let events = loop {
/// match inotify.read_events(&mut buffer) {
/// Ok(events) => break events,
/// Err(error) if error.kind() == ErrorKind::WouldBlock => continue,
/// _ => panic!("Error while reading events"),
/// }
/// };
///
/// for event in events {
/// // Handle event
/// }
/// ```
///
/// [`read_events_blocking`]: Self::read_events_blocking
/// [`read`]: libc::read
/// [`ErrorKind::UnexpectedEof`]: std::io::ErrorKind::UnexpectedEof
/// [`ErrorKind::InvalidInput`]: std::io::ErrorKind::InvalidInput
pub fn read_events<'a>(&mut self, buffer: &'a mut [u8])
-> io::Result<Events<'a>>
{
let num_bytes = read_into_buffer(**self.fd, buffer);
let num_bytes = match num_bytes {
0 => {
return Err(
io::Error::new(
io::ErrorKind::UnexpectedEof,
"`read` return `0`, signaling end-of-file"
)
);
}
-1 => {
let error = io::Error::last_os_error();
return Err(error);
},
_ if num_bytes < 0 => {
panic!("{} {} {} {} {} {}",
"Unexpected return value from `read`. Received a negative",
"value that was not `-1`. According to the `read` man page",
"this shouldn't happen, as either `-1` is returned on",
"error, `0` on end-of-file, or a positive value for the",
"number of bytes read. Returned value:",
num_bytes,
);
}
_ => {
// The value returned by `read` should be `isize`. Let's quickly
// verify this with the following assignment, so we can be sure
// our cast below is valid.
let num_bytes: isize = num_bytes;
// The type returned by `read` is `isize`, and we've ruled out
// all negative values with the match arms above. This means we
// can safely cast to `usize`.
debug_assert!(num_bytes > 0);
num_bytes as usize
}
};
Ok(Events::new(Arc::downgrade(&self.fd), buffer, num_bytes))
}
/// Deprecated: use `into_event_stream()` instead, which enforces a single `Stream` and predictable reads.
/// Using this method to create multiple `EventStream` instances from one `Inotify` is unsupported,
/// as they will contend over one event source and each produce unpredictable stream contents.
#[deprecated = "use `into_event_stream()` instead, which enforces a single Stream and predictable reads"]
#[cfg(feature = "stream")]
pub fn event_stream<T>(&mut self, buffer: T)
-> io::Result<EventStream<T>>
where
T: AsMut<[u8]> + AsRef<[u8]>,
{
EventStream::new(self.fd.clone(), buffer)
}
/// Create a stream which collects events. Consumes the `Inotify` instance.
///
/// Returns a `Stream` over all events that are available. This stream is an
/// infinite source of events.
///
/// An internal buffer which can hold the largest possible event is used.
#[cfg(feature = "stream")]
pub fn into_event_stream<T>(self, buffer: T)
-> io::Result<EventStream<T>>
where
T: AsMut<[u8]> + AsRef<[u8]>,
{
EventStream::new(self.fd, buffer)
}
/// Creates an `Inotify` instance using the file descriptor which was originally
/// initialized in `Inotify::init`. This is intended to be used to transform an
/// `EventStream` back into an `Inotify`. Do not attempt to clone `Inotify` with this.
#[cfg(feature = "stream")]
pub(crate) fn from_file_descriptor(fd: Arc<FdGuard>) -> Self
{
Inotify {
fd,
}
}
/// Closes the inotify instance
///
/// Closes the file descriptor referring to the inotify instance. The user
/// usually doesn't have to call this function, as the underlying inotify
/// instance is closed automatically, when [`Inotify`] is dropped.
///
/// # Errors
///
/// Directly returns the error from the call to [`close`], without adding any
/// error conditions of its own.
///
/// # Examples
///
/// ```
/// use inotify::Inotify;
///
/// let mut inotify = Inotify::init()
/// .expect("Failed to initialize an inotify instance");
///
/// inotify.close()
/// .expect("Failed to close inotify instance");
/// ```
///
/// [`close`]: libc::close
pub fn close(self) -> io::Result<()> {
// `self` will be dropped when this method returns. If this is the only
// owner of `fd`, the `Arc` will also be dropped. The `Drop`
// implementation for `FdGuard` will attempt to close the file descriptor
// again, unless this flag here is cleared.
self.fd.should_not_close();
match unsafe { ffi::close(**self.fd) } {
0 => Ok(()),
_ => Err(io::Error::last_os_error()),
}
}
}
impl AsRawFd for Inotify {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}
impl FromRawFd for Inotify {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Inotify {
fd: Arc::new(FdGuard::from_raw_fd(fd))
}
}
}
impl IntoRawFd for Inotify {
#[inline]
fn into_raw_fd(self) -> RawFd {
self.fd.should_not_close();
self.fd.fd
}
}
impl AsFd for Inotify {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
impl From<Inotify> for OwnedFd {
fn from(fd: Inotify) -> OwnedFd {
unsafe { OwnedFd::from_raw_fd(fd.into_raw_fd()) }
}
}
impl From<OwnedFd> for Inotify {
fn from(fd: OwnedFd) -> Inotify {
unsafe { Inotify::from_raw_fd(fd.into_raw_fd()) }
}
}

105
vendor/inotify/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,105 @@
//! Idiomatic inotify wrapper for the Rust programming language
//!
//! # About
//!
//! [inotify-rs] is an idiomatic wrapper around the Linux kernel's [inotify] API
//! for the Rust programming language. It can be used for monitoring changes to
//! files or directories.
//!
//! The [`Inotify`] struct is the main entry point into the API.
//!
//! # Example
//!
//! ```
//! use inotify::{
//! Inotify,
//! WatchMask,
//! };
//!
//! let mut inotify = Inotify::init()
//! .expect("Error while initializing inotify instance");
//!
//! # // Create a temporary file, so `Watches::add` won't return an error.
//! # use std::fs::File;
//! # let mut test_file = File::create("/tmp/inotify-rs-test-file")
//! # .expect("Failed to create test file");
//! #
//! // Watch for modify and close events.
//! inotify
//! .watches()
//! .add(
//! "/tmp/inotify-rs-test-file",
//! WatchMask::MODIFY | WatchMask::CLOSE,
//! )
//! .expect("Failed to add file watch");
//!
//! # // Modify file, so the following `read_events_blocking` won't block.
//! # use std::io::Write;
//! # write!(&mut test_file, "something\n")
//! # .expect("Failed to write something to test file");
//! #
//! // Read events that were added with `Watches::add` above.
//! let mut buffer = [0; 1024];
//! let events = inotify.read_events_blocking(&mut buffer)
//! .expect("Error while reading events");
//!
//! for event in events {
//! // Handle event
//! }
//! ```
//!
//! # Attention: inotify gotchas
//!
//! inotify (as in, the Linux API, not this wrapper) has many edge cases, making
//! it hard to use correctly. This can lead to weird and hard to find bugs in
//! applications that are based on it. inotify-rs does its best to fix these
//! issues, but sometimes this would require an amount of runtime overhead that
//! is just unacceptable for a low-level wrapper such as this.
//!
//! We've documented any issues that inotify-rs has inherited from inotify, as
//! far as we are aware of them. Please watch out for any further warnings
//! throughout this documentation. If you want to be on the safe side, in case
//! we have missed something, please read the [inotify man pages] carefully.
//!
//! [inotify-rs]: https://crates.io/crates/inotify
//! [inotify]: https://en.wikipedia.org/wiki/Inotify
//! [inotify man pages]: http://man7.org/linux/man-pages/man7/inotify.7.html
#![deny(missing_docs)]
#![deny(warnings)]
#![deny(missing_debug_implementations)]
#[macro_use]
extern crate bitflags;
mod events;
mod fd_guard;
mod inotify;
mod util;
mod watches;
#[cfg(feature = "stream")]
mod stream;
pub use crate::events::{
Event,
EventMask,
EventOwned,
Events,
};
pub use crate::inotify::Inotify;
pub use crate::util::{
get_buffer_size,
get_absolute_path_buffer_size,
};
pub use crate::watches::{
Watches,
WatchDescriptor,
WatchMask,
};
#[cfg(feature = "stream")]
pub use self::stream::EventStream;

120
vendor/inotify/src/stream.rs vendored Normal file
View File

@@ -0,0 +1,120 @@
use std::{
io,
os::unix::io::{AsRawFd, RawFd},
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
use futures_core::{ready, Stream};
use tokio::io::unix::AsyncFd;
use crate::events::{Event, EventOwned};
use crate::fd_guard::FdGuard;
use crate::Inotify;
use crate::util::read_into_buffer;
use crate::watches::Watches;
/// Stream of inotify events
///
/// Allows for streaming events returned by [`Inotify::into_event_stream`].
#[derive(Debug)]
pub struct EventStream<T> {
fd: AsyncFd<ArcFdGuard>,
buffer: T,
buffer_pos: usize,
unused_bytes: usize,
}
impl<T> EventStream<T>
where
T: AsMut<[u8]> + AsRef<[u8]>,
{
/// Returns a new `EventStream` associated with the default reactor.
pub(crate) fn new(fd: Arc<FdGuard>, buffer: T) -> io::Result<Self> {
Ok(EventStream {
fd: AsyncFd::new(ArcFdGuard(fd))?,
buffer,
buffer_pos: 0,
unused_bytes: 0,
})
}
/// Returns an instance of `Watches` to add and remove watches.
/// See [`Watches::add`] and [`Watches::remove`].
pub fn watches(&self) -> Watches {
Watches::new(self.fd.get_ref().0.clone())
}
/// Consumes the `EventStream` instance and returns an `Inotify` using the original
/// file descriptor that was passed from `Inotify` to create the `EventStream`.
pub fn into_inotify(self) -> Inotify {
Inotify::from_file_descriptor(self.fd.into_inner().0)
}
}
impl<T> Stream for EventStream<T>
where
T: AsMut<[u8]> + AsRef<[u8]>,
{
type Item = io::Result<EventOwned>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
// Safety: safe because we never move out of `self_`.
let self_ = unsafe { self.get_unchecked_mut() };
if self_.unused_bytes == 0 {
// Nothing usable in buffer. Need to reset and fill buffer.
self_.buffer_pos = 0;
self_.unused_bytes = ready!(read(&self_.fd, self_.buffer.as_mut(), cx))?;
}
if self_.unused_bytes == 0 {
// The previous read returned `0` signalling end-of-file. Let's
// signal end-of-stream to the caller.
return Poll::Ready(None);
}
// We have bytes in the buffer. inotify doesn't put partial events in
// there, and we only take complete events out. That means we have at
// least one event in there and can call `from_buffer` to take it out.
let (bytes_consumed, event) = Event::from_buffer(
Arc::downgrade(&self_.fd.get_ref().0),
&self_.buffer.as_ref()[self_.buffer_pos..],
);
self_.buffer_pos += bytes_consumed;
self_.unused_bytes -= bytes_consumed;
Poll::Ready(Some(Ok(event.to_owned())))
}
}
// Newtype wrapper because AsRawFd isn't implemented for Arc<T> where T: AsRawFd.
#[derive(Debug)]
struct ArcFdGuard(Arc<FdGuard>);
impl AsRawFd for ArcFdGuard {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
fn read(fd: &AsyncFd<ArcFdGuard>, buffer: &mut [u8], cx: &mut Context) -> Poll<io::Result<usize>> {
let mut guard = ready!(fd.poll_read_ready(cx))?;
let result = guard.try_io(|_| {
let read = read_into_buffer(fd.as_raw_fd(), buffer);
if read == -1 {
return Err(io::Error::last_os_error());
}
Ok(read as usize)
});
match result {
Ok(result) => Poll::Ready(result),
Err(_would_block) => {
cx.waker().wake_by_ref();
Poll::Pending
}
}
}

70
vendor/inotify/src/util.rs vendored Normal file
View File

@@ -0,0 +1,70 @@
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 {
ffi::read(
fd,
buffer.as_mut_ptr() as *mut c_void,
buffer.len() as size_t
)
}
}
/// 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>
///
/// 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>
///
/// Unfortunately, we can't just do the same with max path length itself.
///
/// See: <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
}
}

461
vendor/inotify/src/watches.rs vendored Normal file
View File

@@ -0,0 +1,461 @@
use std::{
cmp::Ordering,
ffi::CString,
hash::{
Hash,
Hasher,
},
io,
os::raw::c_int,
os::unix::ffi::OsStrExt,
path::Path,
sync::{
Arc,
Weak,
},
};
use inotify_sys as ffi;
use crate::fd_guard::FdGuard;
bitflags! {
/// Describes a file system watch
///
/// Passed to [`Watches::add`], to describe what file system events
/// to watch for, and how to do that.
///
/// # Examples
///
/// `WatchMask` constants can be passed to [`Watches::add`] as is. For
/// example, here's how to create a watch that triggers an event when a file
/// is accessed:
///
/// ``` rust
/// # use inotify::{
/// # Inotify,
/// # WatchMask,
/// # };
/// #
/// # let mut inotify = Inotify::init().unwrap();
/// #
/// # // Create a temporary file, so `Watches::add` won't return an error.
/// # use std::fs::File;
/// # File::create("/tmp/inotify-rs-test-file")
/// # .expect("Failed to create test file");
/// #
/// inotify.watches().add("/tmp/inotify-rs-test-file", WatchMask::ACCESS)
/// .expect("Error adding watch");
/// ```
///
/// You can also combine multiple `WatchMask` constants. Here we add a watch
/// this is triggered both when files are created or deleted in a directory:
///
/// ``` rust
/// # use inotify::{
/// # Inotify,
/// # WatchMask,
/// # };
/// #
/// # let mut inotify = Inotify::init().unwrap();
/// inotify.watches().add("/tmp/", WatchMask::CREATE | WatchMask::DELETE)
/// .expect("Error adding watch");
/// ```
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct WatchMask: u32 {
/// File was accessed
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_ACCESS`].
const ACCESS = ffi::IN_ACCESS;
/// Metadata (permissions, timestamps, ...) changed
///
/// When watching a directory, this event can be triggered for the
/// directory itself, as well as objects inside the directory.
///
/// See [`inotify_sys::IN_ATTRIB`].
const ATTRIB = ffi::IN_ATTRIB;
/// File opened for writing was closed
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_CLOSE_WRITE`].
const CLOSE_WRITE = ffi::IN_CLOSE_WRITE;
/// File or directory not opened for writing was closed
///
/// When watching a directory, this event can be triggered for the
/// directory itself, as well as objects inside the directory.
///
/// See [`inotify_sys::IN_CLOSE_NOWRITE`].
const CLOSE_NOWRITE = ffi::IN_CLOSE_NOWRITE;
/// File/directory created in watched directory
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_CREATE`].
const CREATE = ffi::IN_CREATE;
/// File/directory deleted from watched directory
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_DELETE`].
const DELETE = ffi::IN_DELETE;
/// Watched file/directory was deleted
///
/// See [`inotify_sys::IN_DELETE_SELF`].
const DELETE_SELF = ffi::IN_DELETE_SELF;
/// File was modified
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_MODIFY`].
const MODIFY = ffi::IN_MODIFY;
/// Watched file/directory was moved
///
/// See [`inotify_sys::IN_MOVE_SELF`].
const MOVE_SELF = ffi::IN_MOVE_SELF;
/// File was renamed/moved; watched directory contained old name
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_MOVED_FROM`].
const MOVED_FROM = ffi::IN_MOVED_FROM;
/// File was renamed/moved; watched directory contains new name
///
/// When watching a directory, this event is only triggered for objects
/// inside the directory, not the directory itself.
///
/// See [`inotify_sys::IN_MOVED_TO`].
const MOVED_TO = ffi::IN_MOVED_TO;
/// File or directory was opened
///
/// When watching a directory, this event can be triggered for the
/// directory itself, as well as objects inside the directory.
///
/// See [`inotify_sys::IN_OPEN`].
const OPEN = ffi::IN_OPEN;
/// Watch for all events
///
/// This constant is simply a convenient combination of the following
/// other constants:
///
/// - [`ACCESS`](Self::ACCESS)
/// - [`ATTRIB`](Self::ATTRIB)
/// - [`CLOSE_WRITE`](Self::CLOSE_WRITE)
/// - [`CLOSE_NOWRITE`](Self::CLOSE_NOWRITE)
/// - [`CREATE`](Self::CREATE)
/// - [`DELETE`](Self::DELETE)
/// - [`DELETE_SELF`](Self::DELETE_SELF)
/// - [`MODIFY`](Self::MODIFY)
/// - [`MOVE_SELF`](Self::MOVE_SELF)
/// - [`MOVED_FROM`](Self::MOVED_FROM)
/// - [`MOVED_TO`](Self::MOVED_TO)
/// - [`OPEN`](Self::OPEN)
///
/// See [`inotify_sys::IN_ALL_EVENTS`].
const ALL_EVENTS = ffi::IN_ALL_EVENTS;
/// Watch for all move events
///
/// This constant is simply a convenient combination of the following
/// other constants:
///
/// - [`MOVED_FROM`](Self::MOVED_FROM)
/// - [`MOVED_TO`](Self::MOVED_TO)
///
/// See [`inotify_sys::IN_MOVE`].
const MOVE = ffi::IN_MOVE;
/// Watch for all close events
///
/// This constant is simply a convenient combination of the following
/// other constants:
///
/// - [`CLOSE_WRITE`](Self::CLOSE_WRITE)
/// - [`CLOSE_NOWRITE`](Self::CLOSE_NOWRITE)
///
/// See [`inotify_sys::IN_CLOSE`].
const CLOSE = ffi::IN_CLOSE;
/// Don't dereference the path if it is a symbolic link
///
/// See [`inotify_sys::IN_DONT_FOLLOW`].
const DONT_FOLLOW = ffi::IN_DONT_FOLLOW;
/// Filter events for directory entries that have been unlinked
///
/// See [`inotify_sys::IN_EXCL_UNLINK`].
const EXCL_UNLINK = ffi::IN_EXCL_UNLINK;
/// If a watch for the inode exists, amend it instead of replacing it
///
/// See [`inotify_sys::IN_MASK_ADD`].
const MASK_ADD = ffi::IN_MASK_ADD;
/// Only receive one event, then remove the watch
///
/// See [`inotify_sys::IN_ONESHOT`].
const ONESHOT = ffi::IN_ONESHOT;
/// Only watch path, if it is a directory
///
/// See [`inotify_sys::IN_ONLYDIR`].
const ONLYDIR = ffi::IN_ONLYDIR;
}
}
impl WatchMask {
/// Wrapper around [`Self::from_bits_retain`] for backwards compatibility
///
/// # Safety
///
/// This function is not actually unsafe. It is just a wrapper around the
/// safe [`Self::from_bits_retain`].
#[deprecated = "Use the safe `from_bits_retain` method instead"]
pub unsafe fn from_bits_unchecked(bits: u32) -> Self {
Self::from_bits_retain(bits)
}
}
impl WatchDescriptor {
/// Getter method for a watcher's id.
///
/// Can be used to distinguish events for files with the same name.
pub fn get_watch_descriptor_id(&self) -> c_int {
self.id
}
}
/// Interface for adding and removing watches
#[derive(Clone, Debug)]
pub struct Watches {
pub(crate) fd: Arc<FdGuard>,
}
impl Watches {
/// Init watches with an inotify file descriptor
pub(crate) fn new(fd: Arc<FdGuard>) -> Self {
Watches {
fd,
}
}
/// Adds or updates a watch for the given path
///
/// Adds a new watch or updates an existing one for the file referred to by
/// `path`. Returns a watch descriptor that can be used to refer to this
/// watch later.
///
/// The `mask` argument defines what kind of changes the file should be
/// watched for, and how to do that. See the documentation of [`WatchMask`]
/// for details.
///
/// If this method is used to add a new watch, a new [`WatchDescriptor`] is
/// returned. If it is used to update an existing watch, a
/// [`WatchDescriptor`] that equals the previously returned
/// [`WatchDescriptor`] for that watch is returned instead.
///
/// Under the hood, this method just calls [`inotify_add_watch`] and does
/// some trivial translation between the types on the Rust side and the C
/// side.
///
/// # Attention: Updating watches and hardlinks
///
/// As mentioned above, this method can be used to update an existing watch.
/// This is usually done by calling this method with the same `path`
/// argument that it has been called with before. But less obviously, it can
/// also happen if the method is called with a different path that happens
/// to link to the same inode.
///
/// You can detect this by keeping track of [`WatchDescriptor`]s and the
/// paths they have been returned for. If the same [`WatchDescriptor`] is
/// returned for a different path (and you haven't freed the
/// [`WatchDescriptor`] by removing the watch), you know you have two paths
/// pointing to the same inode, being watched by the same watch.
///
/// # Errors
///
/// Directly returns the error from the call to
/// [`inotify_add_watch`][`inotify_add_watch`] (translated into an
/// `io::Error`), without adding any error conditions of
/// its own.
///
/// # Examples
///
/// ```
/// use inotify::{
/// Inotify,
/// WatchMask,
/// };
///
/// let mut inotify = Inotify::init()
/// .expect("Failed to initialize an inotify instance");
///
/// # // Create a temporary file, so `Watches::add` won't return an error.
/// # use std::fs::File;
/// # File::create("/tmp/inotify-rs-test-file")
/// # .expect("Failed to create test file");
/// #
/// inotify.watches().add("/tmp/inotify-rs-test-file", WatchMask::MODIFY)
/// .expect("Failed to add file watch");
///
/// // Handle events for the file here
/// ```
///
/// [`inotify_add_watch`]: inotify_sys::inotify_add_watch
pub fn add<P>(&mut self, path: P, mask: WatchMask)
-> io::Result<WatchDescriptor>
where P: AsRef<Path>
{
let path = CString::new(path.as_ref().as_os_str().as_bytes())?;
let wd = unsafe {
ffi::inotify_add_watch(
**self.fd,
path.as_ptr() as *const _,
mask.bits(),
)
};
match wd {
-1 => Err(io::Error::last_os_error()),
_ => Ok(WatchDescriptor{ id: wd, fd: Arc::downgrade(&self.fd) }),
}
}
/// Stops watching a file
///
/// Removes the watch represented by the provided [`WatchDescriptor`] by
/// calling [`inotify_rm_watch`]. [`WatchDescriptor`]s can be obtained via
/// [`Watches::add`], or from the `wd` field of [`Event`].
///
/// # Errors
///
/// Directly returns the error from the call to [`inotify_rm_watch`].
/// Returns an [`io::Error`] with [`ErrorKind`]`::InvalidInput`, if the given
/// [`WatchDescriptor`] did not originate from this [`Inotify`] instance.
///
/// # Examples
///
/// ```
/// use inotify::Inotify;
///
/// let mut inotify = Inotify::init()
/// .expect("Failed to initialize an inotify instance");
///
/// # // Create a temporary file, so `Watches::add` won't return an error.
/// # use std::fs::File;
/// # let mut test_file = File::create("/tmp/inotify-rs-test-file")
/// # .expect("Failed to create test file");
/// #
/// # // Add a watch and modify the file, so the code below doesn't block
/// # // forever.
/// # use inotify::WatchMask;
/// # inotify.watches().add("/tmp/inotify-rs-test-file", WatchMask::MODIFY)
/// # .expect("Failed to add file watch");
/// # use std::io::Write;
/// # write!(&mut test_file, "something\n")
/// # .expect("Failed to write something to test file");
/// #
/// let mut buffer = [0; 1024];
/// let events = inotify
/// .read_events_blocking(&mut buffer)
/// .expect("Error while waiting for events");
/// let mut watches = inotify.watches();
///
/// for event in events {
/// watches.remove(event.wd);
/// }
/// ```
///
/// [`inotify_rm_watch`]: inotify_sys::inotify_rm_watch
/// [`Event`]: crate::Event
/// [`Inotify`]: crate::Inotify
/// [`io::Error`]: std::io::Error
/// [`ErrorKind`]: std::io::ErrorKind
pub fn remove(&mut self, wd: WatchDescriptor) -> io::Result<()> {
if wd.fd.upgrade().as_ref() != Some(&self.fd) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid WatchDescriptor",
));
}
let result = unsafe { ffi::inotify_rm_watch(**self.fd, wd.id) };
match result {
0 => Ok(()),
-1 => Err(io::Error::last_os_error()),
_ => panic!(
"unexpected return code from inotify_rm_watch ({})", result)
}
}
}
/// Represents a watch on an inode
///
/// Can be obtained from [`Watches::add`] or from an [`Event`]. A watch
/// descriptor can be used to get inotify to stop watching an inode by passing
/// it to [`Watches::remove`].
///
/// [`Event`]: crate::Event
#[derive(Clone, Debug)]
pub struct WatchDescriptor{
pub(crate) id: c_int,
pub(crate) fd: Weak<FdGuard>,
}
impl Eq for WatchDescriptor {}
impl PartialEq for WatchDescriptor {
fn eq(&self, other: &Self) -> bool {
let self_fd = self.fd.upgrade();
let other_fd = other.fd.upgrade();
self.id == other.id && self_fd.is_some() && self_fd == other_fd
}
}
impl Ord for WatchDescriptor {
fn cmp(&self, other: &Self) -> Ordering {
self.id.cmp(&other.id)
}
}
impl PartialOrd for WatchDescriptor {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Hash for WatchDescriptor {
fn hash<H: Hasher>(&self, state: &mut H) {
// This function only takes `self.id` into account, as `self.fd` is a
// weak pointer that might no longer be available. Since neither
// panicking nor changing the hash depending on whether it's available
// is acceptable, we just don't look at it at all.
// I don't think that this influences storage in a `HashMap` or
// `HashSet` negatively, as storing `WatchDescriptor`s from different
// `Inotify` instances seems like something of an anti-pattern anyway.
self.id.hash(state);
}
}