Bug #5188
Fix inotify case where IN_MOVED_TO comes in subsequent read after read contaning IN_MOVED_FROM
Start date:
2018-09-02
Due date:
% Done:
0%
Estimated time:
Found in version:
4.3
Affected Versions:
Description
While trying to move lots of recordings from one directory to another, I found that tvheadend only noticed (via inotify) about 60% of the changes, thus the dvr/log files were not updated.
After some debugging, I tracked the problem down to this note from the man page:
Applications should also allow for the possibility that the IN_MOVED_FROM event was the last event that could fit in the buffer returned by the current call to read(2), and the accompanying IN_MOVED_TO event might be fetched only on the next read(2), which should be done with a (small) timeout to allow for the fact that insertion of the IN_MOVED_FROM-IN_MOVED_TO event pair is not atomic, and also the possibility that there may not be any IN_MOVED_TO event.
Also, the mask for IN_CREATE was being set, but never checked for. This also aggravated the problem by producing lots of unnecessary IN_CREATE events in the reads.
I created a pull request, but I am sure you will want to remove the extra tvhdebug lines I included.
Here is the diff:
diff --git a/src/dvr/dvr_inotify.c b/src/dvr/dvr_inotify.c index 71137df20..a8db57c09 100644 --- a/src/dvr/dvr_inotify.c +++ b/src/dvr/dvr_inotify.c @@ -33,7 +33,7 @@ /* inotify limits */ #define EVENT_SIZE ( sizeof (struct inotify_event) ) #define EVENT_BUF_LEN ( 10 * ( EVENT_SIZE + 16 ) ) -#define EVENT_MASK IN_CREATE | IN_DELETE | IN_DELETE_SELF |\ +#define EVENT_MASK IN_DELETE | IN_DELETE_SELF |\ IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO static int _inot_fd; @@ -378,6 +378,11 @@ void* _dvr_inotify_thread ( void *p ) const char *from; int fromfd; int cookie; + char from_prev[50]; + int fromfd_prev =0; + int cookie_prev =0; + + from_prev[0] = '\0'; while (tvheadend_is_running()) { @@ -397,6 +402,7 @@ void* _dvr_inotify_thread ( void *p ) pthread_mutex_lock(&global_lock); while ( i < len ) { struct inotify_event *ev = (struct inotify_event*)&buf[i]; + tvhdebug(LS_DVR, "inotify: i= %d len= %d ", i, len); i += EVENT_SIZE + ev->len; if (i > len) break; @@ -406,12 +412,21 @@ void* _dvr_inotify_thread ( void *p ) from = ev->name; fromfd = ev->wd; cookie = ev->cookie; + tvhdebug(LS_DVR, "inotify: i= %d len= %d from = %s cookie = %d ", i, len, from, cookie); continue; - } else if ((ev->mask & IN_MOVED_TO) && from && ev->cookie == cookie) { - _dvr_inotify_moved(fromfd, from, ev->name, ev->wd); - from = NULL; - + } else if (ev->mask & IN_MOVED_TO) { + tvhdebug(LS_DVR, "inotify: i= %d len= %d to_cookie= %d from_cookie = %d cookie_prev = %d", i, len, ev->cookie,cookie,cookie_prev); + if ( from && ev->cookie == cookie) { + _dvr_inotify_moved(fromfd, from, ev->name, ev->wd); + from = NULL; + cookie = 0; + } else if (from_prev[0] != '\0' && ev->cookie == cookie_prev) { + _dvr_inotify_moved(fromfd_prev, from_prev, ev->name, ev->wd); + from_prev[0] = '\0'; + cookie_prev = 0; + } + /* Removed */ } else if (ev->mask & IN_DELETE) { _dvr_inotify_delete(ev->wd, ev->name); @@ -424,15 +439,26 @@ void* _dvr_inotify_thread ( void *p ) } else if (ev->mask & IN_DELETE_SELF) { _dvr_inotify_delete_all(ev->wd); } - +/* if (from) { _dvr_inotify_moved(fromfd, from, NULL, -1); from = NULL; cookie = 0; } +*/ + } + // if still old "from", assume matching "to" is not coming + if (from_prev[0] != '\0') { + _dvr_inotify_moved(fromfd_prev, from_prev, NULL, -1); + from_prev[0] = '\0'; + cookie_prev = 0; } + // if unmatched "from", save in case matching "to" is coming in next read if (from) { - _dvr_inotify_moved(fromfd, from, NULL, -1); + strcpy ( from_prev, from); + fromfd_prev = fromfd; + cookie_prev = cookie; + tvhdebug(LS_DVR, "inotify: i= %d len= %d cookie_prev = %d from_prev = %s fd = %d END OF READ", i, len, cookie_prev, from_prev, fromfd_prev); } pthread_mutex_unlock(&global_lock); }