#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;
}