Replacing the Directory Poll With inotify
Now that Linux 2.6.13 includes inotify, I replaced a timer in a small file-watching utility. The timer scanned a directory every second, which was simple, portable, and wasteful. It also introduced the charming choice between delayed updates and more frequent useless work.
My first inotify version assumed each read() returned one complete event. It worked during gentle testing and became nonsense when several files changed quickly. The interface returns a byte stream containing one or more variable-length struct inotify_event records. Correct code walks the buffer by each fixed header plus its len field.
union {
struct inotify_event event;
char bytes[4096];
} buffer;
ssize_t count;
size_t offset = 0;
do {
count = read(fd, buffer.bytes, sizeof(buffer.bytes));
} while (count < 0 && errno == EINTR);
if (count < 0) {
perror("read");
exit(EXIT_FAILURE);
}
if (count == 0) {
fprintf(stderr, "inotify descriptor reached EOF\n");
exit(EXIT_FAILURE);
}
while (offset < (size_t)count) {
struct inotify_event *event;
size_t event_size;
if ((size_t)count - offset < sizeof(*event)) {
fprintf(stderr, "short inotify event header\n");
exit(EXIT_FAILURE);
}
event = (struct inotify_event *)(buffer.bytes + offset);
event_size = sizeof(*event) + event->len;
if (event_size > (size_t)count - offset) {
fprintf(stderr, "short inotify event name\n");
exit(EXIT_FAILURE);
}
handle_event(event);
offset += event_size;
}
The union gives the byte storage suitable alignment for struct inotify_event; a plain character array doesn’t promise that. The code also deals with interruption, read failure, EOF, and truncated records before treating bytes as an event. A watch identifies a pathname to the kernel, and events report changes associated with a watch descriptor.
The largest conceptual correction was learning that notifications are hints about state changes, not a private journal guaranteed to preserve my worldview. Events can arrive in bursts. A queue can overflow. A watched object can move or disappear. On overflow, the safe response is to rescan and rebuild known state rather than guess what was missed.
Renames deserve similar care. Related move events carry a cookie that can help pair them, but the application still needs sensible behavior when only one side is visible. Watching a directory also does not automatically mean recursively watching every directory below it.
I kept one function that performs a complete scan and made event handling update the same internal model. That gives the program a recovery path and keeps startup behavior consistent with overflow recovery. The event loop is an optimization over known state, not the only source of truth.
Compared with polling, inotify is immediate and avoids repeated directory walks when nothing changes. It also exposes more edge cases because the kernel reports actual operations rather than letting a periodic snapshot blur them together.
I prefer the new mechanism, with one reservation: event-driven doesn’t mean infallible. Read complete buffers, parse every record, expect overflow, and keep a rescan path. The kernel says something happened; deciding what the application should now believe is still our job.