Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

445
third-party/vendor/inotify/src/events.rs vendored Normal file
View file

@ -0,0 +1,445 @@
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;
use crate::util::align_buffer;
/// Iterator over inotify events
///
/// Allows for iteration over the events returned by
/// [`Inotify::read_events_blocking`] or [`Inotify::read_events`].
///
/// [`Inotify::read_events_blocking`]: struct.Inotify.html#method.read_events_blocking
/// [`Inotify::read_events`]: struct.Inotify.html#method.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 : fd,
buffer : buffer,
num_bytes: 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 [`Inotify::add_watch`]. To
/// retrieve events, call [`Inotify::read_events_blocking`] or
/// [`Inotify::read_events`].
///
/// [`Inotify::add_watch`]: struct.Inotify.html#method.add_watch
/// [`Inotify::read_events_blocking`]: struct.Inotify.html#method.read_events_blocking
/// [`Inotify::read_events`]: struct.Inotify.html#method.read_events
#[derive(Clone, Debug)]
pub struct Event<S> {
/// Identifies the watch this event originates from
///
/// This [`WatchDescriptor`] is equal to the one that [`Inotify::add_watch`]
/// returned when interest for this event was registered. The
/// [`WatchDescriptor`] can be used to remove the watch using
/// [`Inotify::rm_watch`], thereby preventing future events of this type
/// from being created.
///
/// [`WatchDescriptor`]: struct.WatchDescriptor.html
/// [`Inotify::add_watch`]: struct.Inotify.html#method.add_watch
/// [`Inotify::rm_watch`]: struct.Inotify.html#method.rm_watch
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`]: event_mask/constant.MOVED_FROM.html
/// [`MOVED_TO`]: event_mask/constant.MOVED_TO.html
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 == "" {
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>();
let event_align = mem::align_of::<ffi::inotify_event>();
// Make sure that the buffer can satisfy the alignment requirements for `inotify_event`
assert!(buffer.len() >= event_align);
// Discard the unaligned portion, if any, of the supplied buffer
let buffer = align_buffer(buffer);
// Make sure that the aligned 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 event = 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
// convert that pointer into a reference.
let event = unsafe { &*event };
// 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 >= 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 + 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,
event,
OsStr::from_bytes(name),
);
(bytes_consumed, event)
}
/// Returns an owned copy of the event.
#[must_use = "cloning is often expensive and is not expected to have side effects"]
pub fn into_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.
///
/// [`Event`]: struct.Event.html
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`].
///
/// [`inotify_sys::IN_ACCESS`]: ../inotify_sys/constant.IN_ACCESS.html
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`].
///
/// [`inotify_sys::IN_ATTRIB`]: ../inotify_sys/constant.IN_ATTRIB.html
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`].
///
/// [`inotify_sys::IN_CLOSE_WRITE`]: ../inotify_sys/constant.IN_CLOSE_WRITE.html
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`].
///
/// [`inotify_sys::IN_CLOSE_NOWRITE`]: ../inotify_sys/constant.IN_CLOSE_NOWRITE.html
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`].
///
/// [`inotify_sys::IN_CREATE`]: ../inotify_sys/constant.IN_CREATE.html
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`].
///
/// [`inotify_sys::IN_DELETE`]: ../inotify_sys/constant.IN_DELETE.html
const DELETE = ffi::IN_DELETE;
/// Watched file/directory was deleted
///
/// See [`inotify_sys::IN_DELETE_SELF`].
///
/// [`inotify_sys::IN_DELETE_SELF`]: ../inotify_sys/constant.IN_DELETE_SELF.html
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`].
///
/// [`inotify_sys::IN_MODIFY`]: ../inotify_sys/constant.IN_MODIFY.html
const MODIFY = ffi::IN_MODIFY;
/// Watched file/directory was moved
///
/// See [`inotify_sys::IN_MOVE_SELF`].
///
/// [`inotify_sys::IN_MOVE_SELF`]: ../inotify_sys/constant.IN_MOVE_SELF.html
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`].
///
/// [`inotify_sys::IN_MOVED_FROM`]: ../inotify_sys/constant.IN_MOVED_FROM.html
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`].
///
/// [`inotify_sys::IN_MOVED_TO`]: ../inotify_sys/constant.IN_MOVED_TO.html
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`].
///
/// [`inotify_sys::IN_OPEN`]: ../inotify_sys/constant.IN_OPEN.html
const OPEN = ffi::IN_OPEN;
/// Watch was removed
///
/// This event will be generated, if the watch was removed explicitly
/// (via [`Inotify::rm_watch`]), or automatically (because the file was
/// deleted or the file system was unmounted).
///
/// See [`inotify_sys::IN_IGNORED`].
///
/// [`inotify_sys::IN_IGNORED`]: ../inotify_sys/constant.IN_IGNORED.html
const IGNORED = ffi::IN_IGNORED;
/// Event related to a directory
///
/// The subject of the event is a directory.
///
/// See [`inotify_sys::IN_ISDIR`].
///
/// [`inotify_sys::IN_ISDIR`]: ../inotify_sys/constant.IN_ISDIR.html
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`].
///
/// [`inotify_sys::IN_Q_OVERFLOW`]: ../inotify_sys/constant.IN_Q_OVERFLOW.html
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 [`WatchMask::IGNORED`] will subsequently be
/// generated for the same watch descriptor.
///
/// See [`inotify_sys::IN_UNMOUNT`].
///
/// [`WatchMask::IGNORED`]: #associatedconstant.IGNORED
/// [`inotify_sys::IN_UNMOUNT`]: ../inotify_sys/constant.IN_UNMOUNT.html
const UNMOUNT = ffi::IN_UNMOUNT;
}
}
#[cfg(test)]
mod tests {
use std::{
io::prelude::*,
mem,
slice,
sync,
};
use crate::util;
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];
// Make sure the buffer is properly aligned before writing raw events into it
let buffer = util::align_buffer_mut(&mut buffer);
// 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);
}
}

View file

@ -0,0 +1,84 @@
use std::{
ops::Deref,
os::unix::io::{
AsRawFd,
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 PartialEq for FdGuard {
fn eq(&self, other: &FdGuard) -> bool {
self.fd == other.fd
}
}

View file

@ -0,0 +1,485 @@
use std::{
ffi::CString,
io,
os::unix::ffi::OsStrExt,
os::unix::io::{
AsRawFd,
FromRawFd,
IntoRawFd,
RawFd,
},
path::Path,
sync::{
atomic::AtomicBool,
Arc,
}
};
use inotify_sys as ffi;
use libc::{
F_GETFL,
F_SETFD,
F_SETFL,
FD_CLOEXEC,
O_NONBLOCK,
fcntl,
};
use crate::events::Events;
use crate::fd_guard::FdGuard;
use crate::util::read_into_buffer;
use crate::watches::{
WatchDescriptor,
WatchMask,
};
#[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]: index.html
#[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`]: struct.Inotify.html
/// [`inotify_init1`]: ../inotify_sys/fn.inotify_init1.html
/// [`IN_CLOEXEC`]: ../inotify_sys/constant.IN_CLOEXEC.html
/// [`IN_NONBLOCK`]: ../inotify_sys/constant.IN_NONBLOCK.html
pub fn init() -> io::Result<Inotify> {
// Initialize inotify and set CLOEXEC and NONBLOCK flags.
//
// 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.
//
// 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
let fd = unsafe {
let fd = ffi::inotify_init();
if fd == -1 {
return Err(io::Error::last_os_error());
}
if fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 {
return Err(io::Error::last_os_error());
}
if fcntl(fd, F_SETFL, O_NONBLOCK) == -1 {
return Err(io::Error::last_os_error());
}
fd
};
Ok(Inotify {
fd: Arc::new(FdGuard {
fd,
close_on_drop: AtomicBool::new(true),
}),
})
}
/// 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 `add_watch` won't return an error.
/// # use std::fs::File;
/// # File::create("/tmp/inotify-rs-test-file")
/// # .expect("Failed to create test file");
/// #
/// inotify.add_watch("/tmp/inotify-rs-test-file", WatchMask::MODIFY)
/// .expect("Failed to add file watch");
///
/// // Handle events for the file here
/// ```
///
/// [`inotify_add_watch`]: ../inotify_sys/fn.inotify_add_watch.html
/// [`WatchMask`]: struct.WatchMask.html
/// [`WatchDescriptor`]: struct.WatchDescriptor.html
pub fn add_watch<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
/// [`Inotify::add_watch`], 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 `add_watch` 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.add_watch("/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");
///
/// for event in events {
/// inotify.rm_watch(event.wd);
/// }
/// ```
///
/// [`WatchDescriptor`]: struct.WatchDescriptor.html
/// [`inotify_rm_watch`]: ../inotify_sys/fn.inotify_rm_watch.html
/// [`Inotify::add_watch`]: struct.Inotify.html#method.add_watch
/// [`Event`]: struct.Event.html
/// [`Inotify`]: struct.Inotify.html
/// [`io::Error`]: https://doc.rust-lang.org/std/io/struct.Error.html
/// [`ErrorKind`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html
pub fn rm_watch(&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)
}
}
/// 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.
///
/// [`Inotify::read_events`]: struct.Inotify.html#method.read_events
/// [`read`]: ../libc/fn.read.html
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`]
/// (except EGAIN/EWOULDBLOCK, which result in an empty iterator). 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
///
/// ```
/// use inotify::Inotify;
///
/// let mut inotify = Inotify::init()
/// .expect("Failed to initialize an inotify instance");
///
/// let mut buffer = [0; 1024];
/// let events = inotify.read_events(&mut buffer)
/// .expect("Error while reading events");
///
/// for event in events {
/// // Handle event
/// }
/// ```
///
/// [`read_events_blocking`]: struct.Inotify.html#method.read_events_blocking
/// [`read`]: ../libc/fn.read.html
/// [`ErrorKind::UnexpectedEof`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.UnexpectedEof
/// [`ErrorKind::InvalidInput`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.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();
if error.kind() == io::ErrorKind::WouldBlock {
return Ok(Events::new(Arc::downgrade(&self.fd), buffer, 0));
}
else {
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))
}
/// Create a stream which collects events
///
/// 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 event_stream<T>(&mut self, buffer: T)
-> io::Result<EventStream<T>>
where
T: AsMut<[u8]> + AsRef<[u8]>,
{
EventStream::new(self.fd.clone(), buffer)
}
/// 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");
/// ```
///
/// [`Inotify`]: struct.Inotify.html
/// [`close`]: ../libc/fn.close.html
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
}
}

104
third-party/vendor/inotify/src/lib.rs vendored Normal file
View file

@ -0,0 +1,104 @@
//! 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 `add_watch` 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
//! .add_watch(
//! "/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 `add_watch` 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`]: struct.Inotify.html
//! [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::{
WatchDescriptor,
WatchMask,
};
#[cfg(feature = "stream")]
pub use self::stream::EventStream;

108
third-party/vendor/inotify/src/stream.rs vendored Normal file
View file

@ -0,0 +1,108 @@
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::util::read_into_buffer;
/// Stream of inotify events
///
/// Allows for streaming events returned by [`Inotify::event_stream`].
///
/// [`Inotify::event_stream`]: struct.Inotify.html#method.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,
buffer_pos: 0,
unused_bytes: 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.into_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
}
}
}

93
third-party/vendor/inotify/src/util.rs vendored Normal file
View file

@ -0,0 +1,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
}
}

View file

@ -0,0 +1,327 @@
use std::{
hash::{
Hash,
Hasher,
},
cmp::Ordering,
os::raw::c_int,
sync::Weak,
};
use inotify_sys as ffi;
use crate::fd_guard::FdGuard;
bitflags! {
/// Describes a file system watch
///
/// Passed to [`Inotify::add_watch`], to describe what file system events
/// to watch for, and how to do that.
///
/// # Examples
///
/// `WatchMask` constants can be passed to [`Inotify::add_watch`] 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 `add_watch` won't return an error.
/// # use std::fs::File;
/// # File::create("/tmp/inotify-rs-test-file")
/// # .expect("Failed to create test file");
/// #
/// inotify.add_watch("/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.add_watch("/tmp/", WatchMask::CREATE | WatchMask::DELETE)
/// .expect("Error adding watch");
/// ```
///
/// [`Inotify::add_watch`]: struct.Inotify.html#method.add_watch
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`].
///
/// [`inotify_sys::IN_ACCESS`]: ../inotify_sys/constant.IN_ACCESS.html
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`].
///
/// [`inotify_sys::IN_ATTRIB`]: ../inotify_sys/constant.IN_ATTRIB.html
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`].
///
/// [`inotify_sys::IN_CLOSE_WRITE`]: ../inotify_sys/constant.IN_CLOSE_WRITE.html
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`].
///
/// [`inotify_sys::IN_CLOSE_NOWRITE`]: ../inotify_sys/constant.IN_CLOSE_NOWRITE.html
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`].
///
/// [`inotify_sys::IN_CREATE`]: ../inotify_sys/constant.IN_CREATE.html
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`].
///
/// [`inotify_sys::IN_DELETE`]: ../inotify_sys/constant.IN_DELETE.html
const DELETE = ffi::IN_DELETE;
/// Watched file/directory was deleted
///
/// See [`inotify_sys::IN_DELETE_SELF`].
///
/// [`inotify_sys::IN_DELETE_SELF`]: ../inotify_sys/constant.IN_DELETE_SELF.html
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`].
///
/// [`inotify_sys::IN_MODIFY`]: ../inotify_sys/constant.IN_MODIFY.html
const MODIFY = ffi::IN_MODIFY;
/// Watched file/directory was moved
///
/// See [`inotify_sys::IN_MOVE_SELF`].
///
/// [`inotify_sys::IN_MOVE_SELF`]: ../inotify_sys/constant.IN_MOVE_SELF.html
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`].
///
/// [`inotify_sys::IN_MOVED_FROM`]: ../inotify_sys/constant.IN_MOVED_FROM.html
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`].
///
/// [`inotify_sys::IN_MOVED_TO`]: ../inotify_sys/constant.IN_MOVED_TO.html
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`].
///
/// [`inotify_sys::IN_OPEN`]: ../inotify_sys/constant.IN_OPEN.html
const OPEN = ffi::IN_OPEN;
/// Watch for all events
///
/// This constant is simply a convenient combination of the following
/// other constants:
///
/// - [`ACCESS`]
/// - [`ATTRIB`]
/// - [`CLOSE_WRITE`]
/// - [`CLOSE_NOWRITE`]
/// - [`CREATE`]
/// - [`DELETE`]
/// - [`DELETE_SELF`]
/// - [`MODIFY`]
/// - [`MOVE_SELF`]
/// - [`MOVED_FROM`]
/// - [`MOVED_TO`]
/// - [`OPEN`]
///
/// See [`inotify_sys::IN_ALL_EVENTS`].
///
/// [`ACCESS`]: #associatedconstant.ACCESS
/// [`ATTRIB`]: #associatedconstant.ATTRIB
/// [`CLOSE_WRITE`]: #associatedconstant.CLOSE_WRITE
/// [`CLOSE_NOWRITE`]: #associatedconstant.CLOSE_NOWRITE
/// [`CREATE`]: #associatedconstant.CREATE
/// [`DELETE`]: #associatedconstant.DELETE
/// [`DELETE_SELF`]: #associatedconstant.DELETE_SELF
/// [`MODIFY`]: #associatedconstant.MODIFY
/// [`MOVE_SELF`]: #associatedconstant.MOVE_SELF
/// [`MOVED_FROM`]: #associatedconstant.MOVED_FROM
/// [`MOVED_TO`]: #associatedconstant.MOVED_TO
/// [`OPEN`]: #associatedconstant.OPEN
/// [`inotify_sys::IN_ALL_EVENTS`]: ../inotify_sys/constant.IN_ALL_EVENTS.html
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`]
/// - [`MOVED_TO`]
///
/// See [`inotify_sys::IN_MOVE`].
///
/// [`MOVED_FROM`]: #associatedconstant.MOVED_FROM
/// [`MOVED_TO`]: #associatedconstant.MOVED_TO
/// [`inotify_sys::IN_MOVE`]: ../inotify_sys/constant.IN_MOVE.html
const MOVE = ffi::IN_MOVE;
/// Watch for all close events
///
/// This constant is simply a convenient combination of the following
/// other constants:
///
/// - [`CLOSE_WRITE`]
/// - [`CLOSE_NOWRITE`]
///
/// See [`inotify_sys::IN_CLOSE`].
///
/// [`CLOSE_WRITE`]: #associatedconstant.CLOSE_WRITE
/// [`CLOSE_NOWRITE`]: #associatedconstant.CLOSE_NOWRITE
/// [`inotify_sys::IN_CLOSE`]: ../inotify_sys/constant.IN_CLOSE.html
const CLOSE = ffi::IN_CLOSE;
/// Don't dereference the path if it is a symbolic link
///
/// See [`inotify_sys::IN_DONT_FOLLOW`].
///
/// [`inotify_sys::IN_DONT_FOLLOW`]: ../inotify_sys/constant.IN_DONT_FOLLOW.html
const DONT_FOLLOW = ffi::IN_DONT_FOLLOW;
/// Filter events for directory entries that have been unlinked
///
/// See [`inotify_sys::IN_EXCL_UNLINK`].
///
/// [`inotify_sys::IN_EXCL_UNLINK`]: ../inotify_sys/constant.IN_EXCL_UNLINK.html
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`].
///
/// [`inotify_sys::IN_MASK_ADD`]: ../inotify_sys/constant.IN_MASK_ADD.html
const MASK_ADD = ffi::IN_MASK_ADD;
/// Only receive one event, then remove the watch
///
/// See [`inotify_sys::IN_ONESHOT`].
///
/// [`inotify_sys::IN_ONESHOT`]: ../inotify_sys/constant.IN_ONESHOT.html
const ONESHOT = ffi::IN_ONESHOT;
/// Only watch path, if it is a directory
///
/// See [`inotify_sys::IN_ONLYDIR`].
///
/// [`inotify_sys::IN_ONLYDIR`]: ../inotify_sys/constant.IN_ONLYDIR.html
const ONLYDIR = ffi::IN_ONLYDIR;
}
}
/// Represents a watch on an inode
///
/// Can be obtained from [`Inotify::add_watch`] or from an [`Event`]. A watch
/// descriptor can be used to get inotify to stop watching an inode by passing
/// it to [`Inotify::rm_watch`].
///
/// [`Inotify::add_watch`]: struct.Inotify.html#method.add_watch
/// [`Inotify::rm_watch`]: struct.Inotify.html#method.rm_watch
/// [`Event`]: struct.Event.html
#[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);
}
}