From 3d2d4b8efe44af1da7206b10e4565e59dbdb5121 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Thu, 15 Jan 2015 11:08:35 -0500 Subject: [PATCH] inotify: send CHANGES_DONE when new files 'appear' We generally assume that an IN_CREATE event is the start of a series of events in which another process is doing this: fd = creat (...) -> IN_CREATE write (fd, ..) -> IN_MODIFY write (fd, ..) -> IN_MODIFY close (fd) -> IN_CLOSE_WRITE and as such, we use the CHANGES_DONE_HINT event after CREATED in order to show when this sequence of events has completed (ie: when we receive IN_CLOSE_WRITE when the user closes the file). Renaming a file into place is handled by IN_MOVED_FROM so we don't have to worry about that. There are many other cases, however, where a new file 'appears' in a directory in its completed form already, and the kernel reports IN_CREATE. Examples include mkdir, mknod, and the creation of hardlinks. In these cases, there is no corresponding IN_CLOSE_WRITE event and the CHANGES_DONE_HINT will have to be emitted by an arbitrary timeout. Try to detect some of these cases and report CHANGES_DONE_HINT immediately. This is not perfect. There are some cases that will not be reliably detected. An example is if the user makes a hardlink and then immediately deletes the original (before we can stat the new file). Another example is if the user creates a file with O_TMPFILE. In both of these cases, CHANGES_DONE_HINT will still eventually be delivered via the timeout. --- gio/inotify/inotify-helper.c | 39 +++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/gio/inotify/inotify-helper.c b/gio/inotify/inotify-helper.c index 843db2655..9e52f60e8 100644 --- a/gio/inotify/inotify-helper.c +++ b/gio/inotify/inotify-helper.c @@ -27,6 +27,7 @@ #include #include #include +#include /* Just include the local header to stop all the pain */ #include #include @@ -197,13 +198,49 @@ ih_event_callback (ik_event_t *event, /* unpaired event -- no 'other' field */ g_file_monitor_source_handle_event (sub->user_data, ih_mask_to_EventFlags (event->mask), event->name, NULL, NULL, event->timestamp); + + if (event->mask & IN_CREATE) + { + const gchar *parent_dir; + gchar *fullname; + struct stat buf; + gint s; + + /* The kernel reports IN_CREATE for two types of events: + * + * - creat(), in which case IN_CLOSE_WRITE will come soon; or + * - link(), mkdir(), mknod(), etc., in which case it won't + * + * We can attempt to detect the second case and send the + * CHANGES_DONE immediately so that the user isn't left waiting. + * + * The detection for link() is not 100% reliable since the link + * count could be 1 if the original link was deleted or if + * O_TMPFILE was being used, but in that case the virtual + * CHANGES_DONE will be emitted to close the loop. + */ + + parent_dir = _ip_get_path_for_wd (event->wd); + fullname = _ih_fullpath_from_event (event, parent_dir, NULL); + s = stat (fullname, &buf); + g_free (fullname); + + /* if it doesn't look like the result of creat()... */ + if (s != 0 || !S_ISREG (buf.st_mode) || buf.st_nlink != 1) + g_file_monitor_source_handle_event (sub->user_data, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, + event->name, NULL, NULL, event->timestamp); + } } static void ih_not_missing_callback (inotify_sub *sub) { + gint now = g_get_monotonic_time (); + g_file_monitor_source_handle_event (sub->user_data, G_FILE_MONITOR_EVENT_CREATED, - sub->filename, NULL, NULL, g_get_monotonic_time ()); + sub->filename, NULL, NULL, now); + g_file_monitor_source_handle_event (sub->user_data, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, + sub->filename, NULL, NULL, now); } /* Transforms a inotify event to a GVFS event. */