<pre style='margin:0'>
Mihai Moldovan (Ionic) pushed a commit to branch master
in repository macports-base.

</pre>
<p><a href="https://github.com/macports/macports-base/commit/bd36a3b185e0a8f1db5a15634bb88b9ed280f224">https://github.com/macports/macports-base/commit/bd36a3b185e0a8f1db5a15634bb88b9ed280f224</a></p>
<pre style="white-space: pre; background: #F8F8F8"><span style='display:block; white-space:pre;color:#808000;'>commit bd36a3b185e0a8f1db5a15634bb88b9ed280f224
</span>Author: Mihai Moldovan <ionic@ionic.de>
AuthorDate: Tue Feb 21 06:51:49 2017 +0100

<span style='display:block; white-space:pre;color:#404040;'>    pextlib1.0: implement mount point FS case-sensitivity caching.
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    The implementation is done in Pextlib itself, with Tracelib using an
</span><span style='display:block; white-space:pre;color:#404040;'>    opaque pointer, fetching a new data structure and clearing it via
</span><span style='display:block; white-space:pre;color:#404040;'>    Pextlib's implementation on-demand.
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    No sophistication: we just insert mount points as string and the value
</span><span style='display:block; white-space:pre;color:#404040;'>    directly, then check mount point values via strcmp later. The caching
</span><span style='display:block; white-space:pre;color:#404040;'>    data is allocated in Pextlib as necessary, with one big block allocated
</span><span style='display:block; white-space:pre;color:#404040;'>    for pointers to the actual entries. Every addition yields a realloc()
</span><span style='display:block; white-space:pre;color:#404040;'>    call for the pointers block.
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    Since mount point counts are generally small (normally users have one or
</span><span style='display:block; white-space:pre;color:#404040;'>    two different mount points that might be accessed), using more
</span><span style='display:block; white-space:pre;color:#404040;'>    complicated data structures is not necessary.
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    Caching is completely disabled on systems that do not have an API for
</span><span style='display:block; white-space:pre;color:#404040;'>    easily getting the mount point associated with a specific file. This
</span><span style='display:block; white-space:pre;color:#404040;'>    holds true for Linux, as well. While it would be possible to get the
</span><span style='display:block; white-space:pre;color:#404040;'>    associated mount point via a chain of stat() - working our way up the
</span><span style='display:block; white-space:pre;color:#404040;'>    directory hierarchy - and getmntent() calls, just doing the three
</span><span style='display:block; white-space:pre;color:#404040;'>    non-caching lstat() calls in the fallback function is probably faster
</span><span style='display:block; white-space:pre;color:#404040;'>    than getting the mount point by brute force.
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    BSD-based systems, including OS X, luckily expose this information
</span><span style='display:block; white-space:pre;color:#404040;'>    directly in their statfs structure.
</span>---
 src/pextlib1.0/Pextlib.c  | 263 ++++++++++++++++++++++++++++++++++++++++++----
 src/pextlib1.0/Pextlib.h  |   9 +-
 src/pextlib1.0/tracelib.c |  22 +++-
 3 files changed, 271 insertions(+), 23 deletions(-)

<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/pextlib1.0/Pextlib.c b/src/pextlib1.0/Pextlib.c
</span><span style='display:block; white-space:pre;color:#808080;'>index 0a125cf..328076b 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/pextlib1.0/Pextlib.c
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/pextlib1.0/Pextlib.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -53,6 +53,7 @@
</span> #include <sys/stat.h>
 #include <sys/wait.h>
 #include <sys/param.h>
<span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <sys/mount.h>
</span> #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -67,6 +68,7 @@
</span> #include <string.h>
 #include <strings.h>
 #include <unistd.h>
<span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <assert.h>
</span> 
 #ifdef __MACH__
 #include <mach-o/loader.h>
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -649,10 +651,188 @@ int SetMaxOpenFilesCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int obj
</span>   return TCL_OK;
 }
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+/* Mount point file system case-sensitivity caching infrastructure. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+typedef struct _mount_cs_cache_entry {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char *mountpoint;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int case_sensitive;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+} mount_cs_cache_entry_t, *mount_cs_cache_entry_list_t;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+struct _mount_cs_cache {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    size_t count;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    mount_cs_cache_entry_t **entries;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Returns a new pre-allocated mount_cs_cache_t object.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+mount_cs_cache_t* new_mount_cs_cache() {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    mount_cs_cache_t *ret = malloc(sizeof(mount_cs_cache_t));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (ret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ret->count = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ret->entries = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Resets a mount cache object.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+void reset_mount_cs_cache(mount_cs_cache_t *cache) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (cache) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        /*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+         * Assume that if the count of cached entries is zero,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+         * the entries handle is set to NULL as well. Likewise,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+         * if the count is non-zero, the entries pointer should
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+         * be non-NULL. Any other combination probably means
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+         * something is very, very wrong.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+         */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        assert((!cache->count && !cache->entries) || (cache->count && cache->entries));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        for (size_t i = 0; i < cache->count; ++i) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            free(cache->entries[i]->mountpoint);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            cache->entries[i]->mountpoint = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            free(cache->entries[i]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            cache->entries[i] = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        free(cache->entries);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        cache->entries = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Rollback mount cache object.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static void rollback_mount_cs_cache(Tcl_Interp *interp, mount_cs_cache_t *cache) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (cache) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        mount_cs_cache_entry_t **rollback_data = realloc(cache->entries, (cache->count) * sizeof(mount_cs_cache_entry_list_t));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (!rollback_data) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ui_info(interp, "pextlib: unable to roll changes to FS cache back.");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            cache->entries[cache->count++] = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            cache->entries = rollback_data;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Adds a new entry to a mount cache object.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Returns zero on success, -1 on failure.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int add_to_mount_cs_cache(Tcl_Interp *interp, mount_cs_cache_t *cache, const char *mountpoint, int case_sensitive) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int ret = -1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if ((cache) && (mountpoint)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        /*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+         * Go the greedy road and resize as needed. Good enough probably, since
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+         * systems normally don't have a huge amount of mounts to start with.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+         */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        mount_cs_cache_entry_t **new_data = realloc(cache->entries, (cache->count + 1) * sizeof(mount_cs_cache_entry_list_t));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (!new_data) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ui_info(interp, "pextlib: unable to reallocate FS cache entries list, leaving untouched.");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        cache->entries = new_data;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        mount_cs_cache_entry_t *new_entry = malloc(sizeof(mount_cs_cache_entry_t));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (!new_entry) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ui_info(interp, "pextlib: unable to create new FS cache entry, leaving untouched and rolling back.");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            rollback_mount_cs_cache(interp, cache);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new_entry->mountpoint = strdup(mountpoint);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (!new_entry->mountpoint) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ui_info(interp, "pextlib: unable to copy mountpoint value to new FS cache entry, leaving untouched and rolling back.");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            rollback_mount_cs_cache(interp, cache);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            free(new_entry);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new_entry->case_sensitive = case_sensitive;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        cache->entries[cache->count++] = new_entry;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ret = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Looks up a cached mountpoint case-sensitivity value.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Returns 1 if the mountpoint's FS is case-sensitive,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * 0 if it's case-insensitive and -1 on error or if
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * no such entry was found.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int lookup_mount_cs_cache(mount_cs_cache_t *cache, const char *mountpoint) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int ret = -1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    /* No specified mount point doesn't make sense, so assert it. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    assert(mountpoint);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (cache && mountpoint) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        for (size_t i = 0; i < cache->count; ++i) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if ((cache->entries[i]) && (cache->entries[i]->mountpoint)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if (0 == strcmp(cache->entries[i]->mountpoint, mountpoint)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    return cache->entries[i]->case_sensitive;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Gets the corresponding mount point for a file.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Returns NULL if looking up the mount point was not possible.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static char* get_mntpoint(const char *path) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char *ret = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    struct statfs f = { 0 };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (-1 != statfs(path, &f)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (f.f_mntonname) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ret = strdup(f.f_mntonname);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#else
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    /*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+     * Systems like Solaris, IRIX, True64, AIX and others have no way to get this information easily.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+     *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+     * Neither does Linux. We could go ahead and try to "compute" the mount point
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+     * using a series of stat calls and the like, but it doesn't make any sense.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+     * Running our three lstat() calls should be less resource hungry than getting
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+     * the mount point on this system, so disable caching there as well.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+     */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    ret = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> #ifdef __APPLE__
 
 #include <sys/attr.h>
<span style='display:block; white-space:pre;background:#ffe0e0;'>-#include <sys/mount.h>
</span> 
 typedef struct volcaps {
       u_int32_t size;
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -663,38 +843,59 @@ typedef struct volcaps {
</span>  * Default function for determining the FS case sensitivity on Darwin.
  * Using getattrlist().
  */
<span style='display:block; white-space:pre;background:#ffe0e0;'>-int fs_case_sensitive_darwin(const char *path) {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    /* TODO: cache mount point CS value. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int fs_case_sensitive_darwin(Tcl_Interp *interp, const char *path, mount_cs_cache_t *cache) {
</span>     int ret = -1;
 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    struct statfs f = { 0 };
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    struct attrlist attrlist = { 0 };
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    volcaps_t volcaps = { 0 };
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-
</span>     if (!path) {
         return ret;
     }
 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    if (-1 == statfs(path, &f)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char *mntpoint = get_mntpoint(path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (!mntpoint) {
</span>         return ret;
     }
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (cache) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ret = lookup_mount_cs_cache(cache, mntpoint);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (-1 != ret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            free(mntpoint);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    struct attrlist attrlist = { 0 };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    volcaps_t volcaps = { 0 };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span>     attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
     attrlist.volattr = ATTR_VOL_CAPABILITIES;
 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    if (-1 == getattrlist(f.f_mntonname, &attrlist, &volcaps, sizeof(volcaps), 0)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (-1 == getattrlist(mntpoint, &attrlist, &volcaps, sizeof(volcaps), 0)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        free(mntpoint);
</span>         return ret;
     }
 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    if ((attrlist.volattr & ATTR_VOL_CAPABILITIES) == 0) {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-        return ret;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (-1 == ret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if ((attrlist.volattr & ATTR_VOL_CAPABILITIES) == 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            free(mntpoint);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        /* In case entry is not cached, fetch value, if possible. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if ((volcaps.volcaps.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_SENSITIVE)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            /* capabilities bit for case-sensitivity valid */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ret = (volcaps.volcaps.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_SENSITIVE) != 0;
</span> 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    if ((volcaps.volcaps.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_SENSITIVE)) {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-        /* capabilities bit for case-sensitivity valid */
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-        ret = (volcaps.volcaps.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_SENSITIVE) != 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            /*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+             * Note that we only add a new entry if the case-sensitivity value could be determined.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+             * In case of errors, let the code try again - the failure might have been temporary.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+             */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            add_to_mount_cs_cache(interp, cache, mntpoint, ret);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span>     }
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    free(mntpoint);
</span>     return ret;
 }
 
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -710,9 +911,22 @@ int fs_case_sensitive_darwin(const char *path) {
</span>  * In case of errors (e.g., if the original file does not exist),
  * -1 is returned.
  */
<span style='display:block; white-space:pre;background:#ffe0e0;'>-int fs_case_sensitive_fallback(const char *path) {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    /* TODO: fetch mount point and cache mount point CS value. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int fs_case_sensitive_fallback(Tcl_Interp *interp, const char *path, mount_cs_cache_t *cache) {
</span>     int ret = -1;
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    char *mntpoint = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (cache) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        mntpoint = get_mntpoint(path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (mntpoint) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ret = lookup_mount_cs_cache(cache, mntpoint);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if (-1 != ret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                free(mntpoint);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span> 
     if (!path) {
         return ret;
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -725,6 +939,8 @@ int fs_case_sensitive_fallback(const char *path) {
</span>         free(lowercase_path);
         free(uppercase_path);
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+        free(mntpoint);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span>         return ret;
     }
 
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -745,6 +961,8 @@ int fs_case_sensitive_fallback(const char *path) {
</span>         free(lowercase_path);
         free(uppercase_path);
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+        free(mntpoint);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span>         return ret;
     }
 
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -803,9 +1021,16 @@ int fs_case_sensitive_fallback(const char *path) {
</span>         }
     }
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (-1 != ret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        /* No error if cache or mntpoint are NULL, the called function shall catch that. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        add_to_mount_cs_cache(interp, cache, mntpoint, ret);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span>     free(lowercase_path);
     free(uppercase_path);
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    free(mntpoint);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span>     return ret;
 }
 
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -826,11 +1051,11 @@ int FSCaseSensitiveCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int obj
</span>     char *path = Tcl_GetString(objv[1]);
 
 #ifdef __APPLE__
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    ret = fs_case_sensitive_darwin(path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    ret = fs_case_sensitive_darwin(interp, path, NULL);
</span> #endif /* __APPLE__ */
 
     if (-1 == ret) {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-        ret = fs_case_sensitive_fallback(path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ret = fs_case_sensitive_fallback(interp, path, NULL);
</span>     }
 
     if (-1 == ret) {
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/pextlib1.0/Pextlib.h b/src/pextlib1.0/Pextlib.h
</span><span style='display:block; white-space:pre;color:#808080;'>index aaee1fa..2b6b1d2 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/pextlib1.0/Pextlib.h
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/pextlib1.0/Pextlib.h
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -36,8 +36,13 @@ void ui_notice(Tcl_Interp *interp, const char *format, ...) __attribute__((forma
</span> void ui_info(Tcl_Interp *interp, const char *format, ...) __attribute__((format(printf, 2, 3)));
 void ui_debug(Tcl_Interp *interp, const char *format, ...) __attribute__((format(printf, 2, 3)));
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+/* Mount point file system case-sensitivity caching infrastructure. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+typedef struct _mount_cs_cache mount_cs_cache_t;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+mount_cs_cache_t* new_mount_cs_cache();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+void reset_mount_cs_cache(mount_cs_cache_t *cache);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> #ifdef __APPLE__
<span style='display:block; white-space:pre;background:#ffe0e0;'>-int fs_case_sensitive_darwin(const char *path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int fs_case_sensitive_darwin(Tcl_Interp *interp, const char *path, mount_cs_cache_t *cache);
</span> #endif /* __APPLE__ */
 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-int fs_case_sensitive_fallback(const char *path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int fs_case_sensitive_fallback(Tcl_Interp *interp, const char *path, mount_cs_cache_t *cache);
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/pextlib1.0/tracelib.c b/src/pextlib1.0/tracelib.c
</span><span style='display:block; white-space:pre;color:#808080;'>index cb9c146..1706c5f 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/pextlib1.0/tracelib.c
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/pextlib1.0/tracelib.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -110,6 +110,8 @@ static int selfpipe[2];
</span> static int enable_fence = 0;
 static Tcl_Interp *interp;
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+static mount_cs_cache_t *mount_cs_cache;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> /**
  * Mutex that shall be acquired to exclusively lock checking and acting upon
  * the value of kq, indicating whether the event loop has started. If it has
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -616,11 +618,11 @@ static void dep_check(int sock, char *path) {
</span>     }
 
 #ifdef __APPLE__
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    fs_cs = fs_case_sensitive_darwin(path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    fs_cs = fs_case_sensitive_darwin(interp, path, mount_cs_cache);
</span> #endif /* __APPLE__ */
 
     if (-1 == fs_cs) {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-        fs_cs = fs_case_sensitive_fallback(path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        fs_cs = fs_case_sensitive_fallback(interp, path, mount_cs_cache);
</span>     }
 
     if (-1 == fs_cs) {
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -727,6 +729,14 @@ static int TracelibRunCmd(Tcl_Interp *in) {
</span>     int opensockcount = 0;
     bool break_eventloop = false;
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    /* (Re-)initialize mount point FS case-sensitivity cache. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (mount_cs_cache) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reset_mount_cs_cache(mount_cs_cache);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        mount_cs_cache = new_mount_cs_cache();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span>     pthread_mutex_lock(&evloop_mutex);
     /* bring all variables into a defined state so the cleanup code can be
      * called from anywhere */
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -932,6 +942,14 @@ error_locked:
</span>     // wake up any waiting threads in TracelibCloseSocketCmd
     pthread_cond_broadcast(&evloop_signal);
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    /* Free mount_cs_cache object. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (mount_cs_cache) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reset_mount_cs_cache(mount_cs_cache);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        free(mount_cs_cache);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        mount_cs_cache = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span>     return retval;
 }
 
</pre><pre style='margin:0'>

</pre>