[glib] inotify: send CHANGES_DONE when new files 'appear'



commit 3d2d4b8efe44af1da7206b10e4565e59dbdb5121
Author: Ryan Lortie <desrt desrt ca>
Date:   Thu Jan 15 11:08:35 2015 -0500

    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 files changed, 38 insertions(+), 1 deletions(-)
---
diff --git a/gio/inotify/inotify-helper.c b/gio/inotify/inotify-helper.c
index 843db26..9e52f60 100644
--- a/gio/inotify/inotify-helper.c
+++ b/gio/inotify/inotify-helper.c
@@ -27,6 +27,7 @@
 #include <time.h>
 #include <string.h>
 #include <sys/ioctl.h>
+#include <sys/stat.h>
 /* Just include the local header to stop all the pain */
 #include <sys/inotify.h>
 #include <gio/glocalfilemonitor.h>
@@ -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. */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]