summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChun-wei Fan <fanchunwei@src.gnome.org>2015-03-30 14:24:38 +0800
committerChun-wei Fan <fanchunwei@src.gnome.org>2015-03-30 14:24:38 +0800
commit55847d9258df41b6d937c75ea4e3631168649686 (patch)
treeac07484567a5a89eb4a851d8fc4a5866b0207e65
parent01ce152b8f12cc4b659fce9cd0547620ef539fcb (diff)
downloadglib-wip/desrt/gio-tool.tar.gz
Continue to Improve File Monitoring on Windowswip/desrt/gio-tool
This updates the file monitoring on Windows by: -Clean up the code, and ensure things are indeed being freed when we exit. -Have attributes changes in files (when monitoring directories) properly done. -Split up the code, to ease readability To Possibly Do: -Check on whether we want to monitor the case when we try to pull out a USB stick without ejecting in in Windows (the monitoring mechanism understandbly denies such ejection requests, since the monitored file/directory is obviously in use in this case) -Investigate on the interesting/boredom algorithm, whether we can do it here. Possible Limitations: -If moving a file out of the directory (or vice versa), the system reports that as a delete event (or create event in the vice versa case), so in the current form without using NTFS hournals (which is not optimal as that would trigger UAC), so we can't reliably emit MOVE OUT or MOVE IN events on Windows. -Hard links are supported transparently, but notifications are only sent by the system on changes when the monitored file/directory (or monitored hard link) is being changed, when the file is modified via the hard link or original file respectively in this case. https://bugzilla.gnome.org/show_bug.cgi?id=730116
-rw-r--r--gio/Makefile.am3
-rw-r--r--gio/win32/Makefile.am8
-rw-r--r--gio/win32/gwin32filemonitor.c117
-rw-r--r--gio/win32/gwin32filemonitor.h6
-rw-r--r--gio/win32/gwin32filemonitorevents.c400
-rw-r--r--gio/win32/gwin32filemonitorevents.h32
-rw-r--r--gio/win32/gwin32filemonitorutils.c454
-rw-r--r--gio/win32/gwin32filemonitorutils.h89
-rw-r--r--gio/win32/gwin32fsmonitorutils.c422
-rw-r--r--gio/win32/gwin32fsmonitorutils.h76
10 files changed, 1089 insertions, 518 deletions
diff --git a/gio/Makefile.am b/gio/Makefile.am
index d0e53b333..d79c2c409 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -317,7 +317,8 @@ win32_more_sources_for_vcproj = \
win32/gwinhttpfileinputstream.c \
win32/gwinhttpfileoutputstream.c \
win32/gwinhttpvfs.c \
- win32/gwin32fsmonitorutils.c \
+ win32/gwin32filemonitorevents.c \
+ win32/gwin32filemonitorutils.c \
win32/gwin32filemonitor.c
if OS_WIN32
diff --git a/gio/win32/Makefile.am b/gio/win32/Makefile.am
index 2fc10f65e..909bb53aa 100644
--- a/gio/win32/Makefile.am
+++ b/gio/win32/Makefile.am
@@ -2,9 +2,11 @@ include $(top_srcdir)/glib.mk
noinst_LTLIBRARIES += libgiowin32.la
-libgiowin32_la_SOURCES = \
- gwin32fsmonitorutils.c \
- gwin32fsmonitorutils.h \
+libgiowin32_la_SOURCES = \
+ gwin32filemonitorevents.c \
+ gwin32filemonitorevents.h \
+ gwin32filemonitorutils.c \
+ gwin32filemonitorutils.h \
gwin32filemonitor.c \
gwin32filemonitor.h \
gwinhttpvfs.c \
diff --git a/gio/win32/gwin32filemonitor.c b/gio/win32/gwin32filemonitor.c
index 1b93b82e4..30acef4cd 100644
--- a/gio/win32/gwin32filemonitor.c
+++ b/gio/win32/gwin32filemonitor.c
@@ -1,7 +1,7 @@
/* GIO - GLib Input, Output and Streaming Library
*
- * Copyright (C) 2006-2007 Red Hat, Inc.
- * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2006-2015 Red Hat, Inc.
+ * Copyright (C) 2015 Chun-wei Fan
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -24,15 +24,56 @@
#include "config.h"
#include "gwin32filemonitor.h"
-#include "gwin32fsmonitorutils.h"
-
-#include <windows.h>
+#include "gwin32filemonitorutils.h"
G_DEFINE_TYPE_WITH_CODE (GWin32FileMonitor, g_win32_file_monitor, G_TYPE_LOCAL_FILE_MONITOR,
g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
g_define_type_id, "win32filemonitor", 20))
static void
+g_win32_file_monitor_close_handle (HANDLE hDirectory)
+{
+ /* This triggers a last callback() with nBytes==0. */
+
+ /* Actually I am not so sure about that, it seems to trigger a last
+ * callback allright, but the way to recognize that it is the final
+ * one is not to check for nBytes==0, I think that was a
+ * misunderstanding.
+ */
+ if (hDirectory != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle (hDirectory);
+ hDirectory = INVALID_HANDLE_VALUE;
+ }
+}
+
+static void
+g_win32_file_monitor_free (GWin32FileMonitorInfo *info)
+{
+ if (info != NULL)
+ {
+ g_free (info->shortname);
+ g_free (info->longname);
+ g_free (info->name);
+ g_free (info->dirname_with_long_prefix);
+ g_free (info);
+ }
+}
+
+static GWin32FileMonitorPrivate*
+g_win32_file_monitor_create (void)
+{
+ GWin32FileMonitorPrivate* monitor = (GWin32FileMonitorPrivate*) g_new0 (GWin32FileMonitorPrivate, 1);
+ g_assert (monitor != 0);
+
+ monitor->buffer_allocated_bytes = 32784;
+ monitor->file_notify_buffer = g_new0 (FILE_NOTIFY_INFORMATION, monitor->buffer_allocated_bytes);
+ g_assert (monitor->file_notify_buffer);
+
+ return monitor;
+}
+
+static void
g_win32_file_monitor_start (GLocalFileMonitor *monitor,
const gchar *dirname,
const gchar *basename,
@@ -43,14 +84,32 @@ g_win32_file_monitor_start (GLocalFileMonitor *monitor,
gboolean isfile = (filename == NULL && basename == NULL) ? FALSE : TRUE;
win32_monitor->priv->fms = source;
+ win32_monitor->priv->ht_watched_names =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_win32_file_monitor_free);
+
+ win32_monitor->priv->ht_watched_dirs =
+ g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ g_win32_file_monitor_close_handle,
+ g_free);
+
+ if (!isfile)
+ win32_monitor->priv->ht_files_attribs =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
if (isfile)
if (basename != NULL)
- g_win32_fs_monitor_init (win32_monitor->priv, dirname, basename, TRUE);
+ g_win32_file_monitor_prepare (win32_monitor->priv, dirname, basename, TRUE);
else
- g_win32_fs_monitor_init (win32_monitor->priv, NULL, filename, TRUE);
+ g_win32_file_monitor_prepare (win32_monitor->priv, NULL, filename, TRUE);
else
- g_win32_fs_monitor_init (win32_monitor->priv, dirname, NULL, FALSE);
+ g_win32_file_monitor_prepare (win32_monitor->priv, dirname, NULL, FALSE);
}
static gboolean
@@ -62,18 +121,51 @@ g_win32_file_monitor_is_supported (void)
static void
g_win32_file_monitor_init (GWin32FileMonitor* monitor)
{
- monitor->priv = g_win32_fs_monitor_create (TRUE);
+ monitor->priv = g_win32_file_monitor_create ();
monitor->priv->self = G_FILE_MONITOR (monitor);
}
static void
+g_win32_destroy_monitor_hashtables (GWin32FileMonitorPrivate *monitor)
+{
+ if (monitor->ht_files_attribs != NULL)
+ {
+ GList *l_attrib_keys = g_hash_table_get_keys (monitor->ht_files_attribs);
+ for (;l_attrib_keys != NULL; l_attrib_keys = l_attrib_keys->next)
+ {
+ GHashTable *ht_attribs = g_hash_table_lookup (monitor->ht_files_attribs,
+ l_attrib_keys->data);
+ if (ht_attribs != NULL)
+ g_hash_table_destroy (ht_attribs);
+ }
+ }
+
+ if (monitor->ht_watched_names != NULL)
+ {
+ g_hash_table_destroy (monitor->ht_watched_names);
+ monitor->ht_watched_names = NULL;
+ }
+
+ if (monitor->ht_watched_dirs != NULL)
+ {
+ g_hash_table_destroy (monitor->ht_watched_dirs);
+ monitor->ht_watched_dirs = NULL;
+ }
+}
+
+static void
g_win32_file_monitor_finalize (GObject *base)
{
GWin32FileMonitor *monitor;
monitor = G_WIN32_FILE_MONITOR (base);
- g_win32_fs_monitor_finalize (monitor->priv);
+ g_win32_destroy_monitor_hashtables (monitor->priv);
+
+ g_free (monitor->priv->file_notify_buffer);
+ monitor->priv->file_notify_buffer = NULL;
+
+ g_free (monitor->priv);
if (G_OBJECT_CLASS (g_win32_file_monitor_parent_class)->finalize)
(*G_OBJECT_CLASS (g_win32_file_monitor_parent_class)->finalize) (base);
@@ -82,10 +174,9 @@ g_win32_file_monitor_finalize (GObject *base)
static gboolean
g_win32_file_monitor_cancel (GFileMonitor* monitor)
{
- GWin32FileMonitor *file_monitor;
- file_monitor = G_WIN32_FILE_MONITOR (monitor);
+ GWin32FileMonitor *file_monitor = G_WIN32_FILE_MONITOR (monitor);
- g_win32_fs_monitor_close_handle (file_monitor->priv);
+ g_win32_destroy_monitor_hashtables (file_monitor->priv);
return TRUE;
}
diff --git a/gio/win32/gwin32filemonitor.h b/gio/win32/gwin32filemonitor.h
index 5aef2ba3f..185dd77c7 100644
--- a/gio/win32/gwin32filemonitor.h
+++ b/gio/win32/gwin32filemonitor.h
@@ -1,6 +1,6 @@
/* GIO - GLib Input, Output and Streaming Library
*
- * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2015 Chun-wei Fan
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -31,7 +31,7 @@
#include "gio/glocalfilemonitor.h"
#include "gio/giomodule.h"
-#include "gwin32fsmonitorutils.h"
+#include "gwin32filemonitorutils.h"
G_BEGIN_DECLS
@@ -48,7 +48,7 @@ typedef struct _GWin32FileMonitorPrivate GWin32FileMonitorPrivate;
struct _GWin32FileMonitor {
GLocalFileMonitor parent_instance;
- GWin32FSMonitorPrivate * priv;
+ GWin32FileMonitorPrivate * priv;
};
struct _GWin32FileMonitorClass {
GLocalFileMonitorClass parent_class;
diff --git a/gio/win32/gwin32filemonitorevents.c b/gio/win32/gwin32filemonitorevents.c
new file mode 100644
index 000000000..620f49888
--- /dev/null
+++ b/gio/win32/gwin32filemonitorevents.c
@@ -0,0 +1,400 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2015 Chun-wei Fan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Chun-wei Fan <fanc999@yahoo.com.tw>
+ *
+ */
+
+#include "config.h"
+
+#include "gwin32filemonitorevents.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+static gboolean
+g_win32_file_monitor_handle_event (GWin32FileMonitorPrivate *monitor,
+ gchar *filename,
+ PFILE_NOTIFY_INFORMATION pfni,
+ gboolean is_renamed_to)
+{
+ GFileMonitorEvent fme;
+ PFILE_NOTIFY_INFORMATION pfni_next;
+ gchar *from_to_file = NULL;
+ glong file_name_len = 0;
+ gboolean reacquire_names = FALSE;
+ gchar *fullpath = g_hash_table_lookup (monitor->ht_watched_dirs, monitor->hDirectory);
+ GWin32FileMonitorInfo *info = g_hash_table_lookup (monitor->ht_watched_names, fullpath);
+
+ gboolean is_attrib_changed = FALSE;
+
+ gboolean update_all_attribs = FALSE;
+ gchar *update_file = NULL;
+
+ switch (pfni->Action)
+ {
+ case FILE_ACTION_ADDED:
+ fme = G_FILE_MONITOR_EVENT_CREATED;
+ reacquire_names = TRUE;
+ break;
+
+ case FILE_ACTION_REMOVED:
+ fme = G_FILE_MONITOR_EVENT_DELETED;
+ reacquire_names = TRUE;
+ break;
+
+ case FILE_ACTION_MODIFIED:
+ if (info->isfile)
+ is_attrib_changed = g_win32_file_monitor_check_attrib_changed (info, filename, NULL);
+ else
+ {
+ GHashTable *ht_attribs = g_hash_table_lookup (monitor->ht_files_attribs, fullpath);
+ is_attrib_changed = g_win32_file_monitor_check_attrib_changed (info, filename, ht_attribs);
+ if (is_attrib_changed)
+ {
+ g_hash_table_remove (monitor->ht_files_attribs, fullpath);
+ g_hash_table_insert (monitor->ht_files_attribs, g_strdup (fullpath), ht_attribs);
+ }
+ }
+ if (is_attrib_changed)
+ fme = G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
+ else
+ fme = G_FILE_MONITOR_EVENT_CHANGED;
+
+ break;
+
+ case FILE_ACTION_RENAMED_OLD_NAME:
+ if (pfni->NextEntryOffset != 0)
+ {
+ /* If the file was renamed in the same directory, we would get a
+ * FILE_ACTION_RENAMED_NEW_NAME action in the next FILE_NOTIFY_INFORMATION
+ * structure.
+ */
+
+ pfni_next = (PFILE_NOTIFY_INFORMATION) ((BYTE*)pfni + pfni->NextEntryOffset);
+ if (pfni_next->Action == FILE_ACTION_RENAMED_NEW_NAME)
+ {
+ from_to_file = g_utf16_to_utf8 (pfni_next->FileName,
+ pfni_next->FileNameLength / sizeof(WCHAR),
+ NULL, &file_name_len,
+ NULL);
+
+ fme = G_FILE_MONITOR_EVENT_RENAMED;
+ }
+ else
+ fme = G_FILE_MONITOR_EVENT_MOVED_OUT;
+ }
+ else
+ fme = G_FILE_MONITOR_EVENT_MOVED_OUT;
+
+ reacquire_names = TRUE;
+ break;
+
+ case FILE_ACTION_RENAMED_NEW_NAME:
+ if (monitor->pfni_prev != NULL &&
+ monitor->pfni_prev->Action == FILE_ACTION_RENAMED_OLD_NAME)
+ {
+ if (is_renamed_to)
+ {
+ from_to_file = g_utf16_to_utf8 (monitor->pfni_prev->FileName,
+ monitor->pfni_prev->FileNameLength / sizeof(WCHAR),
+ NULL,
+ &file_name_len,
+ NULL);
+ fme = G_FILE_MONITOR_EVENT_RENAMED;
+ }
+ else
+ /* don't bother sending events, was already sent (renamed from monitored file) */
+ fme = -1;
+ }
+ else
+ fme = G_FILE_MONITOR_EVENT_MOVED_IN;
+
+ reacquire_names = TRUE;
+
+ break;
+
+ default:
+ /* The possible Windows actions are all above, so shouldn't get here */
+ g_assert_not_reached ();
+ break;
+ }
+ if (fme != -1)
+ {
+ gboolean interesting;
+
+ if (info->isfile)
+ {
+ if (pfni->Action == FILE_ACTION_RENAMED_NEW_NAME &&
+ is_renamed_to)
+ {
+ /* we are renaming to the monitored file */
+ interesting = g_file_monitor_source_handle_event (monitor->fms,
+ fme,
+ from_to_file,
+ filename,
+ NULL,
+ g_get_monotonic_time ());
+ }
+ else
+ {
+ interesting = g_file_monitor_source_handle_event (monitor->fms,
+ fme,
+ filename,
+ from_to_file,
+ NULL,
+ g_get_monotonic_time ());
+ }
+ }
+ else
+ {
+ gchar *from_to_file_stripped = NULL;
+ gchar *filename_stripped = NULL;
+
+ if (filename != NULL)
+ {
+ if (strrchr (filename, G_DIR_SEPARATOR) != NULL)
+ filename_stripped = g_strdup (strrchr (filename, G_DIR_SEPARATOR) + 1);
+ else
+ filename_stripped = g_strdup (filename);
+ }
+ if (from_to_file != NULL)
+ {
+ if (strrchr (from_to_file, G_DIR_SEPARATOR) != NULL)
+ from_to_file_stripped = g_strdup (strrchr (from_to_file, G_DIR_SEPARATOR) + 1);
+ else
+ from_to_file_stripped = g_strdup (from_to_file);
+ }
+
+ if (pfni->Action == FILE_ACTION_RENAMED_NEW_NAME &&
+ is_renamed_to)
+ {
+ /* we are renaming to the monitored file */
+ interesting = g_file_monitor_source_handle_event (monitor->fms,
+ fme,
+ from_to_file_stripped,
+ filename_stripped,
+ NULL,
+ g_get_monotonic_time ());
+ }
+ else
+ {
+ interesting = g_file_monitor_source_handle_event (monitor->fms,
+ fme,
+ filename_stripped,
+ from_to_file_stripped,
+ NULL,
+ g_get_monotonic_time ());
+ }
+ if (filename_stripped != NULL)
+ g_free (filename_stripped);
+ if (from_to_file_stripped != NULL)
+ g_free (from_to_file_stripped);
+ }
+
+ if (from_to_file != NULL)
+ g_free (from_to_file);
+ if (reacquire_names)
+ {
+ g_win32_file_monitor_set_names (info);
+ if (!info->isfile)
+ g_win32_file_monitor_dir_refresh_attribs (monitor, info);
+ }
+
+ return interesting;
+ }
+ else
+ return FALSE;
+}
+
+void CALLBACK
+g_win32_file_monitor_callback (DWORD error,
+ DWORD nBytes,
+ LPOVERLAPPED lpOverlapped)
+{
+ gulong offset;
+ PFILE_NOTIFY_INFORMATION pfile_notify_walker;
+
+ GWin32FileMonitorPrivate *monitor = (GWin32FileMonitorPrivate *) lpOverlapped;
+ GWin32FileMonitorInfo *info;
+
+ DWORD notify_filter;
+
+ gchar *fullpath;
+ gchar *changed_file;
+ glong file_name_len;
+ GHashTable *ht_attribs;
+
+ fullpath = g_hash_table_lookup (monitor->ht_watched_dirs, monitor->hDirectory);
+
+ info = g_hash_table_lookup (monitor->ht_watched_names, fullpath);
+
+ notify_filter = info->isfile ?
+ (FILE_NOTIFY_CHANGE_FILE_NAME |
+ FILE_NOTIFY_CHANGE_DIR_NAME |
+ FILE_NOTIFY_CHANGE_ATTRIBUTES |
+ FILE_NOTIFY_CHANGE_SIZE) :
+ (FILE_NOTIFY_CHANGE_FILE_NAME |
+ FILE_NOTIFY_CHANGE_DIR_NAME |
+ FILE_NOTIFY_CHANGE_ATTRIBUTES |
+ FILE_NOTIFY_CHANGE_SIZE);
+ if (!info->isfile)
+ ht_attribs = g_hash_table_lookup (monitor->ht_files_attribs,
+ fullpath);
+
+ /* If monitor->self is NULL the GWin32FileMonitor object has been destroyed. */
+ if (monitor->self == NULL ||
+ g_file_monitor_is_cancelled (monitor->self) ||
+ monitor->file_notify_buffer == NULL)
+ {
+ g_free (monitor->file_notify_buffer);
+
+ g_free (monitor);
+ return;
+ }
+
+ offset = 0;
+
+ do
+ {
+ pfile_notify_walker = (PFILE_NOTIFY_INFORMATION)((BYTE*)monitor->file_notify_buffer + offset);
+ if (pfile_notify_walker->Action > 0)
+ {
+ gboolean is_rename = FALSE;
+ gboolean is_renamed_to = FALSE;
+ gboolean is_handle_event = FALSE;
+ gchar *shortname_casefold, *longname_casefold, *changed_file_casefold;
+
+ gint long_filename_length = g_utf8_strlen (info->longname, -1);
+ gint short_filename_length = g_utf8_strlen (info->shortname, -1);
+ PFILE_NOTIFY_INFORMATION pfni_next =
+ (PFILE_NOTIFY_INFORMATION)((BYTE*)pfile_notify_walker + pfile_notify_walker->NextEntryOffset);
+
+ gchar *longfilename_with_path, *shortfilename_with_path, *changed_file_with_path;
+
+ longname_casefold = g_utf8_casefold (info->longname, -1);
+ shortname_casefold = g_utf8_casefold (info->shortname, -1);
+
+ longfilename_with_path = g_strconcat (info->dirname_with_long_prefix,
+ G_DIR_SEPARATOR_S,
+ longname_casefold,
+ NULL);
+ shortfilename_with_path = g_strconcat (info->dirname_with_long_prefix,
+ G_DIR_SEPARATOR_S,
+ shortname_casefold,
+ NULL);
+
+ changed_file = g_utf16_to_utf8 (pfile_notify_walker->FileName,
+ pfile_notify_walker->FileNameLength / sizeof(WCHAR),
+ NULL,
+ &file_name_len,
+ NULL);
+ changed_file_casefold = g_utf8_casefold (changed_file, -1);
+ changed_file_with_path = g_strconcat (info->dirname_with_long_prefix,
+ G_DIR_SEPARATOR_S,
+ changed_file_casefold,
+ NULL);
+
+ if (pfile_notify_walker->Action == FILE_ACTION_RENAMED_OLD_NAME &&
+ pfni_next->Action == FILE_ACTION_RENAMED_NEW_NAME)
+ {
+ is_rename = TRUE;
+ }
+
+ /* If monitoring a file, check that the changed file
+ * in the directory matches the file that is to be monitored
+ * We need to check both the long and short file names for the same file, as
+ * ReadDirectoryChangesW() could return either one of them.
+ *
+ * We need to send in the name of the monitored file, not its long (or short) variant,
+ * if such long and/or short filenames exist.
+ */
+ if (info->isfile)
+ is_handle_event = g_str_equal (longfilename_with_path, changed_file_with_path) ||
+ g_str_equal (shortfilename_with_path, changed_file_with_path);
+
+ else
+ {
+ /* don't monitor next directory, at least for now */
+ gint changed_file_length = g_utf8_strlen (g_utf8_strrchr(changed_file_with_path, -1, G_DIR_SEPARATOR), -1);
+ gchar *changed_dir = g_utf8_substring (changed_file_with_path,
+ 0,
+ g_utf8_strlen (changed_file_with_path, -1) - changed_file_length);
+
+ is_handle_event = g_str_equal (longfilename_with_path, changed_file_with_path) ||
+ g_str_equal (shortfilename_with_path, changed_file_with_path) ||
+ g_str_equal (longfilename_with_path, changed_dir) ||
+ g_str_equal (shortfilename_with_path, changed_dir);
+
+ g_free (changed_dir);
+ }
+
+ if (is_rename = TRUE &&
+ monitor->pfni_prev != NULL &&
+ monitor->pfni_prev->Action == FILE_ACTION_RENAMED_OLD_NAME &&
+ pfile_notify_walker->Action == FILE_ACTION_RENAMED_NEW_NAME)
+ {
+ gchar *next_file = g_utf16_to_utf8 (pfile_notify_walker->FileName,
+ pfile_notify_walker->FileNameLength / sizeof(WCHAR),
+ NULL,
+ &file_name_len,
+ NULL);
+ gchar *next_file_with_path = g_strconcat (info->dirname_with_long_prefix,
+ G_DIR_SEPARATOR_S,
+ next_file,
+ NULL);
+ if (g_str_equal (longfilename_with_path, next_file_with_path) ||
+ g_str_equal (shortfilename_with_path, next_file_with_path))
+ {
+ is_renamed_to = TRUE;
+ is_handle_event = TRUE;
+ }
+ g_free (next_file);
+ }
+ g_free (longfilename_with_path);
+ g_free (shortfilename_with_path);
+ g_free (changed_file_with_path);
+
+ if (is_handle_event)
+ {
+ if (info->isfile)
+ g_win32_file_monitor_handle_event (monitor,
+ info->name,
+ pfile_notify_walker,
+ is_renamed_to);
+ else
+ g_win32_file_monitor_handle_event (monitor,
+ changed_file,
+ pfile_notify_walker,
+ is_renamed_to);
+ }
+ g_free (changed_file);
+ }
+ monitor->pfni_prev = pfile_notify_walker;
+ offset += pfile_notify_walker->NextEntryOffset;
+ }
+ while (pfile_notify_walker->NextEntryOffset);
+
+ ReadDirectoryChangesW (monitor->hDirectory,
+ monitor->file_notify_buffer,
+ monitor->buffer_allocated_bytes,
+ !info->isfile,
+ notify_filter,
+ &monitor->buffer_filled_bytes,
+ &monitor->overlapped,
+ g_win32_file_monitor_callback);
+}
diff --git a/gio/win32/gwin32filemonitorevents.h b/gio/win32/gwin32filemonitorevents.h
new file mode 100644
index 000000000..9c125beda
--- /dev/null
+++ b/gio/win32/gwin32filemonitorevents.h
@@ -0,0 +1,32 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2015 Chun-wei Fan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Chun-wei Fan <fanc999@yahoo.com.tw>
+ *
+ */
+
+#include "gwin32filemonitorutils.h"
+#include "gio/gfile.h"
+
+#ifndef __G_WIN32_FILE_MONITOR_EVENTS_H__
+#define __G_WIN32_FILE_MONITOR_EVENTS_H__
+
+void CALLBACK g_win32_file_monitor_callback (DWORD error,
+ DWORD nBytes,
+ LPOVERLAPPED lpOverlapped);
+
+#endif /* __G_WIN32_FILE_MONITOR_EVENTS_H__ */
diff --git a/gio/win32/gwin32filemonitorutils.c b/gio/win32/gwin32filemonitorutils.c
new file mode 100644
index 000000000..e0a51e2f8
--- /dev/null
+++ b/gio/win32/gwin32filemonitorutils.c
@@ -0,0 +1,454 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2015 Red Hat, Inc.
+ * Copyright (C) 2015 Chun-wei Fan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Vlad Grecescu <b100dian@gmail.com>
+ * Author: Chun-wei Fan <fanc999@yahoo.com.tw>
+ *
+ */
+
+#include "config.h"
+
+#include "gwin32filemonitorutils.h"
+#include "gwin32filemonitorevents.h"
+#include "gio/gfile.h"
+
+/*
+ * We are using the Wide/Unicode (W) variants of the Windows APIs as:
+ * -There isn't a ReadDirectoryChangesA()
+ * -We want to support long paths, over 256 characters, and using the W variants is required in this case
+ */
+
+/*
+ * Note: We don't support monitoring for "normal" unmounts in Windows as the Windows file/directory monitoring
+ * mechanism will disallow the unmounting of the monitored volume (unless of course we pull out the USB stick,
+ * for instance, without ejecting it
+ */
+
+/*
+ * On Windows, there is the notion of hard links, but they are treated more or less like standard files, but any
+ * changes to them will also cause a notification to be sent for the directory where the original file resides,
+ * so ReadDirectoryChangesW() also works with hard links, though transparently, so there isn't a special code path
+ * for hard links here. The notification, however, may be sent only when the file (or hard link) in question
+ * is being accessed.
+ */
+
+gboolean
+g_win32_file_monitor_long_pfx_needed (const gchar* path)
+{
+ if (g_utf8_strlen (path, -1) > MAX_PATH)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+GHashTable*
+g_win32_file_monitor_dir_refresh_attribs (GWin32FileMonitorPrivate *monitor,
+ GWin32FileMonitorInfo *info)
+{
+ GHashTable *ht_attribs;
+ WIN32_FILE_ATTRIBUTE_DATA *attrib_data;
+ gchar *fullpath, *dirname, *dirname_casefold;
+ wchar_t *wfullpath;
+ HANDLE hFind = INVALID_HANDLE_VALUE;
+ WIN32_FIND_DATA file_data = {0,};
+
+ if (info->isfile)
+ return NULL;
+
+ fullpath = g_strconcat (info->dirname_with_long_prefix,
+ G_DIR_SEPARATOR_S,
+ info->name,
+ G_DIR_SEPARATOR_S,
+ "*",
+ NULL);
+ wfullpath = g_utf8_to_utf16 (fullpath, -1, NULL, NULL, NULL);
+ dirname = g_strconcat (info->dirname_with_long_prefix + STRIP_PFX_LENGTH,
+ G_DIR_SEPARATOR_S,
+ info->name,
+ NULL);
+ dirname_casefold = g_utf8_casefold (dirname, -1);
+
+ hFind = FindFirstFileExW (wfullpath,
+ FindExInfoBasic,
+ &file_data,
+ 0,
+ NULL,
+ 0);
+
+ ht_attribs = g_hash_table_lookup (monitor->ht_files_attribs, dirname_casefold);
+ if (ht_attribs == NULL)
+ {
+ ht_attribs = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_free);
+ }
+ else
+ g_hash_table_remove_all (ht_attribs);
+
+ g_free (dirname_casefold);
+ g_free (dirname);
+
+ if (hFind == INVALID_HANDLE_VALUE)
+ /*
+ * Initially, we won't get here, if we started monitoring on a non-existing directory.
+ * If we do later on, the directory was moved or deleted, forget about updating the info
+ */
+ return NULL;
+ else
+ {
+ do
+ {
+ attrib_data = g_new0 (WIN32_FILE_ATTRIBUTE_DATA, 1);
+ attrib_data->dwFileAttributes = file_data.dwFileAttributes;
+ attrib_data->ftCreationTime = file_data.ftCreationTime;
+ attrib_data->ftLastAccessTime = file_data.ftLastAccessTime;
+ attrib_data->ftLastWriteTime = file_data.ftLastWriteTime;
+ attrib_data->nFileSizeHigh = file_data.nFileSizeHigh;
+ attrib_data->nFileSizeLow = file_data.nFileSizeLow;
+
+ g_hash_table_insert (ht_attribs,
+ g_utf8_casefold (file_data.cFileName, -1),
+ attrib_data);
+ }
+ while (FindNextFile (hFind, &file_data));
+ FindClose (hFind);
+ }
+ return ht_attribs;
+}
+
+/*
+ * ReadDirectoryChangesW() can return the normal filename or the
+ * "8.3" format filename, so we need to keep track of both these names
+ * so that we can check against them later when it returns
+ */
+void
+g_win32_file_monitor_set_names (GWin32FileMonitorInfo *info)
+{
+ wchar_t *wfullpath, *wbasename_long, *wbasename_short;
+ wchar_t wlongname[MAX_PATH_LONG];
+ wchar_t wshortname[MAX_PATH_LONG];
+ gboolean success_attribs;
+
+ gchar *fullpath_with_long_pfx = g_strconcat (info->dirname_with_long_prefix,
+ G_DIR_SEPARATOR_S,
+ info->name,
+ NULL);
+
+ wfullpath = g_utf8_to_utf16 (fullpath_with_long_pfx, -1, NULL, NULL, NULL);
+ if (info->longname != NULL)
+ g_free (info->longname);
+ if (info->shortname != NULL)
+ g_free (info->shortname);
+
+ /*
+ * Get long and short paths of the specified path, if it exists, otherwise
+ * just assume the filename part in the passed in filename and directory as
+ * the long and short filenames
+ */
+ if (GetLongPathNameW (wfullpath, wlongname, MAX_PATH_LONG) != 0)
+ {
+ wbasename_long = wcsrchr (wlongname, G_DIR_SEPARATOR_W) != NULL ?
+ wcsrchr (wlongname, G_DIR_SEPARATOR_W) + 1 :
+ wlongname + STRIP_PFX_LENGTH;
+ }
+ else
+ {
+ wbasename_long = wcsrchr (wfullpath, G_DIR_SEPARATOR_W) != NULL ?
+ wcsrchr (wfullpath, G_DIR_SEPARATOR_W) + 1 :
+ wfullpath + STRIP_PFX_LENGTH;
+ }
+ if (GetShortPathNameW (wfullpath, wshortname, MAX_PATH_LONG) != 0)
+ {
+ wbasename_short = wcsrchr (wshortname, G_DIR_SEPARATOR_W) != NULL ?
+ wcsrchr (wshortname, G_DIR_SEPARATOR_W) + 1 :
+ wshortname + STRIP_PFX_LENGTH;
+ }
+ else
+ {
+ wbasename_short = wcsrchr (wfullpath, G_DIR_SEPARATOR_W) != NULL ?
+ wcsrchr (wfullpath, G_DIR_SEPARATOR_W) + 1 :
+ wfullpath + STRIP_PFX_LENGTH;
+ }
+
+ info->longname = g_utf16_to_utf8 (wbasename_long, -1, NULL, NULL, NULL);
+ info->shortname = g_utf16_to_utf8 (wbasename_short, -1, NULL, NULL, NULL);
+
+ /* Store up current file attributes */
+ success_attribs = GetFileAttributesExW (wfullpath,
+ GetFileExInfoStandard,
+ &info->attribs);
+
+ g_free (wfullpath);
+ g_free (fullpath_with_long_pfx);
+
+ if (!success_attribs)
+ info->attribs.dwFileAttributes = INVALID_FILE_ATTRIBUTES;
+}
+
+gboolean
+g_win32_file_monitor_check_attrib_changed (GWin32FileMonitorInfo *info,
+ gchar *filename,
+ GHashTable *ht_attribs)
+{
+ gchar *fullpath_with_long_prefix = g_strconcat (info->dirname_with_long_prefix,
+ G_DIR_SEPARATOR_S,
+ filename,
+ NULL);
+ wchar_t *wfullpath_with_long_prefix = g_utf8_to_utf16 (fullpath_with_long_prefix, -1, NULL, NULL, NULL);
+ gboolean is_attribs_changed = FALSE;
+ WIN32_FILE_ATTRIBUTE_DATA attrib_data = {0, };
+ gboolean success_attribs = GetFileAttributesExW (wfullpath_with_long_prefix,
+ GetFileExInfoStandard,
+ &attrib_data);
+
+ if (!success_attribs)
+ attrib_data.dwFileAttributes = INVALID_FILE_ATTRIBUTES;
+
+ if (info->isfile)
+ {
+ /*
+ * Monitoring a file: Simply check if attributes changed, or file changed, and
+ * store up the new attributes for the file if needed.
+ */
+ if (info->attribs.dwFileAttributes != INVALID_FILE_ATTRIBUTES &&
+ success_attribs &&
+ attrib_data.dwFileAttributes != info->attribs.dwFileAttributes)
+ {
+ is_attribs_changed = TRUE;
+ info->attribs = attrib_data;
+ }
+ return is_attribs_changed;
+ }
+ else
+ {
+ /*
+ * Monitoring directories: The situation is more complex, we need to first see whether
+ * a file changed, or a directory changed. If it is just the directory that changed,
+ * than it's like the file case, that is to check only the attribute of the directory.
+ * If it is on a file in a directory, we need to check against the attributes of the file,
+ * against the attribues of the files in the directory, which we stored up when we were
+ * creating the monitor object, and update our stored records on that file if the attributes
+ * of that file changed.
+ */
+
+ WIN32_FILE_ATTRIBUTE_DATA *attrib_data_orig;
+ gchar *filename_casefold;
+
+ if (g_utf8_strrchr (filename, -1, G_DIR_SEPARATOR) != NULL)
+ {
+ /* A file in the monitored directory changed */
+ gboolean attribs_success;
+ gchar *longname;
+ wchar_t wfullpath_long[MAX_PATH_LONG];
+
+ WIN32_FILE_ATTRIBUTE_DATA *curr_attrib_data = g_new0 (WIN32_FILE_ATTRIBUTE_DATA, 1);
+ gchar *fullpath = g_strconcat (info->dirname_with_long_prefix,
+ G_DIR_SEPARATOR_S,
+ info->name,
+ G_DIR_SEPARATOR_S,
+ g_utf8_strrchr (filename, -1, G_DIR_SEPARATOR) + 1,
+ NULL);
+ wchar_t *wfullpath = g_utf8_to_utf16 (fullpath, -1, NULL, NULL, NULL);
+
+ attribs_success = GetFileAttributesExW (wfullpath,
+ GetFileExInfoStandard,
+ curr_attrib_data);
+
+ if (!attribs_success)
+ {
+ curr_attrib_data->dwFileAttributes = INVALID_FILE_ATTRIBUTES;
+ longname = g_strdup (g_utf8_strrchr (longname, -1, G_DIR_SEPARATOR) + 1);
+ }
+ else
+ {
+ if (GetLongPathNameW (wfullpath, wfullpath_long, MAX_PATH_LONG) != 0)
+ longname = g_utf16_to_utf8 (wcsrchr (wfullpath_long, G_DIR_SEPARATOR_W) + 1,
+ -1,
+ NULL,
+ NULL,
+ NULL);
+ else
+ longname = g_strdup (g_utf8_strrchr (longname, -1, G_DIR_SEPARATOR) + 1);
+ }
+
+ filename_casefold = g_utf8_casefold (longname, -1);
+ g_free (longname);
+ attrib_data_orig = g_hash_table_lookup (ht_attribs, filename_casefold);
+
+ if (attrib_data_orig->dwFileAttributes != INVALID_FILE_ATTRIBUTES &&
+ success_attribs &&
+ curr_attrib_data->dwFileAttributes != attrib_data_orig->dwFileAttributes)
+ {
+ is_attribs_changed = TRUE;
+ }
+
+ if (is_attribs_changed)
+ g_hash_table_replace (ht_attribs, g_strdup (filename_casefold), curr_attrib_data);
+ else
+ g_free (curr_attrib_data);
+
+ g_free (filename_casefold);
+ return is_attribs_changed;
+ }
+ else
+ {
+ /* The monitored directory itself changed */
+ if (info->attribs.dwFileAttributes != INVALID_FILE_ATTRIBUTES &&
+ success_attribs &&
+ attrib_data.dwFileAttributes != info->attribs.dwFileAttributes)
+ {
+ is_attribs_changed = TRUE;
+ info->attribs = attrib_data;
+ }
+ return is_attribs_changed;
+ }
+ }
+}
+
+static GWin32FileMonitorInfo *
+g_win32_file_monitor_set_paths (GWin32FileMonitorPrivate *monitor,
+ const gchar *dirname,
+ const gchar *filename)
+{
+ GWin32FileMonitorInfo *info = g_new0 (GWin32FileMonitorInfo, 1);
+ gboolean updated;
+
+ if (filename != NULL)
+ {
+ /* monitor a file */
+ gchar *fullpath;
+ info->isfile = TRUE;
+ info->dirname_with_long_prefix = g_strconcat (LONGPFX, dirname, NULL);
+ info->name = g_strdup (filename);
+ fullpath = g_strconcat (dirname,
+ G_DIR_SEPARATOR_S,
+ info->name,
+ NULL);
+ updated = g_hash_table_replace (monitor->ht_watched_names,
+ g_utf8_casefold (fullpath, -1),
+ info);
+ g_free (fullpath);
+ }
+ else
+ {
+ /* monitor a directory */
+ gchar *parentdir = g_malloc0 (g_utf8_strlen (dirname, -1) + 1);
+ GHashTable *ht_attribs;
+
+ g_utf8_strncpy (parentdir,
+ dirname,
+ g_utf8_strlen (dirname, -1) -
+ g_utf8_strlen (g_utf8_strrchr (dirname, -1, G_DIR_SEPARATOR), -1));
+
+ info->isfile = FALSE;
+ info->dirname_with_long_prefix = g_strconcat (LONGPFX, parentdir, NULL);
+ info->name = g_strdup (g_utf8_strrchr (dirname, -1, G_DIR_SEPARATOR) + 1);
+
+ g_free (parentdir);
+
+ ht_attribs = g_win32_file_monitor_dir_refresh_attribs (monitor, info);
+
+ if (ht_attribs != NULL)
+ g_hash_table_replace (monitor->ht_files_attribs,
+ g_utf8_casefold (dirname, -1),
+ ht_attribs);
+ g_hash_table_replace (monitor->ht_watched_names,
+ g_utf8_casefold (dirname, -1),
+ info);
+ }
+
+ g_win32_file_monitor_set_names (info);
+ return info;
+}
+
+/* Set up call to ReadDirectoryChangesW() */
+static void
+g_win32_file_monitor_begin_monitor (GWin32FileMonitorPrivate *monitor,
+ GWin32FileMonitorInfo *info)
+{
+ gchar *fullpath;
+ wchar_t *wdirname;
+
+ DWORD notify_filter = info->isfile ?
+ (FILE_NOTIFY_CHANGE_FILE_NAME |
+ FILE_NOTIFY_CHANGE_ATTRIBUTES |
+ FILE_NOTIFY_CHANGE_SIZE) :
+ (FILE_NOTIFY_CHANGE_FILE_NAME |
+ FILE_NOTIFY_CHANGE_DIR_NAME |
+ FILE_NOTIFY_CHANGE_ATTRIBUTES |
+ FILE_NOTIFY_CHANGE_SIZE);
+
+ fullpath = g_strconcat (info->dirname_with_long_prefix + STRIP_PFX_LENGTH,
+ G_DIR_SEPARATOR_S,
+ info->name,
+ NULL);
+
+ wdirname = g_utf8_to_utf16 (info->dirname_with_long_prefix, -1, NULL, NULL, NULL);
+
+ monitor->hDirectory = CreateFileW (wdirname,
+ FILE_GENERIC_READ | FILE_GENERIC_WRITE,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
+ NULL);
+
+ g_hash_table_insert (monitor->ht_watched_dirs,
+ monitor->hDirectory,
+ g_utf8_casefold (fullpath, -1));
+
+ if (monitor->hDirectory != INVALID_HANDLE_VALUE)
+ {
+ ReadDirectoryChangesW (monitor->hDirectory,
+ monitor->file_notify_buffer,
+ monitor->buffer_allocated_bytes,
+ !info->isfile,
+ notify_filter,
+ &monitor->buffer_filled_bytes,
+ &monitor->overlapped,
+ g_win32_file_monitor_callback);
+ }
+}
+
+void
+g_win32_file_monitor_prepare (GWin32FileMonitorPrivate *monitor,
+ gchar *dirname,
+ gchar *filename,
+ gboolean isfile)
+{
+ GWin32FileMonitorInfo *info;
+ gchar *fullpath_with_long_prefix, *dirname_with_long_prefix;
+ DWORD notify_filter = isfile ?
+ (FILE_NOTIFY_CHANGE_FILE_NAME |
+ FILE_NOTIFY_CHANGE_ATTRIBUTES |
+ FILE_NOTIFY_CHANGE_SIZE) :
+ (FILE_NOTIFY_CHANGE_FILE_NAME |
+ FILE_NOTIFY_CHANGE_DIR_NAME |
+ FILE_NOTIFY_CHANGE_ATTRIBUTES |
+ FILE_NOTIFY_CHANGE_SIZE);
+
+ monitor->pfni_prev = NULL;
+
+ if (isfile)
+ info = g_win32_file_monitor_set_paths (monitor, dirname, filename);
+ else
+ info = g_win32_file_monitor_set_paths (monitor, dirname, NULL);
+
+ g_win32_file_monitor_begin_monitor (monitor, info);
+}
diff --git a/gio/win32/gwin32filemonitorutils.h b/gio/win32/gwin32filemonitorutils.h
new file mode 100644
index 000000000..090980869
--- /dev/null
+++ b/gio/win32/gwin32filemonitorutils.h
@@ -0,0 +1,89 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2015 Red Hat, Inc.
+ * Copyright (C) 2015 Chun-wei Fan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Vlad Grecescu <b100dian@gmail.com>
+ * Author: Chun-wei Fan <fanc999@yahoo.com.tw>
+ *
+ */
+
+#ifndef __G_WIN32_FILE_MONITOR_UTILS_H__
+#define __G_WIN32_FILE_MONITOR_UTILS_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include "gio/glocalfilemonitor.h"
+
+#include "gio/gfilemonitor.h"
+
+#define MAX_PATH_LONG 32767 /* Support Paths longer than MAX_PATH (260) characters */
+#define LONGPFX "\\\\?\\" /* MSDN: Append Paths with \\?\ to support over paths over 256 characters */
+#define G_DIR_SEPARATOR_W L'\\'
+#define STRIP_PFX_LENGTH g_utf8_strlen (LONGPFX, -1)
+
+G_BEGIN_DECLS
+
+typedef struct _GWin32FileMonitorPrivate GWin32FileMonitorPrivate;
+typedef struct _GWin32FileMonitorInfo GWin32FileMonitorInfo;
+typedef struct _GWin32FileMonitorDirInfo GWin32FileMonitorDirInfo;
+
+struct _GWin32FileMonitorPrivate
+{
+ OVERLAPPED overlapped;
+ DWORD buffer_allocated_bytes;
+ PFILE_NOTIFY_INFORMATION file_notify_buffer;
+ DWORD buffer_filled_bytes;
+ HANDLE hDirectory;
+ gboolean isfile;
+ GHashTable *ht_watched_dirs;
+ GHashTable *ht_watched_names;
+ GHashTable *ht_files_attribs;
+ DWORD file_attribs;
+ PFILE_NOTIFY_INFORMATION pfni_prev;
+ /* Needed in the APC where we only have this private struct */
+ GFileMonitor *self;
+ GFileMonitorSource *fms;
+};
+
+struct _GWin32FileMonitorInfo
+{
+ gchar *name;
+ gchar *dirname_with_long_prefix;
+ gchar *longname;
+ gchar *shortname;
+ gboolean isfile;
+ WIN32_FILE_ATTRIBUTE_DATA attribs;
+};
+
+void g_win32_file_monitor_prepare (GWin32FileMonitorPrivate *monitor,
+ gchar *dirname,
+ gchar *filename,
+ gboolean isfile);
+
+void g_win32_file_monitor_set_names (GWin32FileMonitorInfo *info);
+
+GHashTable* g_win32_file_monitor_dir_refresh_attribs (GWin32FileMonitorPrivate *monitor,
+ GWin32FileMonitorInfo *info);
+
+gboolean g_win32_file_monitor_check_attrib_changed (GWin32FileMonitorInfo *info,
+ gchar *filename,
+ GHashTable *ht_attribs);
+
+G_END_DECLS
+
+#endif
diff --git a/gio/win32/gwin32fsmonitorutils.c b/gio/win32/gwin32fsmonitorutils.c
deleted file mode 100644
index 6901561b8..000000000
--- a/gio/win32/gwin32fsmonitorutils.c
+++ /dev/null
@@ -1,422 +0,0 @@
-/* GIO - GLib Input, Output and Streaming Library
- *
- * Copyright (C) 2006-2007 Red Hat, Inc.
- * Copyright (C) 2015 Chun-wei Fan
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- * Author: Vlad Grecescu <b100dian@gmail.com>
- * Author: Chun-wei Fan <fanc999@yahoo.com.tw>
- *
- */
-
-#include "config.h"
-
-#include "gwin32fsmonitorutils.h"
-#include "gio/gfile.h"
-
-#include <windows.h>
-
-#define MAX_PATH_LONG 32767 /* Support Paths longer than MAX_PATH (260) characters */
-
-static gboolean
-g_win32_fs_monitor_handle_event (GWin32FSMonitorPrivate *monitor,
- gchar *filename,
- PFILE_NOTIFY_INFORMATION pfni)
-{
- GFileMonitorEvent fme;
- PFILE_NOTIFY_INFORMATION pfni_next;
- WIN32_FILE_ATTRIBUTE_DATA attrib_data = {0, };
- gchar *renamed_file = NULL;
-
- switch (pfni->Action)
- {
- case FILE_ACTION_ADDED:
- fme = G_FILE_MONITOR_EVENT_CREATED;
- break;
-
- case FILE_ACTION_REMOVED:
- fme = G_FILE_MONITOR_EVENT_DELETED;
- break;
-
- case FILE_ACTION_MODIFIED:
- {
- gboolean success_attribs = GetFileAttributesExW (monitor->wfullpath_with_long_prefix,
- GetFileExInfoStandard,
- &attrib_data);
-
- if (monitor->file_attribs != INVALID_FILE_ATTRIBUTES &&
- success_attribs &&
- attrib_data.dwFileAttributes != monitor->file_attribs)
- fme = G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
- else
- fme = G_FILE_MONITOR_EVENT_CHANGED;
-
- monitor->file_attribs = attrib_data.dwFileAttributes;
- }
- break;
-
- case FILE_ACTION_RENAMED_OLD_NAME:
- if (pfni->NextEntryOffset != 0)
- {
- /* If the file was renamed in the same directory, we would get a
- * FILE_ACTION_RENAMED_NEW_NAME action in the next FILE_NOTIFY_INFORMATION
- * structure.
- */
- glong file_name_len = 0;
-
- pfni_next = (PFILE_NOTIFY_INFORMATION) ((BYTE*)pfni + pfni->NextEntryOffset);
- renamed_file = g_utf16_to_utf8 (pfni_next->FileName, pfni_next->FileNameLength / sizeof(WCHAR), NULL, &file_name_len, NULL);
- if (pfni_next->Action == FILE_ACTION_RENAMED_NEW_NAME)
- fme = G_FILE_MONITOR_EVENT_RENAMED;
- else
- fme = G_FILE_MONITOR_EVENT_MOVED_OUT;
- }
- else
- fme = G_FILE_MONITOR_EVENT_MOVED_OUT;
- break;
-
- case FILE_ACTION_RENAMED_NEW_NAME:
- if (monitor->pfni_prev != NULL &&
- monitor->pfni_prev->Action == FILE_ACTION_RENAMED_OLD_NAME)
- {
- /* don't bother sending events, was already sent (rename) */
- fme = -1;
- }
- else
- fme = G_FILE_MONITOR_EVENT_MOVED_IN;
- break;
-
- default:
- /* The possible Windows actions are all above, so shouldn't get here */
- g_assert_not_reached ();
- break;
- }
- if (fme != -1)
- return g_file_monitor_source_handle_event (monitor->fms,
- fme,
- filename,
- renamed_file,
- NULL,
- g_get_monotonic_time ());
- else
- return FALSE;
-}
-
-
-static void CALLBACK
-g_win32_fs_monitor_callback (DWORD error,
- DWORD nBytes,
- LPOVERLAPPED lpOverlapped)
-{
- gulong offset;
- PFILE_NOTIFY_INFORMATION pfile_notify_walker;
- GWin32FSMonitorPrivate *monitor = (GWin32FSMonitorPrivate *) lpOverlapped;
-
- DWORD notify_filter = monitor->isfile ?
- (FILE_NOTIFY_CHANGE_FILE_NAME |
- FILE_NOTIFY_CHANGE_ATTRIBUTES |
- FILE_NOTIFY_CHANGE_SIZE) :
- (FILE_NOTIFY_CHANGE_FILE_NAME |
- FILE_NOTIFY_CHANGE_DIR_NAME |
- FILE_NOTIFY_CHANGE_ATTRIBUTES |
- FILE_NOTIFY_CHANGE_SIZE);
-
- /* If monitor->self is NULL the GWin32FileMonitor object has been destroyed. */
- if (monitor->self == NULL ||
- g_file_monitor_is_cancelled (monitor->self) ||
- monitor->file_notify_buffer == NULL)
- {
- g_free (monitor->file_notify_buffer);
- g_free (monitor);
- return;
- }
-
- offset = 0;
-
- do
- {
- pfile_notify_walker = (PFILE_NOTIFY_INFORMATION)((BYTE*)monitor->file_notify_buffer + offset);
- if (pfile_notify_walker->Action > 0)
- {
- glong file_name_len;
- gchar *changed_file;
-
- changed_file = g_utf16_to_utf8 (pfile_notify_walker->FileName, pfile_notify_walker->FileNameLength / sizeof(WCHAR), NULL, &file_name_len, NULL);
-
- if (monitor->isfile)
- {
- gint long_filename_length = wcslen (monitor->wfilename_long);
- gint short_filename_length = wcslen (monitor->wfilename_short);
- enum GWin32FileMonitorFileAlias alias_state;
-
- /* If monitoring a file, check that the changed file
- * in the directory matches the file that is to be monitored
- * We need to check both the long and short file names for the same file.
- *
- * We need to send in the name of the monitored file, not its long (or short) variant,
- * if they exist.
- */
-
- if (_wcsnicmp (pfile_notify_walker->FileName,
- monitor->wfilename_long,
- long_filename_length) == 0)
- {
- if (_wcsnicmp (pfile_notify_walker->FileName,
- monitor->wfilename_short,
- short_filename_length) == 0)
- {
- alias_state = G_WIN32_FILE_MONITOR_NO_ALIAS;
- }
- else
- alias_state = G_WIN32_FILE_MONITOR_LONG_FILENAME;
- }
- else if (_wcsnicmp (pfile_notify_walker->FileName,
- monitor->wfilename_short,
- short_filename_length) == 0)
- {
- alias_state = G_WIN32_FILE_MONITOR_SHORT_FILENAME;
- }
- else
- alias_state = G_WIN32_FILE_MONITOR_NO_MATCH_FOUND;
-
- if (alias_state != G_WIN32_FILE_MONITOR_NO_MATCH_FOUND)
- {
- wchar_t *monitored_file_w;
- gchar *monitored_file;
-
- switch (alias_state)
- {
- case G_WIN32_FILE_MONITOR_NO_ALIAS:
- monitored_file = g_strdup (changed_file);
- break;
- case G_WIN32_FILE_MONITOR_LONG_FILENAME:
- case G_WIN32_FILE_MONITOR_SHORT_FILENAME:
- monitored_file_w = wcsrchr (monitor->wfullpath_with_long_prefix, L'\\');
- monitored_file = g_utf16_to_utf8 (monitored_file_w + 1, -1, NULL, NULL, NULL);
- break;
- default:
- g_assert_not_reached ();
- break;
- }
-
- g_win32_fs_monitor_handle_event (monitor, monitored_file, pfile_notify_walker);
- g_free (monitored_file);
- }
- }
- else
- g_win32_fs_monitor_handle_event (monitor, changed_file, pfile_notify_walker);
-
- g_free (changed_file);
- }
- monitor->pfni_prev = pfile_notify_walker;
- offset += pfile_notify_walker->NextEntryOffset;
- }
- while (pfile_notify_walker->NextEntryOffset);
-
- ReadDirectoryChangesW (monitor->hDirectory,
- monitor->file_notify_buffer,
- monitor->buffer_allocated_bytes,
- FALSE,
- notify_filter,
- &monitor->buffer_filled_bytes,
- &monitor->overlapped,
- g_win32_fs_monitor_callback);
-}
-
-void
-g_win32_fs_monitor_init (GWin32FSMonitorPrivate *monitor,
- gchar *dirname,
- gchar *filename,
- gboolean isfile)
-{
- wchar_t *wdirname_with_long_prefix = NULL;
- const gchar LONGPFX[] = "\\\\?\\";
- gchar *fullpath_with_long_prefix, *dirname_with_long_prefix;
- DWORD notify_filter = isfile ?
- (FILE_NOTIFY_CHANGE_FILE_NAME |
- FILE_NOTIFY_CHANGE_ATTRIBUTES |
- FILE_NOTIFY_CHANGE_SIZE) :
- (FILE_NOTIFY_CHANGE_FILE_NAME |
- FILE_NOTIFY_CHANGE_DIR_NAME |
- FILE_NOTIFY_CHANGE_ATTRIBUTES |
- FILE_NOTIFY_CHANGE_SIZE);
-
- gboolean success_attribs;
- WIN32_FILE_ATTRIBUTE_DATA attrib_data = {0, };
-
-
- if (dirname != NULL)
- {
- dirname_with_long_prefix = g_strconcat (LONGPFX, dirname, NULL);
- wdirname_with_long_prefix = g_utf8_to_utf16 (dirname_with_long_prefix, -1, NULL, NULL, NULL);
-
- if (isfile)
- {
- gchar *fullpath;
- wchar_t wlongname[MAX_PATH_LONG];
- wchar_t wshortname[MAX_PATH_LONG];
- wchar_t *wfullpath, *wbasename_long, *wbasename_short;
-
- fullpath = g_build_filename (dirname, filename, NULL);
- fullpath_with_long_prefix = g_strconcat (LONGPFX, fullpath, NULL);
-
- wfullpath = g_utf8_to_utf16 (fullpath, -1, NULL, NULL, NULL);
-
- monitor->wfullpath_with_long_prefix =
- g_utf8_to_utf16 (fullpath_with_long_prefix, -1, NULL, NULL, NULL);
-
- /* ReadDirectoryChangesW() can return the normal filename or the
- * "8.3" format filename, so we need to keep track of both these names
- * so that we can check against them later when it returns
- */
- if (GetLongPathNameW (monitor->wfullpath_with_long_prefix, wlongname, MAX_PATH_LONG) == 0)
- {
- wbasename_long = wcsrchr (monitor->wfullpath_with_long_prefix, L'\\');
- monitor->wfilename_long = wbasename_long != NULL ?
- wcsdup (wbasename_long + 1) :
- wcsdup (wfullpath);
- }
- else
- {
- wbasename_long = wcsrchr (wlongname, L'\\');
- monitor->wfilename_long = wbasename_long != NULL ?
- wcsdup (wbasename_long + 1) :
- wcsdup (wlongname);
-
- }
-
- if (GetShortPathNameW (monitor->wfullpath_with_long_prefix, wshortname, MAX_PATH_LONG) == 0)
- {
- wbasename_short = wcsrchr (monitor->wfullpath_with_long_prefix, L'\\');
- monitor->wfilename_short = wbasename_short != NULL ?
- wcsdup (wbasename_short + 1) :
- wcsdup (wfullpath);
- }
- else
- {
- wbasename_short = wcsrchr (wshortname, L'\\');
- monitor->wfilename_short = wbasename_short != NULL ?
- wcsdup (wbasename_short + 1) :
- wcsdup (wshortname);
- }
-
- g_free (fullpath);
- }
- else
- {
- monitor->wfilename_short = NULL;
- monitor->wfilename_long = NULL;
- monitor->wfullpath_with_long_prefix = g_utf8_to_utf16 (dirname_with_long_prefix, -1, NULL, NULL, NULL);
- }
-
- monitor->isfile = isfile;
- }
- else
- {
- dirname_with_long_prefix = g_strconcat (LONGPFX, filename, NULL);
- monitor->wfullpath_with_long_prefix = g_utf8_to_utf16 (dirname_with_long_prefix, -1, NULL, NULL, NULL);
- monitor->wfilename_long = NULL;
- monitor->wfilename_short = NULL;
- monitor->isfile = FALSE;
- }
-
- success_attribs = GetFileAttributesExW (monitor->wfullpath_with_long_prefix,
- GetFileExInfoStandard,
- &attrib_data);
- if (success_attribs)
- monitor->file_attribs = attrib_data.dwFileAttributes; /* Store up original attributes */
- else
- monitor->file_attribs = INVALID_FILE_ATTRIBUTES;
- monitor->pfni_prev = NULL;
- monitor->hDirectory = CreateFileW (wdirname_with_long_prefix != NULL ? wdirname_with_long_prefix : monitor->wfullpath_with_long_prefix,
- FILE_GENERIC_READ | FILE_GENERIC_WRITE,
- FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
- NULL);
-
- if (wdirname_with_long_prefix != NULL)
- g_free (wdirname_with_long_prefix);
- g_free (dirname_with_long_prefix);
-
- if (monitor->hDirectory != INVALID_HANDLE_VALUE)
- {
- ReadDirectoryChangesW (monitor->hDirectory,
- monitor->file_notify_buffer,
- monitor->buffer_allocated_bytes,
- FALSE,
- notify_filter,
- &monitor->buffer_filled_bytes,
- &monitor->overlapped,
- g_win32_fs_monitor_callback);
- }
-}
-
-GWin32FSMonitorPrivate* g_win32_fs_monitor_create (gboolean isfile)
-{
- GWin32FSMonitorPrivate* monitor = (GWin32FSMonitorPrivate*) g_new0 (GWin32FSMonitorPrivate, 1);
- g_assert (monitor != 0);
-
- monitor->buffer_allocated_bytes = 32784;
- monitor->file_notify_buffer = g_new0 (FILE_NOTIFY_INFORMATION, monitor->buffer_allocated_bytes);
- g_assert (monitor->file_notify_buffer);
-
- return monitor;
-}
-
-void g_win32_fs_monitor_finalize (GWin32FSMonitorPrivate *monitor)
-{
- if (monitor->hDirectory == INVALID_HANDLE_VALUE)
- {
- /* If we don't have a directory handle we can free
- * monitor->file_notify_buffer and monitor here. The
- * callback won't be called obviously any more (and presumably
- * never has been called).
- */
- g_free (monitor->file_notify_buffer);
- monitor->file_notify_buffer = NULL;
- g_free (monitor);
- }
- else
- {
- /* If we have a directory handle, the OVERLAPPED struct is
- * passed once more to the callback as a result of the
- * CloseHandle() done in the cancel method, so monitor has to
- * be kept around. The GWin32DirectoryMonitor object is
- * disappearing, so can't leave a pointer to it in
- * monitor->self.
- */
- monitor->self = NULL;
- }
- g_free (monitor->wfullpath_with_long_prefix);
- if (monitor->wfilename_long != NULL)
- g_free (monitor->wfilename_long);
- if (monitor->wfilename_short != NULL)
- g_free (monitor->wfilename_short);
-}
-
-void g_win32_fs_monitor_close_handle (GWin32FSMonitorPrivate *monitor)
-{
- /* This triggers a last callback() with nBytes==0. */
-
- /* Actually I am not so sure about that, it seems to trigger a last
- * callback allright, but the way to recognize that it is the final
- * one is not to check for nBytes==0, I think that was a
- * misunderstanding.
- */
- if (monitor->hDirectory != INVALID_HANDLE_VALUE)
- CloseHandle (monitor->hDirectory);
-}
diff --git a/gio/win32/gwin32fsmonitorutils.h b/gio/win32/gwin32fsmonitorutils.h
deleted file mode 100644
index 0606d671d..000000000
--- a/gio/win32/gwin32fsmonitorutils.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* GIO - GLib Input, Output and Streaming Library
- *
- * Copyright (C) 2006-2007 Red Hat, Inc.
- * Copyright (C) 2014 Chun-wei Fan
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- * Author: Vlad Grecescu <b100dian@gmail.com>
- * Author: Chun-wei Fan <fanc999@yahoo.com.tw>
- *
- */
-
-#ifndef __G_WIN32_FS_MONITOR_UTILS_H__
-#define __G_WIN32_FS_MONITOR_UTILS_H__
-
-#include <windows.h>
-
-#include "gio/glocalfilemonitor.h"
-
-#include "gio/gfilemonitor.h"
-
-G_BEGIN_DECLS
-
-typedef struct _GWin32FSMonitorPrivate GWin32FSMonitorPrivate;
-
-struct _GWin32FSMonitorPrivate
-{
- OVERLAPPED overlapped;
- DWORD buffer_allocated_bytes;
- PFILE_NOTIFY_INFORMATION file_notify_buffer;
- DWORD buffer_filled_bytes;
- HANDLE hDirectory;
- gboolean isfile;
- wchar_t *wfullpath_with_long_prefix;
- wchar_t *wfilename_short;
- wchar_t *wfilename_long;
- DWORD file_attribs;
- PFILE_NOTIFY_INFORMATION pfni_prev;
- /* Needed in the APC where we only have this private struct */
- GFileMonitor *self;
- GFileMonitorSource *fms;
-};
-
-enum GWin32FileMonitorFileAlias
-{
- G_WIN32_FILE_MONITOR_NO_ALIAS = 0,
- G_WIN32_FILE_MONITOR_LONG_FILENAME,
- G_WIN32_FILE_MONITOR_SHORT_FILENAME,
- G_WIN32_FILE_MONITOR_NO_MATCH_FOUND
-};
-
-GWin32FSMonitorPrivate* g_win32_fs_monitor_create (gboolean isfile);
-
-void g_win32_fs_monitor_init (GWin32FSMonitorPrivate *monitor,
- gchar *dirname,
- gchar *filename,
- gboolean isfile);
-
-void g_win32_fs_monitor_finalize (GWin32FSMonitorPrivate *monitor);
-
-void g_win32_fs_monitor_close_handle (GWin32FSMonitorPrivate *monitor);
-
-G_END_DECLS
-
-#endif