Report the directories a growing tree could not watch
Extending an automatically watched tree to a new directory swallowed every error, so a reached watch limit or an unreadable directory left part of the tree unwatched without any sign. No call of the consumer is running at that moment, so the failures now arrive in the event stream as InotifyEvent.watchFailed, after the event that triggered the extension. The library watches what it can first: a reached limit ends the attempt, an unreadable directory is skipped with its subtree, and a directory that vanished in between is not reported. The buffer now carries the library's own events next to the kernel's, which keeps them in order and hides the stream's element type. The test runner drops root's DAC capabilities so that an unreadable directory can be tested.
This commit is contained in:
@@ -4,7 +4,7 @@ Monitor filesystem events on Linux using modern Swift concurrency.
|
||||
|
||||
## Overview
|
||||
|
||||
The Inotify library wraps the Linux [inotify](https://man7.org/linux/man-pages/man7/inotify.7.html) API in a Swift-native interface built around actors and async sequences. You create an ``Inotify/Inotify`` actor, add watches for the paths you care about, and iterate over the ``Inotify/Inotify/events`` property to receive ``InotifyEvent`` values as they occur. Most of them carry a ``FileSystemEvent`` describing a change to a watched item; the others tell you when the instance cannot deliver every change, such as after a kernel queue overflow.
|
||||
The Inotify library wraps the Linux [inotify](https://man7.org/linux/man-pages/man7/inotify.7.html) API in a Swift-native interface built around actors and async sequences. You create an ``Inotify/Inotify`` actor, add watches for the paths you care about, and iterate over the ``Inotify/Inotify/events`` property to receive ``InotifyEvent`` values as they occur. Most of them carry a ``FileSystemEvent`` describing a change to a watched item; the others tell you when the instance cannot deliver every change, after a kernel queue overflow or when a new directory of a watched tree could not be watched.
|
||||
|
||||
```swift
|
||||
let inotify = try Inotify()
|
||||
@@ -16,6 +16,8 @@ for await event in await inotify.events {
|
||||
print("\(change.mask) at \(change.path)")
|
||||
case .queueOverflow:
|
||||
print("events were dropped, rescan")
|
||||
case .watchFailed(let path, let error):
|
||||
print("changes below \(path) go unreported: \(error)")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -35,6 +35,27 @@ Internally this listens for `CREATE` and `MOVED_TO` events carrying the ``Inotif
|
||||
|
||||
When a directory is moved out of the watched tree, the watches on it and on its subdirectories are removed, so no events are reported under the stale path.
|
||||
|
||||
#### When a New Directory Cannot Be Watched
|
||||
|
||||
Extending the watch can fail, most often because the user's watch limit, `fs.inotify.max_user_watches`, is reached, or because the process may not read the new directory. No call of yours is running at that moment, so the library watches what it can and reports every directory it could not watch as ``InotifyEvent/watchFailed(path:error:)``, after the event of the directory whose appearance triggered the extension:
|
||||
|
||||
```swift
|
||||
for await event in await inotify.events {
|
||||
switch event {
|
||||
case .fileSystem(let change):
|
||||
handle(change)
|
||||
case .queueOverflow:
|
||||
rescan()
|
||||
case .watchFailed(let path, let error):
|
||||
log("changes below \(path) go unreported: \(error)")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A reached limit ends the extension, since nothing more can be watched until watches are freed, so only the first directory that failed is reported. An unreadable directory is reported and skipped together with its subtree, while its readable siblings are watched. A directory that vanished before it could be watched is not reported, because its removal arrives as an event of its own.
|
||||
|
||||
The explicit calls above behave differently: they either watch the whole tree or throw, and a call that throws removes the watches it had added.
|
||||
|
||||
### Excluding Directories
|
||||
|
||||
When watching large trees you often want to skip certain subdirectories entirely — version-control metadata, build artefacts, dependency caches, and so on. Call ``Inotify/Inotify/exclude(names:)`` or ``Inotify/Inotify/exclude(patterns:)`` **before** adding a recursive or automatic-subtree watch:
|
||||
|
||||
Reference in New Issue
Block a user