Skip to content

Commit 233114c

Browse files
Add inotify support (#479)
1 parent 25a06eb commit 233114c

File tree

7 files changed

+261
-0
lines changed

7 files changed

+261
-0
lines changed

src/backend/libc/fs/inotify.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//! inotify support for working with inotifies
2+
3+
use super::super::c;
4+
use super::super::conv::{borrowed_fd, c_str, ret, ret_c_int, ret_owned_fd};
5+
use crate::fd::{BorrowedFd, OwnedFd};
6+
use crate::io;
7+
use bitflags::bitflags;
8+
9+
bitflags! {
10+
/// `IN_*` for use with [`Inotify::new`].
11+
pub struct CreateFlags: c::c_int {
12+
/// `IN_CLOEXEC`
13+
const CLOEXEC = c::IN_CLOEXEC;
14+
/// `IN_NONBLOCK`
15+
const NONBLOCK = c::IN_NONBLOCK;
16+
}
17+
}
18+
19+
bitflags! {
20+
/// `IN*` for use with [`Inotify::add_watch`].
21+
#[derive(Default)]
22+
pub struct WatchFlags: u32 {
23+
/// `IN_ACCESS`
24+
const ACCESS = c::IN_ACCESS;
25+
/// `IN_ATTRIB`
26+
const ATTRIB = c::IN_ATTRIB;
27+
/// `IN_CLOSE_NOWRITE`
28+
const CLOSE_NOWRITE = c::IN_CLOSE_NOWRITE;
29+
/// `IN_CLOSE_WRITE`
30+
const CLOSE_WRITE = c::IN_CLOSE_WRITE;
31+
/// `IN_CREATE `
32+
const CREATE = c::IN_CREATE;
33+
/// `IN_DELETE`
34+
const DELETE = c::IN_DELETE;
35+
/// `IN_DELETE_SELF`
36+
const DELETE_SELF = c::IN_DELETE_SELF;
37+
/// `IN_MODIFY`
38+
const MODIFY = c::IN_MODIFY;
39+
/// `IN_MOVE_SELF`
40+
const MOVE_SELF = c::IN_MOVE_SELF;
41+
/// `IN_MOVED_FROM`
42+
const MOVED_FROM = c::IN_MOVED_FROM;
43+
/// `IN_MOVED_TO`
44+
const MOVED_TO = c::IN_MOVED_TO;
45+
/// `IN_OPEN`
46+
const OPEN = c::IN_OPEN;
47+
48+
/// `IN_CLOSE`
49+
const CLOSE = c::IN_CLOSE;
50+
/// `IN_MOVE`
51+
const MOVE = c::IN_MOVE;
52+
/// `IN_ALL_EVENTS`
53+
const ALL_EVENTS = c::IN_ALL_EVENTS;
54+
55+
/// `IN_DONT_FOLLOW`
56+
const DONT_FOLLOW = c::IN_DONT_FOLLOW;
57+
/// `IN_EXCL_UNLINK`
58+
const EXCL_UNLINK = 1;
59+
/// `IN_MASK_ADD`
60+
const MASK_ADD = 1;
61+
/// `IN_MASK_CREATE`
62+
const MASK_CREATE = 1;
63+
/// `IN_ONESHOT`
64+
const ONESHOT = c::IN_ONESHOT;
65+
/// `IN_ONLYDIR`
66+
const ONLYDIR = c::IN_ONLYDIR;
67+
}
68+
}
69+
70+
/// `inotify_init1(flags)`—Creates a new `Inotify`.
71+
///
72+
/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file
73+
/// descriptor from being implicitly passed across `exec` boundaries.
74+
#[doc(alias = "inotify_init1")]
75+
pub fn inotify_init(flags: CreateFlags) -> io::Result<OwnedFd> {
76+
// Safety: `inotify_init1` has no safety preconditions.
77+
unsafe { ret_owned_fd(c::inotify_init1(flags.bits())) }
78+
}
79+
80+
/// `inotify_add_watch(self, path, flags)`-Adds a watch to inotify
81+
///
82+
/// This registers or updates a watch for the filesystem path `path`
83+
/// and returns a watch descriptor corresponding to this watch.
84+
///
85+
/// Note: Due to the existence of hardlinks, providing two
86+
/// different paths to this method may result in it returning
87+
/// the same watch descriptor. An application should keep track of this
88+
/// externally to avoid logic errors.
89+
pub fn inotify_add_watch<P: crate::path::Arg>(
90+
inot: BorrowedFd<'_>,
91+
path: P,
92+
flags: WatchFlags,
93+
) -> io::Result<i32> {
94+
let path = path.as_cow_c_str().unwrap();
95+
// Safety: The fd and path we are passing is guranteed valid by the type system.
96+
unsafe {
97+
ret_c_int(c::inotify_add_watch(
98+
borrowed_fd(inot),
99+
c_str(&path),
100+
flags.bits(),
101+
))
102+
}
103+
}
104+
105+
/// `inotify_rm_watch(self, wd)`-Removes a watch from this inotify
106+
///
107+
/// The watch descriptor provided should have previously been returned
108+
/// by [`Self::add_watch()`] and not previously have been removed.
109+
#[doc(alias = "inotify_rm_watch")]
110+
pub fn inotify_remove_watch<P: crate::path::Arg>(inot: BorrowedFd<'_>, wd: i32) -> io::Result<()> {
111+
// Android's `inotify_rm_watch` takes u32 despite `inotify_add_watch` is i32.
112+
#[cfg(target_os = "android")]
113+
let wd = wd as u32;
114+
// Safety: The fd is valid and closing an arbitrary wd is valid.
115+
unsafe { ret(c::inotify_rm_watch(borrowed_fd(inot), wd)) }
116+
}

src/backend/libc/fs/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#[cfg(not(target_os = "redox"))]
22
pub(crate) mod dir;
3+
#[cfg(any(target_os = "android", target_os = "linux"))]
4+
pub mod inotify;
35
#[cfg(not(any(
46
target_os = "dragonfly",
57
target_os = "haiku",

src/backend/linux_raw/conv.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,22 @@ impl<'a, Num: ArgNumber> From<crate::fs::AtFlags> for ArgReg<'a, Num> {
313313
}
314314
}
315315

316+
#[cfg(feature = "fs")]
317+
impl<'a, Num: ArgNumber> From<crate::fs::inotify::CreateFlags> for ArgReg<'a, Num> {
318+
#[inline]
319+
fn from(flags: crate::fs::inotify::CreateFlags) -> Self {
320+
c_uint(flags.bits())
321+
}
322+
}
323+
324+
#[cfg(feature = "fs")]
325+
impl<'a, Num: ArgNumber> From<crate::fs::inotify::WatchFlags> for ArgReg<'a, Num> {
326+
#[inline]
327+
fn from(flags: crate::fs::inotify::WatchFlags) -> Self {
328+
c_uint(flags.bits())
329+
}
330+
}
331+
316332
#[cfg(feature = "fs")]
317333
impl<'a, Num: ArgNumber> From<crate::fs::MemfdFlags> for ArgReg<'a, Num> {
318334
#[inline]

src/backend/linux_raw/fs/inotify.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//! inotify support for working with inotifies
2+
3+
use super::super::c;
4+
use crate::backend::fs::syscalls;
5+
use crate::fd::{BorrowedFd, OwnedFd};
6+
use crate::io;
7+
use bitflags::bitflags;
8+
9+
bitflags! {
10+
/// `IN_*` for use with [`Inotify::new`].
11+
pub struct CreateFlags: c::c_uint {
12+
/// `IN_CLOEXEC`
13+
const CLOEXEC = linux_raw_sys::general::IN_CLOEXEC;
14+
/// `IN_NONBLOCK`
15+
const NONBLOCK = linux_raw_sys::general::IN_NONBLOCK;
16+
}
17+
}
18+
19+
bitflags! {
20+
/// `IN*` for use with [`Inotify::add_watch`].
21+
#[derive(Default)]
22+
pub struct WatchFlags: c::c_uint {
23+
/// `IN_ACCESS`
24+
const ACCESS = linux_raw_sys::general::IN_ACCESS;
25+
/// `IN_ATTRIB`
26+
const ATTRIB = linux_raw_sys::general::IN_ATTRIB;
27+
/// `IN_CLOSE_NOWRITE`
28+
const CLOSE_NOWRITE = linux_raw_sys::general::IN_CLOSE_NOWRITE;
29+
/// `IN_CLOSE_WRITE`
30+
const CLOSE_WRITE = linux_raw_sys::general::IN_CLOSE_WRITE;
31+
/// `IN_CREATE `
32+
const CREATE = linux_raw_sys::general::IN_CREATE;
33+
/// `IN_DELETE`
34+
const DELETE = linux_raw_sys::general::IN_DELETE;
35+
/// `IN_DELETE_SELF`
36+
const DELETE_SELF = linux_raw_sys::general::IN_DELETE_SELF;
37+
/// `IN_MODIFY`
38+
const MODIFY = linux_raw_sys::general::IN_MODIFY;
39+
/// `IN_MOVE_SELF`
40+
const MOVE_SELF = linux_raw_sys::general::IN_MOVE_SELF;
41+
/// `IN_MOVED_FROM`
42+
const MOVED_FROM = linux_raw_sys::general::IN_MOVED_FROM;
43+
/// `IN_MOVED_TO`
44+
const MOVED_TO = linux_raw_sys::general::IN_MOVED_TO;
45+
/// `IN_OPEN`
46+
const OPEN = linux_raw_sys::general::IN_OPEN;
47+
48+
/// `IN_CLOSE`
49+
const CLOSE = linux_raw_sys::general::IN_CLOSE;
50+
/// `IN_MOVE`
51+
const MOVE = linux_raw_sys::general::IN_MOVE;
52+
/// `IN_ALL_EVENTS`
53+
const ALL_EVENTS = linux_raw_sys::general::IN_ALL_EVENTS;
54+
55+
/// `IN_DONT_FOLLOW`
56+
const DONT_FOLLOW = linux_raw_sys::general::IN_DONT_FOLLOW;
57+
/// `IN_EXCL_UNLINK`
58+
const EXCL_UNLINK = linux_raw_sys::general::IN_EXCL_UNLINK;
59+
/// `IN_MASK_ADD`
60+
const MASK_ADD = linux_raw_sys::general::IN_MASK_ADD;
61+
/// `IN_MASK_CREATE`
62+
const MASK_CREATE = linux_raw_sys::general::IN_MASK_CREATE;
63+
/// `IN_ONESHOT`
64+
const ONESHOT = linux_raw_sys::general::IN_ONESHOT;
65+
/// `IN_ONLYDIR`
66+
const ONLYDIR = linux_raw_sys::general::IN_ONLYDIR;
67+
}
68+
}
69+
70+
/// `inotify_init1(flags)`—Creates a new `Inotify`.
71+
///
72+
/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file
73+
/// descriptor from being implicitly passed across `exec` boundaries.
74+
#[doc(alias = "inotify_init1")]
75+
pub fn inotify_init(flags: CreateFlags) -> io::Result<OwnedFd> {
76+
syscalls::inotify_init1(flags)
77+
}
78+
79+
/// `inotify_add_watch(self, path, flags)`-Adds a watch to inotify
80+
///
81+
/// This registers or updates a watch for the filesystem path `path`
82+
/// and returns a watch descriptor corresponding to this watch.
83+
///
84+
/// Note: Due to the existence of hardlinks, providing two
85+
/// different paths to this method may result in it returning
86+
/// the same watch descriptor. An application should keep track of this
87+
/// externally to avoid logic errors.
88+
pub fn inotify_add_watch<P: crate::path::Arg>(
89+
inot: BorrowedFd<'_>,
90+
path: P,
91+
flags: WatchFlags,
92+
) -> io::Result<i32> {
93+
let path = path.as_cow_c_str().unwrap();
94+
syscalls::inotify_add_watch(inot, &path, flags)
95+
}
96+
97+
/// `inotify_rm_watch(self, wd)`-Removes a watch from this inotify
98+
///
99+
/// The watch descriptor provided should have previously been returned
100+
/// by [`Self::add_watch()`] and not previously have been removed.
101+
#[doc(alias = "inotify_rm_watch")]
102+
pub fn inotify_remove_watch<P: crate::path::Arg>(inot: BorrowedFd<'_>, wd: i32) -> io::Result<()> {
103+
syscalls::inotify_rm_watch(inot, wd)
104+
}

src/backend/linux_raw/fs/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub(crate) mod dir;
2+
pub mod inotify;
23
pub(crate) mod makedev;
34
pub(crate) mod syscalls;
45
pub(crate) mod types;

src/backend/linux_raw/fs/syscalls.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use super::super::conv::{loff_t, loff_t_from_u64, ret_u64};
2323
use crate::fd::AsFd;
2424
use crate::fd::{BorrowedFd, OwnedFd};
2525
use crate::ffi::CStr;
26+
use crate::fs::inotify;
2627
use crate::fs::{
2728
Access, Advice, AtFlags, FallocateFlags, FileType, FlockOperation, MemfdFlags, Mode, OFlags,
2829
RenameFlags, ResolveFlags, SealFlags, Stat, StatFs, StatVfs, StatVfsMountFlags, StatxFlags,
@@ -1422,3 +1423,22 @@ pub(crate) fn mount(
14221423
pub(crate) fn unmount(target: &CStr, flags: super::types::UnmountFlags) -> io::Result<()> {
14231424
unsafe { ret(syscall_readonly!(__NR_umount2, target, flags)) }
14241425
}
1426+
1427+
#[inline]
1428+
pub(crate) fn inotify_init1(flags: inotify::CreateFlags) -> io::Result<OwnedFd> {
1429+
unsafe { ret_owned_fd(syscall_readonly!(__NR_inotify_init1, flags)) }
1430+
}
1431+
1432+
#[inline]
1433+
pub(crate) fn inotify_add_watch(
1434+
infd: BorrowedFd<'_>,
1435+
path: &CStr,
1436+
flags: inotify::WatchFlags,
1437+
) -> io::Result<i32> {
1438+
unsafe { ret_c_int(syscall_readonly!(__NR_inotify_add_watch, infd, path, flags)) }
1439+
}
1440+
1441+
#[inline]
1442+
pub(crate) fn inotify_rm_watch(infd: BorrowedFd<'_>, wfd: i32) -> io::Result<()> {
1443+
unsafe { ret(syscall_readonly!(__NR_inotify_rm_watch, infd, c_int(wfd))) }
1444+
}

src/fs/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ mod sendfile;
5858
#[cfg(any(target_os = "android", target_os = "linux"))]
5959
mod statx;
6060

61+
#[cfg(any(target_os = "android", target_os = "linux"))]
62+
pub use crate::backend::fs::inotify;
6163
#[cfg(not(any(
6264
target_os = "haiku",
6365
target_os = "illumos",

0 commit comments

Comments
 (0)