/* * Enables or disables the cache. Note that the cache is read-only, changes to * the working directory are NOT reflected in the cache while enabled. */ int fscache_enable(int enable) { int result; if (!initialized) { /* allow the cache to be disabled entirely */ if (!core_fscache) return 0; InitializeCriticalSection(&mutex); hashmap_init(&map, (hashmap_cmp_fn) fsentry_cmp, NULL, 0); initialized = 1; } result = enable ? InterlockedIncrement(&enabled) : InterlockedDecrement(&enabled); if (enable && result == 1) { /* redirect opendir and lstat to the fscache implementations */ opendir = fscache_opendir; lstat = fscache_lstat; } else if (!enable && !result) { /* reset opendir and lstat to the original implementations */ opendir = dirent_opendir; lstat = mingw_lstat; EnterCriticalSection(&mutex); fscache_clear(); LeaveCriticalSection(&mutex); } trace_printf_key(&trace_fscache, "fscache: enable(%d)\n", enable); return result; }
void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate) { uint32_t hdr_version; uint64_t tm; uint32_t ewah_start; uint32_t ewah_size = 0; int fixup = 0; put_be32(&hdr_version, INDEX_EXTENSION_VERSION); strbuf_add(sb, &hdr_version, sizeof(uint32_t)); put_be64(&tm, istate->fsmonitor_last_update); strbuf_add(sb, &tm, sizeof(uint64_t)); fixup = sb->len; strbuf_add(sb, &ewah_size, sizeof(uint32_t)); /* we'll fix this up later */ ewah_start = sb->len; ewah_serialize_strbuf(istate->fsmonitor_dirty, sb); ewah_free(istate->fsmonitor_dirty); istate->fsmonitor_dirty = NULL; /* fix up size field */ put_be32(&ewah_size, sb->len - ewah_start); memcpy(sb->buf + fixup, &ewah_size, sizeof(uint32_t)); trace_printf_key(&trace_fsmonitor, "write fsmonitor extension successful"); }
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; if (sz < sizeof(uint32_t) + sizeof(uint64_t) + sizeof(uint32_t)) return error("corrupt fsmonitor extension (too short)"); hdr_version = get_be32(index); index += sizeof(uint32_t); if (hdr_version != INDEX_EXTENSION_VERSION) return error("bad fsmonitor version %d", hdr_version); istate->fsmonitor_last_update = get_be64(index); index += sizeof(uint64_t); 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"); } istate->fsmonitor_dirty = fsmonitor_dirty; trace_printf_key(&trace_fsmonitor, "read fsmonitor extension successful"); return 0; }
void remove_fsmonitor(struct index_state *istate) { if (istate->fsmonitor_last_update) { trace_printf_key(&trace_fsmonitor, "remove fsmonitor"); istate->cache_changed |= FSMONITOR_CHANGED; istate->fsmonitor_last_update = 0; } }
/* * Enables the cache. Note that the cache is read-only, changes to * the working directory are NOT reflected in the cache while enabled. */ int fscache_enable(size_t initial_size) { int fscache; struct fscache *cache; int result = 0; /* allow the cache to be disabled entirely */ fscache = git_env_bool("GIT_TEST_FSCACHE", -1); if (fscache != -1) core_fscache = fscache; if (!core_fscache) return 0; /* * refcount the global fscache initialization so that the * opendir and lstat function pointers are redirected if * any threads are using the fscache. */ EnterCriticalSection(&fscache_cs); if (!initialized) { if (!dwTlsIndex) { dwTlsIndex = TlsAlloc(); if (dwTlsIndex == TLS_OUT_OF_INDEXES) return 0; } /* redirect opendir and lstat to the fscache implementations */ opendir = fscache_opendir; lstat = fscache_lstat; } initialized++; LeaveCriticalSection(&fscache_cs); /* refcount the thread specific initialization */ cache = fscache_getcache(); if (cache) { cache->enabled++; } else { cache = (struct fscache *)xcalloc(1, sizeof(*cache)); cache->enabled = 1; /* * avoid having to rehash by leaving room for the parent dirs. * '4' was determined empirically by testing several repos */ hashmap_init(&cache->map, (hashmap_cmp_fn)fsentry_cmp, NULL, initial_size * 4); mem_pool_init(&cache->mem_pool, 0); if (!TlsSetValue(dwTlsIndex, cache)) BUG("TlsSetValue error"); } trace_printf_key(&trace_fscache, "fscache: enable\n"); return result; }
/* * Disables the cache. */ void fscache_disable(void) { struct fscache *cache; if (!core_fscache) return; /* update the thread specific fscache initialization */ cache = fscache_getcache(); if (!cache) BUG("fscache_disable() called on a thread where fscache has not been initialized"); if (!cache->enabled) BUG("fscache_disable() called on an fscache that is already disabled"); cache->enabled--; if (!cache->enabled) { TlsSetValue(dwTlsIndex, NULL); trace_printf_key(&trace_fscache, "fscache_disable: lstat %u, opendir %u, " "total requests/misses %u/%u\n", cache->lstat_requests, cache->opendir_requests, cache->fscache_requests, cache->fscache_misses); mem_pool_discard(cache->mem_pool, 0); hashmap_free(&cache->map, 0); free(cache); } /* update the global fscache initialization */ EnterCriticalSection(&fscache_cs); initialized--; if (!initialized) { /* reset opendir and lstat to the original implementations */ opendir = dirent_opendir; lstat = mingw_lstat; } LeaveCriticalSection(&fscache_cs); trace_printf_key(&trace_fscache, "fscache: disable\n"); return; }
/* FIXME: move prefix to startup_info struct and get rid of this arg */ void trace_repo_setup(const char *prefix) { static const char *key = "GIT_TRACE_SETUP"; const char *git_work_tree; char cwd[PATH_MAX]; if (!trace_want(key)) return; if (!getcwd(cwd, PATH_MAX)) die("Unable to get current working directory"); if (!(git_work_tree = get_git_work_tree())) git_work_tree = "(null)"; if (!prefix) prefix = "(null)"; trace_printf_key(key, "setup: git_dir: %s\n", quote_crnl(get_git_dir())); trace_printf_key(key, "setup: worktree: %s\n", quote_crnl(git_work_tree)); trace_printf_key(key, "setup: cwd: %s\n", quote_crnl(cwd)); trace_printf_key(key, "setup: prefix: %s\n", quote_crnl(prefix)); }
static void fsmonitor_refresh_callback(struct index_state *istate, const char *name) { int pos = index_name_pos(istate, name, strlen(name)); if (pos >= 0) { struct cache_entry *ce = istate->cache[pos]; ce->ce_flags &= ~CE_FSMONITOR_VALID; } /* * 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); untracked_cache_invalidate_path(istate, name, 0); }
void fscache_merge(struct fscache *dest) { struct hashmap_iter iter; struct hashmap_entry *e; struct fscache *cache = fscache_getcache(); /* * Only do the merge if fscache was enabled and we have a dest * cache to merge into. */ if (!dest) { fscache_enable(0); return; } if (!cache) BUG("fscache_merge() called on a thread where fscache has not been initialized"); TlsSetValue(dwTlsIndex, NULL); trace_printf_key(&trace_fscache, "fscache_merge: lstat %u, opendir %u, " "total requests/misses %u/%u\n", cache->lstat_requests, cache->opendir_requests, cache->fscache_requests, cache->fscache_misses); /* * This is only safe because the primary thread we're merging into * isn't being used so the critical section only needs to prevent * the the child threads from stomping on each other. */ EnterCriticalSection(&fscache_cs); hashmap_iter_init(&cache->map, &iter); while ((e = hashmap_iter_next(&iter))) hashmap_add(&dest->map, e); mem_pool_combine(dest->mem_pool, cache->mem_pool); dest->lstat_requests += cache->lstat_requests; dest->opendir_requests += cache->opendir_requests; dest->fscache_requests += cache->fscache_requests; dest->fscache_misses += cache->fscache_misses; initialized--; LeaveCriticalSection(&fscache_cs); free(cache); }
void add_fsmonitor(struct index_state *istate) { int i; if (!istate->fsmonitor_last_update) { trace_printf_key(&trace_fsmonitor, "add fsmonitor"); istate->cache_changed |= FSMONITOR_CHANGED; istate->fsmonitor_last_update = getnanotime(); /* 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); } }
static int update_shallow_ref(struct command *cmd, struct shallow_info *si) { static struct lock_file shallow_lock; struct sha1_array extra = SHA1_ARRAY_INIT; const char *alt_file; uint32_t mask = 1 << (cmd->index % 32); int i; trace_printf_key(&trace_shallow, "shallow: update_shallow_ref %s\n", cmd->ref_name); for (i = 0; i < si->shallow->nr; i++) if (si->used_shallow[i] && (si->used_shallow[i][cmd->index / 32] & mask) && !delayed_reachability_test(si, i)) sha1_array_append(&extra, si->shallow->sha1[i]); setup_alternate_shallow(&shallow_lock, &alt_file, &extra); if (check_shallow_connected(command_singleton_iterator, 0, cmd, alt_file)) { rollback_lock_file(&shallow_lock); sha1_array_clear(&extra); return -1; } commit_lock_file(&shallow_lock); /* * Make sure setup_alternate_shallow() for the next ref does * not lose these new roots.. */ for (i = 0; i < extra.nr; i++) register_shallow(extra.sha1[i]); si->shallow_ref[cmd->index] = 0; sha1_array_clear(&extra); return 0; }
void refresh_fsmonitor(struct index_state *istate) { static int has_run_once = 0; struct strbuf query_result = STRBUF_INIT; int query_success = 0; size_t bol; /* beginning of line */ uint64_t last_update; char *buf; int i; if (!core_fsmonitor || has_run_once) return; has_run_once = 1; 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(); /* * If we have a last update time, call query_fsmonitor for the set of * changes since that time, else assume everything is possibly dirty * and check it all. */ if (istate->fsmonitor_last_update) { query_success = !query_fsmonitor(HOOK_INTERFACE_VERSION, istate->fsmonitor_last_update, &query_result); 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 */ if (query_success && query_result.buf[0] != '/') { /* Mark all entries returned by the monitor as dirty */ buf = query_result.buf; bol = 0; for (i = 0; i < query_result.len; i++) { 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); } else { /* Mark all entries invalid */ for (i = 0; i < istate->cache_nr; i++) istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID; /* If we're going to check every file, ensure we save the results */ istate->cache_changed |= FSMONITOR_CHANGED; if (istate->untracked) istate->untracked->use_fsmonitor = 0; } strbuf_release(&query_result); /* Now that we've updated istate, save the last_update time */ istate->fsmonitor_last_update = last_update; }
/* * Print changed/unmerged items. * We write raw (not c-quoted) pathname(s). The rename_source is only * set when status computed a rename/copy. * * We ALWAYS write a final LF to the packet-line (for debugging) * even though Linux pathnames allow LFs. */ static inline void wt_serialize_v1_changed(struct wt_status *s, int fd, struct string_list_item *item) { struct wt_status_change_data *d = item->util; struct wt_status_serialize_data sd; char *begin; char *end; char *p; int len_path, len_rename_source; trace_printf_key(&trace_serialize, "change: %d %d %d %d %o %o %o %d %d %s %s '%s' '%s'", d->worktree_status, d->index_status, d->stagemask, d->rename_score, d->mode_head, d->mode_index, d->mode_worktree, d->dirty_submodule, d->new_submodule_commits, oid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index), item->string, (d->rename_source ? d->rename_source : "")); sd.fixed.worktree_status = htonl(d->worktree_status); sd.fixed.index_status = htonl(d->index_status); sd.fixed.stagemask = htonl(d->stagemask); sd.fixed.rename_score = htonl(d->rename_score); sd.fixed.mode_head = htonl(d->mode_head); sd.fixed.mode_index = htonl(d->mode_index); sd.fixed.mode_worktree = htonl(d->mode_worktree); sd.fixed.dirty_submodule = htonl(d->dirty_submodule); sd.fixed.new_submodule_commits = htonl(d->new_submodule_commits); oidcpy(&sd.fixed.oid_head, &d->oid_head); oidcpy(&sd.fixed.oid_index, &d->oid_index); begin = (char *)&sd; end = begin + sizeof(sd); p = sd.variant; /* * Write <path> NUL [<rename_source>] NUL LF at the end of the buffer. */ len_path = strlen(item->string); len_rename_source = d->rename_source ? strlen(d->rename_source) : 0; /* * This is a bit of a hack, but I don't want to split the * status detail record across multiple pkt-lines. */ if (p + len_path + 1 + len_rename_source + 1 + 1 >= end) BUG("path to long to serialize '%s'", item->string); memcpy(p, item->string, len_path); p += len_path; *p++ = '\0'; if (len_rename_source) { memcpy(p, d->rename_source, len_rename_source); p += len_rename_source; } *p++ = '\0'; *p++ = '\n'; if (packet_write_gently(fd, begin, (p - begin))) BUG("cannot serialize '%s'", item->string); }
/* * Create an fsentry-based directory listing (similar to opendir / readdir). * Dir should not contain trailing '/'. Use an empty string for the current * directory (not "."!). */ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct fsentry *dir, int *dir_not_found) { wchar_t pattern[MAX_LONG_PATH]; NTSTATUS status; IO_STATUS_BLOCK iosb; PFILE_FULL_DIR_INFORMATION di; HANDLE h; int wlen; struct fsentry *list, **phead; DWORD err; *dir_not_found = 0; /* convert name to UTF-16 and check length */ if ((wlen = xutftowcs_path_ex(pattern, dir->name, MAX_LONG_PATH, dir->len, MAX_PATH - 2, core_long_paths)) < 0) return NULL; /* handle CWD */ if (!wlen) { wlen = GetCurrentDirectoryW(ARRAY_SIZE(pattern), pattern); if (!wlen || wlen >= ARRAY_SIZE(pattern)) { errno = wlen ? ENAMETOOLONG : err_win_to_posix(GetLastError()); return NULL; } } h = CreateFileW(pattern, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { err = GetLastError(); *dir_not_found = 1; /* or empty directory */ errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err); trace_printf_key(&trace_fscache, "fscache: error(%d) '%.*s'\n", errno, dir->len, dir->name); return NULL; } /* allocate object to hold directory listing */ list = fsentry_alloc(cache, NULL, dir->name, dir->len); list->st_mode = S_IFDIR; /* walk directory and build linked list of fsentry structures */ phead = &list->next; status = NtQueryDirectoryFile(h, NULL, 0, 0, &iosb, cache->buffer, sizeof(cache->buffer), FileFullDirectoryInformation, FALSE, NULL, FALSE); if (!NT_SUCCESS(status)) { /* * NtQueryDirectoryFile returns STATUS_INVALID_PARAMETER when * asked to enumerate an invalid directory (ie it is a file * instead of a directory). Verify that is the actual cause * of the error. */ if (status == STATUS_INVALID_PARAMETER) { DWORD attributes = GetFileAttributesW(pattern); if (!(attributes & FILE_ATTRIBUTE_DIRECTORY)) status = ERROR_DIRECTORY; } goto Error; } di = (PFILE_FULL_DIR_INFORMATION)(cache->buffer); for (;;) { *phead = fseentry_create_entry(cache, list, di); phead = &(*phead)->next; /* If there is no offset in the entry, the buffer has been exhausted. */ if (di->NextEntryOffset == 0) { status = NtQueryDirectoryFile(h, NULL, 0, 0, &iosb, cache->buffer, sizeof(cache->buffer), FileFullDirectoryInformation, FALSE, NULL, FALSE); if (!NT_SUCCESS(status)) { if (status == STATUS_NO_MORE_FILES) break; goto Error; } di = (PFILE_FULL_DIR_INFORMATION)(cache->buffer); continue; } /* Advance to the next entry. */ di = (PFILE_FULL_DIR_INFORMATION)(((PUCHAR)di) + di->NextEntryOffset); } CloseHandle(h); return list; Error: errno = (status == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(status); trace_printf_key(&trace_fscache, "fscache: error(%d) unable to query directory contents '%.*s'\n", errno, dir->len, dir->name); CloseHandle(h); fsentry_release(list); return NULL; }
/* * Create an fsentry-based directory listing (similar to opendir / readdir). * Dir should not contain trailing '/'. Use an empty string for the current * directory (not "."!). */ static struct fsentry *fsentry_create_list(const struct fsentry *dir, int *dir_not_found) { wchar_t pattern[MAX_LONG_PATH + 2]; /* + 2 for "\*" */ WIN32_FIND_DATAW fdata; HANDLE h; int wlen; struct fsentry *list, **phead; DWORD err; *dir_not_found = 0; /* convert name to UTF-16 and check length */ if ((wlen = xutftowcs_path_ex(pattern, dir->name, MAX_LONG_PATH, dir->len, MAX_PATH - 2, core_long_paths)) < 0) return NULL; /* * append optional '\' and wildcard '*'. Note: we need to use '\' as * Windows doesn't translate '/' to '\' for "\\?\"-prefixed paths. */ if (wlen) pattern[wlen++] = '\\'; pattern[wlen++] = '*'; pattern[wlen] = 0; /* open find handle */ h = FindFirstFileW(pattern, &fdata); if (h == INVALID_HANDLE_VALUE) { err = GetLastError(); *dir_not_found = 1; /* or empty directory */ errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err); trace_printf_key(&trace_fscache, "fscache: error(%d) '%.*s'\n", errno, dir->len, dir->name); return NULL; } /* allocate object to hold directory listing */ list = fsentry_alloc(NULL, dir->name, dir->len); list->st_mode = S_IFDIR; /* walk directory and build linked list of fsentry structures */ phead = &list->next; do { *phead = fseentry_create_entry(list, &fdata); phead = &(*phead)->next; } while (FindNextFileW(h, &fdata)); /* remember result of last FindNextFile, then close find handle */ err = GetLastError(); FindClose(h); /* return the list if we've got all the files */ if (err == ERROR_NO_MORE_FILES) return list; /* otherwise free the list and return error */ fsentry_release(list); errno = err_win_to_posix(err); return NULL; }