From 707bc4a40f48f1a361071a8b8b13d96743e89561 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Wed, 18 Nov 2009 17:59:14 -0600 Subject: [PATCH] gio + inotify support for hard links Add a new GFileMonitorFlag: G_FILE_MONITOR_WATCH_HARD_LINKS. When set, changes made to the file via another hard link will be detected. Implement the new flag for the inotify backend. https://bugzilla.gnome.org/show_bug.cgi?id=532815 --- gio/gfile.c | 19 +- gio/gioenums.h | 9 +- gio/inotify/ginotifydirectorymonitor.c | 2 +- gio/inotify/ginotifyfilemonitor.c | 3 + gio/inotify/inotify-helper.c | 20 +- gio/inotify/inotify-path.c | 325 +++++++++++++++++++++---- gio/inotify/inotify-path.h | 2 +- gio/inotify/inotify-sub.c | 4 +- gio/inotify/inotify-sub.h | 11 +- 9 files changed, 328 insertions(+), 67 deletions(-) diff --git a/gio/gfile.c b/gio/gfile.c index ba8c4da4b..cd02b66e6 100644 --- a/gio/gfile.c +++ b/gio/gfile.c @@ -4845,6 +4845,12 @@ g_file_eject_mountable_with_operation_finish (GFile *file, * triggering the cancellable object from another thread. If the operation * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. * + * It does not make sense for @flags to contain + * %G_FILE_MONITOR_WATCH_HARD_LINKS, since hard links can not be made to + * directories. It is not possible to monitor all the files in a + * directory for changes made via hard links; if you want to do this then + * you must register individual watches with g_file_monitor(). + * * Virtual: monitor_dir * Returns: (transfer full): a #GFileMonitor for the given @file, * or %NULL on error. @@ -4859,6 +4865,7 @@ g_file_monitor_directory (GFile *file, GFileIface *iface; g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (~flags & G_FILE_MONITOR_WATCH_HARD_LINKS, NULL); if (g_cancellable_set_error_if_cancelled (cancellable, error)) return NULL; @@ -4891,6 +4898,14 @@ g_file_monitor_directory (GFile *file, * triggering the cancellable object from another thread. If the operation * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. * + * If @flags contains %G_FILE_MONITOR_WATCH_HARD_LINKS then the monitor + * will also attempt to report changes made to the file via another + * filename (ie, a hard link). Without this flag, you can only rely on + * changes made through the filename contained in @file to be + * reported. Using this flag may result in an increase in resource + * usage, and may not have any effect depending on the #GFileMonitor + * backend and/or filesystem type. + * * Returns: (transfer full): a #GFileMonitor for the given @file, * or %NULL on error. * Free the returned object with g_object_unref(). @@ -4951,7 +4966,9 @@ g_file_monitor (GFile *file, GError **error) { if (g_file_query_file_type (file, 0, cancellable) == G_FILE_TYPE_DIRECTORY) - return g_file_monitor_directory (file, flags, cancellable, error); + return g_file_monitor_directory (file, + flags & ~G_FILE_MONITOR_WATCH_HARD_LINKS, + cancellable, error); else return g_file_monitor_file (file, flags, cancellable, error); } diff --git a/gio/gioenums.h b/gio/gioenums.h index 39c43c06d..9842d3457 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -308,13 +308,16 @@ typedef enum { * event instead (NB: not supported on all backends; the default * behaviour -without specifying this flag- is to send single DELETED * and CREATED events). + * @G_FILE_MONITOR_WATCH_HARD_LINKS: Watch for changes to the file made + * via another hard link. * * Flags used to set what a #GFileMonitor will watch for. */ typedef enum { - G_FILE_MONITOR_NONE = 0, - G_FILE_MONITOR_WATCH_MOUNTS = (1 << 0), - G_FILE_MONITOR_SEND_MOVED = (1 << 1) + G_FILE_MONITOR_NONE = 0, + G_FILE_MONITOR_WATCH_MOUNTS = (1 << 0), + G_FILE_MONITOR_SEND_MOVED = (1 << 1), + G_FILE_MONITOR_WATCH_HARD_LINKS = (1 << 2) } GFileMonitorFlags; diff --git a/gio/inotify/ginotifydirectorymonitor.c b/gio/inotify/ginotifydirectorymonitor.c index 13bdd52c1..89c818c69 100644 --- a/gio/inotify/ginotifydirectorymonitor.c +++ b/gio/inotify/ginotifydirectorymonitor.c @@ -96,7 +96,7 @@ g_inotify_directory_monitor_constructor (GType type, pair_moves = G_LOCAL_DIRECTORY_MONITOR (obj)->flags & G_FILE_MONITOR_SEND_MOVED; - sub = _ih_sub_new (dirname, NULL, pair_moves, inotify_monitor); + sub = _ih_sub_new (dirname, NULL, pair_moves, FALSE, inotify_monitor); /* FIXME: what to do about errors here? we can't return NULL or another * kind of error and an assertion is probably too hard */ g_assert (sub != NULL); diff --git a/gio/inotify/ginotifyfilemonitor.c b/gio/inotify/ginotifyfilemonitor.c index bdfea194e..d70dca723 100644 --- a/gio/inotify/ginotifyfilemonitor.c +++ b/gio/inotify/ginotifyfilemonitor.c @@ -88,6 +88,7 @@ g_inotify_file_monitor_constructor (GType type, GObjectClass *parent_class; GInotifyFileMonitor *inotify_monitor; const gchar *filename = NULL; + gboolean watch_hardlinks; inotify_sub *sub = NULL; gboolean pair_moves; gboolean ret_ih_startup; /* return value of _ih_startup, for asserting */ @@ -114,10 +115,12 @@ g_inotify_file_monitor_constructor (GType type, g_assert (ret_ih_startup); pair_moves = G_LOCAL_FILE_MONITOR (obj)->flags & G_FILE_MONITOR_SEND_MOVED; + watch_hardlinks = G_LOCAL_FILE_MONITOR (obj)->flags & G_FILE_MONITOR_WATCH_HARDLINKS; sub = _ih_sub_new (inotify_monitor->dirname, inotify_monitor->filename, pair_moves, + watch_hardlinks, inotify_monitor); /* FIXME: what to do about errors here? we can't return NULL or another diff --git a/gio/inotify/inotify-helper.c b/gio/inotify/inotify-helper.c index b1a9cf153..650e88cf5 100644 --- a/gio/inotify/inotify-helper.c +++ b/gio/inotify/inotify-helper.c @@ -40,7 +40,9 @@ static gboolean ih_debug_enabled = FALSE; #define IH_W if (ih_debug_enabled) g_warning -static void ih_event_callback (ik_event_t *event, inotify_sub *sub); +static void ih_event_callback (ik_event_t *event, + inotify_sub *sub, + gboolean file_event); static void ih_not_missing_callback (inotify_sub *sub); /* We share this lock with inotify-kernel.c and inotify-missing.c @@ -133,11 +135,15 @@ _ih_sub_cancel (inotify_sub *sub) } static char * -_ih_fullpath_from_event (ik_event_t *event, const char *dirname) +_ih_fullpath_from_event (ik_event_t *event, + const char *dirname, + const char *filename) { char *fullpath; - if (event->name) + if (filename) + fullpath = g_strdup_printf ("%s/%s", dirname, filename); + else if (event->name) fullpath = g_strdup_printf ("%s/%s", dirname, event->name); else fullpath = g_strdup_printf ("%s/", dirname); @@ -161,7 +167,8 @@ ih_event_is_paired_move (ik_event_t *event) static void ih_event_callback (ik_event_t *event, - inotify_sub *sub) + inotify_sub *sub, + gboolean file_event) { gchar *fullpath; GFileMonitorEvent eflags; @@ -169,14 +176,15 @@ ih_event_callback (ik_event_t *event, GFile* other; eflags = ih_mask_to_EventFlags (event->mask); - fullpath = _ih_fullpath_from_event (event, sub->dirname); + fullpath = _ih_fullpath_from_event (event, sub->dirname, + file_event ? sub->filename : NULL); child = g_file_new_for_path (fullpath); g_free (fullpath); if (ih_event_is_paired_move (event) && sub->pair_moves) { const char *parent_dir = (char *) _ip_get_path_for_wd (event->pair->wd); - fullpath = _ih_fullpath_from_event (event->pair, parent_dir); + fullpath = _ih_fullpath_from_event (event->pair, parent_dir, NULL); other = g_file_new_for_path (fullpath); g_free (fullpath); eflags = G_FILE_MONITOR_EVENT_MOVED; diff --git a/gio/inotify/inotify-path.c b/gio/inotify/inotify-path.c index 9ff20c8eb..27e97563e 100644 --- a/gio/inotify/inotify-path.c +++ b/gio/inotify/inotify-path.c @@ -3,6 +3,7 @@ /* inotify-path.c - GVFS Directory Monitor based on inotify. Copyright (C) 2006 John McCutchan + Copyright (C) 2009 Codethink Limited The Gnome Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as @@ -21,6 +22,7 @@ Authors: John McCutchan + Ryan Lortie */ #include "config.h" @@ -35,13 +37,23 @@ #include "inotify-path.h" #include "inotify-missing.h" -#define IP_INOTIFY_MASK (IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_DELETE|IN_CREATE|IN_DELETE_SELF|IN_UNMOUNT|IN_MOVE_SELF|IN_CLOSE_WRITE) +#define IP_INOTIFY_DIR_MASK (IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_DELETE|IN_CREATE|IN_DELETE_SELF|IN_UNMOUNT|IN_MOVE_SELF|IN_CLOSE_WRITE) + +#define IP_INOTIFY_FILE_MASK (IN_MODIFY|IN_ATTRIB|IN_CLOSE_WRITE) /* Older libcs don't have this */ #ifndef IN_ONLYDIR #define IN_ONLYDIR 0 #endif +typedef struct ip_watched_file_s { + gchar *filename; + gchar *path; + gint32 wd; + + GList *subs; +} ip_watched_file_t; + typedef struct ip_watched_dir_s { char *path; /* TODO: We need to maintain a tree of watched directories @@ -51,6 +63,12 @@ typedef struct ip_watched_dir_s { struct ip_watched_dir_s* parent; GList* children; + /* basename -> ip_watched_file_t + * Maps basename to a ip_watched_file_t if the file is currently + * being directly watched for changes (ie: 'hardlinks' mode). + */ + GHashTable *files_hash; + /* Inotify state */ gint32 wd; @@ -74,6 +92,11 @@ static GHashTable * sub_dir_hash = NULL; * the same wd */ static GHashTable * wd_dir_hash = NULL; +/* This hash holds GLists of ip_watched_file_t *'s + * We need to hold a list because links can share + * the same wd + */ +static GHashTable * wd_file_hash = NULL; static ip_watched_dir_t *ip_watched_dir_new (const char *path, int wd); @@ -81,10 +104,10 @@ static void ip_watched_dir_free (ip_watched_dir_t *dir); static void ip_event_callback (ik_event_t *event); -static void (*event_callback)(ik_event_t *event, inotify_sub *sub); +static void (*event_callback)(ik_event_t *event, inotify_sub *sub, gboolean file_event); gboolean -_ip_startup (void (*cb)(ik_event_t *event, inotify_sub *sub)) +_ip_startup (void (*cb)(ik_event_t *event, inotify_sub *sub, gboolean file_event)) { static gboolean initialized = FALSE; static gboolean result = FALSE; @@ -101,6 +124,7 @@ _ip_startup (void (*cb)(ik_event_t *event, inotify_sub *sub)) path_dir_hash = g_hash_table_new (g_str_hash, g_str_equal); sub_dir_hash = g_hash_table_new (g_direct_hash, g_direct_equal); wd_dir_hash = g_hash_table_new (g_direct_hash, g_direct_equal); + wd_file_hash = g_hash_table_new (g_direct_hash, g_direct_equal); initialized = TRUE; return TRUE; @@ -136,6 +160,92 @@ ip_map_wd_dir (gint32 wd, g_hash_table_replace (wd_dir_hash, GINT_TO_POINTER (dir->wd), dir_list); } +static void +ip_map_wd_file (gint32 wd, + ip_watched_file_t *file) +{ + GList *file_list; + + g_assert (wd >= 0 && file); + file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (wd)); + file_list = g_list_prepend (file_list, file); + g_hash_table_replace (wd_file_hash, GINT_TO_POINTER (wd), file_list); +} + +static void +ip_unmap_wd_file (gint32 wd, + ip_watched_file_t *file) +{ + GList *file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (wd)); + + if (!file_list) + return; + + g_assert (wd >= 0 && file); + file_list = g_list_remove (file_list, file); + if (file_list == NULL) + g_hash_table_remove (wd_file_hash, GINT_TO_POINTER (wd)); + else + g_hash_table_replace (wd_file_hash, GINT_TO_POINTER (wd), file_list); +} + + +static ip_watched_file_t * +ip_watched_file_new (const gchar *dirname, + const gchar *filename) +{ + ip_watched_file_t *file; + + file = g_new0 (ip_watched_file_t, 1); + file->path = g_strjoin ("/", dirname, filename, NULL); + file->filename = g_strdup (filename); + file->wd = -1; + + return file; +} + +static void +ip_watched_file_free (ip_watched_file_t *file) +{ + g_assert (file->subs == NULL); + g_free (file->filename); + g_free (file->path); +} + +static void +ip_watched_file_add_sub (ip_watched_file_t *file, + inotify_sub *sub) +{ + file->subs = g_list_prepend (file->subs, sub); +} + +static void +ip_watched_file_start (ip_watched_file_t *file) +{ + if (file->wd < 0) + { + gint err; + + file->wd = _ik_watch (file->path, + IP_INOTIFY_FILE_MASK, + &err); + + if (file->wd >= 0) + ip_map_wd_file (file->wd, file); + } +} + +static void +ip_watched_file_stop (ip_watched_file_t *file) +{ + if (file->wd >= 0) + { + _ik_ignore (file->path, file->wd); + ip_unmap_wd_file (file->wd, file); + file->wd = -1; + } +} + gboolean _ip_start_watching (inotify_sub *sub) { @@ -149,31 +259,46 @@ _ip_start_watching (inotify_sub *sub) IP_W ("Starting to watch %s\n", sub->dirname); dir = g_hash_table_lookup (path_dir_hash, sub->dirname); - if (dir) + + if (dir == NULL) { - IP_W ("Already watching\n"); - goto out; - } - - IP_W ("Trying to add inotify watch "); - wd = _ik_watch (sub->dirname, IP_INOTIFY_MASK|IN_ONLYDIR, &err); - if (wd < 0) - { - IP_W ("Failed\n"); - return FALSE; + IP_W ("Trying to add inotify watch "); + wd = _ik_watch (sub->dirname, IP_INOTIFY_DIR_MASK|IN_ONLYDIR, &err); + if (wd < 0) + { + IP_W ("Failed\n"); + return FALSE; + } + else + { + /* Create new watched directory and associate it with the + * wd hash and path hash + */ + IP_W ("Success\n"); + dir = ip_watched_dir_new (sub->dirname, wd); + ip_map_wd_dir (wd, dir); + ip_map_path_dir (sub->dirname, dir); + } } else + IP_W ("Already watching\n"); + + if (sub->hardlinks) { - /* Create new watched directory and associate it with the - * wd hash and path hash - */ - IP_W ("Success\n"); - dir = ip_watched_dir_new (sub->dirname, wd); - ip_map_wd_dir (wd, dir); - ip_map_path_dir (sub->dirname, dir); + ip_watched_file_t *file; + + file = g_hash_table_lookup (dir->files_hash, sub->filename); + + if (file == NULL) + { + file = ip_watched_file_new (sub->dirname, sub->filename); + g_hash_table_insert (dir->files_hash, file->filename, file); + } + + ip_watched_file_add_sub (file, sub); + ip_watched_file_start (file); } - - out: + ip_map_sub_dir (sub, dir); return TRUE; @@ -216,26 +341,34 @@ ip_unmap_wd (gint32 wd) } static void -ip_unmap_sub_dir (inotify_sub *sub, +ip_unmap_sub_dir (inotify_sub *sub, ip_watched_dir_t *dir) { g_assert (sub && dir); g_hash_table_remove (sub_dir_hash, sub); dir->subs = g_list_remove (dir->subs, sub); -} + + if (sub->hardlinks) + { + ip_watched_file_t *file; + + file = g_hash_table_lookup (dir->files_hash, sub->filename); + file->subs = g_list_remove (file->subs, sub); + + if (file->subs == NULL) + { + g_hash_table_remove (dir->files_hash, sub->filename); + ip_watched_file_stop (file); + ip_watched_file_free (file); + } + } + } static void ip_unmap_all_subs (ip_watched_dir_t *dir) { - GList *l = NULL; - - for (l = dir->subs; l; l = l->next) - { - inotify_sub *sub = l->data; - g_hash_table_remove (sub_dir_hash, sub); - } - g_list_free (dir->subs); - dir->subs = NULL; + while (dir->subs != NULL) + ip_unmap_sub_dir (dir->subs->data, dir); } gboolean @@ -269,6 +402,7 @@ ip_watched_dir_new (const char *path, ip_watched_dir_t *dir = g_new0 (ip_watched_dir_t, 1); dir->path = g_strdup (path); + dir->files_hash = g_hash_table_new (g_str_hash, g_str_equal); dir->wd = wd; return dir; @@ -277,6 +411,7 @@ ip_watched_dir_new (const char *path, static void ip_watched_dir_free (ip_watched_dir_t *dir) { + g_assert_cmpint (g_hash_table_size (dir->files_hash), ==, 0); g_assert (dir->subs == NULL); g_free (dir->path); g_free (dir); @@ -304,17 +439,19 @@ ip_wd_delete (gpointer data, static void ip_event_dispatch (GList *dir_list, GList *pair_dir_list, + GList *file_list, + GList *pair_file_list, ik_event_t *event) { - GList *dirl; + GList *l; if (!event) return; - for (dirl = dir_list; dirl; dirl = dirl->next) + for (l = dir_list; l; l = l->next) { GList *subl; - ip_watched_dir_t *dir = dirl->data; + ip_watched_dir_t *dir = l->data; for (subl = dir->subs; subl; subl = subl->next) { @@ -331,27 +468,67 @@ ip_event_dispatch (GList *dir_list, /* If the subscription has a filename * but this event doesn't, we don't - * deliever this event. + * deliver this event. */ if (sub->filename && !event->name) continue; + /* If we're also watching the file directly + * don't report events that will also be + * reported on the file itself. + */ + if (sub->hardlinks) + { + event->mask &= ~IP_INOTIFY_FILE_MASK; + if (!event->mask) + continue; + } + /* FIXME: We might need to synthesize * DELETE/UNMOUNT events when * the filename doesn't match */ - event_callback (event, sub); - } + event_callback (event, sub, FALSE); + + if (sub->hardlinks) + { + ip_watched_file_t *file; + + file = g_hash_table_lookup (dir->files_hash, sub->filename); + + if (file != NULL) + { + if (event->mask & (IN_MOVED_FROM | IN_DELETE)) + ip_watched_file_stop (file); + + if (event->mask & (IN_MOVED_TO | IN_CREATE)) + ip_watched_file_start (file); + } + } + } + } + + for (l = file_list; l; l = l->next) + { + ip_watched_file_t *file = l->data; + GList *subl; + + for (subl = file->subs; subl; subl = subl->next) + { + inotify_sub *sub = subl->data; + + event_callback (event, sub, TRUE); + } } if (!event->pair) return; - for (dirl = pair_dir_list; dirl; dirl = dirl->next) + for (l = pair_dir_list; l; l = l->next) { GList *subl; - ip_watched_dir_t *dir = dirl->data; + ip_watched_dir_t *dir = l->data; for (subl = dir->subs; subl; subl = subl->next) { @@ -368,19 +545,59 @@ ip_event_dispatch (GList *dir_list, /* If the subscription has a filename * but this event doesn't, we don't - * deliever this event. + * deliver this event. */ if (sub->filename && !event->pair->name) continue; + /* If we're also watching the file directly + * don't report events that will also be + * reported on the file itself. + */ + if (sub->hardlinks) + { + event->mask &= ~IP_INOTIFY_FILE_MASK; + if (!event->mask) + continue; + } + /* FIXME: We might need to synthesize * DELETE/UNMOUNT events when * the filename doesn't match */ - event_callback (event->pair, sub); + event_callback (event->pair, sub, FALSE); + + if (sub->hardlinks) + { + ip_watched_file_t *file; + + file = g_hash_table_lookup (dir->files_hash, sub->filename); + + if (file != NULL) + { + if (event->pair->mask & (IN_MOVED_FROM | IN_DELETE)) + ip_watched_file_stop (file); + + if (event->pair->mask & (IN_MOVED_TO | IN_CREATE)) + ip_watched_file_start (file); + } + } } } + + for (l = pair_file_list; l; l = l->next) + { + ip_watched_file_t *file = l->data; + GList *subl; + + for (subl = file->subs; subl; subl = subl->next) + { + inotify_sub *sub = subl->data; + + event_callback (event->pair, sub, TRUE); + } + } } static void @@ -388,9 +605,9 @@ ip_event_callback (ik_event_t *event) { GList* dir_list = NULL; GList* pair_dir_list = NULL; - - dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (event->wd)); - + GList *file_list = NULL; + GList *pair_file_list = NULL; + /* We can ignore the IGNORED events */ if (event->mask & IN_IGNORED) { @@ -398,11 +615,17 @@ ip_event_callback (ik_event_t *event) return; } - if (event->pair) - pair_dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (event->pair->wd)); + dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (event->wd)); + file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (event->wd)); - if (event->mask & IP_INOTIFY_MASK) - ip_event_dispatch (dir_list, pair_dir_list, event); + if (event->pair) + { + pair_dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (event->pair->wd)); + pair_file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (event->pair->wd)); + } + + if (event->mask & IP_INOTIFY_DIR_MASK) + ip_event_dispatch (dir_list, pair_dir_list, file_list, pair_file_list, event); /* We have to manage the missing list * when we get an event that means the diff --git a/gio/inotify/inotify-path.h b/gio/inotify/inotify-path.h index 327dc70a3..002726039 100644 --- a/gio/inotify/inotify-path.h +++ b/gio/inotify/inotify-path.h @@ -26,7 +26,7 @@ #include "inotify-kernel.h" #include "inotify-sub.h" -gboolean _ip_startup (void (*event_cb)(ik_event_t *event, inotify_sub *sub)); +gboolean _ip_startup (void (*event_cb)(ik_event_t *event, inotify_sub *sub, gboolean file_event)); gboolean _ip_start_watching (inotify_sub *sub); gboolean _ip_stop_watching (inotify_sub *sub); const char * _ip_get_path_for_wd (gint32 wd); diff --git a/gio/inotify/inotify-sub.c b/gio/inotify/inotify-sub.c index fa2e934a7..a4edc9935 100644 --- a/gio/inotify/inotify-sub.c +++ b/gio/inotify/inotify-sub.c @@ -47,7 +47,8 @@ dup_dirname (const gchar *dirname) inotify_sub* _ih_sub_new (const gchar *dirname, const gchar *filename, - gboolean pair_moves, + gboolean pair_moves, + gboolean watch_hardlinks, gpointer user_data) { inotify_sub *sub = NULL; @@ -56,6 +57,7 @@ _ih_sub_new (const gchar *dirname, sub->dirname = dup_dirname (dirname); sub->filename = g_strdup (filename); sub->pair_moves = pair_moves; + sub->hardlinks = watch_hardlinks; sub->user_data = user_data; IS_W ("new subscription for %s being setup\n", sub->dirname); diff --git a/gio/inotify/inotify-sub.h b/gio/inotify/inotify-sub.h index 7c9b08769..8c9593ae5 100644 --- a/gio/inotify/inotify-sub.h +++ b/gio/inotify/inotify-sub.h @@ -30,10 +30,15 @@ typedef struct gchar* filename; gboolean cancelled; gpointer user_data; - gboolean pair_moves; + gboolean pair_moves; + gboolean hardlinks; } inotify_sub; -inotify_sub* _ih_sub_new (const gchar* dirname, const gchar* filename, gboolean pair_moves, gpointer user_data); -void _ih_sub_free (inotify_sub* sub); +inotify_sub *_ih_sub_new (const gchar *dirname, + const gchar *filename, + gboolean pair_moves, + gboolean watch_hardlinks, + gpointer user_data); +void _ih_sub_free (inotify_sub *sub); #endif /* __INOTIFY_SUB_H */