summaryrefslogtreecommitdiffstats
path: root/compat/fsmonitor/fsm-path-utils-darwin.c
diff options
context:
space:
mode:
Diffstat (limited to 'compat/fsmonitor/fsm-path-utils-darwin.c')
-rw-r--r--compat/fsmonitor/fsm-path-utils-darwin.c135
1 files changed, 135 insertions, 0 deletions
diff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c
new file mode 100644
index 0000000..ce5a8fe
--- /dev/null
+++ b/compat/fsmonitor/fsm-path-utils-darwin.c
@@ -0,0 +1,135 @@
+#include "fsmonitor.h"
+#include "fsmonitor-path-utils.h"
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)
+{
+ struct statfs fs;
+ if (statfs(path, &fs) == -1) {
+ int saved_errno = errno;
+ trace_printf_key(&trace_fsmonitor, "statfs('%s') failed: %s",
+ path, strerror(saved_errno));
+ errno = saved_errno;
+ return -1;
+ }
+
+ trace_printf_key(&trace_fsmonitor,
+ "statfs('%s') [type 0x%08x][flags 0x%08x] '%s'",
+ path, fs.f_type, fs.f_flags, fs.f_fstypename);
+
+ if (!(fs.f_flags & MNT_LOCAL))
+ fs_info->is_remote = 1;
+ else
+ fs_info->is_remote = 0;
+
+ fs_info->typename = xstrdup(fs.f_fstypename);
+
+ trace_printf_key(&trace_fsmonitor,
+ "'%s' is_remote: %d",
+ path, fs_info->is_remote);
+ return 0;
+}
+
+int fsmonitor__is_fs_remote(const char *path)
+{
+ struct fs_info fs;
+ if (fsmonitor__get_fs_info(path, &fs))
+ return -1;
+
+ free(fs.typename);
+
+ return fs.is_remote;
+}
+
+/*
+ * Scan the root directory for synthetic firmlinks that when resolved
+ * are a prefix of the path, stopping at the first one found.
+ *
+ * Some information about firmlinks and synthetic firmlinks:
+ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/
+ *
+ * macOS no longer allows symlinks in the root directory; any link found
+ * there is therefore a synthetic firmlink.
+ *
+ * If this function gets called often, will want to cache all the firmlink
+ * information, but for now there is only one caller of this function.
+ *
+ * If there is more than one alias for the path, that is another
+ * matter altogether.
+ */
+int fsmonitor__get_alias(const char *path, struct alias_info *info)
+{
+ DIR *dir;
+ int retval = -1;
+ const char *const root = "/";
+ struct stat st;
+ struct dirent *de;
+ struct strbuf alias;
+ struct strbuf points_to = STRBUF_INIT;
+
+ dir = opendir(root);
+ if (!dir)
+ return error_errno(_("opendir('%s') failed"), root);
+
+ strbuf_init(&alias, 256);
+
+ while ((de = readdir(dir)) != NULL) {
+ strbuf_reset(&alias);
+ strbuf_addf(&alias, "%s%s", root, de->d_name);
+
+ if (lstat(alias.buf, &st) < 0) {
+ error_errno(_("lstat('%s') failed"), alias.buf);
+ goto done;
+ }
+
+ if (!S_ISLNK(st.st_mode))
+ continue;
+
+ if (strbuf_readlink(&points_to, alias.buf, st.st_size) < 0) {
+ error_errno(_("strbuf_readlink('%s') failed"), alias.buf);
+ goto done;
+ }
+
+ if (!strncmp(points_to.buf, path, points_to.len) &&
+ (path[points_to.len] == '/')) {
+ strbuf_addbuf(&info->alias, &alias);
+ strbuf_addbuf(&info->points_to, &points_to);
+ trace_printf_key(&trace_fsmonitor,
+ "Found alias for '%s' : '%s' -> '%s'",
+ path, info->alias.buf, info->points_to.buf);
+ retval = 0;
+ goto done;
+ }
+ }
+ retval = 0; /* no alias */
+
+done:
+ strbuf_release(&alias);
+ strbuf_release(&points_to);
+ if (closedir(dir) < 0)
+ return error_errno(_("closedir('%s') failed"), root);
+ return retval;
+}
+
+char *fsmonitor__resolve_alias(const char *path,
+ const struct alias_info *info)
+{
+ if (!info->alias.len)
+ return NULL;
+
+ if ((!strncmp(info->alias.buf, path, info->alias.len))
+ && path[info->alias.len] == '/') {
+ struct strbuf tmp = STRBUF_INIT;
+ const char *remainder = path + info->alias.len;
+
+ strbuf_addbuf(&tmp, &info->points_to);
+ strbuf_add(&tmp, remainder, strlen(remainder));
+ return strbuf_detach(&tmp, NULL);
+ }
+
+ return NULL;
+}