From ea7ba944af8a1f8d732a9072d7f7d82299d1b752 Mon Sep 17 00:00:00 2001 From: Parth Kapadia Date: Fri, 10 Feb 2023 13:37:33 +0530 Subject: [PATCH] [FS-1111] Counts dir entries in a dir before writing to it. --- src/fusedav.c | 44 +++++++++++++++++++++++++++++++++++++++++ src/statcache.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ src/statcache.h | 1 + 3 files changed, 97 insertions(+) diff --git a/src/fusedav.c b/src/fusedav.c index 340a6345..e2143eea 100644 --- a/src/fusedav.c +++ b/src/fusedav.c @@ -1376,6 +1376,9 @@ static int dav_mkdir(const char *path, mode_t mode) { long response_code = 500; // seed it as bad so we can enter the loop CURLcode res = CURLE_OK; rwp_t rwp = WRITE; + char *parentpath; + unsigned BLOCKING_THRESHOLD = 5; + unsigned direntcnt; if (use_readonly_mode()) { log_print(LOG_WARNING, SECTION_FUSEDAV_FILE, "dav_mkdir: %s aborted; in readonly mode", path); @@ -1387,6 +1390,25 @@ static int dav_mkdir(const char *path, mode_t mode) { log_print(LOG_INFO, SECTION_FUSEDAV_DIR, "CALLBACK: %s(%s, %04o)", funcname, path, mode); + // Highly populated directory check + parentpath = path_parent(path); + if (parentpath == NULL) { + log_print(LOG_NOTICE, SECTION_FUSEDAV_DIR, "Stat entry for the path %s is NULL.", path); + } else { + direntcnt = log_dirent_count(config->cache, parentpath); + if(direntcnt > BLOCKING_THRESHOLD) { + log_print(LOG_NOTICE, SECTION_FUSEDAV_DIR, "dav_mkdir: %s; dirent count (%u) above threshold for %s", path, direntcnt, parentpath); + // TODO: This is currently just logging but not preventing the dav_mkdir operation. + // Once we are confident on the decision to go ahead with blocking the writes, + // uncomment/modify the code below to enforce it. Specific error to client TBD, ENOSPC used tentatively. + + // log_print(LOG_WARNING, SECTION_FUSEDAV_DIR, "dav_mkdir: %s aborted; dirent count (%u) above threshold for %s", path, direntcnt, parentpath); + // g_set_error(&gerr, fusedav_quark(), ENOSPC, "aborted; dirent count above threshold"); + // return processed_gerror("dav_mkdir: ", path, &gerr); + } + } + free(parentpath); + snprintf(fn, sizeof(fn), "%s/", path); for (int idx = 0; idx < num_filesystem_server_nodes && (res != CURLE_OK || response_code >= 500); idx++) { @@ -2129,6 +2151,9 @@ static int dav_create(const char *path, mode_t mode, struct fuse_file_info *info struct stat_cache_value value; GError *gerr = NULL; int fd; + char *parentpath; + unsigned BLOCKING_THRESHOLD = 5; + unsigned direntcnt; if (use_readonly_mode()) { log_print(LOG_WARNING, SECTION_FUSEDAV_FILE, "dav_create: %s aborted; in readonly mode", path); @@ -2140,6 +2165,25 @@ static int dav_create(const char *path, mode_t mode, struct fuse_file_info *info log_print(LOG_INFO, SECTION_FUSEDAV_FILE, "CALLBACK: dav_create(%s, %04o)", path, mode); + // Highly populated directory check + parentpath = path_parent(path); + if (parentpath == NULL) { + log_print(LOG_NOTICE, SECTION_FUSEDAV_FILE, "Stat entry for the path %s is NULL.", path); + } else { + direntcnt = log_dirent_count(config->cache, parentpath); + if(direntcnt > BLOCKING_THRESHOLD) { + log_print(LOG_NOTICE, SECTION_FUSEDAV_FILE, "dav_create: %s; dirent count (%u) above threshold for %s", path, direntcnt, parentpath); + // TODO: This is currently just logging but not preventing the dav_create operation. + // Once we are confident on the decision to go ahead with blocking the writes, + // uncomment/modify the code below to enforce it. Specific error to client TBD, ENOSPC used tentatively. + + // log_print(LOG_WARNING, SECTION_FUSEDAV_FILE, "dav_create: %s aborted; dirent count (%u) above threshold for %s", path, direntcnt, parentpath); + // g_set_error(&gerr, fusedav_quark(), ENOSPC, "aborted; dirent count above threshold"); + // return processed_gerror("dav_create: ", path, &gerr); + } + } + free(parentpath); + info->flags |= O_CREAT | O_TRUNC; do_open(path, info, &gerr); diff --git a/src/statcache.c b/src/statcache.c index 990052c2..6a3b02c0 100644 --- a/src/statcache.c +++ b/src/statcache.c @@ -931,6 +931,58 @@ int stat_cache_enumerate(stat_cache_t *cache, const char *path_prefix, void (*f) return E_SC_SUCCESS; } +// log_dirent_count is based on stat_cache_enumerate and ignores cache freshness. +unsigned log_dirent_count(stat_cache_t *cache, const char *path_prefix) { + struct stat_cache_iterator *iter; + struct stat_cache_entry *entry; + unsigned found_entries = 0; + + // BUMP(statcache_enumerate); + + // log_print(LOG_DEBUG, SECTION_STATCACHE_ITER, "count_dir_entries(%s)", path_prefix); + + //stat_cache_list_all(cache, path_prefix); + // if (!force) { + // time_t timestamp; + // time_t current_time; + // // Pass NULL for gerr; not tracking error, just zero return + // timestamp = stat_cache_read_updated_children(cache, path_prefix, NULL); + + // if (timestamp == 0) { + // return -STAT_CACHE_NO_DATA; + // } + + // // Check for cache values which are too old; but timestamp = 0 needs to trigger below + // current_time = time(NULL); + // if (current_time - timestamp > STAT_CACHE_NEGATIVE_TTL) { + // log_print(LOG_DEBUG, SECTION_STATCACHE_ITER, "cache value too old: %s %u", path_prefix, (unsigned)timestamp); + // return -STAT_CACHE_OLD_DATA; + // } + // } + + iter = stat_cache_iter_init(cache, path_prefix); + log_print(LOG_NOTICE, SECTION_STATCACHE_ITER, "iterator initialized with prefix: %s", iter->key_prefix); + + while ((entry = stat_cache_iter_current(iter))) { + // log_print(LOG_NOTICE, SECTION_STATCACHE_ITER, "key: %s", entry->key); + // log_print(LOG_NOTICE, SECTION_STATCACHE_ITER, "fn: %s", entry->key + (iter->key_prefix_len - 1)); + // Ignore negative (non-existent) entries, those tagged with st_mode == 0 + if (entry->value->st.st_mode != 0) { + // f(path_prefix, entry->key + (iter->key_prefix_len - 1), user); + ++found_entries; + } + free(entry); + stat_cache_iter_next(iter); + } + stat_cache_iterator_free(iter); + log_print(LOG_NOTICE, SECTION_STATCACHE_ITER, "Done iterating: %u items.", found_entries); + + // if (found_entries == 0) + // return -STAT_CACHE_NO_DATA; + + return found_entries; +} + void stat_cache_walk(void) { leveldb_readoptions_t *roptions; struct leveldb_iterator_t *iter; diff --git a/src/statcache.h b/src/statcache.h index 06316c2e..50fa1933 100644 --- a/src/statcache.h +++ b/src/statcache.h @@ -87,6 +87,7 @@ void stat_cache_delete_older(stat_cache_t *cache, const char *key_prefix, unsign void stat_cache_walk(void); int stat_cache_enumerate(stat_cache_t *cache, const char *key_prefix, void (*f) (const char *path_prefix, const char *filename, void *user), void *user, bool force); +unsigned log_dirent_count(stat_cache_t *cache, const char *key_prefix); bool stat_cache_dir_has_child(stat_cache_t *cache, const char *path); void stat_cache_prune(stat_cache_t *cache, bool first);