15

I have been studying inotify call, but I still a bit flaky when it comes to the read interface. These are the most relevant resourses I could find regarding how to properly interface with inotify using read(2):

They both implement it in the same way, they first define the following sizes:

#define EVENT_SIZE  ( sizeof (struct inotify_event) )
#define BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 )

And then they use them in this manner:

length = read( fd, buffer, BUF_LEN );  

if ( length < 0 ) {
    perror( "read" );
}  

while ( i < length ) {
    struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];
    /* some processing */
    i += EVENT_SIZE + event->len;
}

Now, we know name is part of struct inotify_event and that it has variable length. So, couldn't the last inotify_event in buffer be truncated?

Suppose there is 1023 inotify_events with a path of 16 bytes and one with a path of 32 bytes. What will happen then? Will the later truncated? Or will the kernel see that it won't fit in the buffer and leave it all altogether?

alexisdm
  • 27,787
  • 6
  • 53
  • 84
Rafael Almeida
  • 2,179
  • 3
  • 21
  • 31

1 Answers1

9

Basic usage

According to inotify(7), you can use the FIONREAD ioctl to find out how much data is available to be read and size your buffer accordingly. Here's some (very rough) code that can accomplish this:

unsigned int avail;
ioctl(inotify_fd, FIONREAD, &avail);

char buffer[avail];
read(fd, buffer, avail);

int offset = 0;
while (offset < avail) {
    struct inotify_event *event = (inotify_event*)(buffer + offset);

    // Insert logic here
    my_process_inotify_event(event);

    offset = offset + sizeof(inotify_event) + event->len;
}

More robust usage

inotify-tools provides a higher-level interface to inotify. You can use it instead of accessing inotify, or you can see how it implements inotifytools_next_events to safely and robustly read all available events.

Partial events and truncation

In response to your questions about truncation, I do not think that the kernel will ever return a partial inotify_event or truncate an inotify_event if the buffer given is too small for all events. The following paragraph from the inotify(7) manpage suggests this:

The behavior when the buffer given to read(2) is too small to return information about the next event depends on the kernel version: in kernels before 2.6.21, read(2) returns 0; since kernel 2.6.21, read(2) fails with the error EINVAL.

As do the following comments from inotifytools.c:

// oh... no.  this can't be happening.  An incomplete event.
// Copy what we currently have into first element, call self to
// read remainder.
// oh, and they BETTER NOT overlap.
// Boy I hope this code works.
// But I think this can never happen due to how inotify is written.
Josh Kelley
  • 50,042
  • 19
  • 127
  • 215
  • 1
    I've consulted #kernelnewbies about short reads/truncation. The manpage [states](http://man7.org/linux/man-pages/man7/inotify.7.html) that "Each successful read(2) returns a buffer containing one or more of the following structures [...]". The file name is part of those structures. Interrupts don't matter here and signals can't just happen in the kernel context. The output buffer either contains full structures or nothing. In the kernel [code](http://lxr.free-electrons.com/source/fs/notify/inotify/inotify_user.c) the return value from read() is sizeof(struct inotify_event) + round(name). – wodny Jun 03 '15 at 08:53
  • *"oh... no. this can't be happening. ... Boy I hope this code works."* - robust, huh? – user253751 Jan 31 '16 at 23:17
  • Don't use `avail` to size your buffer. `avail` is how much data is *currently* available, it will be zero when you first start. And setting your buffer size each time through the loop is not a good idea. – Ariel Jul 31 '16 at 22:32
  • @Ariel - I don't understand your concerns. This code would only be called when `select` or similar reports that there's data available to be read, so we know that `avail` is nonzero. And, because `buffer[avail]` is a C99 variable-length array that's allocated on the stack, the cost of setting the buffer size each time through the loop is trivial. – Josh Kelley Aug 01 '16 at 11:20
  • Yes, you could do that, but it doesn't make sense to me to put the `read()` in a different function than the `poll()`. A realistic upper max is 4MB, and a realistic buffer size is about 1K to 64K. Since 4MB on the stack is a little iffy, it's really not worth it to put this on the stack - you are not gaining anything. – Ariel Aug 01 '16 at 17:20