git/compat/fsmonitor/fsm-path-utils-darwin.c
Elijah Newren 68d686460f fsmonitor-ll.h: split this header out of fsmonitor.h
This creates a new fsmonitor-ll.h with most of the functions from
fsmonitor.h, though it leaves three inline functions where they were.
Two-thirds of the files that previously included fsmonitor.h did not
need those three inline functions or the six extra includes those inline
functions required, so this allows them to only include the lower level
header.

Diff best viewed with `--color-moved`.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-21 13:39:54 -07:00

139 lines
3.4 KiB
C

#include "git-compat-util.h"
#include "fsmonitor-ll.h"
#include "fsmonitor-path-utils.h"
#include "gettext.h"
#include "trace.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;
}