diff --git a/gio/Makefile.am b/gio/Makefile.am index a84830b91..44ade08f5 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -313,11 +313,12 @@ win32_actual_sources = \ win32_more_sources_for_vcproj = \ gwin32appinfo.c \ gregistrysettingsbackend.c \ - win32/gwin32directorymonitor.c \ win32/gwinhttpfile.c \ win32/gwinhttpfileinputstream.c \ win32/gwinhttpfileoutputstream.c \ - win32/gwinhttpvfs.c + win32/gwinhttpvfs.c \ + win32/gwin32fsmonitorutils.c \ + win32/gwin32filemonitor.c if OS_WIN32 appinfo_sources += gwin32appinfo.c gwin32appinfo.h diff --git a/gio/win32/Makefile.am b/gio/win32/Makefile.am index a48c68f03..2fc10f65e 100644 --- a/gio/win32/Makefile.am +++ b/gio/win32/Makefile.am @@ -3,8 +3,10 @@ include $(top_srcdir)/glib.mk noinst_LTLIBRARIES += libgiowin32.la libgiowin32_la_SOURCES = \ - gwin32directorymonitor.c \ - gwin32directorymonitor.h \ + gwin32fsmonitorutils.c \ + gwin32fsmonitorutils.h \ + gwin32filemonitor.c \ + gwin32filemonitor.h \ gwinhttpvfs.c \ gwinhttpvfs.h \ gwinhttpfile.c \ diff --git a/gio/win32/gwin32directorymonitor.c b/gio/win32/gwin32directorymonitor.c deleted file mode 100644 index b5f192e03..000000000 --- a/gio/win32/gwin32directorymonitor.c +++ /dev/null @@ -1,244 +0,0 @@ -/* GIO - GLib Input, Output and Streaming Library - * - * Copyright (C) 2006-2007 Red Hat, Inc. - * - * 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 . - * - * Author: Vlad Grecescu - * - */ - -#include "config.h" -#include "gwin32directorymonitor.h" -#include - -G_DEFINE_TYPE_WITH_CODE (GWin32DirectoryMonitor, - g_win32_directory_monitor, - G_TYPE_LOCAL_DIRECTORY_MONITOR, - g_io_extension_point_implement (G_LOCAL_DIRECTORY_MONITOR_EXTENSION_POINT_NAME, - g_define_type_id, - "readdirectorychanges", - 20)) - -struct _GWin32DirectoryMonitorPrivate { - OVERLAPPED overlapped; - DWORD buffer_allocated_bytes; - gchar *file_notify_buffer; - DWORD buffer_filled_bytes; - HANDLE hDirectory; - /* Needed in the APC where we only have this private struct */ - GFileMonitor *self; -}; - -static void g_win32_directory_monitor_finalize (GObject *base); -static gboolean g_win32_directory_monitor_cancel (GFileMonitor *base); - -static GObject *g_win32_directory_monitor_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_properties); - -static gboolean -g_win32_directory_monitor_is_supported (void) -{ - return TRUE; -} - -static void -g_win32_directory_monitor_finalize (GObject *base) -{ - GWin32DirectoryMonitor *self; - self = G_WIN32_DIRECTORY_MONITOR (base); - - if (self->priv->hDirectory == INVALID_HANDLE_VALUE) - { - /* If we don't have a directory handle we can free - * self->priv->file_notify_buffer and self->priv here. The - * callback won't be called obviously any more (and presumably - * never has been called). - */ - g_free (self->priv->file_notify_buffer); - self->priv->file_notify_buffer = NULL; - g_free (self->priv); - } - 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 self->priv has to - * be kept around. The GWin32DirectoryMonitor object is - * disappearing, so can't leave a pointer to it in - * self->priv->self. - */ - self->priv->self = NULL; - } - - if (G_OBJECT_CLASS (g_win32_directory_monitor_parent_class)->finalize) - (*G_OBJECT_CLASS (g_win32_directory_monitor_parent_class)->finalize) (base); -} - -static gboolean -g_win32_directory_monitor_cancel (GFileMonitor *base) -{ - GWin32DirectoryMonitor *self; - self = G_WIN32_DIRECTORY_MONITOR (base); - - /* 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 (self->priv->hDirectory != INVALID_HANDLE_VALUE) - CloseHandle (self->priv->hDirectory); - - if (G_FILE_MONITOR_CLASS (g_win32_directory_monitor_parent_class)->cancel) - (*G_FILE_MONITOR_CLASS (g_win32_directory_monitor_parent_class)->cancel) (base); - return TRUE; -} - -static void CALLBACK -g_win32_directory_monitor_callback (DWORD error, - DWORD nBytes, - LPOVERLAPPED lpOverlapped) -{ - gulong offset; - PFILE_NOTIFY_INFORMATION pfile_notify_walker; - glong file_name_len; - gchar *file_name; - gchar *path; - GFile *file; - GWin32DirectoryMonitorPrivate *priv = (GWin32DirectoryMonitorPrivate *) lpOverlapped; - - static GFileMonitorEvent events[] = - { - 0, - G_FILE_MONITOR_EVENT_CREATED, /* FILE_ACTION_ADDED */ - G_FILE_MONITOR_EVENT_DELETED, /* FILE_ACTION_REMOVED */ - G_FILE_MONITOR_EVENT_CHANGED, /* FILE_ACTION_MODIFIED */ - G_FILE_MONITOR_EVENT_DELETED, /* FILE_ACTION_RENAMED_OLD_NAME */ - G_FILE_MONITOR_EVENT_CREATED, /* FILE_ACTION_RENAMED_NEW_NAME */ - }; - - /* If priv->self is NULL the GWin32DirectoryMonitor object has been destroyed. */ - if (priv->self == NULL || - g_file_monitor_is_cancelled (priv->self) || - priv->file_notify_buffer == NULL) - { - g_free (priv->file_notify_buffer); - g_free (priv); - return; - } - - offset = 0; - do { - pfile_notify_walker = (PFILE_NOTIFY_INFORMATION)(priv->file_notify_buffer + offset); - if (pfile_notify_walker->Action > 0) - { - file_name = g_utf16_to_utf8 (pfile_notify_walker->FileName, pfile_notify_walker->FileNameLength / sizeof(WCHAR), NULL, &file_name_len, NULL); - path = g_build_filename(G_LOCAL_DIRECTORY_MONITOR (priv->self)->dirname, file_name, NULL); - file = g_file_new_for_path (path); - g_file_monitor_emit_event (priv->self, file, NULL, events [pfile_notify_walker->Action]); - g_object_unref (file); - g_free (path); - g_free (file_name); - } - offset += pfile_notify_walker->NextEntryOffset; - } while (pfile_notify_walker->NextEntryOffset); - - ReadDirectoryChangesW (priv->hDirectory, - (gpointer)priv->file_notify_buffer, - priv->buffer_allocated_bytes, - FALSE, - FILE_NOTIFY_CHANGE_FILE_NAME | - FILE_NOTIFY_CHANGE_DIR_NAME | - FILE_NOTIFY_CHANGE_ATTRIBUTES | - FILE_NOTIFY_CHANGE_SIZE, - &priv->buffer_filled_bytes, - &priv->overlapped, - g_win32_directory_monitor_callback); -} - -static GObject * -g_win32_directory_monitor_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_properties) { - GObject *obj; - GWin32DirectoryMonitorClass *klass; - GObjectClass *parent_class; - GWin32DirectoryMonitor *self; - wchar_t *wdirname; - - klass = G_WIN32_DIRECTORY_MONITOR_CLASS (g_type_class_peek (G_TYPE_WIN32_DIRECTORY_MONITOR)); - parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); - obj = parent_class->constructor (type, n_construct_properties, construct_properties); - self = G_WIN32_DIRECTORY_MONITOR (obj); - wdirname = g_utf8_to_utf16 (G_LOCAL_DIRECTORY_MONITOR (obj)->dirname, -1, NULL, NULL, NULL); - - self->priv->hDirectory = CreateFileW (wdirname, - FILE_LIST_DIRECTORY, - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, - NULL); - g_free (wdirname); - if (self->priv->hDirectory == INVALID_HANDLE_VALUE) - { - /* Ignore errors */ - return obj; - } - - ReadDirectoryChangesW (self->priv->hDirectory, - (gpointer)self->priv->file_notify_buffer, - self->priv->buffer_allocated_bytes, - FALSE, - FILE_NOTIFY_CHANGE_FILE_NAME | - FILE_NOTIFY_CHANGE_DIR_NAME | - FILE_NOTIFY_CHANGE_ATTRIBUTES | - FILE_NOTIFY_CHANGE_SIZE, - &self->priv->buffer_filled_bytes, - &self->priv->overlapped, - g_win32_directory_monitor_callback); - /* Ignore errors */ - - return obj; -} - -static void -g_win32_directory_monitor_class_init (GWin32DirectoryMonitorClass *klass) -{ - g_win32_directory_monitor_parent_class = g_type_class_peek_parent (klass); - - G_OBJECT_CLASS (klass)->constructor = g_win32_directory_monitor_constructor; - G_OBJECT_CLASS (klass)->finalize = g_win32_directory_monitor_finalize; - G_FILE_MONITOR_CLASS (klass)->cancel = g_win32_directory_monitor_cancel; - - G_LOCAL_DIRECTORY_MONITOR_CLASS (klass)->mount_notify = FALSE; - G_LOCAL_DIRECTORY_MONITOR_CLASS (klass)->is_supported = g_win32_directory_monitor_is_supported; -} - -static void -g_win32_directory_monitor_init (GWin32DirectoryMonitor *self) -{ - self->priv = (GWin32DirectoryMonitorPrivate*)g_new0 (GWin32DirectoryMonitorPrivate, 1); - g_assert (self->priv != 0); - - self->priv->buffer_allocated_bytes = 32768; - self->priv->file_notify_buffer = g_new0 (gchar, self->priv->buffer_allocated_bytes); - g_assert (self->priv->file_notify_buffer); - - self->priv->self = G_FILE_MONITOR (self); -} diff --git a/gio/win32/gwin32directorymonitor.h b/gio/win32/gwin32directorymonitor.h deleted file mode 100644 index 3e0774879..000000000 --- a/gio/win32/gwin32directorymonitor.h +++ /dev/null @@ -1,60 +0,0 @@ -/* GIO - GLib Input, Output and Streaming Library - * - * Copyright (C) 2006-2007 Red Hat, Inc. - * - * 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 . - * - * Author: Vlad Grecescu - * - */ -#ifndef __G_WIN32_DIRECTORY_MONITOR_H__ -#define __G_WIN32_DIRECTORY_MONITOR_H__ - -#include -#include -#include -#include -#include - -#include "gio/glocaldirectorymonitor.h" -#include "gio/giomodule.h" - -G_BEGIN_DECLS - - -#define G_TYPE_WIN32_DIRECTORY_MONITOR (g_win32_directory_monitor_get_type ()) -#define G_WIN32_DIRECTORY_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_DIRECTORY_MONITOR, GWin32DirectoryMonitor)) -#define G_WIN32_DIRECTORY_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_WIN32_DIRECTORY_MONITOR, GWin32DirectoryMonitorClass)) -#define G_IS_WIN32_DIRECTORY_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_WIN32_DIRECTORY_MONITOR)) -#define G_IS_WIN32_DIRECTORY_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_WIN32_DIRECTORY_MONITOR)) -#define G_WIN32_DIRECTORY_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_WIN32_DIRECTORY_MONITOR, GWin32DirectoryMonitorClass)) - -typedef struct _GWin32DirectoryMonitor GWin32DirectoryMonitor; -typedef struct _GWin32DirectoryMonitorClass GWin32DirectoryMonitorClass; -typedef struct _GWin32DirectoryMonitorPrivate GWin32DirectoryMonitorPrivate; - -struct _GWin32DirectoryMonitor { - GLocalDirectoryMonitor parent_instance; - GWin32DirectoryMonitorPrivate * priv; -}; -struct _GWin32DirectoryMonitorClass { - GLocalDirectoryMonitorClass parent_class; -}; - -GType g_win32_directory_monitor_get_type (void); -void g_win32_directory_monitor_register (GIOModule *module); - -G_END_DECLS - -#endif /* __G_WIN32_DIRECTORY_MONITOR_H__ */ diff --git a/gio/win32/gwin32filemonitor.c b/gio/win32/gwin32filemonitor.c new file mode 100644 index 000000000..1b93b82e4 --- /dev/null +++ b/gio/win32/gwin32filemonitor.c @@ -0,0 +1,105 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * 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 . + * + * Author: Vlad Grecescu + * Author: Chun-wei Fan + * + */ + +#include "config.h" + +#include "gwin32filemonitor.h" +#include "gwin32fsmonitorutils.h" + +#include + +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_start (GLocalFileMonitor *monitor, + const gchar *dirname, + const gchar *basename, + const gchar *filename, + GFileMonitorSource *source) +{ + GWin32FileMonitor *win32_monitor = G_WIN32_FILE_MONITOR (monitor); + gboolean isfile = (filename == NULL && basename == NULL) ? FALSE : TRUE; + + win32_monitor->priv->fms = source; + + if (isfile) + if (basename != NULL) + g_win32_fs_monitor_init (win32_monitor->priv, dirname, basename, TRUE); + else + g_win32_fs_monitor_init (win32_monitor->priv, NULL, filename, TRUE); + else + g_win32_fs_monitor_init (win32_monitor->priv, dirname, NULL, FALSE); +} + +static gboolean +g_win32_file_monitor_is_supported (void) +{ + return TRUE; +} + +static void +g_win32_file_monitor_init (GWin32FileMonitor* monitor) +{ + monitor->priv = g_win32_fs_monitor_create (TRUE); + + monitor->priv->self = G_FILE_MONITOR (monitor); +} + +static void +g_win32_file_monitor_finalize (GObject *base) +{ + GWin32FileMonitor *monitor; + monitor = G_WIN32_FILE_MONITOR (base); + + g_win32_fs_monitor_finalize (monitor->priv); + + if (G_OBJECT_CLASS (g_win32_file_monitor_parent_class)->finalize) + (*G_OBJECT_CLASS (g_win32_file_monitor_parent_class)->finalize) (base); +} + +static gboolean +g_win32_file_monitor_cancel (GFileMonitor* monitor) +{ + GWin32FileMonitor *file_monitor; + file_monitor = G_WIN32_FILE_MONITOR (monitor); + + g_win32_fs_monitor_close_handle (file_monitor->priv); + + return TRUE; +} + +static void +g_win32_file_monitor_class_init (GWin32FileMonitorClass *klass) +{ + GObjectClass* gobject_class = G_OBJECT_CLASS (klass); + GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (klass); + GLocalFileMonitorClass *local_file_monitor_class = G_LOCAL_FILE_MONITOR_CLASS (klass); + + gobject_class->finalize = g_win32_file_monitor_finalize; + file_monitor_class->cancel = g_win32_file_monitor_cancel; + + local_file_monitor_class->is_supported = g_win32_file_monitor_is_supported; + local_file_monitor_class->start = g_win32_file_monitor_start; +} diff --git a/gio/win32/gwin32filemonitor.h b/gio/win32/gwin32filemonitor.h new file mode 100644 index 000000000..5aef2ba3f --- /dev/null +++ b/gio/win32/gwin32filemonitor.h @@ -0,0 +1,62 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * 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 . + * + * Author: Chun-wei Fan + * + */ +#ifndef __G_WIN32_FILE_MONITOR_H__ +#define __G_WIN32_FILE_MONITOR_H__ + +#include +#include +#include +#include +#include + +#include "gio/gfilemonitor.h" +#include "gio/glocalfilemonitor.h" +#include "gio/giomodule.h" + +#include "gwin32fsmonitorutils.h" + +G_BEGIN_DECLS + +#define G_TYPE_WIN32_FILE_MONITOR (g_win32_file_monitor_get_type ()) +#define G_WIN32_FILE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_FILE_MONITOR, GWin32FileMonitor)) +#define G_WIN32_FILE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_WIN32_FILE_MONITOR, GWin32FileMonitorClass)) +#define G_IS_WIN32_FILE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_WIN32_FILE_MONITOR)) +#define G_IS_WIN32_FILE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_WIN32_FILE_MONITOR)) +#define G_WIN32_FILE_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_WIN32_FILE_MONITOR, GWin32FileMonitorClass)) + +typedef struct _GWin32FileMonitor GWin32FileMonitor; +typedef struct _GWin32FileMonitorClass GWin32FileMonitorClass; +typedef struct _GWin32FileMonitorPrivate GWin32FileMonitorPrivate; + +struct _GWin32FileMonitor { + GLocalFileMonitor parent_instance; + GWin32FSMonitorPrivate * priv; +}; +struct _GWin32FileMonitorClass { + GLocalFileMonitorClass parent_class; +}; + +GType g_win32_file_monitor_get_type (void); +void g_win32_file_monitor_register (GIOModule *module); + +G_END_DECLS + +#endif /* __G_WIN32_FILE_MONITOR_H__ */ diff --git a/gio/win32/gwin32fsmonitorutils.c b/gio/win32/gwin32fsmonitorutils.c new file mode 100644 index 000000000..6901561b8 --- /dev/null +++ b/gio/win32/gwin32fsmonitorutils.c @@ -0,0 +1,422 @@ +/* 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 . + * + * Author: Vlad Grecescu + * Author: Chun-wei Fan + * + */ + +#include "config.h" + +#include "gwin32fsmonitorutils.h" +#include "gio/gfile.h" + +#include + +#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 new file mode 100644 index 000000000..0606d671d --- /dev/null +++ b/gio/win32/gwin32fsmonitorutils.h @@ -0,0 +1,76 @@ +/* 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 . + * + * Author: Vlad Grecescu + * Author: Chun-wei Fan + * + */ + +#ifndef __G_WIN32_FS_MONITOR_UTILS_H__ +#define __G_WIN32_FS_MONITOR_UTILS_H__ + +#include + +#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