Possible bug in inotify api

From: Morten Winkler Jørgensen
Date: Thu Jul 21 2011 - 02:54:58 EST

To whom it might concern

I'd like to report a possible malfunction of the inotify api and check if
this issue has been reported.
If this is not the proper place to do so, please let me know. I have
searched the archives and the web and found similar reportings but none
the same.

The observed behavior is when calling inotify_remove_watch. Now and then
it returns error 22, INVALID_ARGUMENT, even though the watchdesriptor
ought to be valid. I've seen that people complain about this behavior on
the dovecot mailing list but nobody seems to have suggested a fix.

I've attached a small program that I use to reproduce the behavior.
The program watches /tmp and whenever it's notified about a directory
created in /tmp, my program adds a watch for that directory as well.

The program is a C++ program (as I will need the inotify functionality in
a C++ program, but the behavior is the same in a pure C implementation)
and to reproduce I do the following:

* g++ main.cpp
* ./a.out

In a shell I go to /tmp and run
* for i in `seq 1 5000`; do mkdir D-$i; done; for i in `seq 1 5000`; do
rmdir D-$i; done

This makes my testprogram output the wd's created and and the wd's my
program tries to remove. I've never seen the program successfully remove
all 5000 watch descriptors and I've seen inotify_rm_watch fail after 0
removes and after approx 2000 removes and anything in between.

I'm running kernel 2.6.23-5 from the Debian repositories.

If what I've written is totally nonsense, if it is expected behavior or if
I've done anything wrong, please let me know. Also, if I can do anything
further to investigate the misbehavior also, please let me know.

Best and thanks in advance,

Attached: C++ program used to reproduce the behavior.
#include <sys/inotify.h>
#include <sys/syscall.h>

// Use this if syscalls not defined
#ifndef __NR_inotify_init
#include <sys/inotify-syscalls.h>
#endif // __NR_inotify_init

#include <stdio.h>
#include <unistd.h>
#include <map>
#include <string>

using namespace std;

typedef map<string, int> StrIntMap;
typedef pair<string, int> StrIntPair;

typedef map<int, string> IntStrMap;
typedef pair<int, string> IntStrPair;

StrIntMap pathsToWds;
IntStrMap wdsToPaths;

int fd;

int AddWatch(const string &path)
int wd = inotify_add_watch( fd, (char*)(path.c_str()), IN_CREATE | IN_DELETE | IN_MODIFY );
pathsToWds.insert(StrIntPair(path, wd));
wdsToPaths.insert(IntStrPair(wd, path));
printf("Adds %s as wd %d. Has %d watches\n", path.c_str(), wd, pathsToWds.size());
return wd;

void RemoveWatch(const string &path)
int wd = pathsToWds[path];
printf("Removes %s as wd %d. Has %d watches\n", path.c_str(), wd, pathsToWds.size());

if( inotify_rm_watch(fd, wd) != -1)
throw string("No no no...");

void RemoveWatch(int wd)

int main( )


int EVENT_SIZE = sizeof (struct inotify_event) ;
int EVENT_BUF_LEN = ( 1024 * ( EVENT_SIZE + 16 ) );
int length, i = 0;
char buffer[EVENT_BUF_LEN];

/*creating the INOTIFY instance*/
fd = inotify_init();

/*checking for error*/
if ( fd < 0 ) {
// perror( "inotify_init" );

/*adding the â??/tmpâ?? directory into watch list. Here, the suggestion is to validate the existence of the directory before adding into monitoring list.*/
string bd = "/tmp";

while(1) {
length = 0;
i = 0;
length = read( fd, buffer, EVENT_BUF_LEN );
/*checking for error*/
if ( length < 0 ) {
perror( "read" );

/*actually read return the list of change events happens. Here, read the change event one by one and process it accordingly.*/
while ( i < length ) {

struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];

if ( event->len ) {

string parentdir;
parentdir = wdsToPaths[event->wd];

if ( event->mask & IN_CREATE ) {
if ( event->mask & IN_ISDIR ) {
printf( "New directory %s created in %s.\n", event->name, parentdir.c_str());

else {
printf( "New file %s created in %s.\n", event->name, parentdir.c_str());
else if ( event->mask & IN_DELETE ) {
if ( event->mask & IN_ISDIR ) {
printf( "Directory %s deleted in %s.\n", event->name, parentdir.c_str() );
else {
printf( "File %s deleted in %s.\n", event->name, parentdir.c_str());
else if ( event->mask & IN_MODIFY)
if ( event->mask & IN_ISDIR ) {
printf( "Directory %s modified in %s.\n", event->name, parentdir.c_str());
else {
printf( "File %s modified in %s.\n", event->name, parentdir.c_str());
i += EVENT_SIZE + event->len;
/*removing the â??/tmpâ?? directory from the watch list.*/

/*closing the INOTIFY instance*/
close( fd );
catch(string s)
printf("Caught: %s\n", s.c_str());