Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 1 | #include "cache.h" |
| 2 | #include "config.h" |
| 3 | #include "dir.h" |
| 4 | #include "ewah/ewok.h" |
| 5 | #include "fsmonitor.h" |
| 6 | #include "run-command.h" |
| 7 | #include "strbuf.h" |
| 8 | |
| 9 | #define INDEX_EXTENSION_VERSION (1) |
| 10 | #define HOOK_INTERFACE_VERSION (1) |
| 11 | |
| 12 | struct trace_key trace_fsmonitor = TRACE_KEY_INIT(FSMONITOR); |
| 13 | |
| 14 | static void fsmonitor_ewah_callback(size_t pos, void *is) |
| 15 | { |
| 16 | struct index_state *istate = (struct index_state *)is; |
| 17 | struct cache_entry *ce = istate->cache[pos]; |
| 18 | |
| 19 | ce->ce_flags &= ~CE_FSMONITOR_VALID; |
| 20 | } |
| 21 | |
| 22 | int read_fsmonitor_extension(struct index_state *istate, const void *data, |
| 23 | unsigned long sz) |
| 24 | { |
| 25 | const char *index = data; |
| 26 | uint32_t hdr_version; |
| 27 | uint32_t ewah_size; |
| 28 | struct ewah_bitmap *fsmonitor_dirty; |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 29 | int ret; |
| 30 | |
| 31 | if (sz < sizeof(uint32_t) + sizeof(uint64_t) + sizeof(uint32_t)) |
| 32 | return error("corrupt fsmonitor extension (too short)"); |
| 33 | |
| 34 | hdr_version = get_be32(index); |
| 35 | index += sizeof(uint32_t); |
| 36 | if (hdr_version != INDEX_EXTENSION_VERSION) |
| 37 | return error("bad fsmonitor version %d", hdr_version); |
| 38 | |
| 39 | istate->fsmonitor_last_update = get_be64(index); |
| 40 | index += sizeof(uint64_t); |
| 41 | |
| 42 | ewah_size = get_be32(index); |
| 43 | index += sizeof(uint32_t); |
| 44 | |
| 45 | fsmonitor_dirty = ewah_new(); |
| 46 | ret = ewah_read_mmap(fsmonitor_dirty, index, ewah_size); |
| 47 | if (ret != ewah_size) { |
| 48 | ewah_free(fsmonitor_dirty); |
| 49 | return error("failed to parse ewah bitmap reading fsmonitor index extension"); |
| 50 | } |
Alex Vandiver | ba1b9ca | 2017-10-27 16:26:37 -0700 | [diff] [blame] | 51 | istate->fsmonitor_dirty = fsmonitor_dirty; |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 52 | |
| 53 | trace_printf_key(&trace_fsmonitor, "read fsmonitor extension successful"); |
| 54 | return 0; |
| 55 | } |
| 56 | |
Alex Vandiver | 3bd28eb | 2017-11-09 11:58:10 -0800 | [diff] [blame] | 57 | void fill_fsmonitor_bitmap(struct index_state *istate) |
| 58 | { |
| 59 | int i; |
| 60 | istate->fsmonitor_dirty = ewah_new(); |
| 61 | for (i = 0; i < istate->cache_nr; i++) |
| 62 | if (!(istate->cache[i]->ce_flags & CE_FSMONITOR_VALID)) |
| 63 | ewah_set(istate->fsmonitor_dirty, i); |
| 64 | } |
| 65 | |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 66 | void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate) |
| 67 | { |
| 68 | uint32_t hdr_version; |
| 69 | uint64_t tm; |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 70 | uint32_t ewah_start; |
| 71 | uint32_t ewah_size = 0; |
| 72 | int fixup = 0; |
| 73 | |
| 74 | put_be32(&hdr_version, INDEX_EXTENSION_VERSION); |
| 75 | strbuf_add(sb, &hdr_version, sizeof(uint32_t)); |
| 76 | |
| 77 | put_be64(&tm, istate->fsmonitor_last_update); |
| 78 | strbuf_add(sb, &tm, sizeof(uint64_t)); |
| 79 | fixup = sb->len; |
| 80 | strbuf_add(sb, &ewah_size, sizeof(uint32_t)); /* we'll fix this up later */ |
| 81 | |
| 82 | ewah_start = sb->len; |
Alex Vandiver | 3bd28eb | 2017-11-09 11:58:10 -0800 | [diff] [blame] | 83 | ewah_serialize_strbuf(istate->fsmonitor_dirty, sb); |
| 84 | ewah_free(istate->fsmonitor_dirty); |
| 85 | istate->fsmonitor_dirty = NULL; |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 86 | |
| 87 | /* fix up size field */ |
| 88 | put_be32(&ewah_size, sb->len - ewah_start); |
| 89 | memcpy(sb->buf + fixup, &ewah_size, sizeof(uint32_t)); |
| 90 | |
| 91 | trace_printf_key(&trace_fsmonitor, "write fsmonitor extension successful"); |
| 92 | } |
| 93 | |
| 94 | /* |
| 95 | * Call the query-fsmonitor hook passing the time of the last saved results. |
| 96 | */ |
| 97 | static int query_fsmonitor(int version, uint64_t last_update, struct strbuf *query_result) |
| 98 | { |
| 99 | struct child_process cp = CHILD_PROCESS_INIT; |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 100 | |
René Scharfe | 735e417 | 2018-05-19 10:27:46 +0200 | [diff] [blame] | 101 | if (!core_fsmonitor) |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 102 | return -1; |
| 103 | |
René Scharfe | 735e417 | 2018-05-19 10:27:46 +0200 | [diff] [blame] | 104 | argv_array_push(&cp.args, core_fsmonitor); |
| 105 | argv_array_pushf(&cp.args, "%d", version); |
| 106 | argv_array_pushf(&cp.args, "%" PRIuMAX, (uintmax_t)last_update); |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 107 | cp.use_shell = 1; |
Alex Vandiver | 11cf33b | 2017-10-27 16:26:34 -0700 | [diff] [blame] | 108 | cp.dir = get_git_work_tree(); |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 109 | |
| 110 | return capture_command(&cp, query_result, 1024); |
| 111 | } |
| 112 | |
| 113 | static void fsmonitor_refresh_callback(struct index_state *istate, const char *name) |
| 114 | { |
| 115 | int pos = index_name_pos(istate, name, strlen(name)); |
| 116 | |
| 117 | if (pos >= 0) { |
| 118 | struct cache_entry *ce = istate->cache[pos]; |
| 119 | ce->ce_flags &= ~CE_FSMONITOR_VALID; |
| 120 | } |
| 121 | |
| 122 | /* |
| 123 | * Mark the untracked cache dirty even if it wasn't found in the index |
| 124 | * as it could be a new untracked file. |
| 125 | */ |
| 126 | trace_printf_key(&trace_fsmonitor, "fsmonitor_refresh_callback '%s'", name); |
Nguyễn Thái Ngọc Duy | 0cacebf | 2018-02-07 16:21:40 +0700 | [diff] [blame] | 127 | untracked_cache_invalidate_path(istate, name, 0); |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 128 | } |
| 129 | |
| 130 | void refresh_fsmonitor(struct index_state *istate) |
| 131 | { |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 132 | struct strbuf query_result = STRBUF_INIT; |
| 133 | int query_success = 0; |
| 134 | size_t bol; /* beginning of line */ |
| 135 | uint64_t last_update; |
| 136 | char *buf; |
| 137 | int i; |
| 138 | |
Johannes Schindelin | 398a3b0 | 2019-05-07 13:10:21 +0200 | [diff] [blame] | 139 | if (!core_fsmonitor || istate->fsmonitor_has_run_once) |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 140 | return; |
Johannes Schindelin | 398a3b0 | 2019-05-07 13:10:21 +0200 | [diff] [blame] | 141 | istate->fsmonitor_has_run_once = 1; |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 142 | |
| 143 | trace_printf_key(&trace_fsmonitor, "refresh fsmonitor"); |
| 144 | /* |
| 145 | * This could be racy so save the date/time now and query_fsmonitor |
| 146 | * should be inclusive to ensure we don't miss potential changes. |
| 147 | */ |
| 148 | last_update = getnanotime(); |
| 149 | |
| 150 | /* |
| 151 | * If we have a last update time, call query_fsmonitor for the set of |
| 152 | * changes since that time, else assume everything is possibly dirty |
| 153 | * and check it all. |
| 154 | */ |
| 155 | if (istate->fsmonitor_last_update) { |
| 156 | query_success = !query_fsmonitor(HOOK_INTERFACE_VERSION, |
| 157 | istate->fsmonitor_last_update, &query_result); |
| 158 | trace_performance_since(last_update, "fsmonitor process '%s'", core_fsmonitor); |
| 159 | trace_printf_key(&trace_fsmonitor, "fsmonitor process '%s' returned %s", |
| 160 | core_fsmonitor, query_success ? "success" : "failure"); |
| 161 | } |
| 162 | |
| 163 | /* a fsmonitor process can return '/' to indicate all entries are invalid */ |
| 164 | if (query_success && query_result.buf[0] != '/') { |
| 165 | /* Mark all entries returned by the monitor as dirty */ |
| 166 | buf = query_result.buf; |
| 167 | bol = 0; |
| 168 | for (i = 0; i < query_result.len; i++) { |
| 169 | if (buf[i] != '\0') |
| 170 | continue; |
| 171 | fsmonitor_refresh_callback(istate, buf + bol); |
| 172 | bol = i + 1; |
| 173 | } |
| 174 | if (bol < query_result.len) |
| 175 | fsmonitor_refresh_callback(istate, buf + bol); |
| 176 | } else { |
| 177 | /* Mark all entries invalid */ |
| 178 | for (i = 0; i < istate->cache_nr; i++) |
| 179 | istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID; |
| 180 | |
Ben Peart | ca598d5 | 2018-04-10 14:14:31 -0400 | [diff] [blame] | 181 | /* If we're going to check every file, ensure we save the results */ |
| 182 | istate->cache_changed |= FSMONITOR_CHANGED; |
| 183 | |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 184 | if (istate->untracked) |
| 185 | istate->untracked->use_fsmonitor = 0; |
| 186 | } |
| 187 | strbuf_release(&query_result); |
| 188 | |
| 189 | /* Now that we've updated istate, save the last_update time */ |
| 190 | istate->fsmonitor_last_update = last_update; |
| 191 | } |
| 192 | |
| 193 | void add_fsmonitor(struct index_state *istate) |
| 194 | { |
| 195 | int i; |
| 196 | |
| 197 | if (!istate->fsmonitor_last_update) { |
| 198 | trace_printf_key(&trace_fsmonitor, "add fsmonitor"); |
| 199 | istate->cache_changed |= FSMONITOR_CHANGED; |
| 200 | istate->fsmonitor_last_update = getnanotime(); |
| 201 | |
| 202 | /* reset the fsmonitor state */ |
| 203 | for (i = 0; i < istate->cache_nr; i++) |
| 204 | istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID; |
| 205 | |
| 206 | /* reset the untracked cache */ |
| 207 | if (istate->untracked) { |
| 208 | add_untracked_cache(istate); |
| 209 | istate->untracked->use_fsmonitor = 1; |
| 210 | } |
| 211 | |
| 212 | /* Update the fsmonitor state */ |
| 213 | refresh_fsmonitor(istate); |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | void remove_fsmonitor(struct index_state *istate) |
| 218 | { |
| 219 | if (istate->fsmonitor_last_update) { |
| 220 | trace_printf_key(&trace_fsmonitor, "remove fsmonitor"); |
| 221 | istate->cache_changed |= FSMONITOR_CHANGED; |
| 222 | istate->fsmonitor_last_update = 0; |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | void tweak_fsmonitor(struct index_state *istate) |
| 227 | { |
Alex Vandiver | ba1b9ca | 2017-10-27 16:26:37 -0700 | [diff] [blame] | 228 | int i; |
| 229 | int fsmonitor_enabled = git_config_get_fsmonitor(); |
| 230 | |
| 231 | if (istate->fsmonitor_dirty) { |
| 232 | if (fsmonitor_enabled) { |
| 233 | /* Mark all entries valid */ |
| 234 | for (i = 0; i < istate->cache_nr; i++) { |
| 235 | istate->cache[i]->ce_flags |= CE_FSMONITOR_VALID; |
| 236 | } |
| 237 | |
| 238 | /* Mark all previously saved entries as dirty */ |
| 239 | ewah_each_bit(istate->fsmonitor_dirty, fsmonitor_ewah_callback, istate); |
| 240 | |
| 241 | /* Now mark the untracked cache for fsmonitor usage */ |
| 242 | if (istate->untracked) |
| 243 | istate->untracked->use_fsmonitor = 1; |
| 244 | } |
| 245 | |
| 246 | ewah_free(istate->fsmonitor_dirty); |
| 247 | istate->fsmonitor_dirty = NULL; |
| 248 | } |
| 249 | |
| 250 | switch (fsmonitor_enabled) { |
Ben Peart | 883e248 | 2017-09-22 12:35:40 -0400 | [diff] [blame] | 251 | case -1: /* keep: do nothing */ |
| 252 | break; |
| 253 | case 0: /* false */ |
| 254 | remove_fsmonitor(istate); |
| 255 | break; |
| 256 | case 1: /* true */ |
| 257 | add_fsmonitor(istate); |
| 258 | break; |
| 259 | default: /* unknown value: do nothing */ |
| 260 | break; |
| 261 | } |
| 262 | } |