Merge branch 'ed/fsmonitor-on-networked-macos'
By default, use of fsmonitor on a repository on networked filesystem is disabled. Add knobs to make it workable on macOS. * ed/fsmonitor-on-networked-macos: fsmonitor: fix leak of warning message fsmonitor: add documentation for allowRemote and socketDir options fsmonitor: check for compatability before communicating with fsmonitor fsmonitor: deal with synthetic firmlinks on macOS fsmonitor: avoid socket location check if using hook fsmonitor: relocate socket file if .git directory is remote fsmonitor: refactor filesystem checks to common interface
This commit is contained in:
commit
7b8cfe34d9
@ -423,6 +423,8 @@ include::config/filter.txt[]
|
||||
|
||||
include::config/fsck.txt[]
|
||||
|
||||
include::config/fsmonitor--daemon.txt[]
|
||||
|
||||
include::config/gc.txt[]
|
||||
|
||||
include::config/gitcvs.txt[]
|
||||
|
11
Documentation/config/fsmonitor--daemon.txt
Normal file
11
Documentation/config/fsmonitor--daemon.txt
Normal file
@ -0,0 +1,11 @@
|
||||
fsmonitor.allowRemote::
|
||||
By default, the fsmonitor daemon refuses to work against network-mounted
|
||||
repositories. Setting `fsmonitor.allowRemote` to `true` overrides this
|
||||
behavior. Only respected when `core.fsmonitor` is set to `true`.
|
||||
|
||||
fsmonitor.socketDir::
|
||||
This Mac OS-specific option, if set, specifies the directory in
|
||||
which to create the Unix domain socket used for communication
|
||||
between the fsmonitor daemon and various Git commands. The directory must
|
||||
reside on a native Mac OS filesystem. Only respected when `core.fsmonitor`
|
||||
is set to `true`.
|
@ -3,7 +3,7 @@ git-fsmonitor{litdd}daemon(1)
|
||||
|
||||
NAME
|
||||
----
|
||||
git-fsmonitor--daemon - A Built-in File System Monitor
|
||||
git-fsmonitor--daemon - A Built-in Filesystem Monitor
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
@ -17,7 +17,7 @@ DESCRIPTION
|
||||
-----------
|
||||
|
||||
A daemon to watch the working directory for file and directory
|
||||
changes using platform-specific file system notification facilities.
|
||||
changes using platform-specific filesystem notification facilities.
|
||||
|
||||
This daemon communicates directly with commands like `git status`
|
||||
using the link:technical/api-simple-ipc.html[simple IPC] interface
|
||||
@ -63,13 +63,44 @@ CAVEATS
|
||||
-------
|
||||
|
||||
The fsmonitor daemon does not currently know about submodules and does
|
||||
not know to filter out file system events that happen within a
|
||||
not know to filter out filesystem events that happen within a
|
||||
submodule. If fsmonitor daemon is watching a super repo and a file is
|
||||
modified within the working directory of a submodule, it will report
|
||||
the change (as happening against the super repo). However, the client
|
||||
will properly ignore these extra events, so performance may be affected
|
||||
but it will not cause an incorrect result.
|
||||
|
||||
By default, the fsmonitor daemon refuses to work against network-mounted
|
||||
repositories; this may be overridden by setting `fsmonitor.allowRemote` to
|
||||
`true`. Note, however, that the fsmonitor daemon is not guaranteed to work
|
||||
correctly with all network-mounted repositories and such use is considered
|
||||
experimental.
|
||||
|
||||
On Mac OS, the inter-process communication (IPC) between various Git
|
||||
commands and the fsmonitor daemon is done via a Unix domain socket (UDS) -- a
|
||||
special type of file -- which is supported by native Mac OS filesystems,
|
||||
but not on network-mounted filesystems, NTFS, or FAT32. Other filesystems
|
||||
may or may not have the needed support; the fsmonitor daemon is not guaranteed
|
||||
to work with these filesystems and such use is considered experimental.
|
||||
|
||||
By default, the socket is created in the `.git` directory, however, if the
|
||||
`.git` directory is on a network-mounted filesystem, it will be instead be
|
||||
created at `$HOME/.git-fsmonitor-*` unless `$HOME` itself is on a
|
||||
network-mounted filesystem in which case you must set the configuration
|
||||
variable `fsmonitor.socketDir` to the path of a directory on a Mac OS native
|
||||
filesystem in which to create the socket file.
|
||||
|
||||
If none of the above directories (`.git`, `$HOME`, or `fsmonitor.socketDir`)
|
||||
is on a native Mac OS file filesystem the fsmonitor daemon will report an
|
||||
error that will cause the daemon and the currently running command to exit.
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
include::includes/cmd-config-section-all.txt[]
|
||||
|
||||
include::config/fsmonitor--daemon.txt[]
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the linkgit:git[1] suite
|
||||
|
2
Makefile
2
Makefile
@ -2038,11 +2038,13 @@ ifdef FSMONITOR_DAEMON_BACKEND
|
||||
COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND
|
||||
COMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o
|
||||
COMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o
|
||||
COMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o
|
||||
endif
|
||||
|
||||
ifdef FSMONITOR_OS_SETTINGS
|
||||
COMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS
|
||||
COMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o
|
||||
COMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o
|
||||
endif
|
||||
|
||||
ifeq ($(TCLTK_PATH),)
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "parse-options.h"
|
||||
#include "fsmonitor.h"
|
||||
#include "fsmonitor-ipc.h"
|
||||
#include "fsmonitor-path-utils.h"
|
||||
#include "compat/fsmonitor/fsm-health.h"
|
||||
#include "compat/fsmonitor/fsm-listen.h"
|
||||
#include "fsmonitor--daemon.h"
|
||||
@ -1282,6 +1283,11 @@ static int fsmonitor_run_daemon(void)
|
||||
strbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));
|
||||
state.nr_paths_watching = 1;
|
||||
|
||||
strbuf_init(&state.alias.alias, 0);
|
||||
strbuf_init(&state.alias.points_to, 0);
|
||||
if ((err = fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)))
|
||||
goto done;
|
||||
|
||||
/*
|
||||
* We create and delete cookie files somewhere inside the .git
|
||||
* directory to help us keep sync with the file system. If
|
||||
@ -1343,7 +1349,8 @@ static int fsmonitor_run_daemon(void)
|
||||
* directory.)
|
||||
*/
|
||||
strbuf_init(&state.path_ipc, 0);
|
||||
strbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));
|
||||
strbuf_addstr(&state.path_ipc,
|
||||
absolute_path(fsmonitor_ipc__get_path(the_repository)));
|
||||
|
||||
/*
|
||||
* Confirm that we can create platform-specific resources for the
|
||||
@ -1390,6 +1397,8 @@ done:
|
||||
strbuf_release(&state.path_gitdir_watch);
|
||||
strbuf_release(&state.path_cookie_prefix);
|
||||
strbuf_release(&state.path_ipc);
|
||||
strbuf_release(&state.alias.alias);
|
||||
strbuf_release(&state.alias.points_to);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
52
compat/fsmonitor/fsm-ipc-darwin.c
Normal file
52
compat/fsmonitor/fsm-ipc-darwin.c
Normal file
@ -0,0 +1,52 @@
|
||||
#include "cache.h"
|
||||
#include "config.h"
|
||||
#include "strbuf.h"
|
||||
#include "fsmonitor.h"
|
||||
#include "fsmonitor-ipc.h"
|
||||
#include "fsmonitor-path-utils.h"
|
||||
|
||||
static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, "fsmonitor--daemon.ipc")
|
||||
|
||||
const char *fsmonitor_ipc__get_path(struct repository *r)
|
||||
{
|
||||
static const char *ipc_path = NULL;
|
||||
SHA_CTX sha1ctx;
|
||||
char *sock_dir = NULL;
|
||||
struct strbuf ipc_file = STRBUF_INIT;
|
||||
unsigned char hash[SHA_DIGEST_LENGTH];
|
||||
|
||||
if (!r)
|
||||
BUG("No repository passed into fsmonitor_ipc__get_path");
|
||||
|
||||
if (ipc_path)
|
||||
return ipc_path;
|
||||
|
||||
|
||||
/* By default the socket file is created in the .git directory */
|
||||
if (fsmonitor__is_fs_remote(r->gitdir) < 1) {
|
||||
ipc_path = fsmonitor_ipc__get_default_path();
|
||||
return ipc_path;
|
||||
}
|
||||
|
||||
SHA1_Init(&sha1ctx);
|
||||
SHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));
|
||||
SHA1_Final(hash, &sha1ctx);
|
||||
|
||||
repo_config_get_string(r, "fsmonitor.socketdir", &sock_dir);
|
||||
|
||||
/* Create the socket file in either socketDir or $HOME */
|
||||
if (sock_dir && *sock_dir) {
|
||||
strbuf_addf(&ipc_file, "%s/.git-fsmonitor-%s",
|
||||
sock_dir, hash_to_hex(hash));
|
||||
} else {
|
||||
strbuf_addf(&ipc_file, "~/.git-fsmonitor-%s", hash_to_hex(hash));
|
||||
}
|
||||
free(sock_dir);
|
||||
|
||||
ipc_path = interpolate_path(ipc_file.buf, 1);
|
||||
if (!ipc_path)
|
||||
die(_("Invalid path: %s"), ipc_file.buf);
|
||||
|
||||
strbuf_release(&ipc_file);
|
||||
return ipc_path;
|
||||
}
|
9
compat/fsmonitor/fsm-ipc-win32.c
Normal file
9
compat/fsmonitor/fsm-ipc-win32.c
Normal file
@ -0,0 +1,9 @@
|
||||
#include "config.h"
|
||||
#include "fsmonitor-ipc.h"
|
||||
|
||||
const char *fsmonitor_ipc__get_path(struct repository *r) {
|
||||
static char *ret;
|
||||
if (!ret)
|
||||
ret = git_pathdup("fsmonitor--daemon.ipc");
|
||||
return ret;
|
||||
}
|
@ -26,6 +26,7 @@
|
||||
#include "fsmonitor.h"
|
||||
#include "fsm-listen.h"
|
||||
#include "fsmonitor--daemon.h"
|
||||
#include "fsmonitor-path-utils.h"
|
||||
|
||||
struct fsm_listen_data
|
||||
{
|
||||
@ -198,8 +199,9 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
|
||||
struct string_list cookie_list = STRING_LIST_INIT_DUP;
|
||||
const char *path_k;
|
||||
const char *slash;
|
||||
int k;
|
||||
char *resolved = NULL;
|
||||
struct strbuf tmp = STRBUF_INIT;
|
||||
int k;
|
||||
|
||||
/*
|
||||
* Build a list of all filesystem changes into a private/local
|
||||
@ -209,7 +211,12 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
|
||||
/*
|
||||
* On Mac, we receive an array of absolute paths.
|
||||
*/
|
||||
path_k = paths[k];
|
||||
free(resolved);
|
||||
resolved = fsmonitor__resolve_alias(paths[k], &state->alias);
|
||||
if (resolved)
|
||||
path_k = resolved;
|
||||
else
|
||||
path_k = paths[k];
|
||||
|
||||
/*
|
||||
* If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.
|
||||
@ -238,6 +245,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
|
||||
fsmonitor_force_resync(state);
|
||||
fsmonitor_batch__free_list(batch);
|
||||
string_list_clear(&cookie_list, 0);
|
||||
batch = NULL;
|
||||
|
||||
/*
|
||||
* We assume that any events that we received
|
||||
@ -360,12 +368,14 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
|
||||
}
|
||||
}
|
||||
|
||||
free(resolved);
|
||||
fsmonitor_publish(state, batch, &cookie_list);
|
||||
string_list_clear(&cookie_list, 0);
|
||||
strbuf_release(&tmp);
|
||||
return;
|
||||
|
||||
force_shutdown:
|
||||
free(resolved);
|
||||
fsmonitor_batch__free_list(batch);
|
||||
string_list_clear(&cookie_list, 0);
|
||||
|
||||
|
135
compat/fsmonitor/fsm-path-utils-darwin.c
Normal file
135
compat/fsmonitor/fsm-path-utils-darwin.c
Normal file
@ -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;
|
||||
}
|
145
compat/fsmonitor/fsm-path-utils-win32.c
Normal file
145
compat/fsmonitor/fsm-path-utils-win32.c
Normal file
@ -0,0 +1,145 @@
|
||||
#include "cache.h"
|
||||
#include "fsmonitor.h"
|
||||
#include "fsmonitor-path-utils.h"
|
||||
|
||||
/*
|
||||
* Check remote working directory protocol.
|
||||
*
|
||||
* Return -1 if client machine cannot get remote protocol information.
|
||||
*/
|
||||
static int check_remote_protocol(wchar_t *wpath)
|
||||
{
|
||||
HANDLE h;
|
||||
FILE_REMOTE_PROTOCOL_INFO proto_info;
|
||||
|
||||
h = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
error(_("[GLE %ld] unable to open for read '%ls'"),
|
||||
GetLastError(), wpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,
|
||||
&proto_info, sizeof(proto_info))) {
|
||||
error(_("[GLE %ld] unable to get protocol information for '%ls'"),
|
||||
GetLastError(), wpath);
|
||||
CloseHandle(h);
|
||||
return -1;
|
||||
}
|
||||
|
||||
CloseHandle(h);
|
||||
|
||||
trace_printf_key(&trace_fsmonitor,
|
||||
"check_remote_protocol('%ls') remote protocol %#8.8lx",
|
||||
wpath, proto_info.Protocol);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notes for testing:
|
||||
*
|
||||
* (a) Windows allows a network share to be mapped to a drive letter.
|
||||
* (This is the normal method to access it.)
|
||||
*
|
||||
* $ NET USE Z: \\server\share
|
||||
* $ git -C Z:/repo status
|
||||
*
|
||||
* (b) Windows allows a network share to be referenced WITHOUT mapping
|
||||
* it to drive letter.
|
||||
*
|
||||
* $ NET USE \\server\share\dir
|
||||
* $ git -C //server/share/repo status
|
||||
*
|
||||
* (c) Windows allows "SUBST" to create a fake drive mapping to an
|
||||
* arbitrary path (which may be remote)
|
||||
*
|
||||
* $ SUBST Q: Z:\repo
|
||||
* $ git -C Q:/ status
|
||||
*
|
||||
* (d) Windows allows a directory symlink to be created on a local
|
||||
* file system that points to a remote repo.
|
||||
*
|
||||
* $ mklink /d ./link //server/share/repo
|
||||
* $ git -C ./link status
|
||||
*/
|
||||
int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)
|
||||
{
|
||||
wchar_t wpath[MAX_PATH];
|
||||
wchar_t wfullpath[MAX_PATH];
|
||||
size_t wlen;
|
||||
UINT driveType;
|
||||
|
||||
/*
|
||||
* Do everything in wide chars because the drive letter might be
|
||||
* a multi-byte sequence. See win32_has_dos_drive_prefix().
|
||||
*/
|
||||
if (xutftowcs_path(wpath, path) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* GetDriveTypeW() requires a final slash. We assume that the
|
||||
* worktree pathname points to an actual directory.
|
||||
*/
|
||||
wlen = wcslen(wpath);
|
||||
if (wpath[wlen - 1] != L'\\' && wpath[wlen - 1] != L'/') {
|
||||
wpath[wlen++] = L'\\';
|
||||
wpath[wlen] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalize the path. If nothing else, this converts forward
|
||||
* slashes to backslashes. This is essential to get GetDriveTypeW()
|
||||
* correctly handle some UNC "\\server\share\..." paths.
|
||||
*/
|
||||
if (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
driveType = GetDriveTypeW(wfullpath);
|
||||
trace_printf_key(&trace_fsmonitor,
|
||||
"DriveType '%s' L'%ls' (%u)",
|
||||
path, wfullpath, driveType);
|
||||
|
||||
if (driveType == DRIVE_REMOTE) {
|
||||
fs_info->is_remote = 1;
|
||||
if (check_remote_protocol(wfullpath) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
fs_info->is_remote = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
return fs.is_remote;
|
||||
}
|
||||
|
||||
/*
|
||||
* No-op for now.
|
||||
*/
|
||||
int fsmonitor__get_alias(const char *path, struct alias_info *info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* No-op for now.
|
||||
*/
|
||||
char *fsmonitor__resolve_alias(const char *path,
|
||||
const struct alias_info *info)
|
||||
{
|
||||
return NULL;
|
||||
}
|
@ -1,32 +1,10 @@
|
||||
#include "cache.h"
|
||||
#include "config.h"
|
||||
#include "repository.h"
|
||||
#include "fsmonitor-settings.h"
|
||||
#include "fsmonitor.h"
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
#include "fsmonitor-ipc.h"
|
||||
#include "fsmonitor-settings.h"
|
||||
#include "fsmonitor-path-utils.h"
|
||||
|
||||
/*
|
||||
* [1] Remote working directories are problematic for FSMonitor.
|
||||
*
|
||||
* The underlying file system on the server machine and/or the remote
|
||||
* mount type (NFS, SAMBA, etc.) dictates whether notification events
|
||||
* are available at all to remote client machines.
|
||||
*
|
||||
* Kernel differences between the server and client machines also
|
||||
* dictate the how (buffering, frequency, de-dup) the events are
|
||||
* delivered to client machine processes.
|
||||
*
|
||||
* A client machine (such as a laptop) may choose to suspend/resume
|
||||
* and it is unclear (without lots of testing) whether the watcher can
|
||||
* resync after a resume. We might be able to treat this as a normal
|
||||
* "events were dropped by the kernel" event and do our normal "flush
|
||||
* and resync" --or-- we might need to close the existing (zombie?)
|
||||
* notification fd and create a new one.
|
||||
*
|
||||
* In theory, the above issues need to be addressed whether we are
|
||||
* using the Hook or IPC API.
|
||||
*
|
||||
/*
|
||||
* For the builtin FSMonitor, we create the Unix domain socket for the
|
||||
* IPC in the .git directory. If the working directory is remote,
|
||||
* then the socket will be created on the remote file system. This
|
||||
@ -38,52 +16,47 @@
|
||||
* be taken to ensure that $HOME is actually local and not a managed
|
||||
* file share.)
|
||||
*
|
||||
* So (for now at least), mark remote working directories as
|
||||
* incompatible.
|
||||
*
|
||||
*
|
||||
* [2] FAT32 and NTFS working directories are problematic too.
|
||||
* FAT32 and NTFS working directories are problematic too.
|
||||
*
|
||||
* The builtin FSMonitor uses a Unix domain socket in the .git
|
||||
* directory for IPC. These Windows drive formats do not support
|
||||
* Unix domain sockets, so mark them as incompatible for the daemon.
|
||||
*
|
||||
*/
|
||||
static enum fsmonitor_reason check_volume(struct repository *r)
|
||||
static enum fsmonitor_reason check_uds_volume(struct repository *r)
|
||||
{
|
||||
struct statfs fs;
|
||||
struct fs_info fs;
|
||||
const char *ipc_path = fsmonitor_ipc__get_path(r);
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
strbuf_add(&path, ipc_path, strlen(ipc_path));
|
||||
|
||||
if (statfs(r->worktree, &fs) == -1) {
|
||||
int saved_errno = errno;
|
||||
trace_printf_key(&trace_fsmonitor, "statfs('%s') failed: %s",
|
||||
r->worktree, strerror(saved_errno));
|
||||
errno = saved_errno;
|
||||
if (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {
|
||||
strbuf_release(&path);
|
||||
return FSMONITOR_REASON_ERROR;
|
||||
}
|
||||
|
||||
trace_printf_key(&trace_fsmonitor,
|
||||
"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'",
|
||||
r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);
|
||||
strbuf_release(&path);
|
||||
|
||||
if (!(fs.f_flags & MNT_LOCAL))
|
||||
return FSMONITOR_REASON_REMOTE;
|
||||
|
||||
if (!strcmp(fs.f_fstypename, "msdos")) /* aka FAT32 */
|
||||
return FSMONITOR_REASON_NOSOCKETS;
|
||||
|
||||
if (!strcmp(fs.f_fstypename, "ntfs"))
|
||||
if (fs.is_remote ||
|
||||
!strcmp(fs.typename, "msdos") ||
|
||||
!strcmp(fs.typename, "ntfs")) {
|
||||
free(fs.typename);
|
||||
return FSMONITOR_REASON_NOSOCKETS;
|
||||
}
|
||||
|
||||
free(fs.typename);
|
||||
return FSMONITOR_REASON_OK;
|
||||
}
|
||||
|
||||
enum fsmonitor_reason fsm_os__incompatible(struct repository *r)
|
||||
enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)
|
||||
{
|
||||
enum fsmonitor_reason reason;
|
||||
|
||||
reason = check_volume(r);
|
||||
if (reason != FSMONITOR_REASON_OK)
|
||||
return reason;
|
||||
if (ipc) {
|
||||
reason = check_uds_volume(r);
|
||||
if (reason != FSMONITOR_REASON_OK)
|
||||
return reason;
|
||||
}
|
||||
|
||||
return FSMONITOR_REASON_OK;
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
#include "cache.h"
|
||||
#include "config.h"
|
||||
#include "repository.h"
|
||||
#include "fsmonitor-settings.h"
|
||||
#include "fsmonitor.h"
|
||||
#include "fsmonitor-settings.h"
|
||||
#include "fsmonitor-path-utils.h"
|
||||
|
||||
/*
|
||||
* VFS for Git is incompatible with FSMonitor.
|
||||
@ -24,172 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)
|
||||
return FSMONITOR_REASON_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if monitoring remote working directories is allowed.
|
||||
*
|
||||
* By default, monitoring remote working directories is
|
||||
* disabled. Users may override this behavior in enviroments where
|
||||
* they have proper support.
|
||||
*/
|
||||
static int check_config_allowremote(struct repository *r)
|
||||
{
|
||||
int allow;
|
||||
|
||||
if (!repo_config_get_bool(r, "fsmonitor.allowremote", &allow))
|
||||
return allow;
|
||||
|
||||
return -1; /* fsmonitor.allowremote not set */
|
||||
}
|
||||
|
||||
/*
|
||||
* Check remote working directory protocol.
|
||||
*
|
||||
* Error if client machine cannot get remote protocol information.
|
||||
*/
|
||||
static int check_remote_protocol(wchar_t *wpath)
|
||||
{
|
||||
HANDLE h;
|
||||
FILE_REMOTE_PROTOCOL_INFO proto_info;
|
||||
|
||||
h = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
error(_("[GLE %ld] unable to open for read '%ls'"),
|
||||
GetLastError(), wpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,
|
||||
&proto_info, sizeof(proto_info))) {
|
||||
error(_("[GLE %ld] unable to get protocol information for '%ls'"),
|
||||
GetLastError(), wpath);
|
||||
CloseHandle(h);
|
||||
return -1;
|
||||
}
|
||||
|
||||
CloseHandle(h);
|
||||
|
||||
trace_printf_key(&trace_fsmonitor,
|
||||
"check_remote_protocol('%ls') remote protocol %#8.8lx",
|
||||
wpath, proto_info.Protocol);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remote working directories are problematic for FSMonitor.
|
||||
*
|
||||
* The underlying file system on the server machine and/or the remote
|
||||
* mount type dictates whether notification events are available at
|
||||
* all to remote client machines.
|
||||
*
|
||||
* Kernel differences between the server and client machines also
|
||||
* dictate the how (buffering, frequency, de-dup) the events are
|
||||
* delivered to client machine processes.
|
||||
*
|
||||
* A client machine (such as a laptop) may choose to suspend/resume
|
||||
* and it is unclear (without lots of testing) whether the watcher can
|
||||
* resync after a resume. We might be able to treat this as a normal
|
||||
* "events were dropped by the kernel" event and do our normal "flush
|
||||
* and resync" --or-- we might need to close the existing (zombie?)
|
||||
* notification fd and create a new one.
|
||||
*
|
||||
* In theory, the above issues need to be addressed whether we are
|
||||
* using the Hook or IPC API.
|
||||
*
|
||||
* So (for now at least), mark remote working directories as
|
||||
* incompatible.
|
||||
*
|
||||
* Notes for testing:
|
||||
*
|
||||
* (a) Windows allows a network share to be mapped to a drive letter.
|
||||
* (This is the normal method to access it.)
|
||||
*
|
||||
* $ NET USE Z: \\server\share
|
||||
* $ git -C Z:/repo status
|
||||
*
|
||||
* (b) Windows allows a network share to be referenced WITHOUT mapping
|
||||
* it to drive letter.
|
||||
*
|
||||
* $ NET USE \\server\share\dir
|
||||
* $ git -C //server/share/repo status
|
||||
*
|
||||
* (c) Windows allows "SUBST" to create a fake drive mapping to an
|
||||
* arbitrary path (which may be remote)
|
||||
*
|
||||
* $ SUBST Q: Z:\repo
|
||||
* $ git -C Q:/ status
|
||||
*
|
||||
* (d) Windows allows a directory symlink to be created on a local
|
||||
* file system that points to a remote repo.
|
||||
*
|
||||
* $ mklink /d ./link //server/share/repo
|
||||
* $ git -C ./link status
|
||||
*/
|
||||
static enum fsmonitor_reason check_remote(struct repository *r)
|
||||
{
|
||||
int ret;
|
||||
wchar_t wpath[MAX_PATH];
|
||||
wchar_t wfullpath[MAX_PATH];
|
||||
size_t wlen;
|
||||
UINT driveType;
|
||||
|
||||
/*
|
||||
* Do everything in wide chars because the drive letter might be
|
||||
* a multi-byte sequence. See win32_has_dos_drive_prefix().
|
||||
*/
|
||||
if (xutftowcs_path(wpath, r->worktree) < 0)
|
||||
return FSMONITOR_REASON_ERROR;
|
||||
|
||||
/*
|
||||
* GetDriveTypeW() requires a final slash. We assume that the
|
||||
* worktree pathname points to an actual directory.
|
||||
*/
|
||||
wlen = wcslen(wpath);
|
||||
if (wpath[wlen - 1] != L'\\' && wpath[wlen - 1] != L'/') {
|
||||
wpath[wlen++] = L'\\';
|
||||
wpath[wlen] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalize the path. If nothing else, this converts forward
|
||||
* slashes to backslashes. This is essential to get GetDriveTypeW()
|
||||
* correctly handle some UNC "\\server\share\..." paths.
|
||||
*/
|
||||
if (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))
|
||||
return FSMONITOR_REASON_ERROR;
|
||||
|
||||
driveType = GetDriveTypeW(wfullpath);
|
||||
trace_printf_key(&trace_fsmonitor,
|
||||
"DriveType '%s' L'%ls' (%u)",
|
||||
r->worktree, wfullpath, driveType);
|
||||
|
||||
if (driveType == DRIVE_REMOTE) {
|
||||
trace_printf_key(&trace_fsmonitor,
|
||||
"check_remote('%s') true",
|
||||
r->worktree);
|
||||
|
||||
ret = check_remote_protocol(wfullpath);
|
||||
if (ret < 0)
|
||||
return FSMONITOR_REASON_ERROR;
|
||||
|
||||
switch (check_config_allowremote(r)) {
|
||||
case 0: /* config overrides and disables */
|
||||
return FSMONITOR_REASON_REMOTE;
|
||||
case 1: /* config overrides and enables */
|
||||
return FSMONITOR_REASON_OK;
|
||||
default:
|
||||
break; /* config has no opinion */
|
||||
}
|
||||
|
||||
return FSMONITOR_REASON_REMOTE;
|
||||
}
|
||||
|
||||
return FSMONITOR_REASON_OK;
|
||||
}
|
||||
|
||||
enum fsmonitor_reason fsm_os__incompatible(struct repository *r)
|
||||
enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)
|
||||
{
|
||||
enum fsmonitor_reason reason;
|
||||
|
||||
@ -197,9 +33,5 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)
|
||||
if (reason != FSMONITOR_REASON_OK)
|
||||
return reason;
|
||||
|
||||
reason = check_remote(r);
|
||||
if (reason != FSMONITOR_REASON_OK)
|
||||
return reason;
|
||||
|
||||
return FSMONITOR_REASON_OK;
|
||||
}
|
||||
|
@ -308,6 +308,8 @@ if(SUPPORTS_SIMPLE_IPC)
|
||||
add_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)
|
||||
list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)
|
||||
list(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)
|
||||
list(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)
|
||||
list(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)
|
||||
|
||||
add_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)
|
||||
list(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)
|
||||
@ -315,6 +317,8 @@ if(SUPPORTS_SIMPLE_IPC)
|
||||
add_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)
|
||||
list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)
|
||||
list(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)
|
||||
list(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)
|
||||
list(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)
|
||||
|
||||
add_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)
|
||||
list(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "run-command.h"
|
||||
#include "simple-ipc.h"
|
||||
#include "thread-utils.h"
|
||||
#include "fsmonitor-path-utils.h"
|
||||
|
||||
struct fsmonitor_batch;
|
||||
struct fsmonitor_token_data;
|
||||
@ -43,6 +44,7 @@ struct fsmonitor_daemon_state {
|
||||
|
||||
struct strbuf path_worktree_watch;
|
||||
struct strbuf path_gitdir_watch;
|
||||
struct alias_info alias;
|
||||
int nr_paths_watching;
|
||||
|
||||
struct fsmonitor_token_data *current_token_data;
|
||||
@ -59,6 +61,7 @@ struct fsmonitor_daemon_state {
|
||||
|
||||
struct ipc_server_data *ipc_server_data;
|
||||
struct strbuf path_ipc;
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -18,7 +18,7 @@ int fsmonitor_ipc__is_supported(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *fsmonitor_ipc__get_path(void)
|
||||
const char *fsmonitor_ipc__get_path(struct repository *r)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@ -47,11 +47,9 @@ int fsmonitor_ipc__is_supported(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
GIT_PATH_FUNC(fsmonitor_ipc__get_path, "fsmonitor--daemon.ipc")
|
||||
|
||||
enum ipc_active_state fsmonitor_ipc__get_state(void)
|
||||
{
|
||||
return ipc_get_active_state(fsmonitor_ipc__get_path());
|
||||
return ipc_get_active_state(fsmonitor_ipc__get_path(the_repository));
|
||||
}
|
||||
|
||||
static int spawn_daemon(void)
|
||||
@ -81,8 +79,8 @@ int fsmonitor_ipc__send_query(const char *since_token,
|
||||
trace2_data_string("fsm_client", NULL, "query/command", tok);
|
||||
|
||||
try_again:
|
||||
state = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,
|
||||
&connection);
|
||||
state = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),
|
||||
&options, &connection);
|
||||
|
||||
switch (state) {
|
||||
case IPC_STATE__LISTENING:
|
||||
@ -117,13 +115,13 @@ try_again:
|
||||
|
||||
case IPC_STATE__INVALID_PATH:
|
||||
ret = error(_("fsmonitor_ipc__send_query: invalid path '%s'"),
|
||||
fsmonitor_ipc__get_path());
|
||||
fsmonitor_ipc__get_path(the_repository));
|
||||
goto done;
|
||||
|
||||
case IPC_STATE__OTHER_ERROR:
|
||||
default:
|
||||
ret = error(_("fsmonitor_ipc__send_query: unspecified error on '%s'"),
|
||||
fsmonitor_ipc__get_path());
|
||||
fsmonitor_ipc__get_path(the_repository));
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -149,8 +147,8 @@ int fsmonitor_ipc__send_command(const char *command,
|
||||
options.wait_if_busy = 1;
|
||||
options.wait_if_not_found = 0;
|
||||
|
||||
state = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,
|
||||
&connection);
|
||||
state = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),
|
||||
&options, &connection);
|
||||
if (state != IPC_STATE__LISTENING) {
|
||||
die(_("fsmonitor--daemon is not running"));
|
||||
return -1;
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include "simple-ipc.h"
|
||||
|
||||
struct repository;
|
||||
|
||||
/*
|
||||
* Returns true if built-in file system monitor daemon is defined
|
||||
* for this platform.
|
||||
@ -16,7 +18,7 @@ int fsmonitor_ipc__is_supported(void);
|
||||
*
|
||||
* Returns NULL if the daemon is not supported on this platform.
|
||||
*/
|
||||
const char *fsmonitor_ipc__get_path(void);
|
||||
const char *fsmonitor_ipc__get_path(struct repository *r);
|
||||
|
||||
/*
|
||||
* Try to determine whether there is a `git-fsmonitor--daemon` process
|
||||
|
60
fsmonitor-path-utils.h
Normal file
60
fsmonitor-path-utils.h
Normal file
@ -0,0 +1,60 @@
|
||||
#ifndef FSM_PATH_UTILS_H
|
||||
#define FSM_PATH_UTILS_H
|
||||
|
||||
#include "strbuf.h"
|
||||
|
||||
struct alias_info
|
||||
{
|
||||
struct strbuf alias;
|
||||
struct strbuf points_to;
|
||||
};
|
||||
|
||||
struct fs_info {
|
||||
int is_remote;
|
||||
char *typename;
|
||||
};
|
||||
|
||||
/*
|
||||
* Get some basic filesystem information for the given path
|
||||
*
|
||||
* The caller owns the storage that is occupied by fs_info and
|
||||
* is responsible for releasing it.
|
||||
*
|
||||
* Returns -1 on error, zero otherwise.
|
||||
*/
|
||||
int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);
|
||||
|
||||
/*
|
||||
* Determines if the filesystem that path resides on is remote.
|
||||
*
|
||||
* Returns -1 on error, 0 if not remote, 1 if remote.
|
||||
*/
|
||||
int fsmonitor__is_fs_remote(const char *path);
|
||||
|
||||
/*
|
||||
* Get the alias in given path, if any.
|
||||
*
|
||||
* Sets alias to the first alias that matches any part of the path.
|
||||
*
|
||||
* If an alias is found, info.alias and info.points_to are set to the
|
||||
* found mapping.
|
||||
*
|
||||
* Returns -1 on error, 0 otherwise.
|
||||
*
|
||||
* The caller owns the storage that is occupied by info.alias and
|
||||
* info.points_to and is responsible for releasing it.
|
||||
*/
|
||||
int fsmonitor__get_alias(const char *path, struct alias_info *info);
|
||||
|
||||
/*
|
||||
* Resolve the path against the given alias.
|
||||
*
|
||||
* Returns the resolved path if there is one, NULL otherwise.
|
||||
*
|
||||
* The caller owns the storage that the returned string occupies and
|
||||
* is responsible for releasing it.
|
||||
*/
|
||||
char *fsmonitor__resolve_alias(const char *path,
|
||||
const struct alias_info *info);
|
||||
|
||||
#endif
|
@ -1,7 +1,9 @@
|
||||
#include "cache.h"
|
||||
#include "config.h"
|
||||
#include "repository.h"
|
||||
#include "fsmonitor-ipc.h"
|
||||
#include "fsmonitor-settings.h"
|
||||
#include "fsmonitor-path-utils.h"
|
||||
|
||||
/*
|
||||
* We keep this structure defintion private and have getters
|
||||
@ -13,7 +15,53 @@ struct fsmonitor_settings {
|
||||
char *hook_path;
|
||||
};
|
||||
|
||||
static enum fsmonitor_reason check_for_incompatible(struct repository *r)
|
||||
/*
|
||||
* Remote working directories are problematic for FSMonitor.
|
||||
*
|
||||
* The underlying file system on the server machine and/or the remote
|
||||
* mount type dictates whether notification events are available at
|
||||
* all to remote client machines.
|
||||
*
|
||||
* Kernel differences between the server and client machines also
|
||||
* dictate the how (buffering, frequency, de-dup) the events are
|
||||
* delivered to client machine processes.
|
||||
*
|
||||
* A client machine (such as a laptop) may choose to suspend/resume
|
||||
* and it is unclear (without lots of testing) whether the watcher can
|
||||
* resync after a resume. We might be able to treat this as a normal
|
||||
* "events were dropped by the kernel" event and do our normal "flush
|
||||
* and resync" --or-- we might need to close the existing (zombie?)
|
||||
* notification fd and create a new one.
|
||||
*
|
||||
* In theory, the above issues need to be addressed whether we are
|
||||
* using the Hook or IPC API.
|
||||
*
|
||||
* So (for now at least), mark remote working directories as
|
||||
* incompatible unless 'fsmonitor.allowRemote' is true.
|
||||
*
|
||||
*/
|
||||
#ifdef HAVE_FSMONITOR_OS_SETTINGS
|
||||
static enum fsmonitor_reason check_remote(struct repository *r)
|
||||
{
|
||||
int allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */
|
||||
int is_remote = fsmonitor__is_fs_remote(r->worktree);
|
||||
|
||||
switch (is_remote) {
|
||||
case 0:
|
||||
return FSMONITOR_REASON_OK;
|
||||
case 1:
|
||||
repo_config_get_bool(r, "fsmonitor.allowremote", &allow_remote);
|
||||
if (allow_remote < 1)
|
||||
return FSMONITOR_REASON_REMOTE;
|
||||
else
|
||||
return FSMONITOR_REASON_OK;
|
||||
default:
|
||||
return FSMONITOR_REASON_ERROR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)
|
||||
{
|
||||
if (!r->worktree) {
|
||||
/*
|
||||
@ -27,7 +75,10 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)
|
||||
{
|
||||
enum fsmonitor_reason reason;
|
||||
|
||||
reason = fsm_os__incompatible(r);
|
||||
reason = check_remote(r);
|
||||
if (reason != FSMONITOR_REASON_OK)
|
||||
return reason;
|
||||
reason = fsm_os__incompatible(r, ipc);
|
||||
if (reason != FSMONITOR_REASON_OK)
|
||||
return reason;
|
||||
}
|
||||
@ -112,7 +163,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)
|
||||
|
||||
void fsm_settings__set_ipc(struct repository *r)
|
||||
{
|
||||
enum fsmonitor_reason reason = check_for_incompatible(r);
|
||||
enum fsmonitor_reason reason = check_for_incompatible(r, 1);
|
||||
|
||||
if (reason != FSMONITOR_REASON_OK) {
|
||||
fsm_settings__set_incompatible(r, reason);
|
||||
@ -135,7 +186,7 @@ void fsm_settings__set_ipc(struct repository *r)
|
||||
|
||||
void fsm_settings__set_hook(struct repository *r, const char *path)
|
||||
{
|
||||
enum fsmonitor_reason reason = check_for_incompatible(r);
|
||||
enum fsmonitor_reason reason = check_for_incompatible(r, 0);
|
||||
|
||||
if (reason != FSMONITOR_REASON_OK) {
|
||||
fsm_settings__set_incompatible(r, reason);
|
||||
@ -192,10 +243,11 @@ enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)
|
||||
return r->settings.fsmonitor->reason;
|
||||
}
|
||||
|
||||
char *fsm_settings__get_incompatible_msg(const struct repository *r,
|
||||
char *fsm_settings__get_incompatible_msg(struct repository *r,
|
||||
enum fsmonitor_reason reason)
|
||||
{
|
||||
struct strbuf msg = STRBUF_INIT;
|
||||
const char *socket_dir;
|
||||
|
||||
switch (reason) {
|
||||
case FSMONITOR_REASON_UNTESTED:
|
||||
@ -231,9 +283,11 @@ char *fsm_settings__get_incompatible_msg(const struct repository *r,
|
||||
goto done;
|
||||
|
||||
case FSMONITOR_REASON_NOSOCKETS:
|
||||
socket_dir = dirname((char *)fsmonitor_ipc__get_path(r));
|
||||
strbuf_addf(&msg,
|
||||
_("repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"),
|
||||
r->worktree);
|
||||
_("socket directory '%s' is incompatible with fsmonitor due"
|
||||
" to lack of Unix sockets support"),
|
||||
socket_dir);
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);
|
||||
const char *fsm_settings__get_hook_path(struct repository *r);
|
||||
|
||||
enum fsmonitor_reason fsm_settings__get_reason(struct repository *r);
|
||||
char *fsm_settings__get_incompatible_msg(const struct repository *r,
|
||||
char *fsm_settings__get_incompatible_msg(struct repository *r,
|
||||
enum fsmonitor_reason reason);
|
||||
|
||||
struct fsmonitor_settings;
|
||||
@ -48,7 +48,7 @@ struct fsmonitor_settings;
|
||||
* fsm_os__* routines should considered private to fsm_settings__
|
||||
* routines.
|
||||
*/
|
||||
enum fsmonitor_reason fsm_os__incompatible(struct repository *r);
|
||||
enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);
|
||||
#endif /* HAVE_FSMONITOR_OS_SETTINGS */
|
||||
|
||||
#endif /* FSMONITOR_SETTINGS_H */
|
||||
|
@ -295,6 +295,7 @@ static int fsmonitor_force_update_threshold = 100;
|
||||
|
||||
void refresh_fsmonitor(struct index_state *istate)
|
||||
{
|
||||
static int warn_once = 0;
|
||||
struct strbuf query_result = STRBUF_INIT;
|
||||
int query_success = 0, hook_version = -1;
|
||||
size_t bol = 0; /* beginning of line */
|
||||
@ -305,6 +306,14 @@ void refresh_fsmonitor(struct index_state *istate)
|
||||
int is_trivial = 0;
|
||||
struct repository *r = istate->repo ? istate->repo : the_repository;
|
||||
enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
|
||||
enum fsmonitor_reason reason = fsm_settings__get_reason(r);
|
||||
|
||||
if (!warn_once && reason > FSMONITOR_REASON_OK) {
|
||||
char *msg = fsm_settings__get_incompatible_msg(r, reason);
|
||||
warn_once = 1;
|
||||
warning("%s", msg);
|
||||
free(msg);
|
||||
}
|
||||
|
||||
if (fsm_mode <= FSMONITOR_MODE_DISABLED ||
|
||||
istate->fsmonitor_has_run_once)
|
||||
|
Loading…
Reference in New Issue
Block a user