2017-09-22 16:35:40 +00:00
|
|
|
#include "cache.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "dir.h"
|
|
|
|
#include "ewah/ewok.h"
|
|
|
|
#include "fsmonitor.h"
|
|
|
|
#include "run-command.h"
|
|
|
|
#include "strbuf.h"
|
|
|
|
|
2020-01-07 19:04:28 +00:00
|
|
|
#define INDEX_EXTENSION_VERSION1 (1)
|
|
|
|
#define INDEX_EXTENSION_VERSION2 (2)
|
2020-01-07 19:04:29 +00:00
|
|
|
#define HOOK_INTERFACE_VERSION1 (1)
|
|
|
|
#define HOOK_INTERFACE_VERSION2 (2)
|
2017-09-22 16:35:40 +00:00
|
|
|
|
|
|
|
struct trace_key trace_fsmonitor = TRACE_KEY_INIT(FSMONITOR);
|
|
|
|
|
2021-01-23 19:58:14 +00:00
|
|
|
static void assert_index_minimum(struct index_state *istate, size_t pos)
|
|
|
|
{
|
|
|
|
if (pos > istate->cache_nr)
|
|
|
|
BUG("fsmonitor_dirty has more entries than the index (%"PRIuMAX" > %u)",
|
|
|
|
(uintmax_t)pos, istate->cache_nr);
|
|
|
|
}
|
|
|
|
|
2017-09-22 16:35:40 +00:00
|
|
|
static void fsmonitor_ewah_callback(size_t pos, void *is)
|
|
|
|
{
|
|
|
|
struct index_state *istate = (struct index_state *)is;
|
2019-10-11 20:11:23 +00:00
|
|
|
struct cache_entry *ce;
|
2017-09-22 16:35:40 +00:00
|
|
|
|
2021-01-23 19:58:14 +00:00
|
|
|
assert_index_minimum(istate, pos + 1);
|
2019-10-11 20:11:23 +00:00
|
|
|
|
|
|
|
ce = istate->cache[pos];
|
2017-09-22 16:35:40 +00:00
|
|
|
ce->ce_flags &= ~CE_FSMONITOR_VALID;
|
|
|
|
}
|
|
|
|
|
2020-01-07 19:04:29 +00:00
|
|
|
static int fsmonitor_hook_version(void)
|
|
|
|
{
|
|
|
|
int hook_version;
|
|
|
|
|
|
|
|
if (git_config_get_int("core.fsmonitorhookversion", &hook_version))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (hook_version == HOOK_INTERFACE_VERSION1 ||
|
|
|
|
hook_version == HOOK_INTERFACE_VERSION2)
|
|
|
|
return hook_version;
|
|
|
|
|
|
|
|
warning("Invalid hook version '%i' in core.fsmonitorhookversion. "
|
|
|
|
"Must be 1 or 2.", hook_version);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-09-22 16:35:40 +00:00
|
|
|
int read_fsmonitor_extension(struct index_state *istate, const void *data,
|
|
|
|
unsigned long sz)
|
|
|
|
{
|
|
|
|
const char *index = data;
|
|
|
|
uint32_t hdr_version;
|
|
|
|
uint32_t ewah_size;
|
|
|
|
struct ewah_bitmap *fsmonitor_dirty;
|
|
|
|
int ret;
|
2020-01-07 19:04:28 +00:00
|
|
|
uint64_t timestamp;
|
|
|
|
struct strbuf last_update = STRBUF_INIT;
|
2017-09-22 16:35:40 +00:00
|
|
|
|
2020-01-07 19:04:28 +00:00
|
|
|
if (sz < sizeof(uint32_t) + 1 + sizeof(uint32_t))
|
2017-09-22 16:35:40 +00:00
|
|
|
return error("corrupt fsmonitor extension (too short)");
|
|
|
|
|
|
|
|
hdr_version = get_be32(index);
|
|
|
|
index += sizeof(uint32_t);
|
2020-01-07 19:04:28 +00:00
|
|
|
if (hdr_version == INDEX_EXTENSION_VERSION1) {
|
|
|
|
timestamp = get_be64(index);
|
|
|
|
strbuf_addf(&last_update, "%"PRIu64"", timestamp);
|
|
|
|
index += sizeof(uint64_t);
|
|
|
|
} else if (hdr_version == INDEX_EXTENSION_VERSION2) {
|
|
|
|
strbuf_addstr(&last_update, index);
|
|
|
|
index += last_update.len + 1;
|
|
|
|
} else {
|
2017-09-22 16:35:40 +00:00
|
|
|
return error("bad fsmonitor version %d", hdr_version);
|
2020-01-07 19:04:28 +00:00
|
|
|
}
|
2017-09-22 16:35:40 +00:00
|
|
|
|
2020-01-07 19:04:28 +00:00
|
|
|
istate->fsmonitor_last_update = strbuf_detach(&last_update, NULL);
|
2017-09-22 16:35:40 +00:00
|
|
|
|
|
|
|
ewah_size = get_be32(index);
|
|
|
|
index += sizeof(uint32_t);
|
|
|
|
|
|
|
|
fsmonitor_dirty = ewah_new();
|
|
|
|
ret = ewah_read_mmap(fsmonitor_dirty, index, ewah_size);
|
|
|
|
if (ret != ewah_size) {
|
|
|
|
ewah_free(fsmonitor_dirty);
|
|
|
|
return error("failed to parse ewah bitmap reading fsmonitor index extension");
|
|
|
|
}
|
2017-10-27 23:26:37 +00:00
|
|
|
istate->fsmonitor_dirty = fsmonitor_dirty;
|
2017-09-22 16:35:40 +00:00
|
|
|
|
2021-01-23 19:58:14 +00:00
|
|
|
if (!istate->split_index)
|
|
|
|
assert_index_minimum(istate, istate->fsmonitor_dirty->bit_size);
|
2019-10-11 20:11:23 +00:00
|
|
|
|
2021-02-03 15:34:48 +00:00
|
|
|
trace2_data_string("index", NULL, "extension/fsmn/read/token",
|
|
|
|
istate->fsmonitor_last_update);
|
|
|
|
trace_printf_key(&trace_fsmonitor,
|
|
|
|
"read fsmonitor extension successful '%s'",
|
|
|
|
istate->fsmonitor_last_update);
|
2017-09-22 16:35:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-11-09 19:58:10 +00:00
|
|
|
void fill_fsmonitor_bitmap(struct index_state *istate)
|
|
|
|
{
|
2019-10-11 20:11:23 +00:00
|
|
|
unsigned int i, skipped = 0;
|
2017-11-09 19:58:10 +00:00
|
|
|
istate->fsmonitor_dirty = ewah_new();
|
2019-10-11 20:11:23 +00:00
|
|
|
for (i = 0; i < istate->cache_nr; i++) {
|
|
|
|
if (istate->cache[i]->ce_flags & CE_REMOVE)
|
|
|
|
skipped++;
|
|
|
|
else if (!(istate->cache[i]->ce_flags & CE_FSMONITOR_VALID))
|
|
|
|
ewah_set(istate->fsmonitor_dirty, i - skipped);
|
|
|
|
}
|
2017-11-09 19:58:10 +00:00
|
|
|
}
|
|
|
|
|
2017-09-22 16:35:40 +00:00
|
|
|
void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)
|
|
|
|
{
|
|
|
|
uint32_t hdr_version;
|
|
|
|
uint32_t ewah_start;
|
|
|
|
uint32_t ewah_size = 0;
|
|
|
|
int fixup = 0;
|
|
|
|
|
2021-01-23 19:58:14 +00:00
|
|
|
if (!istate->split_index)
|
|
|
|
assert_index_minimum(istate, istate->fsmonitor_dirty->bit_size);
|
2019-10-11 20:11:23 +00:00
|
|
|
|
2020-01-07 19:04:28 +00:00
|
|
|
put_be32(&hdr_version, INDEX_EXTENSION_VERSION2);
|
2017-09-22 16:35:40 +00:00
|
|
|
strbuf_add(sb, &hdr_version, sizeof(uint32_t));
|
|
|
|
|
2020-01-07 19:04:28 +00:00
|
|
|
strbuf_addstr(sb, istate->fsmonitor_last_update);
|
|
|
|
strbuf_addch(sb, 0); /* Want to keep a NUL */
|
|
|
|
|
2017-09-22 16:35:40 +00:00
|
|
|
fixup = sb->len;
|
|
|
|
strbuf_add(sb, &ewah_size, sizeof(uint32_t)); /* we'll fix this up later */
|
|
|
|
|
|
|
|
ewah_start = sb->len;
|
2017-11-09 19:58:10 +00:00
|
|
|
ewah_serialize_strbuf(istate->fsmonitor_dirty, sb);
|
|
|
|
ewah_free(istate->fsmonitor_dirty);
|
|
|
|
istate->fsmonitor_dirty = NULL;
|
2017-09-22 16:35:40 +00:00
|
|
|
|
|
|
|
/* fix up size field */
|
|
|
|
put_be32(&ewah_size, sb->len - ewah_start);
|
|
|
|
memcpy(sb->buf + fixup, &ewah_size, sizeof(uint32_t));
|
|
|
|
|
2021-02-03 15:34:48 +00:00
|
|
|
trace2_data_string("index", NULL, "extension/fsmn/write/token",
|
|
|
|
istate->fsmonitor_last_update);
|
|
|
|
trace_printf_key(&trace_fsmonitor,
|
|
|
|
"write fsmonitor extension successful '%s'",
|
|
|
|
istate->fsmonitor_last_update);
|
2017-09-22 16:35:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2020-01-07 19:04:28 +00:00
|
|
|
* Call the query-fsmonitor hook passing the last update token of the saved results.
|
2017-09-22 16:35:40 +00:00
|
|
|
*/
|
2020-01-07 19:04:28 +00:00
|
|
|
static int query_fsmonitor(int version, const char *last_update, struct strbuf *query_result)
|
2017-09-22 16:35:40 +00:00
|
|
|
{
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
2021-02-03 15:34:47 +00:00
|
|
|
int result;
|
2017-09-22 16:35:40 +00:00
|
|
|
|
2018-05-19 08:27:46 +00:00
|
|
|
if (!core_fsmonitor)
|
2017-09-22 16:35:40 +00:00
|
|
|
return -1;
|
|
|
|
|
2020-07-28 20:24:53 +00:00
|
|
|
strvec_push(&cp.args, core_fsmonitor);
|
|
|
|
strvec_pushf(&cp.args, "%d", version);
|
|
|
|
strvec_pushf(&cp.args, "%s", last_update);
|
2017-09-22 16:35:40 +00:00
|
|
|
cp.use_shell = 1;
|
2017-10-27 23:26:34 +00:00
|
|
|
cp.dir = get_git_work_tree();
|
2017-09-22 16:35:40 +00:00
|
|
|
|
2021-02-03 15:34:47 +00:00
|
|
|
trace2_region_enter("fsm_hook", "query", NULL);
|
|
|
|
|
|
|
|
result = capture_command(&cp, query_result, 1024);
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
trace2_data_intmax("fsm_hook", NULL, "query/failed", result);
|
|
|
|
else {
|
|
|
|
trace2_data_intmax("fsm_hook", NULL, "query/response-length",
|
|
|
|
query_result->len);
|
|
|
|
|
|
|
|
if (fsmonitor_is_trivial_response(query_result))
|
|
|
|
trace2_data_intmax("fsm_hook", NULL,
|
|
|
|
"query/trivial-response", 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
trace2_region_leave("fsm_hook", "query", NULL);
|
|
|
|
|
|
|
|
return result;
|
2017-09-22 16:35:40 +00:00
|
|
|
}
|
|
|
|
|
2021-02-03 15:34:47 +00:00
|
|
|
int fsmonitor_is_trivial_response(const struct strbuf *query_result)
|
2017-09-22 16:35:40 +00:00
|
|
|
{
|
2021-02-03 15:34:47 +00:00
|
|
|
static char trivial_response[3] = { '\0', '/', '\0' };
|
|
|
|
|
fsmonitor: avoid global-buffer-overflow READ when checking trivial response
query_result can be be an empty strbuf (STRBUF_INIT) - in that case
trying to read 3 bytes triggers a buffer overflow read (as
query_result.buf = '\0').
Therefore we need to check query_result's length before trying to read 3
bytes.
This overflow was introduced in:
940b94f35c (fsmonitor: log invocation of FSMonitor hook to trace2, 2021-02-03)
It was found when running the test-suite against ASAN, and can be most
easily reproduced with the following command:
make GIT_TEST_OPTS="-v" DEFAULT_TEST_TARGET="t7519-status-fsmonitor.sh" \
SANITIZE=address DEVELOPER=1 test
==2235==ERROR: AddressSanitizer: global-buffer-overflow on address 0x0000019e6e5e at pc 0x00000043745c bp 0x7fffd382c520 sp 0x7fffd382bcc8
READ of size 3 at 0x0000019e6e5e thread T0
#0 0x43745b in MemcmpInterceptorCommon(void*, int (*)(void const*, void const*, unsigned long), void const*, void const*, unsigned long) /home/abuild/rpmbuild/BUILD/llvm-11.0.0.src/build/../projects/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:842:7
#1 0x43786d in bcmp /home/abuild/rpmbuild/BUILD/llvm-11.0.0.src/build/../projects/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:887:10
#2 0x80b146 in fsmonitor_is_trivial_response /home/ahunt/oss-fuzz/git/fsmonitor.c:192:10
#3 0x80b146 in query_fsmonitor /home/ahunt/oss-fuzz/git/fsmonitor.c:175:7
#4 0x80a749 in refresh_fsmonitor /home/ahunt/oss-fuzz/git/fsmonitor.c:267:21
#5 0x80bad1 in tweak_fsmonitor /home/ahunt/oss-fuzz/git/fsmonitor.c:429:4
#6 0x90f040 in read_index_from /home/ahunt/oss-fuzz/git/read-cache.c:2321:3
#7 0x8e5d08 in repo_read_index_preload /home/ahunt/oss-fuzz/git/preload-index.c:164:15
#8 0x52dd45 in prepare_index /home/ahunt/oss-fuzz/git/builtin/commit.c:363:6
#9 0x52a188 in cmd_commit /home/ahunt/oss-fuzz/git/builtin/commit.c:1588:15
#10 0x4ce77e in run_builtin /home/ahunt/oss-fuzz/git/git.c:453:11
#11 0x4ccb18 in handle_builtin /home/ahunt/oss-fuzz/git/git.c:704:3
#12 0x4cb01c in run_argv /home/ahunt/oss-fuzz/git/git.c:771:4
#13 0x4cb01c in cmd_main /home/ahunt/oss-fuzz/git/git.c:902:19
#14 0x6aca8d in main /home/ahunt/oss-fuzz/git/common-main.c:52:11
#15 0x7fb027bf5349 in __libc_start_main (/lib64/libc.so.6+0x24349)
#16 0x4206b9 in _start /home/abuild/rpmbuild/BUILD/glibc-2.26/csu/../sysdeps/x86_64/start.S:120
0x0000019e6e5e is located 2 bytes to the left of global variable 'strbuf_slopbuf' defined in 'strbuf.c:51:6' (0x19e6e60) of size 1
'strbuf_slopbuf' is ascii string ''
0x0000019e6e5e is located 126 bytes to the right of global variable 'signals' defined in 'sigchain.c:11:31' (0x19e6be0) of size 512
SUMMARY: AddressSanitizer: global-buffer-overflow /home/abuild/rpmbuild/BUILD/llvm-11.0.0.src/build/../projects/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:842:7 in MemcmpInterceptorCommon(void*, int (*)(void const*, void const*, unsigned long), void const*, void const*, unsigned long)
Shadow bytes around the buggy address:
0x000080334d70: f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 00 00 00 00
0x000080334d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080334d90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080334da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080334db0: 00 00 00 00 00 00 00 00 00 00 00 00 f9 f9 f9 f9
=>0x000080334dc0: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9[f9]01 f9 f9 f9
0x000080334dd0: f9 f9 f9 f9 03 f9 f9 f9 f9 f9 f9 f9 02 f9 f9 f9
0x000080334de0: f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9
0x000080334df0: f9 f9 f9 f9 01 f9 f9 f9 f9 f9 f9 f9 00 00 00 00
0x000080334e00: f9 f9 f9 f9 00 00 00 00 f9 f9 f9 f9 01 f9 f9 f9
0x000080334e10: f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
Signed-off-by: Andrzej Hunt <ajrhunt@google.com>
Acked-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-03-15 16:39:23 +00:00
|
|
|
return query_result->len >= 3 &&
|
|
|
|
!memcmp(trivial_response,
|
|
|
|
&query_result->buf[query_result->len - 3], 3);
|
2017-09-22 16:35:40 +00:00
|
|
|
}
|
|
|
|
|
2021-02-03 15:34:49 +00:00
|
|
|
static void fsmonitor_refresh_callback(struct index_state *istate, char *name)
|
2017-09-22 16:35:40 +00:00
|
|
|
{
|
2021-02-03 15:34:49 +00:00
|
|
|
int i, len = strlen(name);
|
|
|
|
if (name[len - 1] == '/') {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO We should binary search to find the first path with
|
|
|
|
* TODO this directory prefix. Then linearly update entries
|
|
|
|
* TODO while the prefix matches. Taking care to search without
|
|
|
|
* TODO the trailing slash -- because '/' sorts after a few
|
|
|
|
* TODO interesting special chars, like '.' and ' '.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Mark all entries for the folder invalid */
|
|
|
|
for (i = 0; i < istate->cache_nr; i++) {
|
|
|
|
if (istate->cache[i]->ce_flags & CE_FSMONITOR_VALID &&
|
|
|
|
starts_with(istate->cache[i]->name, name))
|
|
|
|
istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
|
|
|
|
}
|
|
|
|
/* Need to remove the / from the path for the untracked cache */
|
|
|
|
name[len - 1] = '\0';
|
|
|
|
} else {
|
|
|
|
int pos = index_name_pos(istate, name, strlen(name));
|
2017-09-22 16:35:40 +00:00
|
|
|
|
2021-02-03 15:34:49 +00:00
|
|
|
if (pos >= 0) {
|
|
|
|
struct cache_entry *ce = istate->cache[pos];
|
|
|
|
ce->ce_flags &= ~CE_FSMONITOR_VALID;
|
|
|
|
}
|
2017-09-22 16:35:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark the untracked cache dirty even if it wasn't found in the index
|
|
|
|
* as it could be a new untracked file.
|
|
|
|
*/
|
|
|
|
trace_printf_key(&trace_fsmonitor, "fsmonitor_refresh_callback '%s'", name);
|
dir.c: ignore paths containing .git when invalidating untracked cache
read_directory() code ignores all paths named ".git" even if it's not
a valid git repository. See treat_path() for details. Since ".git" is
basically invisible to read_directory(), when we are asked to
invalidate a path that contains ".git", we can safely ignore it
because the slow path would not consider it anyway.
This helps when fsmonitor is used and we have a real ".git" repo at
worktree top. Occasionally .git/index will be updated and if the
fsmonitor hook does not filter it, untracked cache is asked to
invalidate the path ".git/index".
Without this patch, we invalidate the root directory unncessarily,
which:
- makes read_directory() fall back to slow path for root directory
(slower)
- makes the index dirty (because UNTR extension is updated). Depending
on the index size, writing it down could also be slow.
A note about the new "safe_path" knob. Since this new check could be
relatively expensive, avoid it when we know it's not needed. If the
path comes from the index, it can't contain ".git". If it does
contain, we may be screwed up at many more levels, not just this one.
Noticed-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-07 09:21:40 +00:00
|
|
|
untracked_cache_invalidate_path(istate, name, 0);
|
2017-09-22 16:35:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void refresh_fsmonitor(struct index_state *istate)
|
|
|
|
{
|
|
|
|
struct strbuf query_result = STRBUF_INIT;
|
2020-01-07 19:04:29 +00:00
|
|
|
int query_success = 0, hook_version = -1;
|
|
|
|
size_t bol = 0; /* beginning of line */
|
2017-09-22 16:35:40 +00:00
|
|
|
uint64_t last_update;
|
2020-01-07 19:04:28 +00:00
|
|
|
struct strbuf last_update_token = STRBUF_INIT;
|
2017-09-22 16:35:40 +00:00
|
|
|
char *buf;
|
2019-06-15 16:11:35 +00:00
|
|
|
unsigned int i;
|
2017-09-22 16:35:40 +00:00
|
|
|
|
2019-05-07 11:10:21 +00:00
|
|
|
if (!core_fsmonitor || istate->fsmonitor_has_run_once)
|
2017-09-22 16:35:40 +00:00
|
|
|
return;
|
2020-01-07 19:04:29 +00:00
|
|
|
|
|
|
|
hook_version = fsmonitor_hook_version();
|
|
|
|
|
2019-05-07 11:10:21 +00:00
|
|
|
istate->fsmonitor_has_run_once = 1;
|
2017-09-22 16:35:40 +00:00
|
|
|
|
|
|
|
trace_printf_key(&trace_fsmonitor, "refresh fsmonitor");
|
|
|
|
/*
|
|
|
|
* This could be racy so save the date/time now and query_fsmonitor
|
|
|
|
* should be inclusive to ensure we don't miss potential changes.
|
|
|
|
*/
|
|
|
|
last_update = getnanotime();
|
2020-01-07 19:04:29 +00:00
|
|
|
if (hook_version == HOOK_INTERFACE_VERSION1)
|
|
|
|
strbuf_addf(&last_update_token, "%"PRIu64"", last_update);
|
2017-09-22 16:35:40 +00:00
|
|
|
|
|
|
|
/*
|
2020-01-07 19:04:29 +00:00
|
|
|
* If we have a last update token, call query_fsmonitor for the set of
|
|
|
|
* changes since that token, else assume everything is possibly dirty
|
2017-09-22 16:35:40 +00:00
|
|
|
* and check it all.
|
|
|
|
*/
|
|
|
|
if (istate->fsmonitor_last_update) {
|
2020-01-07 19:04:29 +00:00
|
|
|
if (hook_version == -1 || hook_version == HOOK_INTERFACE_VERSION2) {
|
|
|
|
query_success = !query_fsmonitor(HOOK_INTERFACE_VERSION2,
|
|
|
|
istate->fsmonitor_last_update, &query_result);
|
|
|
|
|
|
|
|
if (query_success) {
|
|
|
|
if (hook_version < 0)
|
|
|
|
hook_version = HOOK_INTERFACE_VERSION2;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First entry will be the last update token
|
|
|
|
* Need to use a char * variable because static
|
|
|
|
* analysis was suggesting to use strbuf_addbuf
|
|
|
|
* but we don't want to copy the entire strbuf
|
2020-07-28 20:45:38 +00:00
|
|
|
* only the chars up to the first NUL
|
2020-01-07 19:04:29 +00:00
|
|
|
*/
|
|
|
|
buf = query_result.buf;
|
|
|
|
strbuf_addstr(&last_update_token, buf);
|
|
|
|
if (!last_update_token.len) {
|
|
|
|
warning("Empty last update token.");
|
|
|
|
query_success = 0;
|
|
|
|
} else {
|
|
|
|
bol = last_update_token.len + 1;
|
|
|
|
}
|
|
|
|
} else if (hook_version < 0) {
|
|
|
|
hook_version = HOOK_INTERFACE_VERSION1;
|
|
|
|
if (!last_update_token.len)
|
|
|
|
strbuf_addf(&last_update_token, "%"PRIu64"", last_update);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hook_version == HOOK_INTERFACE_VERSION1) {
|
|
|
|
query_success = !query_fsmonitor(HOOK_INTERFACE_VERSION1,
|
|
|
|
istate->fsmonitor_last_update, &query_result);
|
|
|
|
}
|
|
|
|
|
2017-09-22 16:35:40 +00:00
|
|
|
trace_performance_since(last_update, "fsmonitor process '%s'", core_fsmonitor);
|
|
|
|
trace_printf_key(&trace_fsmonitor, "fsmonitor process '%s' returned %s",
|
|
|
|
core_fsmonitor, query_success ? "success" : "failure");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* a fsmonitor process can return '/' to indicate all entries are invalid */
|
2020-01-07 19:04:29 +00:00
|
|
|
if (query_success && query_result.buf[bol] != '/') {
|
2017-09-22 16:35:40 +00:00
|
|
|
/* Mark all entries returned by the monitor as dirty */
|
|
|
|
buf = query_result.buf;
|
2020-01-07 19:04:29 +00:00
|
|
|
for (i = bol; i < query_result.len; i++) {
|
2017-09-22 16:35:40 +00:00
|
|
|
if (buf[i] != '\0')
|
|
|
|
continue;
|
|
|
|
fsmonitor_refresh_callback(istate, buf + bol);
|
|
|
|
bol = i + 1;
|
|
|
|
}
|
|
|
|
if (bol < query_result.len)
|
|
|
|
fsmonitor_refresh_callback(istate, buf + bol);
|
2019-11-20 08:32:17 +00:00
|
|
|
|
|
|
|
/* Now mark the untracked cache for fsmonitor usage */
|
|
|
|
if (istate->untracked)
|
|
|
|
istate->untracked->use_fsmonitor = 1;
|
2017-09-22 16:35:40 +00:00
|
|
|
} else {
|
2019-11-20 08:32:17 +00:00
|
|
|
|
|
|
|
/* We only want to run the post index changed hook if we've actually changed entries, so keep track
|
|
|
|
* if we actually changed entries or not */
|
|
|
|
int is_cache_changed = 0;
|
2017-09-22 16:35:40 +00:00
|
|
|
/* Mark all entries invalid */
|
2019-11-20 08:32:17 +00:00
|
|
|
for (i = 0; i < istate->cache_nr; i++) {
|
|
|
|
if (istate->cache[i]->ce_flags & CE_FSMONITOR_VALID) {
|
|
|
|
is_cache_changed = 1;
|
|
|
|
istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
|
|
|
|
}
|
|
|
|
}
|
2017-09-22 16:35:40 +00:00
|
|
|
|
2018-04-10 18:14:31 +00:00
|
|
|
/* If we're going to check every file, ensure we save the results */
|
2019-11-20 08:32:17 +00:00
|
|
|
if (is_cache_changed)
|
|
|
|
istate->cache_changed |= FSMONITOR_CHANGED;
|
2018-04-10 18:14:31 +00:00
|
|
|
|
2017-09-22 16:35:40 +00:00
|
|
|
if (istate->untracked)
|
|
|
|
istate->untracked->use_fsmonitor = 0;
|
|
|
|
}
|
|
|
|
strbuf_release(&query_result);
|
|
|
|
|
2020-01-07 19:04:28 +00:00
|
|
|
/* Now that we've updated istate, save the last_update_token */
|
|
|
|
FREE_AND_NULL(istate->fsmonitor_last_update);
|
|
|
|
istate->fsmonitor_last_update = strbuf_detach(&last_update_token, NULL);
|
2017-09-22 16:35:40 +00:00
|
|
|
}
|
|
|
|
|
2021-02-03 15:34:50 +00:00
|
|
|
/*
|
|
|
|
* The caller wants to turn on FSMonitor. And when the caller writes
|
|
|
|
* the index to disk, a FSMonitor extension should be included. This
|
|
|
|
* requires that `istate->fsmonitor_last_update` not be NULL. But we
|
|
|
|
* have not actually talked to a FSMonitor process yet, so we don't
|
|
|
|
* have an initial value for this field.
|
|
|
|
*
|
|
|
|
* For a protocol V1 FSMonitor process, this field is a formatted
|
|
|
|
* "nanoseconds since epoch" field. However, for a protocol V2
|
|
|
|
* FSMonitor process, this field is an opaque token.
|
|
|
|
*
|
|
|
|
* Historically, `add_fsmonitor()` has initialized this field to the
|
|
|
|
* current time for protocol V1 processes. There are lots of race
|
|
|
|
* conditions here, but that code has shipped...
|
|
|
|
*
|
|
|
|
* The only true solution is to use a V2 FSMonitor and get a current
|
|
|
|
* or default token value (that it understands), but we cannot do that
|
|
|
|
* until we have actually talked to an instance of the FSMonitor process
|
|
|
|
* (but the protocol requires that we send a token first...).
|
|
|
|
*
|
|
|
|
* For simplicity, just initialize like we have a V1 process and require
|
|
|
|
* that V2 processes adapt.
|
|
|
|
*/
|
|
|
|
static void initialize_fsmonitor_last_update(struct index_state *istate)
|
|
|
|
{
|
|
|
|
struct strbuf last_update = STRBUF_INIT;
|
|
|
|
|
|
|
|
strbuf_addf(&last_update, "%"PRIu64"", getnanotime());
|
|
|
|
istate->fsmonitor_last_update = strbuf_detach(&last_update, NULL);
|
|
|
|
}
|
|
|
|
|
2017-09-22 16:35:40 +00:00
|
|
|
void add_fsmonitor(struct index_state *istate)
|
|
|
|
{
|
2019-06-15 16:11:35 +00:00
|
|
|
unsigned int i;
|
2017-09-22 16:35:40 +00:00
|
|
|
|
|
|
|
if (!istate->fsmonitor_last_update) {
|
|
|
|
trace_printf_key(&trace_fsmonitor, "add fsmonitor");
|
|
|
|
istate->cache_changed |= FSMONITOR_CHANGED;
|
2021-02-03 15:34:50 +00:00
|
|
|
initialize_fsmonitor_last_update(istate);
|
2017-09-22 16:35:40 +00:00
|
|
|
|
|
|
|
/* reset the fsmonitor state */
|
|
|
|
for (i = 0; i < istate->cache_nr; i++)
|
|
|
|
istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
|
|
|
|
|
|
|
|
/* reset the untracked cache */
|
|
|
|
if (istate->untracked) {
|
|
|
|
add_untracked_cache(istate);
|
|
|
|
istate->untracked->use_fsmonitor = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update the fsmonitor state */
|
|
|
|
refresh_fsmonitor(istate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove_fsmonitor(struct index_state *istate)
|
|
|
|
{
|
|
|
|
if (istate->fsmonitor_last_update) {
|
|
|
|
trace_printf_key(&trace_fsmonitor, "remove fsmonitor");
|
|
|
|
istate->cache_changed |= FSMONITOR_CHANGED;
|
2020-01-07 19:04:28 +00:00
|
|
|
FREE_AND_NULL(istate->fsmonitor_last_update);
|
2017-09-22 16:35:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void tweak_fsmonitor(struct index_state *istate)
|
|
|
|
{
|
2019-06-15 16:11:35 +00:00
|
|
|
unsigned int i;
|
2017-10-27 23:26:37 +00:00
|
|
|
int fsmonitor_enabled = git_config_get_fsmonitor();
|
|
|
|
|
|
|
|
if (istate->fsmonitor_dirty) {
|
|
|
|
if (fsmonitor_enabled) {
|
|
|
|
/* Mark all entries valid */
|
|
|
|
for (i = 0; i < istate->cache_nr; i++) {
|
|
|
|
istate->cache[i]->ce_flags |= CE_FSMONITOR_VALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mark all previously saved entries as dirty */
|
2021-01-23 19:58:14 +00:00
|
|
|
assert_index_minimum(istate, istate->fsmonitor_dirty->bit_size);
|
2017-10-27 23:26:37 +00:00
|
|
|
ewah_each_bit(istate->fsmonitor_dirty, fsmonitor_ewah_callback, istate);
|
|
|
|
|
2019-11-20 08:32:17 +00:00
|
|
|
refresh_fsmonitor(istate);
|
2017-10-27 23:26:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ewah_free(istate->fsmonitor_dirty);
|
|
|
|
istate->fsmonitor_dirty = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (fsmonitor_enabled) {
|
2017-09-22 16:35:40 +00:00
|
|
|
case -1: /* keep: do nothing */
|
|
|
|
break;
|
|
|
|
case 0: /* false */
|
|
|
|
remove_fsmonitor(istate);
|
|
|
|
break;
|
|
|
|
case 1: /* true */
|
|
|
|
add_fsmonitor(istate);
|
|
|
|
break;
|
|
|
|
default: /* unknown value: do nothing */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|