| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  | /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* inotify-helper.c - GVFS Monitor based on inotify.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    Copyright (C) 2007 John McCutchan | 
					
						
							| 
									
										
										
										
											2024-04-17 15:42:54 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |    SPDX-License-Identifier: LGPL-2.1-or-later | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 17:40:22 +01:00
										 |  |  |    This library is free software; you can redistribute it and/or | 
					
						
							| 
									
										
										
										
											2017-01-05 14:51:09 +01:00
										 |  |  |    modify it under the terms of the GNU Lesser General Public | 
					
						
							| 
									
										
										
										
											2016-12-27 17:40:22 +01:00
										 |  |  |    License as published by the Free Software Foundation; either | 
					
						
							| 
									
										
										
										
											2017-01-05 14:51:09 +01:00
										 |  |  |    version 2.1 of the License, or (at your option) any later version. | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 17:40:22 +01:00
										 |  |  |    This library is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
					
						
							| 
									
										
										
										
											2017-01-05 14:51:09 +01:00
										 |  |  |    Lesser General Public License for more details. | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-05 14:51:09 +01:00
										 |  |  |    You should have received a copy of the GNU Lesser General Public License | 
					
						
							| 
									
										
										
										
											2016-12-27 17:40:22 +01:00
										 |  |  |    along with this library; if not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |    Authors:  | 
					
						
							|  |  |  | 		 John McCutchan <john@johnmccutchan.com> | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "config.h"
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #include <time.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <sys/ioctl.h>
 | 
					
						
							| 
									
										
											  
											
												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.
											
										 
											2015-01-15 11:08:35 -05:00
										 |  |  | #include <sys/stat.h>
 | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  | /* Just include the local header to stop all the pain */ | 
					
						
							| 
									
										
										
										
											2008-01-21 10:35:44 +00:00
										 |  |  | #include <sys/inotify.h>
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  | #include <gio/glocalfilemonitor.h>
 | 
					
						
							| 
									
										
										
										
											2008-07-01 06:32:35 +00:00
										 |  |  | #include <gio/gfile.h>
 | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  | #include "inotify-helper.h"
 | 
					
						
							|  |  |  | #include "inotify-missing.h"
 | 
					
						
							|  |  |  | #include "inotify-path.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static gboolean ih_debug_enabled = FALSE; | 
					
						
							|  |  |  | #define IH_W if (ih_debug_enabled) g_warning 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-15 16:38:22 -05:00
										 |  |  | static gboolean ih_event_callback (ik_event_t  *event, | 
					
						
							|  |  |  |                                    inotify_sub *sub, | 
					
						
							|  |  |  |                                    gboolean     file_event); | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  | static void ih_not_missing_callback (inotify_sub *sub); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* We share this lock with inotify-kernel.c and inotify-missing.c
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * inotify-kernel.c takes the lock when it reads events from | 
					
						
							|  |  |  |  * the kernel and when it processes those events | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * inotify-missing.c takes the lock when it is scanning the missing | 
					
						
							|  |  |  |  * list. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * We take the lock in all public functions | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-01-18 13:27:16 -05:00
										 |  |  | G_LOCK_DEFINE (inotify_lock); | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | static GFileMonitorEvent ih_mask_to_EventFlags (guint32 mask); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * _ih_startup: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Initializes the inotify backend.  This must be called before | 
					
						
							|  |  |  |  * any other functions in this module. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2014-02-19 19:35:23 -05:00
										 |  |  |  * Returns: #TRUE if initialization succeeded, #FALSE otherwise | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | gboolean | 
					
						
							|  |  |  | _ih_startup (void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   static gboolean initialized = FALSE; | 
					
						
							|  |  |  |   static gboolean result = FALSE; | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   G_LOCK (inotify_lock); | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   if (initialized == TRUE) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       G_UNLOCK (inotify_lock); | 
					
						
							|  |  |  |       return result; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   result = _ip_startup (ih_event_callback); | 
					
						
							|  |  |  |   if (!result) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       G_UNLOCK (inotify_lock); | 
					
						
							|  |  |  |       return FALSE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   _im_startup (ih_not_missing_callback); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   IH_W ("started gvfs inotify backend\n"); | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   initialized = TRUE; | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   G_UNLOCK (inotify_lock); | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   return TRUE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-18 11:30:48 +02:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  * Adds a subscription to be monitored. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | gboolean | 
					
						
							| 
									
										
										
										
											2008-01-21 05:12:16 +00:00
										 |  |  | _ih_sub_add (inotify_sub *sub) | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  | { | 
					
						
							|  |  |  |   G_LOCK (inotify_lock); | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  |   if (!_ip_start_watching (sub)) | 
					
						
							|  |  |  |     _im_add (sub); | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   G_UNLOCK (inotify_lock); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |   return TRUE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-18 11:30:48 +02:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  * Cancels a subscription which was being monitored. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | gboolean | 
					
						
							| 
									
										
										
										
											2008-01-21 05:12:16 +00:00
										 |  |  | _ih_sub_cancel (inotify_sub *sub) | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  | { | 
					
						
							|  |  |  |   G_LOCK (inotify_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!sub->cancelled) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       IH_W ("cancelling %s\n", sub->dirname); | 
					
						
							|  |  |  |       sub->cancelled = TRUE; | 
					
						
							|  |  |  |       _im_rm (sub); | 
					
						
							|  |  |  |       _ip_stop_watching (sub); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   G_UNLOCK (inotify_lock); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |   return TRUE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-18 15:49:58 +01:00
										 |  |  | static char * | 
					
						
							| 
									
										
										
										
											2009-11-18 17:59:14 -06:00
										 |  |  | _ih_fullpath_from_event (ik_event_t *event, | 
					
						
							|  |  |  | 			 const char *dirname, | 
					
						
							|  |  |  | 			 const char *filename) | 
					
						
							| 
									
										
										
										
											2010-02-18 15:49:58 +01:00
										 |  |  | { | 
					
						
							|  |  |  |   char *fullpath; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-18 17:59:14 -06:00
										 |  |  |   if (filename) | 
					
						
							|  |  |  |     fullpath = g_strdup_printf ("%s/%s", dirname, filename); | 
					
						
							|  |  |  |   else if (event->name) | 
					
						
							| 
									
										
										
										
											2010-02-18 15:49:58 +01:00
										 |  |  |     fullpath = g_strdup_printf ("%s/%s", dirname, event->name); | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     fullpath = g_strdup_printf ("%s/", dirname); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    return fullpath; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-15 16:38:22 -05:00
										 |  |  | static gboolean | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  | ih_event_callback (ik_event_t  *event, | 
					
						
							| 
									
										
										
										
											2009-11-18 17:59:14 -06:00
										 |  |  |                    inotify_sub *sub, | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |                    gboolean     file_event) | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-01-15 16:38:22 -05:00
										 |  |  |   gboolean interesting; | 
					
						
							| 
									
										
										
										
											2017-11-24 20:02:43 +00:00
										 |  |  |   GFileMonitorEvent event_flags; | 
					
						
							| 
									
										
										
										
											2015-01-15 16:38:22 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-24 20:02:43 +00:00
										 |  |  |   event_flags = ih_mask_to_EventFlags (event->mask); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |   if (event->mask & IN_MOVE) | 
					
						
							| 
									
										
										
										
											2010-02-18 15:49:58 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |       /* We either have a rename (in the same directory) or a move
 | 
					
						
							|  |  |  |        * (between different directories). | 
					
						
							|  |  |  |        */ | 
					
						
							|  |  |  |       if (event->pair && event->pair->wd == event->wd) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           /* this is a rename */ | 
					
						
							| 
									
										
										
										
											2015-01-15 16:38:22 -05:00
										 |  |  |           interesting = g_file_monitor_source_handle_event (sub->user_data, G_FILE_MONITOR_EVENT_RENAMED, | 
					
						
							|  |  |  |                                                             event->name, event->pair->name, NULL, event->timestamp); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           GFile *other; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (event->pair) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               const char *parent_dir; | 
					
						
							|  |  |  |               gchar *fullpath; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               parent_dir = _ip_get_path_for_wd (event->pair->wd); | 
					
						
							|  |  |  |               fullpath = _ih_fullpath_from_event (event->pair, parent_dir, NULL); | 
					
						
							|  |  |  |               other = g_file_new_for_path (fullpath); | 
					
						
							|  |  |  |               g_free (fullpath); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             other = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-24 20:02:43 +00:00
										 |  |  |           /* This is either an incoming or outgoing move. Since we checked the
 | 
					
						
							|  |  |  |            * event->mask above, it should have converted to a #GFileMonitorEvent | 
					
						
							|  |  |  |            * properly. If not, the assumption we have made about event->mask | 
					
						
							|  |  |  |            * only ever having a single bit set (apart from IN_ISDIR) is false. | 
					
						
							|  |  |  |            * The kernel documentation is lacking here. */ | 
					
						
							| 
									
										
										
										
											2019-10-08 13:49:40 +01:00
										 |  |  |           g_assert ((int) event_flags != -1); | 
					
						
							| 
									
										
										
										
											2017-11-28 14:25:34 +00:00
										 |  |  |           interesting = g_file_monitor_source_handle_event (sub->user_data, event_flags, | 
					
						
							| 
									
										
										
										
											2015-01-15 16:38:22 -05:00
										 |  |  |                                                             event->name, NULL, other, event->timestamp); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |           if (other) | 
					
						
							|  |  |  |             g_object_unref (other); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-02-18 15:49:58 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-10-08 13:49:40 +01:00
										 |  |  |   else if ((int) event_flags != -1) | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |     /* unpaired event -- no 'other' field */ | 
					
						
							| 
									
										
										
										
											2017-11-28 14:25:34 +00:00
										 |  |  |     interesting = g_file_monitor_source_handle_event (sub->user_data, event_flags, | 
					
						
							| 
									
										
										
										
											2015-01-15 16:38:22 -05:00
										 |  |  |                                                       event->name, NULL, NULL, event->timestamp); | 
					
						
							| 
									
										
										
										
											2018-03-27 16:44:38 +01:00
										 |  |  |   else | 
					
						
							|  |  |  |     interesting = FALSE; | 
					
						
							| 
									
										
											  
											
												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.
											
										 
											2015-01-15 11:08:35 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |   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); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-01-15 16:38:22 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return interesting; | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | ih_not_missing_callback (inotify_sub *sub) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
											  
											
												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.
											
										 
											2015-01-15 11:08:35 -05:00
										 |  |  |   gint now = g_get_monotonic_time (); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |   g_file_monitor_source_handle_event (sub->user_data, G_FILE_MONITOR_EVENT_CREATED, | 
					
						
							| 
									
										
											  
											
												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.
											
										 
											2015-01-15 11:08:35 -05:00
										 |  |  |                                       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); | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Transforms a inotify event to a GVFS event. */ | 
					
						
							|  |  |  | static GFileMonitorEvent | 
					
						
							|  |  |  | ih_mask_to_EventFlags (guint32 mask) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   mask &= ~IN_ISDIR; | 
					
						
							|  |  |  |   switch (mask) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     case IN_MODIFY: | 
					
						
							|  |  |  |       return G_FILE_MONITOR_EVENT_CHANGED; | 
					
						
							|  |  |  |     case IN_CLOSE_WRITE: | 
					
						
							|  |  |  |       return G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT; | 
					
						
							|  |  |  |     case IN_ATTRIB: | 
					
						
							|  |  |  |       return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED; | 
					
						
							|  |  |  |     case IN_MOVE_SELF: | 
					
						
							|  |  |  |     case IN_DELETE: | 
					
						
							|  |  |  |     case IN_DELETE_SELF: | 
					
						
							|  |  |  |       return G_FILE_MONITOR_EVENT_DELETED; | 
					
						
							|  |  |  |     case IN_CREATE: | 
					
						
							|  |  |  |       return G_FILE_MONITOR_EVENT_CREATED; | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |     case IN_MOVED_FROM: | 
					
						
							|  |  |  |       return G_FILE_MONITOR_EVENT_MOVED_OUT; | 
					
						
							|  |  |  |     case IN_MOVED_TO: | 
					
						
							|  |  |  |       return G_FILE_MONITOR_EVENT_MOVED_IN; | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |     case IN_UNMOUNT: | 
					
						
							|  |  |  |       return G_FILE_MONITOR_EVENT_UNMOUNTED; | 
					
						
							|  |  |  |     case IN_Q_OVERFLOW: | 
					
						
							|  |  |  |     case IN_OPEN: | 
					
						
							|  |  |  |     case IN_CLOSE_NOWRITE: | 
					
						
							|  |  |  |     case IN_ACCESS: | 
					
						
							|  |  |  |     case IN_IGNORED: | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |       return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |