From c69cf487463ccec7cf572220c7c0b9353f6e7db9 Mon Sep 17 00:00:00 2001 From: "Carsten Haitzler (Rasterman)" Date: Wed, 27 Nov 2013 19:45:29 +0900 Subject: [PATCH] recursive monitoring and scanning fix for icons and desktop files this fixes T580 ... or SHOULD fix it. there is recursion detection code now and it properly follows symlinks and dirs. it also properly updates the file monitor tree for both icons and desktops and it only monitors dirs, not files (as a dir picks up changes to child data). tested and it seems not to recurse into self-referencing symlinks (once it detects the loop) and detects changes nicely in all my tests. --- src/bin/efreet/efreet_desktop_cache_create.c | 62 ++- src/bin/efreet/efreet_icon_cache_create.c | 4 + src/bin/efreet/efreetd_cache.c | 389 +++++++++++-------- 3 files changed, 270 insertions(+), 185 deletions(-) diff --git a/src/bin/efreet/efreet_desktop_cache_create.c b/src/bin/efreet/efreet_desktop_cache_create.c index 2fc6e917d7..fd8c613305 100644 --- a/src/bin/efreet/efreet_desktop_cache_create.c +++ b/src/bin/efreet/efreet_desktop_cache_create.c @@ -128,54 +128,75 @@ cache_add(const char *path, const char *file_id, int priority EINA_UNUSED, int * static int -cache_scan(const char *path, const char *base_id, int priority, int recurse, int *changed) +cache_scan(Eina_Inarray *stack, const char *path, const char *base_id, + int priority, int recurse, int *changed) { char *file_id = NULL; char id[PATH_MAX]; - char buf[PATH_MAX]; Eina_Iterator *it; Eina_File_Direct_Info *info; + Eina_Bool free_stack = EINA_FALSE; + struct stat st; + unsigned int i; + int ret = 1; if (!ecore_file_is_dir(path)) return 1; - it = eina_file_stat_ls(path); - if (!it) return 1; + if (!stack) + { + free_stack = EINA_TRUE; + stack = eina_inarray_new(sizeof(struct stat), 16); + if (!stack) goto end; + } + if (stat(path, &st) == -1) goto end; + for (i = 0; i < eina_inarray_count(stack); i++) + { + struct stat *st2 = eina_inarray_nth(stack, i); + if ((st2->st_dev == st.st_dev) && (st2->st_ino == st.st_ino)) + goto end; + } + eina_inarray_push(stack, &st); + + it = eina_file_stat_ls(path); + if (!it) goto end; id[0] = '\0'; EINA_ITERATOR_FOREACH(it, info) { - const char *fname; - - fname = info->path + info->name_start; + const char *fname = info->path + info->name_start; + + if (info->path[info->name_start] == '.') continue; if (base_id) { - if (*base_id) - snprintf(id, sizeof(id), "%s-%s", base_id, fname); + if (*base_id) snprintf(id, sizeof(id), "%s-%s", base_id, fname); else { strncpy(id, fname, PATH_MAX); - id[PATH_MAX - 1] = '\0'; + id[PATH_MAX - 1] = 0; } file_id = id; } - snprintf(buf, sizeof(buf), "%s/%s", path, fname); - if (info->type == EINA_FILE_DIR) + if (((info->type == EINA_FILE_LNK) && (ecore_file_is_dir(info->path))) || + (info->type == EINA_FILE_DIR)) { if (recurse) - cache_scan(buf, file_id, priority, recurse, changed); + cache_scan(stack, info->path, file_id, priority, recurse, changed); } else { - if (!cache_add(buf, file_id, priority, changed)) + if (!cache_add(info->path, file_id, priority, changed)) { eina_iterator_free(it); - return 0; + ret = 0; + goto end; } } } eina_iterator_free(it); - return 1; +end: + if (free_stack) eina_inarray_free(stack); + return ret; } static int @@ -342,15 +363,16 @@ main(int argc, char **argv) if (!dirs) goto error; EINA_LIST_FREE(dirs, path) - { + { char file_id[PATH_MAX] = { '\0' }; - if (!cache_scan(path, file_id, priority++, 1, &changed)) goto error; + if (!cache_scan(NULL, path, file_id, priority++, 1, &changed)) + goto error; systemdirs = eina_list_append(systemdirs, path); - } + } EINA_LIST_FOREACH(extra_dirs, l, path) - if (!cache_scan(path, NULL, priority, 0, &changed)) goto error; + if (!cache_scan(NULL, path, NULL, priority, 0, &changed)) goto error; /* store util */ #define STORE_HASH_ARRAY(_hash) \ diff --git a/src/bin/efreet/efreet_icon_cache_create.c b/src/bin/efreet/efreet_icon_cache_create.c index 3b22e0cc00..9759b758ca 100644 --- a/src/bin/efreet/efreet_icon_cache_create.c +++ b/src/bin/efreet/efreet_icon_cache_create.c @@ -618,6 +618,10 @@ cache_theme_scan(const char *dir) (entry->type != EINA_FILE_LNK)) continue; + if ((entry->type == EINA_FILE_LNK) && + (!ecore_file_is_dir(entry->path))) + continue; + name = entry->path + entry->name_start; theme = eina_hash_find(icon_themes, name); diff --git a/src/bin/efreet/efreetd_cache.c b/src/bin/efreet/efreetd_cache.c index 23ff1ab656..6ca556ad25 100644 --- a/src/bin/efreet/efreetd_cache.c +++ b/src/bin/efreet/efreetd_cache.c @@ -14,7 +14,12 @@ #include "efreet_private.h" #include "efreetd_cache.h" -static Eina_Hash *change_monitors = NULL; +#include +#include +#include + +static Eina_Hash *icon_change_monitors = NULL; +static Eina_Hash *desktop_change_monitors = NULL; static Ecore_Event_Handler *cache_exe_del_handler = NULL; static Ecore_Event_Handler *cache_exe_data_handler = NULL; @@ -37,6 +42,9 @@ static Eina_Bool icon_queue = EINA_FALSE; static void desktop_changes_monitor_add(const char *path); +static void icon_changes_listen(void); +static void desktop_changes_listen(void); + /* internal */ static Eina_Bool icon_cache_update_cache_cb(void *data EINA_UNUSED) @@ -53,6 +61,11 @@ icon_cache_update_cache_cb(void *data EINA_UNUSED) icon_queue = EINA_FALSE; if ((!icon_flush) && (!icon_exts)) return ECORE_CALLBACK_CANCEL; + if (icon_change_monitors) eina_hash_free(icon_change_monitors); + icon_change_monitors = eina_hash_string_superfast_new + (EINA_FREE_CB(ecore_file_monitor_del)); + icon_changes_listen(); + /* TODO: Queue if already running */ snprintf(file, sizeof(file), "%s/efreet/" MODULE_ARCH "/efreet_icon_cache_create", @@ -90,16 +103,6 @@ icon_cache_update_cache_cb(void *data EINA_UNUSED) return ECORE_CALLBACK_CANCEL; } -static void -cache_icon_update(Eina_Bool flush) -{ - if (icon_cache_timer) - ecore_timer_del(icon_cache_timer); - if (flush) - icon_flush = flush; - icon_cache_timer = ecore_timer_add(1.0, icon_cache_update_cache_cb, NULL); -} - static Eina_Bool desktop_cache_update_cache_cb(void *data EINA_UNUSED) { @@ -114,6 +117,11 @@ desktop_cache_update_cache_cb(void *data EINA_UNUSED) } desktop_queue = EINA_FALSE; + if (desktop_change_monitors) eina_hash_free(desktop_change_monitors); + desktop_change_monitors = eina_hash_string_superfast_new + (EINA_FREE_CB(ecore_file_monitor_del)); + desktop_changes_listen(); + snprintf(file, sizeof(file), "%s/efreet/" MODULE_ARCH "/efreet_desktop_cache_create", eina_prefix_lib_get(pfx)); @@ -130,64 +138,31 @@ desktop_cache_update_cache_cb(void *data EINA_UNUSED) } } INF("Run desktop cache creation: %s", file); - desktop_cache_exe = - ecore_exe_pipe_run(file, ECORE_EXE_PIPE_READ|ECORE_EXE_PIPE_READ_LINE_BUFFERED, NULL); + desktop_cache_exe = ecore_exe_pipe_run + (file, ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_READ_LINE_BUFFERED, NULL); return ECORE_CALLBACK_CANCEL; } -static Eina_Bool -cache_exe_data_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) +static void +cache_icon_update(Eina_Bool flush) { - Ecore_Exe_Event_Data *ev; - - ev = event; - if (ev->exe == desktop_cache_exe) - { - Eina_Bool update = EINA_FALSE; - - if ((ev->lines) && (*ev->lines->line == 'c')) - update = EINA_TRUE; - - desktop_exists = EINA_TRUE; - send_signal_desktop_cache_update(update); - } - else if (ev->exe == icon_cache_exe) - { - Eina_Bool update = EINA_FALSE; - - if ((ev->lines) && (*ev->lines->line == 'c')) - update = EINA_TRUE; - - send_signal_icon_cache_update(update); - } - return ECORE_CALLBACK_RENEW; + if (icon_cache_timer) ecore_timer_del(icon_cache_timer); + if (flush) icon_flush = flush; + icon_cache_timer = ecore_timer_add(0.2, icon_cache_update_cache_cb, NULL); } -static Eina_Bool -cache_exe_del_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) +void +cache_desktop_update(void) { - Ecore_Exe_Event_Del *ev; - - ev = event; - if (ev->exe == desktop_cache_exe) - { - desktop_cache_exe = NULL; - if (desktop_queue) cache_desktop_update(); - } - else if (ev->exe == icon_cache_exe) - { - icon_cache_exe = NULL; - if (icon_queue) cache_icon_update(EINA_FALSE); - } - return ECORE_CALLBACK_RENEW; + if (desktop_cache_timer) ecore_timer_del(desktop_cache_timer); + desktop_cache_timer = ecore_timer_add(0.2, desktop_cache_update_cache_cb, NULL); } static void icon_changes_cb(void *data EINA_UNUSED, Ecore_File_Monitor *em EINA_UNUSED, - Ecore_File_Event event, const char *path) + Ecore_File_Event event, const char *path EINA_UNUSED) { - /* TODO: If we get a stale symlink, we need to rerun cache creation */ switch (event) { case ECORE_FILE_EVENT_NONE: @@ -198,15 +173,52 @@ icon_changes_cb(void *data EINA_UNUSED, Ecore_File_Monitor *em EINA_UNUSED, case ECORE_FILE_EVENT_DELETED_FILE: case ECORE_FILE_EVENT_MODIFIED: case ECORE_FILE_EVENT_CLOSED: + // a FILE was changed, added or removed + cache_icon_update(EINA_FALSE); + break; + case ECORE_FILE_EVENT_DELETED_DIRECTORY: case ECORE_FILE_EVENT_CREATED_DIRECTORY: - cache_icon_update(EINA_FALSE); - break; + // the whole tree needs re-monitoring + cache_icon_update(EINA_FALSE); + break; case ECORE_FILE_EVENT_DELETED_SELF: - eina_hash_del_by_key(change_monitors, path); - cache_icon_update(EINA_FALSE); + // the whole tree needs re-monitoring + cache_icon_update(EINA_FALSE); + break; + } +} + +static void +desktop_changes_cb(void *data EINA_UNUSED, Ecore_File_Monitor *em EINA_UNUSED, + Ecore_File_Event event, const char *path EINA_UNUSED) +{ + /* TODO: Check for desktop*.cache, as this will be created when app is installed */ + switch (event) + { + case ECORE_FILE_EVENT_NONE: + /* noop */ break; + + case ECORE_FILE_EVENT_CREATED_FILE: + case ECORE_FILE_EVENT_DELETED_FILE: + case ECORE_FILE_EVENT_MODIFIED: + case ECORE_FILE_EVENT_CLOSED: + // a FILE was changed, added or removed + cache_desktop_update(); + break; + + case ECORE_FILE_EVENT_DELETED_DIRECTORY: + case ECORE_FILE_EVENT_CREATED_DIRECTORY: + // the whole tree needs re-monitoring + cache_desktop_update(); + break; + + case ECORE_FILE_EVENT_DELETED_SELF: + // the whole tree needs re-monitoring + cache_desktop_update(); + break; } } @@ -214,21 +226,52 @@ static void icon_changes_monitor_add(const char *path) { Ecore_File_Monitor *mon; + char *realp; - if (eina_hash_find(change_monitors, path)) return; - /* TODO: Check for symlink and monitor the real path */ - mon = ecore_file_monitor_add(path, - icon_changes_cb, - NULL); - if (mon) - eina_hash_add(change_monitors, path, mon); + if (eina_hash_find(icon_change_monitors, path)) return; + realp = ecore_file_realpath(path); + if (!realp) return; + mon = ecore_file_monitor_add(realp, icon_changes_cb, NULL); + free(realp); + if (mon) eina_hash_add(icon_change_monitors, path, mon); } static void -icon_changes_listen_recursive(const char *path, Eina_Bool base) +desktop_changes_monitor_add(const char *path) +{ + Ecore_File_Monitor *mon; + + if (eina_hash_find(desktop_change_monitors, path)) return; + /* TODO: Check for symlink and monitor the real path */ + mon = ecore_file_monitor_add(path, desktop_changes_cb, NULL); + if (mon) + eina_hash_add(desktop_change_monitors, path, mon); +} + +static void +icon_changes_listen_recursive(Eina_Inarray *stack, const char *path, Eina_Bool base) { Eina_Iterator *it; Eina_File_Direct_Info *info; + Eina_Bool free_stack = EINA_FALSE; + struct stat st; + unsigned int i; + + if (!stack) + { + free_stack = EINA_TRUE; + stack = eina_inarray_new(sizeof(struct stat), 16); + if (!stack) return; + } + if (stat(path, &st) == -1) return; + for (i = 0; i < eina_inarray_count(stack); i++) + { + struct stat *st2 = eina_inarray_nth(stack, i); + + if ((st2->st_dev == st.st_dev) && (st2->st_ino == st.st_ino)) + return; + } + eina_inarray_push(stack, &st); if ((!ecore_file_is_dir(path)) && (base)) { @@ -240,16 +283,68 @@ icon_changes_listen_recursive(const char *path, Eina_Bool base) // monitoring the next specific child dir down until we are // monitoring the original path again. } - icon_changes_monitor_add(path); + if (ecore_file_is_dir(path)) icon_changes_monitor_add(path); it = eina_file_stat_ls(path); - if (!it) return; + if (!it) goto end; EINA_ITERATOR_FOREACH(it, info) { + if (info->path[info->name_start] == '.') continue; if (((info->type == EINA_FILE_LNK) && (ecore_file_is_dir(info->path))) || (info->type == EINA_FILE_DIR)) - icon_changes_monitor_add(info->path); + icon_changes_listen_recursive(stack, info->path, EINA_FALSE); } eina_iterator_free(it); +end: + if (free_stack) eina_inarray_free(stack); +} + +static void +desktop_changes_listen_recursive(Eina_Inarray *stack, const char *path, Eina_Bool base) +{ + Eina_Iterator *it; + Eina_File_Direct_Info *info; + Eina_Bool free_stack = EINA_FALSE; + struct stat st; + unsigned int i; + + if (!stack) + { + free_stack = EINA_TRUE; + stack = eina_inarray_new(sizeof(struct stat), 16); + if (!stack) return; + } + if (stat(path, &st) == -1) return; + for (i = 0; i < eina_inarray_count(stack); i++) + { + struct stat *st2 = eina_inarray_nth(stack, i); + + if ((st2->st_dev == st.st_dev) && (st2->st_ino == st.st_ino)) + return; + } + eina_inarray_push(stack, &st); + if ((!ecore_file_is_dir(path)) && (base)) + { + // XXX: if it doesn't exist... walk the parent dirs back down + // to this path until we find one that doesn't exist, then + // monitor its parent, and treat it specially as it needs + // to look for JUST the creation of this specific child + // and when this child is created, replace this monitor with + // monitoring the next specific child dir down until we are + // monitoring the original path again. + } + if (ecore_file_is_dir(path)) desktop_changes_monitor_add(path); + it = eina_file_stat_ls(path); + if (!it) goto end; + EINA_ITERATOR_FOREACH(it, info) + { + if (info->path[info->name_start] == '.') continue; + if (((info->type == EINA_FILE_LNK) && (ecore_file_is_dir(info->path))) || + (info->type == EINA_FILE_DIR)) + desktop_changes_listen_recursive(stack, info->path, EINA_FALSE); + } + eina_iterator_free(it); +end: + if (free_stack) eina_inarray_free(stack); } static void @@ -260,102 +355,30 @@ icon_changes_listen(void) char buf[PATH_MAX]; const char *dir; - icon_changes_listen_recursive(efreet_icon_deprecated_user_dir_get(), EINA_TRUE); - icon_changes_listen_recursive(efreet_icon_user_dir_get(), EINA_TRUE); + icon_changes_listen_recursive(NULL, efreet_icon_deprecated_user_dir_get(), EINA_TRUE); + icon_changes_listen_recursive(NULL, efreet_icon_user_dir_get(), EINA_TRUE); EINA_LIST_FOREACH(icon_extra_dirs, l, dir) { - icon_changes_listen_recursive(dir, EINA_TRUE); + icon_changes_listen_recursive(NULL, dir, EINA_TRUE); } xdg_dirs = efreet_data_dirs_get(); EINA_LIST_FOREACH(xdg_dirs, l, dir) { snprintf(buf, sizeof(buf), "%s/icons", dir); - icon_changes_listen_recursive(buf, EINA_TRUE); + icon_changes_listen_recursive(NULL, buf, EINA_TRUE); } #ifndef STRICT_SPEC EINA_LIST_FOREACH(xdg_dirs, l, dir) { snprintf(buf, sizeof(buf), "%s/pixmaps", dir); - icon_changes_listen_recursive(buf, EINA_TRUE); + icon_changes_listen_recursive(NULL, buf, EINA_TRUE); } #endif - icon_changes_monitor_add("/usr/share/pixmaps"); } -static void -desktop_changes_cb(void *data EINA_UNUSED, Ecore_File_Monitor *em EINA_UNUSED, - Ecore_File_Event event, const char *path) -{ - const char *ext; - - /* TODO: If we get a stale symlink, we need to rerun cache creation */ - /* TODO: Check for desktop*.cache, as this will be created when app is installed */ - /* TODO: Do efreet_cache_icon_update() when app is installed, as it has the same - * symlink problem */ - switch (event) - { - case ECORE_FILE_EVENT_NONE: - /* noop */ - break; - - case ECORE_FILE_EVENT_CREATED_FILE: - case ECORE_FILE_EVENT_DELETED_FILE: - case ECORE_FILE_EVENT_MODIFIED: - case ECORE_FILE_EVENT_CLOSED: - ext = strrchr(path, '.'); - if (ext && (!strcmp(ext, ".desktop") || !strcmp(ext, ".directory"))) - cache_desktop_update(); - break; - - case ECORE_FILE_EVENT_DELETED_SELF: - case ECORE_FILE_EVENT_DELETED_DIRECTORY: - eina_hash_del_by_key(change_monitors, path); - cache_desktop_update(); - break; - - case ECORE_FILE_EVENT_CREATED_DIRECTORY: - desktop_changes_monitor_add(path); - cache_desktop_update(); - break; - } -} - -static void -desktop_changes_monitor_add(const char *path) -{ - Ecore_File_Monitor *mon; - - if (eina_hash_find(change_monitors, path)) return; - /* TODO: Check for symlink and monitor the real path */ - mon = ecore_file_monitor_add(path, - desktop_changes_cb, - NULL); - if (mon) - eina_hash_add(change_monitors, path, mon); -} - -static void -desktop_changes_listen_recursive(const char *path) -{ - Eina_Iterator *it; - Eina_File_Direct_Info *info; - - if (!ecore_file_is_dir(path)) return; - desktop_changes_monitor_add(path); - it = eina_file_stat_ls(path); - if (!it) return; - EINA_ITERATOR_FOREACH(it, info) - { - if (((info->type == EINA_FILE_LNK) && (ecore_file_is_dir(info->path))) || - (info->type == EINA_FILE_DIR)) - desktop_changes_listen_recursive(info->path); - } - eina_iterator_free(it); -} - static void desktop_changes_listen(void) { @@ -363,10 +386,9 @@ desktop_changes_listen(void) const char *path; EINA_LIST_FOREACH(desktop_system_dirs, l, path) - desktop_changes_listen_recursive(path); - + desktop_changes_listen_recursive(NULL, path, EINA_TRUE); EINA_LIST_FOREACH(desktop_extra_dirs, l, path) - desktop_changes_listen_recursive(path); + desktop_changes_listen_recursive(NULL, path, EINA_TRUE); } static void @@ -420,7 +442,48 @@ save_list(const char *file, Eina_List *l) static int strcmplen(const void *data1, const void *data2) { - return strncmp(data1, data2, eina_stringshare_strlen(data1)); + return strncmp(data1, data2, eina_stringshare_strlen(data1)); +} + +static Eina_Bool +cache_exe_data_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) +{ + Ecore_Exe_Event_Data *ev = event; + + if (ev->exe == desktop_cache_exe) + { + Eina_Bool update = EINA_FALSE; + + if ((ev->lines) && (*ev->lines->line == 'c')) update = EINA_TRUE; + desktop_exists = EINA_TRUE; + send_signal_desktop_cache_update(update); + } + else if (ev->exe == icon_cache_exe) + { + Eina_Bool update = EINA_FALSE; + + if ((ev->lines) && (*ev->lines->line == 'c')) update = EINA_TRUE; + send_signal_icon_cache_update(update); + } + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +cache_exe_del_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) +{ + Ecore_Exe_Event_Del *ev = event; + + if (ev->exe == desktop_cache_exe) + { + desktop_cache_exe = NULL; + if (desktop_queue) cache_desktop_update(); + } + else if (ev->exe == icon_cache_exe) + { + icon_cache_exe = NULL; + if (icon_queue) cache_icon_update(EINA_FALSE); + } + return ECORE_CALLBACK_RENEW; } /* external */ @@ -436,7 +499,7 @@ cache_desktop_dir_add(const char *dir) { /* Path is registered, but maybe not monitored */ const char *path = eina_list_data_get(l); - if (!eina_hash_find(change_monitors, path)) + if (!eina_hash_find(desktop_change_monitors, path)) cache_desktop_update(); } else if (!eina_list_search_unsorted_list(desktop_extra_dirs, EINA_COMPARE_CB(strcmp), san)) @@ -476,14 +539,6 @@ cache_icon_ext_add(const char *ext) } } -void -cache_desktop_update(void) -{ - if (desktop_cache_timer) - ecore_timer_del(desktop_cache_timer); - desktop_cache_timer = ecore_timer_add(1.0, desktop_cache_update_cache_cb, NULL); -} - Eina_Bool cache_desktop_exists(void) { @@ -519,7 +574,10 @@ cache_init(void) goto error; } - change_monitors = eina_hash_string_superfast_new(EINA_FREE_CB(ecore_file_monitor_del)); + icon_change_monitors = eina_hash_string_superfast_new + (EINA_FREE_CB(ecore_file_monitor_del)); + desktop_change_monitors = eina_hash_string_superfast_new + (EINA_FREE_CB(ecore_file_monitor_del)); efreet_cache_update = 0; if (!efreet_init()) goto error; @@ -561,9 +619,10 @@ cache_shutdown(void) if (cache_exe_data_handler) ecore_event_handler_del(cache_exe_data_handler); cache_exe_data_handler = NULL; - if (change_monitors) - eina_hash_free(change_monitors); - change_monitors = NULL; + if (icon_change_monitors) eina_hash_free(icon_change_monitors); + icon_change_monitors = NULL; + if (desktop_change_monitors) eina_hash_free(desktop_change_monitors); + desktop_change_monitors = NULL; EINA_LIST_FREE(desktop_system_dirs, data) eina_stringshare_del(data); EINA_LIST_FREE(desktop_extra_dirs, data)